diff --git a/security_token.xslt b/security_token.xslt
new file mode 100644
index 0000000..bd293e3
--- /dev/null
+++ b/security_token.xslt
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/uar.pl b/uar.pl
new file mode 100755
index 0000000..2d2e8e2
--- /dev/null
+++ b/uar.pl
@@ -0,0 +1,318 @@
+#!/usr/bin/perl -w
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This code is not supported by Google
+#
+# uar.pl - Command line tool to manage GSA User Added Results (UAR)
+#
+# Configuration Notes:
+# - REQUEST urls may need to be customized, as well as the ULF flow
+# - You will need to create the security_token frontend
+# (See security_token.xslt)
+
+use strict;
+use Getopt::Long;
+use HTTP::Cookies;
+use LWP;
+use Pod::Usage;
+use URI::Encode qw(uri_encode);
+use XML::XPath;
+
+my $GSA_HOST = ""; # If you want to hardcode the gsa hostname
+my $GSA_FRONTEND = "default_frontend";
+my $HTTP = "https://";
+my $TOKEN_REQUEST = "/search?q=test&access=a&proxystylesheet=security_token";
+my $SEARCH_REQUEST = "/search?access=a&num=1";
+my $ULF_REQUEST = "/security-manager/samlauthn";
+my $UAR_REQUEST = "/uar";
+my $USER = "ericl";
+my $PASSWORD = "test";
+my $security_token = "";
+
+my $help = 0;
+my $verbose = 0;
+my $action = "";
+my $query = "";
+my $uar_name = "";
+my $uar_title = "";
+my $uar_url = "";
+my $uar_rid;
+
+GetOptions ("action=s" => \$action,
+ "query=s" => \$query,
+ "name=s" => \$uar_name,
+ "title=s" => \$uar_title,
+ "url=s" => \$uar_url,
+ "rid=i" => \$uar_rid,
+ "host=s" => \$GSA_HOST,
+ "frontend=s" => \$GSA_FRONTEND,
+ "verbose|v" => \$verbose,
+ "help|?" => \$help)
+ or usage("Error in command line arguments");
+
+# Check command line args
+if ($help) {
+ usage("");
+}
+
+if ($GSA_HOST eq "") {
+ usage("gsa hostname or IP must be specified with --host");
+}
+
+if ($action eq "") {
+ usage("action must be specified");
+}
+
+if ($uar_name ne "") {
+ if ($uar_name !~ /^[a-z][a-z_0-9-]{0,31}$/i) {
+ usage("invalid UAR name specified");
+ }
+}
+
+if ($query ne "") {
+ if ($query =~ /([\&\?])/) {
+ usage("Invalid character in query: '$1'");
+ }
+}
+
+if ($action eq "add") {
+ if ($query eq "" || $uar_name eq "" || $uar_title eq "" or $uar_url eq "") {
+ usage("query, name, tile and url must be specified");
+ }
+}
+
+if ($action eq "edit") {
+ if ($uar_name eq "" || $uar_title eq "" || $uar_url eq "") {
+ usage("name, title and url must be specified");
+ }
+}
+
+if ($action eq "delete") {
+ if (($query eq "" && $uar_rid eq "") || $uar_name eq "") {
+ usage("query|rid & name must be specified");
+ }
+}
+
+# Setup "browser" client
+my $browser = LWP::UserAgent->new(
+ ssl_opts => { SSL_verify_mode => 'SSL_VERIFY_NONE'},
+ requests_redirectable => [ 'GET', 'HEAD', 'POST' ]
+ );
+$browser->ssl_opts(verify_hostname => 0);
+$browser->cookie_jar(HTTP::Cookies->new(file => "$ENV{HOME}/.cookies.txt"));
+if ($verbose) {
+ $browser->add_handler("request_send", \&dump_ua);
+ $browser->add_handler("response_done", \&dump_ua);
+}
+
+# Login to GSA, get a security token
+# May need to modify depending on your authN config
+my $response = $browser->get($HTTP . $GSA_HOST . $TOKEN_REQUEST);
+$response = submit_ulf();
+$security_token = $response->content;
+
+if ($action eq "list") {
+ list_all_uar($query,$uar_name);
+} elsif ($action eq "add") {
+ add_uar($query,$uar_name,$uar_title,$uar_url);
+} elsif ($action eq "delete" && $uar_rid ne "") {
+ delete_single_uar($uar_rid, $uar_name);
+} elsif ($action eq "delete" && $query ne "") {
+ delete_all_uar($query,$uar_name);
+} elsif ($action eq "edit") {
+ edit_uar($query,$uar_name,$uar_rid,$uar_title,$uar_url);
+} else {
+ usage("action of list|add|delete|edit must be specified");
+}
+
+exit();
+
+# Return XML list of UAR entries for a specific query
+sub get_uar {
+ my $query = shift;
+
+ my $response = $browser->get($HTTP . $GSA_HOST . $SEARCH_REQUEST .
+ "&client=" . $GSA_FRONTEND . "&q=" . $query);
+
+ if (!$response->is_success || $response->code != 200) {
+ die("Error getting response from GSA - check request parameters");
+ }
+
+ my $body = $response->content();
+ my $p = XML::Parser->new(NoLWP => 1);
+ my $xp = XML::XPath->new(parser => $p, xml => $body);
+ my $ob = $xp->find('/GSP/ENTOBRESULTS/OBRES/MODULE_RESULT');
+
+ return $ob;
+}
+
+# List all UAR's for a specific query
+sub list_all_uar {
+ my $query = shift;
+
+ my $uar_list = get_uar($query);
+
+ foreach my $node ($uar_list->get_nodelist) {
+ my $title = $node->find('Field[@name="title"]')->string_value;
+ my $url = $node->find('Field[@name="url"]')->string_value;
+ my $rid = $node->find('Field[@name="rid"]')->string_value;
+ my $addedBy = $node->find('Field[@name="addedby"]')->string_value;
+
+ if ($rid ne "new") {
+ print "$rid\n$title\n$url\n\n";
+ }
+ }
+ return;
+}
+
+# Delete all UAR's for a specific query
+sub delete_all_uar {
+ my $query = shift;
+ my $uar_name = shift;
+
+ my $uar_list = get_uar($query);
+
+ foreach my $node ($uar_list->get_nodelist) {
+ my $rid = $node->find('Field[@name="rid"]')->string_value;
+
+ if ($rid ne "new") {
+ delete_single_uar($rid, $uar_name);
+ }
+ }
+ return;
+}
+
+# Delete a single UAR based on rid
+sub delete_single_uar {
+ my $rid = shift;
+ my $uar_name = shift;
+
+ my $response =
+ $browser->post($HTTP . $GSA_HOST . $UAR_REQUEST,
+ {
+ 'action' => "DELETE",
+ 'rid' => $rid,
+ 'oneboxName' => $uar_name,
+ 'token' => $security_token
+ });
+ print $response->code. " - ". $response->content . "\n";
+ return;
+}
+
+# Add a new UAR
+sub add_uar {
+ my $query = shift;
+ my $uar_name = shift;
+ my $uar_title = shift;
+ my $uar_url = shift;
+
+ # Submit UAR Add
+ $response =
+ $browser->post($HTTP . $GSA_HOST . $UAR_REQUEST,
+ {'action' => "ADD",
+ 'url' => uri_encode($uar_url),
+ 'oneboxName' => $uar_name,
+ 'title' => $uar_title,
+ 'query' => $query,
+ 'token' => $security_token
+ });
+
+ print $response->code. " - ". $response->content . "\n";
+}
+
+# Edit existing UAR based on rid
+sub edit_uar {
+ my $query = shift;
+ my $uar_name = shift;
+ my $rid = shift;
+ my $uar_title = shift;
+ my $uar_url = shift;
+
+ # Submit UAR Edit
+ my $response =
+ $browser->post($HTTP . $GSA_HOST . $UAR_REQUEST,
+ {
+ 'action' => "EDIT",
+ 'rid' => $rid,
+ 'url' => uri_encode($uar_url),
+ 'oneboxName' => $uar_name,
+ 'title' => $uar_title,
+ 'query' => $query,
+ 'token' => $security_token
+ });
+
+ print $response->code . " - " . $response->content . "\n";
+ return;
+}
+
+# Submit the ULF
+sub submit_ulf {
+ my $response =
+ $browser->post($HTTP . $GSA_HOST . $ULF_REQUEST,
+ {
+ 'uDefault' => $USER,
+ 'pwDefault' => $PASSWORD
+ });
+
+ if (!$response->is_success || $response->code != 200) {
+ die "Error submitting credentials: " . $response->message;
+ }
+
+ return($response);
+}
+
+# print HTTP request/response when --verbose
+sub dump_ua {
+ my $ua = shift;
+ $ua->dump;
+
+ return;
+}
+
+# usage info
+sub usage {
+ my $message = shift;
+
+ my $command = $0;
+ $command =~ s/^.*[\/\\]//; # Remove path
+
+ print STDERR (
+ "Usage: $command --action --query \n" .
+ " --name \n" .
+ " --title --url --rid \n" .
+ " --host --frontend \n" .
+ " --verbose|-v --help|-?\n\n"
+ );
+
+ print STDERR (
+ "Examples\n" .
+ " List all UARs for a specific query:\n" .
+ " $command --action list --query foo\n\n" .
+ " Add new UAR:\n" .
+ " $command --action add --query foo --name myuar " .
+ "--title=\"Example Title\" --url=http://www.example.com\n\n" .
+ " Edit existing UAR:\n" .
+ " $command --action edit --name myuar --rid 123456789 " .
+ "--title=\"Example Title\" --url=http://www.example.com\n\n" .
+ " Delete single UAR entry:\n" .
+ " $command --action delete --rid 123456789 --name myuar\n\n" .
+ " Delete all UAR for a query:\n" .
+ " $command --action delete --query foo --name myuar\n\n"
+ );
+
+ if ($message ne "") {
+ print STDERR ("\nERROR: " . $message . "\n\n");
+ }
+ exit();
+}