diff --git a/Gatekeeper/WebRoot/META-INF/MANIFEST.MF b/Gatekeeper/WebRoot/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..254272e1
--- /dev/null
+++ b/Gatekeeper/WebRoot/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/Gatekeeper/WebRoot/WEB-INF/conf/gatekeeper.properties b/Gatekeeper/WebRoot/WEB-INF/conf/gatekeeper.properties
new file mode 100644
index 00000000..856dc168
--- /dev/null
+++ b/Gatekeeper/WebRoot/WEB-INF/conf/gatekeeper.properties
@@ -0,0 +1,7 @@
+web.service.version = GateKeeper-0.1
+token.name = auth-token
+token.ttl = 28800000
+token.authgroup = authenticated
+token.publicuser = public
+ldap.keystore = keystore.jks
+token.privatekey = private-key.ser
diff --git a/Gatekeeper/WebRoot/WEB-INF/conf/keystore.jks b/Gatekeeper/WebRoot/WEB-INF/conf/keystore.jks
new file mode 100644
index 00000000..452d963e
Binary files /dev/null and b/Gatekeeper/WebRoot/WEB-INF/conf/keystore.jks differ
diff --git a/Gatekeeper/WebRoot/WEB-INF/conf/log4j.properties b/Gatekeeper/WebRoot/WEB-INF/conf/log4j.properties
new file mode 100644
index 00000000..4db65773
--- /dev/null
+++ b/Gatekeeper/WebRoot/WEB-INF/conf/log4j.properties
@@ -0,0 +1,59 @@
+# /**
+# * '$rcsfile: log4j.properties,v $'
+# * copyright: 2002 regents of the university of california and the
+# * national center for ecological analysis and synthesis
+# * '$author: brooke $'
+# * '$date: 2003/06/24 00:58:49 $'
+# * '$revision: 1.1 $'
+# *
+# * this program is free software; you can redistribute it and/or modify
+# * it under the terms of the gnu general public license as published by
+# * the free software foundation; either version 2 of the license, or
+# * (at your option) any later version.
+# *
+# * this program is distributed in the hope that it will be useful,
+# * but without any warranty; without even the implied warranty of
+# * merchantability or fitness for a particular purpose. see the
+# * gnu general public license for more details.
+# *
+# * you should have received a copy of the gnu general public license
+# * along with this program; if not, write to the free software
+# * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa
+# */
+#
+#
+################################################################################
+################################################################################
+#
+# for conversion/formatting characters, see:
+#
+# http://logging.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html
+#
+################################################################################
+################################################################################
+
+# set the log level to WARN and the log should be printed to stdout.
+log4j.rootLogger=WARN, stdout
+log4j.threshold=FATAL, ERROR, WARN, INFO
+
+
+### LOGGING TO CONSOLE #########################################################
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# define the pattern to be used in the logs...
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} Gatekeeper: [%p] [%c]: %m %n
+
+# %p -> priority level of the event - (e.g. WARN)
+# %m -> message to be printed
+# %c -> category name ... in this case name of the class
+# %d -> Used to output the date of the logging event. example, %d{HH:mm:ss,SSS} or %d{dd MMM yyyy HH:mm:ss,SSS}. Default format is ISO8601 format
+# %M -> print the method name where the event was generated ... can be extremely slow.
+# %L -> print the line number of the event generated ... can be extremely slow.
+# %t -> Used to output the name of the thread that generated the log event
+# %n -> carriage return
+
+################################################################################
+# EXAMPLE: Print only messages of level WARN or above in the package com.foo:
+# log4j.logger.com.foo=WARN
+log4j.logger.edu.lternet.pasta.gatekeeper=INFO
diff --git a/Gatekeeper/WebRoot/WEB-INF/conf/private-key.ser b/Gatekeeper/WebRoot/WEB-INF/conf/private-key.ser
new file mode 100644
index 00000000..d2bdd423
Binary files /dev/null and b/Gatekeeper/WebRoot/WEB-INF/conf/private-key.ser differ
diff --git a/Gatekeeper/WebRoot/WEB-INF/web.xml b/Gatekeeper/WebRoot/WEB-INF/web.xml
new file mode 100644
index 00000000..658e5ad0
--- /dev/null
+++ b/Gatekeeper/WebRoot/WEB-INF/web.xml
@@ -0,0 +1,106 @@
+
+
+
+ CONFIG_DIR
+ WEB-INF/conf
+
+
+ edu.lternet.pasta.gatekeeper.ConfigurationListener
+
+
+ GatekeeperEventManagerProxy
+ org.eclipse.jetty.servlets.ProxyServlet$Transparent
+ 1
+
+ Prefix
+ /eventmanager
+
+
+ ProxyTo
+ http://event.lternet.edu:8080/eventmanager
+
+
+
+ GatekeeperDataPackageManagerProxy
+ org.eclipse.jetty.servlets.ProxyServlet$Transparent
+ 1
+
+ Prefix
+ /package
+
+
+ ProxyTo
+ http://package.lternet.edu:8080/package
+
+
+
+ GatekeeperAuditManagerProxy
+ org.eclipse.jetty.servlets.ProxyServlet$Transparent
+ 1
+
+ Prefix
+ /audit
+
+
+ ProxyTo
+ http://audit.lternet.edu:8080/audit
+
+
+
+ GatekeeperTester
+ org.eclipse.jetty.servlets.ProxyServlet$Transparent
+ 1
+
+ Prefix
+ /test
+
+
+ ProxyTo
+ http://localhost:8080/gatekeepertester
+
+
+
+ api-docs
+ org.eclipse.jetty.servlet.DefaultServlet
+
+
+ demo
+ org.eclipse.jetty.servlet.DefaultServlet
+
+
+ GatekeeperFilter
+ edu.lternet.pasta.gatekeeper.GatekeeperFilter
+
+
+ GatekeeperFilter
+ GatekeeperEventManagerProxy
+ GatekeeperDataPackageManagerProxy
+ GatekeeperAuditManagerProxy
+ GatekeeperTester
+
+
+ GatekeeperEventManagerProxy
+ /eventmanager/*
+
+
+ GatekeeperDataPackageManagerProxy
+ /package/*
+
+
+ GatekeeperAuditManagerProxy
+ /audit/*
+
+
+ GatekeeperTester
+ /test/*
+
+
+ api-docs
+ /docs/*
+
+
+ demo
+ /demo/*
+
+
diff --git a/Gatekeeper/WebRoot/demo/demo.html b/Gatekeeper/WebRoot/demo/demo.html
new file mode 100644
index 00000000..11076b05
--- /dev/null
+++ b/Gatekeeper/WebRoot/demo/demo.html
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+ Response
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+
+
+
diff --git a/Gatekeeper/WebRoot/demo/style.css b/Gatekeeper/WebRoot/demo/style.css
new file mode 100644
index 00000000..04a61d92
--- /dev/null
+++ b/Gatekeeper/WebRoot/demo/style.css
@@ -0,0 +1,69 @@
+.demo {
+ width: 580px;
+ float: left;
+ padding-top: 0px;
+ padding-left: 0px;
+ padding-right: 16px;
+ padding-bottom: 0px;
+}
+
+.mainResult {
+ width: 580px;
+ float: left;
+ padding-top: 0px;
+ padding-left: 8px;
+ padding-right: 0px;
+ padding-bottom: 0px;
+}
+
+.webServiceDescription {
+ width: 580px;
+ height: 80px;
+ float: left;
+ overflow: auto;
+ padding: 4px;
+ margin: 2px;
+ background: #EEEEEE;
+ border-style: solid;
+ border-color: #CFCFCF;
+ border-width: 1px;
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+}
+
+.webServiceResult {
+ width: 580px;
+ height: 400px;
+ float: left;
+ overflow: auto;
+ padding: 4px;
+ margin: 2px;
+ background: #CCE6FF;
+ border-style: solid;
+ border-color: #CFCFCF;
+ border-width: 1px;
+}
+
+.formTextField {
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ width: 580px;
+}
+
+.formLabelField {
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ width: 580px;
+ background: #EEEEEE;
+}
+
+.formTextArea {
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ width: 580px;
+}
+
+.content {
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+}
diff --git a/Gatekeeper/WebRoot/demo/util.js b/Gatekeeper/WebRoot/demo/util.js
new file mode 100644
index 00000000..9508f12f
--- /dev/null
+++ b/Gatekeeper/WebRoot/demo/util.js
@@ -0,0 +1,100 @@
+
+function createCookie(name,value,days) {
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else var expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function alterCookie(name) {
+ createCookie(name, "THISISBROKEN");
+}
+
+function eraseCookie(name) {
+ createCookie(name, "", -1);
+}
+
+function xmlToString(xmlObject) {
+ if (window.ActiveXObject)
+ return xmlObject.xml;
+ else
+ return (new XMLSerializer()).serializeToString(xmlObject);
+}
+
+function getResponseEntityAsString(xmlhttp) {
+ if (xmlhttp.responseText && xmlhttp.responseText != "")
+ return xmlhttp.responseText;
+ if (xmlhttp.responseXml)
+ return xmlToString(xmlhttp.responseXML);
+ return "";
+}
+
+function makeBasicAuth(user, password) {
+ var token = user + ':' + password;
+ var encodedToken = Base64.encode(token);
+ return "Basic " + encodedToken;
+}
+
+function clearRequest() {
+ document.requestForm.description.value = "";
+ document.requestForm.relativeUrl.value = "";
+ document.requestForm.httpVerb.value = "";
+ document.requestForm.user.value = "";
+ document.requestForm.password.value = "";
+ document.requestForm.contentType.value = "";
+ document.requestForm.entity.value = "";
+}
+
+function clearResponse() {
+ document.getElementById("response").value = "";
+}
+
+function clearRequestAndResponse() {
+ clearRequest();
+ clearResponse();
+}
+
+function sendRequest() {
+
+ // Validate input first
+ if (document.requestForm.httpVerb.value == "") {
+ alert("HTTP verb can't be empty.");
+ return;
+ }
+
+ // Initializing response panel
+ document.getElementById("response").value = "Waiting...";
+
+ // Building the request
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.open(document.requestForm.httpVerb.value,
+ encodeURI(document.requestForm.relativeUrl.value), false);
+
+ // Adding user and password to the request
+ if ((document.requestForm.user.value != "") &&
+ (document.requestForm.password.value != "")) {
+ auth = makeBasicAuth(document.requestForm.user.value,
+ document.requestForm.password.value);
+ xmlhttp.setRequestHeader('Authorization', auth);
+ }
+
+ // Adding the entity and content-type to the request, if necessary
+ if (document.requestForm.entity.value == "") {
+ xmlhttp.send();
+ } else {
+ contentType = document.requestForm.contentType.value;
+ xmlhttp.setRequestHeader("Content-Type", contentType);
+ xmlhttp.send(document.requestForm.entity.value);
+ }
+
+ // Building the response to be written to the panel
+ responseText = 'Status: ' + xmlhttp.status + ' ' + xmlhttp.statusText + '\n';
+ responseText += xmlhttp.getAllResponseHeaders() + '\n';
+ responseText += getResponseEntityAsString(xmlhttp);
+
+ // Adding the response to the panel
+ document.getElementById("response").value = responseText;
+}
diff --git a/Gatekeeper/WebRoot/demo/web-service-requests.js b/Gatekeeper/WebRoot/demo/web-service-requests.js
new file mode 100644
index 00000000..d0200ba1
--- /dev/null
+++ b/Gatekeeper/WebRoot/demo/web-service-requests.js
@@ -0,0 +1,59 @@
+var Requests = {
+
+ _relativeUrl : "/test/",
+
+ _setUserAndPassword : function(requestForm) {
+ requestForm.user.value = "uid=ucarroll,o=LTER,dc=ecoinformatics,dc=org";
+ requestForm.password.value = "S@ltL@ke";
+ },
+
+ _clearUserAndPassword : function(requestForm) {
+ requestForm.user.value = "";
+ requestForm.password.value = "";
+ },
+
+ _clearCookie : function(cookie) {
+ var cookies = document.cookie.split(";")
+ for (var i = 0; i < cookies.length; i++)
+ eraseCookie(cookies[i].split("=")[0]);
+ },
+
+ initializeReadBasic : function(requestForm, cookie) {
+ requestForm.description.value = "Perform a GET using basic authentication.";
+ requestForm.relativeUrl.value = this._relativeUrl;
+ requestForm.httpVerb.value = "GET";
+ requestForm.contentType.value = "N/A";
+ requestForm.entity.value = "";
+ this._setUserAndPassword(requestForm);
+ this._clearCookie(cookie);
+ },
+
+ initializeReadToken : function(requestForm) {
+ requestForm.description.value = "Perform a GET using token authentication. Alternately perform a GET using an expired token (wait 60 seconds).";
+ requestForm.relativeUrl.value = this._relativeUrl;
+ requestForm.httpVerb.value = "GET";
+ requestForm.contentType.value = "N/A";
+ requestForm.entity.value = "";
+ this._clearUserAndPassword(requestForm);
+ },
+
+ initializeReadMalformed : function(requestForm) {
+ requestForm.description.value = "Perform a GET using a malformed token for authentication.";
+ requestForm.relativeUrl.value = this._relativeUrl;
+ requestForm.httpVerb.value = "GET";
+ requestForm.contentType.value = "N/A";
+ requestForm.entity.value = "";
+ this._clearUserAndPassword(requestForm);
+ },
+
+ initializeReadPublic : function(requestForm, cookie) {
+ requestForm.description.value = "Perform a GET using public authentication.";
+ requestForm.relativeUrl.value = this._relativeUrl;
+ requestForm.httpVerb.value = "GET";
+ requestForm.contentType.value = "N/A";
+ requestForm.entity.value = "";
+ this._clearUserAndPassword(requestForm);
+ this._clearCookie(cookie);
+ }
+
+}
diff --git a/Gatekeeper/WebRoot/demo/webtoolkit.base64.js b/Gatekeeper/WebRoot/demo/webtoolkit.base64.js
new file mode 100644
index 00000000..5fa3c1ed
--- /dev/null
+++ b/Gatekeeper/WebRoot/demo/webtoolkit.base64.js
@@ -0,0 +1,142 @@
+/**
+*
+* Base64 encode / decode
+* http://www.webtoolkit.info/
+*
+**/
+
+var Base64 = {
+
+ // private property
+ _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+
+ // public method for encoding
+ encode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0;
+
+ input = Base64._utf8_encode(input);
+
+ while (i < input.length) {
+
+ chr1 = input.charCodeAt(i++);
+ chr2 = input.charCodeAt(i++);
+ chr3 = input.charCodeAt(i++);
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+ enc4 = chr3 & 63;
+
+ if (isNaN(chr2)) {
+ enc3 = enc4 = 64;
+ } else if (isNaN(chr3)) {
+ enc4 = 64;
+ }
+
+ output = output +
+ this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
+ this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
+
+ }
+
+ return output;
+ },
+
+ // public method for decoding
+ decode : function (input) {
+ var output = "";
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0;
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ while (i < input.length) {
+
+ enc1 = this._keyStr.indexOf(input.charAt(i++));
+ enc2 = this._keyStr.indexOf(input.charAt(i++));
+ enc3 = this._keyStr.indexOf(input.charAt(i++));
+ enc4 = this._keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output = output + String.fromCharCode(chr1);
+
+ if (enc3 != 64) {
+ output = output + String.fromCharCode(chr2);
+ }
+ if (enc4 != 64) {
+ output = output + String.fromCharCode(chr3);
+ }
+
+ }
+
+ output = Base64._utf8_decode(output);
+
+ return output;
+
+ },
+
+ // private method for UTF-8 encoding
+ _utf8_encode : function (string) {
+ string = string.replace(/\r\n/g,"\n");
+ var utftext = "";
+
+ for (var n = 0; n < string.length; n++) {
+
+ var c = string.charCodeAt(n);
+
+ if (c < 128) {
+ utftext += String.fromCharCode(c);
+ }
+ else if((c > 127) && (c < 2048)) {
+ utftext += String.fromCharCode((c >> 6) | 192);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+ else {
+ utftext += String.fromCharCode((c >> 12) | 224);
+ utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+ utftext += String.fromCharCode((c & 63) | 128);
+ }
+
+ }
+
+ return utftext;
+ },
+
+ // private method for UTF-8 decoding
+ _utf8_decode : function (utftext) {
+ var string = "";
+ var i = 0;
+ var c = c1 = c2 = 0;
+
+ while ( i < utftext.length ) {
+
+ c = utftext.charCodeAt(i);
+
+ if (c < 128) {
+ string += String.fromCharCode(c);
+ i++;
+ }
+ else if((c > 191) && (c < 224)) {
+ c2 = utftext.charCodeAt(i+1);
+ string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+ i += 2;
+ }
+ else {
+ c2 = utftext.charCodeAt(i+1);
+ c3 = utftext.charCodeAt(i+2);
+ string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+ i += 3;
+ }
+
+ }
+
+ return string;
+ }
+
+}
diff --git a/Gatekeeper/WebRoot/docs/api.html b/Gatekeeper/WebRoot/docs/api.html
new file mode 100644
index 00000000..89df7e1d
--- /dev/null
+++ b/Gatekeeper/WebRoot/docs/api.html
@@ -0,0 +1,51 @@
+
+
+
+
+
Gatekeeper - PASTA
+
+
+
+ Gatekeeper web service API
+
+
+
Base URL - https://pasta.lternet.edu
+
+ The Gatekeeper web service handles all authentication from incoming requests.
+
+
The following are the Use Cases for the Gatekeeper:
+
+ If the user submits only BASIC authentication credentials, a token will be
+ generated and returned upon completion of the requested query.
+
+
+ If the user submits a token, the token will be used provided it does not
+ exceed the time to live. In that event, a ServletException is thrown.
+
+
+ If no credentials or tokens are submitted, a token for special user public
+ will be created and the remainder of the query will be done as public. The
+ response will return a public token.
+
+
There is no explicit API for interacting with the Gatekeeper as a service.
+ The requirement for all services is using a basic Authorization header.
+ Once a cookie is received from a request, using that cookie instead of the
+ Authorization header will result in being able to interact with all pasta
+ services without transferring user authentication credentials until the cookie
+ expires or the user determines it necessary to authenticate using alternative
+ credentials.
+
The following services can be used in conjunction with the Gatekeeper:
+
DataPackageManager @ http://pasta.lternet.edu/package/
+
GatekeeperTester @ http://pasta.lternet.edu/test
+
+
+
+
+
diff --git a/Gatekeeper/WebRoot/docs/tutorial.html b/Gatekeeper/WebRoot/docs/tutorial.html
new file mode 100644
index 00000000..711186a5
--- /dev/null
+++ b/Gatekeeper/WebRoot/docs/tutorial.html
@@ -0,0 +1,84 @@
+
+
+
+
Tutorial - GatekeeperTester - PASTA
+
+
+
+ GatekeeperTester
+ web service tutorial
+
+
+
Examples
+
+
+
The command line utility curl
will be used to
+ illustrate the following examples.
+
+
+
This service exists for testing the Gatekeeper
+
+
Intial Authentication Command
+
+
curl -c cookiejar.txt --user uid=ucarroll,o=LTER,dc=ecoinformatics,dc=org:mouse -G
+ https://pasta.lternet.edu/test/
+
+
+
Description
+
+
This command requests that the Gatekeeper create a new
+ token to be returned as a cookie when the request completes. This will then be
+ written to the cookie store cookiejar.txt
for future use. The user is
+ encouraged to use uid=ucarroll,o=LTER,dc=ecoinformatics,dc=org
+ and corresponding password for testing. Should an invalid username or password
+ be used, an appropriate response will be generated indicating such.
+
+
The response from the Gatekeeper depends on the nature of the submitted
+ request, but the Set-Cookie: header should show a Base 64 encoded string has
+ been returned.
+
+
+HTTP/1.1 200 OK
+Date: Thu, 22 Dec 2011 19:07:50 GMT
+Set-Cookie: auth-token="sz46tDcFxqLby2TtlBARREdqGFSSRFbjSHPvMw0hgXLsG2uGlDWrOzjf/zM7Yd7g4n8pK5qKzohvP9UdYqf/xyx/RBAUU1QYwmUXTA5NnUZ5qHjYCtx3Y+DgwyNsQPoz6dQqR92BWWsWb39BilwfaYGtyAtiztJ0ZK4mx3c94VY9HTXmhfbuMzAErLo437zvX4IZDSHWMWNXYMuxC3eQ+A==";Version=1;Expires=Fri, 12-Dec-2053 14:20:40 GMT;Max-Age=1324581170
+Expires: Thu, 01 Jan 1970 00:00:00 GMT
+Server: Apache-Coyote/1.1
+...
+
+
+
Omission of the --user <username>:<password>
will
+ result in public authentication.
+
+
+
+
+
Token Authentication Command
+
+
curl -b cookiejar.txt -G https://pasta.lternet.edu/test/
+
+
+
Description
+
+
This command requests that the Gatekeeper use a previously created
+ token for the request. This token will be retreived from the
+ cookiejar.txt
and submitted appropriately.
+
+
The response from the Gatekeeper depends on the nature of the submitted
+ request and should not include any headers or content indicating anything
+ other than normal operation. If the token time to live has expired, a response
+ will be generated to indicate so.
+
+
+HTTP/1.1 200 OK
+Date: Thu, 22 Dec 2011 19:07:50 GMT
+Server: Apache-Coyote/1.1
+...
+
+
+
+
+
+
+
+
+
diff --git a/Gatekeeper/WebRoot/index.jsp b/Gatekeeper/WebRoot/index.jsp
new file mode 100644
index 00000000..08660428
--- /dev/null
+++ b/Gatekeeper/WebRoot/index.jsp
@@ -0,0 +1,26 @@
+<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
+<%
+String path = request.getContextPath();
+String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
+%>
+
+
+
+
+
+
+
My JSP 'index.jsp' starting page
+
+
+
+
+
+
+
+
+
+ This is my JSP page.
+
+
diff --git a/Gatekeeper/build.properties b/Gatekeeper/build.properties
new file mode 100644
index 00000000..3de5a37c
--- /dev/null
+++ b/Gatekeeper/build.properties
@@ -0,0 +1,3 @@
+# Ant properties
+tomcat.home=/home/pasta/local/jetty
+tomcat.lib=/home/pasta/local/jetty/lib
diff --git a/Gatekeeper/build.xml b/Gatekeeper/build.xml
new file mode 100644
index 00000000..f2231b1a
--- /dev/null
+++ b/Gatekeeper/build.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Gatekeeper/documents/gatekeeper.eap b/Gatekeeper/documents/gatekeeper.eap
new file mode 100644
index 00000000..b20246c1
Binary files /dev/null and b/Gatekeeper/documents/gatekeeper.eap differ
diff --git a/Gatekeeper/src/edu/lternet/pasta/gatekeeper/ConfigurationListener.java b/Gatekeeper/src/edu/lternet/pasta/gatekeeper/ConfigurationListener.java
new file mode 100644
index 00000000..dbee8084
--- /dev/null
+++ b/Gatekeeper/src/edu/lternet/pasta/gatekeeper/ConfigurationListener.java
@@ -0,0 +1,305 @@
+/*
+ *
+ * $Date$ $Author$ $Revision$
+ *
+ * Copyright 2010 the University of New Mexico.
+ *
+ * This work was supported by National Science Foundation Cooperative Agreements
+ * #DEB-0832652 and #DEB-0936498.
+ *
+ * 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.
+ */
+
+package edu.lternet.pasta.gatekeeper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.crypto.SecretKey;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+import edu.lternet.pasta.common.FileUtility;
+import edu.lternet.pasta.common.UserErrorException;
+import edu.lternet.pasta.common.security.auth.SymmetricEncrypter;
+
+/**
+ * The ConfigurationListener class initializes the LTER restful web application.
+ * The initialization code executes when the web application context starts up.
+ * This class is a subclass of ServletContextListener.
+ */
+public class ConfigurationListener implements ServletContextListener
+{
+
+ private static final Logger logger =
+ Logger.getLogger(ConfigurationListener.class);
+
+ /* Name of Gatekeeper's properties file. */
+ public static final String PROPERTIES = "gatekeeper.properties";
+
+ public static final String TOKEN_NAME = "token.name";
+ public static final String TOKEN_TTL = "token.ttl";
+ public static final String AUTH_GROUP = "token.authgroup";
+ public static final String PUBLIC_USER = "token.publicuser";
+ public static final String LDAP_KEY_STORE = "ldap.keystore";
+ public static final String PRIVATE_KEY = "token.privatekey";
+ public static final String WEB_SERVICE_VERSION = "web.service.version";
+
+ private static String tokenName = null;
+ private static long tokenTtl = new Long(-1);
+ private static String authGroup = null;
+ private static String publicUser = null;
+ private static File ldapKeyStore = null;
+ private static SecretKey privateKey = null;
+ private static String webServiceVersion = null;
+
+ private static File configDir;
+ private static File propertiesFile;
+
+ /**
+ * Getter for the configDir.
+ *
+ * @return the directory as a File.
+ */
+ public static File getConfigDir() {
+
+ if (configDir == null) {
+ File f = new File(System.getProperty("user.dir"), "WebRoot");
+ f = new File(f, "WEB-INF");
+ return new File(f, "conf");
+ }
+ return configDir;
+ }
+
+ /**
+ * Getter for the tokenName class field.
+ *
+ * @return the tokenName class field.
+ */
+ public static String getTokenName() {
+ return tokenName;
+ }
+
+ /**
+ * Getter for the tokenTtl class field.
+ *
+ * @return the tokenTtl class field.
+ */
+ public static long getTokenTtl() {
+ return tokenTtl;
+ }
+
+ /**
+ * Getter for the webServiceVersion class field.
+ *
+ * @return the webServiceVersion class field.
+ */
+ public static String getWebServiceVersion() {
+ return webServiceVersion;
+ }
+
+ /**
+ * Getter for the authGroup class field.
+ *
+ * @return the authGroup class field.
+ */
+ public static String getAuthGroup() {
+ return authGroup;
+ }
+
+ /**
+ * Getter for the publicUser class field.
+ *
+ * @return the publicUser class field.
+ */
+ public static String getPublicUser() {
+ return publicUser;
+ }
+
+ /**
+ * Getter for the ldapKeyStore class field.
+ *
+ * @return the ldapKeyStore class field.
+ */
+ public static File getLdapKeyStore() {
+ return ldapKeyStore;
+ }
+
+ /**
+ * Getter for the privateKey class field.
+ *
+ * @return the privateKey class field.
+ */
+ public static SecretKey getPrivateKey() {
+ return privateKey;
+ }
+
+ /**
+ * This method can be used to execute code when the web application shuts
+ * down.
+ *
+ * @param servletContextEvent
+ * The ServletContextEvent object.
+ */
+ public void contextDestroyed(ServletContextEvent servletContextEvent) {
+
+ }
+
+ /**
+ * Run initialization code when at web application start-up.
+ *
+ * @param servletContextEvent
+ * The ServletContextEvent object.
+ */
+ public void contextInitialized(ServletContextEvent servletContextEvent) {
+
+ ServletContext context = servletContextEvent.getServletContext();
+ String CONFIG_DIR = context.getInitParameter("CONFIG_DIR");
+ String realPath = context.getRealPath(CONFIG_DIR);
+ try {
+ setConfigDir(realPath);
+ initialize();
+ }
+ catch (RuntimeException e) {
+ logger.fatal("Initialization of Gatekeeper failed.", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Initialize all Properties files and prepare retrievable values.
+ *
+ * @param dir
+ * The directory containing properties files.
+ */
+ public void initialize(String dir) {
+
+ // necessary for JUnit tests
+ configDir = new File(dir);
+ initialize();
+ }
+
+ /**
+ * Initialize all Properties files and prepare retrievable values with
+ * default configuration directory.
+ */
+ public void initialize() {
+
+ setLog4jProperties();
+ setPropertiesFile();
+ Properties prop = loadPropertiesFile();
+ setTokenName(prop);
+ setTokenTtl(prop);
+ setVersionString(prop);
+ setAuthGroup(prop);
+ setPublicUser(prop);
+ setLdapKeyStore(prop);
+ setPrivateKey(prop);
+ }
+
+ private Properties loadPropertiesFile() {
+
+ Properties properties = new Properties();
+ try {
+ properties.load(new FileInputStream(propertiesFile));
+ }
+ catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ return properties;
+ }
+
+ private void setTokenName(Properties p) {
+
+ tokenName = p.getProperty(TOKEN_NAME);
+ if (tokenName == null)
+ throw new IllegalArgumentException(TOKEN_NAME + " not specified");
+ }
+
+ private void setTokenTtl(Properties p) {
+
+ tokenTtl = new Long(p.getProperty(TOKEN_TTL));
+ if (tokenTtl == -1)
+ throw new IllegalArgumentException(TOKEN_TTL + " not specified");
+ }
+
+ private void setVersionString(Properties p) {
+
+ webServiceVersion = p.getProperty(WEB_SERVICE_VERSION);
+ if (webServiceVersion == null)
+ throw new IllegalArgumentException(WEB_SERVICE_VERSION
+ + " not specified");
+ }
+
+ private void setAuthGroup(Properties p) {
+
+ authGroup = p.getProperty(AUTH_GROUP);
+ if (authGroup == null)
+ throw new IllegalArgumentException(AUTH_GROUP + " not specified");
+ }
+
+ private void setPublicUser(Properties p) {
+
+ publicUser = p.getProperty(PUBLIC_USER);
+ if (publicUser == null)
+ throw new IllegalArgumentException(publicUser + " not specified");
+ }
+
+ private void setPropertiesFile() {
+
+ propertiesFile = FileUtility.assertCanRead(new File(getConfigDir(),
+ PROPERTIES));
+ }
+
+ private void setLdapKeyStore(Properties p) {
+
+ String fileName = p.getProperty(LDAP_KEY_STORE);
+ if (fileName == null || fileName.isEmpty()) {
+ throw new IllegalArgumentException(LDAP_KEY_STORE
+ + " not specified properly");
+ }
+
+ ldapKeyStore = FileUtility.assertCanRead(new File(configDir, fileName));
+ }
+
+ private void setPrivateKey(Properties p) {
+
+ String fileName = p.getProperty(PRIVATE_KEY);
+
+ File f = null;
+ try {
+ f = FileUtility.assertCanRead(new File(configDir, fileName));
+ privateKey = SymmetricEncrypter.readKey(f);
+ }
+ catch (UserErrorException e) {
+ privateKey = SymmetricEncrypter.makeKey();
+ SymmetricEncrypter.writeKey(privateKey, f);
+ }
+
+ }
+
+ private void setLog4jProperties() {
+
+ File properties = new File(getConfigDir(), "log4j.properties");
+ PropertyConfigurator.configureAndWatch(properties.getAbsolutePath());
+ }
+
+ private void setConfigDir(String realPath) {
+ configDir = FileUtility.assertCanRead(realPath);
+ }
+
+}
diff --git a/Gatekeeper/src/edu/lternet/pasta/gatekeeper/GatekeeperFilter.java b/Gatekeeper/src/edu/lternet/pasta/gatekeeper/GatekeeperFilter.java
new file mode 100644
index 00000000..8ce2f28b
--- /dev/null
+++ b/Gatekeeper/src/edu/lternet/pasta/gatekeeper/GatekeeperFilter.java
@@ -0,0 +1,349 @@
+/*
+ *
+ * $Date$ $Author$ $Revision$
+ *
+ * Copyright 2010 the University of New Mexico.
+ *
+ * This work was supported by National Science Foundation Cooperative Agreements
+ * #DEB-0832652 and #DEB-0936498.
+ *
+ * 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.
+ */
+
+package edu.lternet.pasta.gatekeeper;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+
+import org.apache.log4j.Logger;
+
+import edu.lternet.pasta.common.security.access.UnauthorizedException;
+import edu.lternet.pasta.common.security.auth.AuthSystemDef;
+import edu.lternet.pasta.common.security.auth.KnbAuthSystem;
+import edu.lternet.pasta.common.security.auth.SymmetricEncrypter;
+import edu.lternet.pasta.common.security.token.AuthToken;
+import edu.lternet.pasta.common.security.token.AuthTokenFactory;
+import edu.lternet.pasta.common.security.token.AuthTokenWithPassword;
+import edu.lternet.pasta.common.security.token.BasicAuthToken;
+
+/**
+ *
+ * The Gatekeeper web service handles all authentication from incoming requests.
+ *
+ *
+ *
+ * If the user submits only BASIC authentication credentials, a token will be
+ * generated and returned upon completion of the requested query.
+ *
+ *
+ *
+ * If the user submits a token, the token will be used provided it does not
+ * exceed the time to live. In that event, a ServletException is thrown.
+ *
+ *
+ *
+ * If no credentials or tokens are submitted, a token for special user public
+ * will be created and the remainder of the query will be done as public. The
+ * response will return a public token.
+ *
+ *
+ * @webservicename Gatekeeper
+ * @baseurl https://pasta.lternet.edu/
+ */
+public final class GatekeeperFilter implements Filter
+{
+
+ private static Logger logger = Logger.getLogger(GatekeeperFilter.class);
+ private FilterConfig filterConfig;
+ private static int BAD_REQUEST_CODE = 400;
+ private static int UNAUTHORIZED_CODE = 401;
+ private boolean publicUser = false;
+
+ private enum CookieUse {
+ EXTERNAL, INTERNAL
+ }
+
+ /**
+ * Overridden init method that sets the filterConfig.
+ */
+ @Override
+ public void init(FilterConfig config) throws ServletException {
+ filterConfig = config;
+ }
+
+ /**
+ * Overridden destroy method that free's the filterConfig.
+ */
+ @Override
+ public void destroy() {
+ filterConfig = null;
+ }
+
+ /**
+ * Overridden doFilter method.
+ * @param request ServletRequest representing the incoming user http(s)
+ * request.
+ * @param request ServletResponse representing the associated response
+ * that will eventually be passed on to the
+ * next servlet.
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse res = (HttpServletResponse) response;
+ try {
+ Cookie internalCookie = hasAuthToken(req.getCookies()) ?
+ doCookie(req) : doHeader(req, res);
+ chain.doFilter(new PastaRequestWrapper(req, internalCookie), res);
+ }
+ catch (IllegalStateException e) {
+ res.setStatus(BAD_REQUEST_CODE);
+ PrintWriter out = res.getWriter();
+ out.println(e);
+ }
+ catch (UnauthorizedException e) {
+ res.setStatus(UNAUTHORIZED_CODE);
+ PrintWriter out = res.getWriter();
+ out.println(e.getMessage());
+ }
+ catch (IllegalArgumentException e) {
+ res.setStatus(UNAUTHORIZED_CODE);
+ PrintWriter out = res.getWriter();
+ out.println(e.getMessage());
+ }
+
+
+ }
+
+ private Cookie doCookie(HttpServletRequest req)
+ throws IllegalArgumentException, IllegalStateException, UnauthorizedException {
+
+ AuthToken token = null;
+ String authTokenStr = retrieveAuthTokenString(req.getCookies());
+ /* Check Validity */
+ token = decryptToken(authTokenStr);
+ /* Check TTL */
+ assertTimeToLive(token);
+ return makeAuthTokenCookie(token, CookieUse.INTERNAL);
+ }
+
+ private Cookie doHeader(HttpServletRequest req, HttpServletResponse res) {
+
+ AuthToken authToken =
+ makeAuthenticated(req.getHeader(HttpHeaders.AUTHORIZATION));
+ Cookie externalCookie =
+ makeAuthTokenCookie(authToken, CookieUse.EXTERNAL);
+
+ if (!publicUser) res.addCookie(externalCookie);
+ return makeAuthTokenCookie(authToken, CookieUse.INTERNAL);
+ }
+
+ private void assertTimeToLive(AuthToken attrlist) throws UnauthorizedException {
+
+ if (attrlist == null) {
+ String s = "Token not found.";
+ throw new UnauthorizedException(s);
+ }
+ long ttl = attrlist.getExpirationDate() - (new Date().getTime());
+ if (ttl < 1) {
+ String s = "Token has expired.";
+ throw new UnauthorizedException(s);
+ }
+ }
+
+ private boolean hasAuthToken(Cookie[] cookies) {
+ if (retrieveAuthTokenString(cookies) == null) return false;
+ return true;
+ }
+
+ private AuthToken decryptToken(String tokenStr) throws IllegalStateException {
+
+ String errorMsg = "Invalid AuthToken Submitted.";
+
+ if (tokenStr == null || tokenStr.isEmpty()) {
+ throw new IllegalStateException(errorMsg);
+ }
+
+ String decrypted = null;
+ try {
+ decrypted =
+ SymmetricEncrypter.decrypt(tokenStr,
+ ConfigurationListener.getPrivateKey());
+ }
+ catch (IllegalArgumentException e) {
+ throw new IllegalStateException(errorMsg);
+ }
+
+ return AuthTokenFactory.makeCookieAuthToken(decrypted);
+ }
+
+ private String retrieveAuthTokenString(Cookie[] cookies) {
+
+ /* no cookies */
+ if (cookies == null) return null;
+ for (Cookie c : cookies) {
+ if (c.getName().equals(ConfigurationListener.getTokenName())) {
+ /* found correct cookie */
+ return c.getValue();
+ }
+ }
+ return null;
+ }
+
+ private AuthToken makeAuthenticated(String rawHeader) {
+
+ String tmpHeader = null;
+ if (rawHeader == null || rawHeader.isEmpty()) {
+ tmpHeader = BasicAuthToken.makeTokenString(
+ ConfigurationListener.getPublicUser(),
+ ConfigurationListener.getPublicUser());
+ publicUser = true;
+ }
+ else {
+ tmpHeader = rawHeader;
+ publicUser = false;
+ }
+
+ KnbAuthSystem knb = new KnbAuthSystem(ConfigurationListener.getLdapKeyStore());
+
+ AuthTokenWithPassword basicToken =
+ AuthTokenFactory.makeAuthTokenWithPassword(tmpHeader);
+ String user = basicToken.getUserId();
+
+ Set
groups = new HashSet();
+ if (!user.equals(ConfigurationListener.getPublicUser())) {
+
+ if (!knb.authenticate(user, basicToken.getPassword())) {
+ String s = "The user '" + user
+ + "' could not be authenticated "
+ + "using the LTER's or KNB's LDAP server.";
+ throw new UnauthorizedException(s); // Handle this better
+ }
+ // groups = knb.getGroups(user); // No groups currently stored here
+ groups.add(ConfigurationListener.getAuthGroup());
+ }
+ AuthSystemDef authSystem = knb.getAuthSystemDef();
+ long expirationDate =
+ new Date().getTime() + ConfigurationListener.getTokenTtl();
+ AuthToken token =
+ AuthTokenFactory.makeCookieAuthToken(user, authSystem,
+ expirationDate, groups);
+
+ return token;
+ }
+
+ private Cookie makeAuthTokenCookie(AuthToken attrlist, CookieUse use) {
+
+ String cookieValue = null;
+ switch (use) {
+ case EXTERNAL:
+ cookieValue = SymmetricEncrypter.encrypt(attrlist.getTokenString(),
+ ConfigurationListener.getPrivateKey());
+ break;
+ case INTERNAL:
+ cookieValue = attrlist.getTokenString();
+ break;
+ }
+ Cookie c = new Cookie(ConfigurationListener.getTokenName(), cookieValue);
+ Long expiry = attrlist.getExpirationDate() / 1000L;
+ c.setMaxAge(expiry.intValue());
+ return c;
+ }
+
+ public static class PastaRequestWrapper extends HttpServletRequestWrapper
+ {
+
+ private static Logger logger = Logger.getLogger(PastaRequestWrapper.class);
+ private Cookie cookie;
+
+ public PastaRequestWrapper(HttpServletRequest request, Cookie cookie) {
+
+ super(request);
+ this.cookie = cookie;
+ }
+
+ public String getHeader(String name) {
+
+ if (name.equals(HttpHeaders.AUTHORIZATION)) return null;
+ String header = super.getHeader(name);
+ if (name.equals(HttpHeaders.COOKIE) && header.isEmpty()
+ && (cookie != null))
+ return cookie.getName();
+
+ return header;
+ }
+
+ public Enumeration getHeaders(String name) {
+
+ Enumeration enumStr = super.getHeaders(name);
+
+ if (name.equals(HttpHeaders.AUTHORIZATION)) {
+ List ls = new ArrayList();
+ enumStr = Collections.enumeration(ls);
+ }
+
+ if (!name.equals(HttpHeaders.COOKIE) || (cookie == null))
+ return enumStr;
+
+ ArrayList list = Collections.list(enumStr);
+ list.add(cookie.getName() + "=" + cookie.getValue());
+ return Collections.enumeration(list);
+ }
+
+ public Enumeration getHeaderNames() {
+
+ Enumeration enumStr = super.getHeaderNames();
+ ArrayList list = Collections.list(enumStr);
+ if (!list.contains(HttpHeaders.COOKIE) && (cookie != null)) {
+ list.add(HttpHeaders.COOKIE);
+ }
+
+ return Collections.enumeration(list);
+ }
+
+ public Cookie[] getCookies() {
+
+ Cookie[] cookies = super.getCookies();
+ if (cookie == null) return cookies;
+
+ ArrayList list = (cookies == null) ? new ArrayList()
+ : new ArrayList(Arrays.asList(cookies));
+
+ list.add(cookie);
+ cookies = new Cookie[list.size()];
+ return list.toArray(cookies);
+ }
+
+ }
+
+}