From 46739fbd23f59c899423d1f38be84792088eee6b Mon Sep 17 00:00:00 2001
From: Matthew B <106352182+artntek@users.noreply.github.com>
Date: Wed, 19 Nov 2025 09:41:44 -0800
Subject: [PATCH 1/6] remove 2005 proposal from Bill Baker - appears no longer
relevant
---
.../MetacatGsiClient.java | 112 -
lib/certificateAuthenCode/READE.txt | 49 -
.../bbaker_diff_metacat.txt | 3421 -----------------
.../bbaker_diff_utilities.txt | 138 -
.../metacat_additions.tar.gz | Bin 3412744 -> 0 bytes
5 files changed, 3720 deletions(-)
delete mode 100644 lib/certificateAuthenCode/MetacatGsiClient.java
delete mode 100644 lib/certificateAuthenCode/READE.txt
delete mode 100644 lib/certificateAuthenCode/bbaker_diff_metacat.txt
delete mode 100644 lib/certificateAuthenCode/bbaker_diff_utilities.txt
delete mode 100644 lib/certificateAuthenCode/metacat_additions.tar.gz
diff --git a/lib/certificateAuthenCode/MetacatGsiClient.java b/lib/certificateAuthenCode/MetacatGsiClient.java
deleted file mode 100644
index 55bf5e1dc..000000000
--- a/lib/certificateAuthenCode/MetacatGsiClient.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package edu.ucsb.nceas.metacat.client.gsi;
-
-import edu.ucsb.nceas.metacat.client.MetacatAuthException;
-import edu.ucsb.nceas.metacat.client.MetacatClient;
-import edu.ucsb.nceas.metacat.client.MetacatInaccessibleException;
-import edu.ucsb.nceas.utilities.HttpMessage;
-import org.ietf.jgss.GSSCredential;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLStreamHandler;
-import java.util.Properties;
-
-/** An extension of the Metacat client that uses Grid Security Infrastructure
- * (GSI) enabled HTTPS instead of HTTP to communicate.
- *
- *
Note that not all client deployments will include the JARs necessary to
- * run this version of the Metacat client; therefore, we should make sure that
- * the superclass (MetacatClient) can run even if this class can't be loaded.
- * That is, catch (and log) NoClassDefFoundError, etc. */
-public class MetacatGsiClient extends MetacatClient {
-
- /** The current user's GSS credential, as an alternative to
- * username/password. Needed for every connection.
- * Set via {@link #login(GSSCredential)}. */
- private GSSCredential credential;
-
- private void initCredential(GSSCredential credential)
- throws MetacatAuthException
- {
- if (credential == null)
- throw new NullPointerException("Credential is null.");
- if (this.credential != null)
- throw new MetacatAuthException
- ("Credential already initialized; please create a new "
- + getClass().getName() + " to start a new session.");
- this.credential = credential;
- }
-
- public String login(GSSCredential credential)
- throws MetacatAuthException, MetacatInaccessibleException
- {
- initCredential(credential);
-
- // code below mostly copied from super.login(username, password)
- Properties prop = new Properties();
- prop.put("action", "login");
- prop.put("qformat", "xml");
-
- String response;
- try {
- response = sendDataForString(prop, null, null, 0);
- } catch (Exception e) {
- throw new MetacatInaccessibleException(e);
- }
-
- if (response.indexOf("") == -1) {
- setSessionId("");
- throw new MetacatAuthException(response);
- } else {
- int start = response.indexOf("") + 11;
- int end = response.indexOf("");
- if ((start != -1) && (end != -1)) {
- setSessionId(response.substring(start,end));
- }
- }
- return response;
- }
-
- /** Parse the Metacat URL and, if we are using a GSI credential,
- * ensure that the protocol is an SSL-based one (HTTPS or HTTPG). */
- private URL parseAndCheckURL() throws MetacatInaccessibleException {
- try {
- URL url = new URL(getMetacatUrl().trim());
-
- if (credential != null) {
- URLStreamHandler gsiHandler;
- try {
- gsiHandler = (URLStreamHandler) Class
- .forName("org.globus.net.protocol.https.Handler")
- .newInstance();
- } catch (Exception e) {
- throw new MetacatInaccessibleException
- ("Unable to create protocol handler for HTTPS+GSI.", e);
- }
- // reconstruct with correct handler
- url = new URL(url.getProtocol(), url.getHost(), url.getPort(),
- url.getFile(), gsiHandler);
- }
- return url;
- }
- catch (MalformedURLException e) {
- throw new MetacatInaccessibleException
- ("Unable to parse URL to contact Metacat server: \""
- + getMetacatUrl() + "\".", e);
- }
- }
-
- /** Create an HttpMessage that can send messages to the server.
- * If using a GSI credential, use the credential to set up an SSL
- * connection (HTTPS / HTTPG). If using HTTP and username/password,
- * just use a regular HTTP conenction. */
- protected HttpMessage createHttpMessage()
- throws MetacatInaccessibleException, MetacatAuthException, IOException
- {
- if (credential != null)
- return new HttpGsiMessage(credential, parseAndCheckURL());
- else
- return super.createHttpMessage();
- }
-}
diff --git a/lib/certificateAuthenCode/READE.txt b/lib/certificateAuthenCode/READE.txt
deleted file mode 100644
index 760212547..000000000
--- a/lib/certificateAuthenCode/READE.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-metacat_additions.tar.gz, MetacatGsiClient.java, bbaker_diff_metacat.txt and bbaker_diff_utilities.txt files came from Bill Baker at UIUC. His work is part of effort on Pilot project. Baker's work focused on making Metacat support HTTPS+GSI (Grid Security Infrastructure) POST.
-
-Here are the emails from Baker:
-
----------- Forwarded message ----------
-Date: Mon, 17 Oct 2005 14:42:28 -0500
-From: Bill Baker
-To: Jing Tao
-Cc: Matt Jones , Saurabh Garg ,
- Von Welch , mfreemon@ncsa.uiuc.edu
-Subject: Re: Notes for Metacat Call
-
-I just found a way to simplify the HTTPS+GSI protocol handling in Java, while I was writing some documentation -- I'm surprised I didn't see it before,
-because it's just the use of a different URL constructor. As a bonus, it should eliminate the compile dependency on JGlobus JARs.
-
-The change is:
-
-(1) delete the class edu.ucsb.nceas.protocols.httpg -- it
- is no longer necesary
-(2) update MetacatGSIClient.java (attached)
-
-I tested it out in the LTER grid pilot project app and ran some unit tests, and it seems to work fine.
-
--- Bill
-
-At 14:19 2005-10-06, Bill Baker wrote:
-Here, finally, are my Metacat diffs. I've included three files:
-
- bbaker_diff_metacat.txt -- a cvs diff of the metacat module
- bbaker_diff_utilities.txt -- a cvs diff of the utilities module
- metacat_additions.tar.gz -- the files that I have added to metacat,in the directories where they belong in the metacat module
-
-As for our unit tests, the easiest way to get them is probably to download from CVS, at :pserver:anonymous@cvs.ncsa.uiuc.edu/CVS/grids -- check out
-the LTERPilotApp module, which has classes MetacatTest, which has GSI login tests and some simple queries with GSI authentication, and
-MetacatQueryTest, which does more complex querying.
-You may also find the metacat CVS module helpful -- it has our changes and a few extra Ant scripts for easy Metacat deployment in our particular
-configuration, as seen on Roadrunner.
-
-Bill Baker wrote:
-An update on the Metacat patches that I'm working on:
-I'm working on getting some changes into JGlobus to support HTTPS+GSI POST (it only supported GET before), including new support for streaming
-data -- my previous additions to it buffered the entire POST in memory before transmitting. Once I finish with that, I plan to package up the
-Metacat changes and send them to you.
-I have a question: unit tests. We wrote some unit tests for the LTER Grid pilot project that covered HTTPS+GSI, but they are outside of the
-Metacat unit testing framework. They only tested logging in and querying over HTTPS+GSI -- not harvesting or any of the update/delete
-operations. I'll include those tests when I send you my diffs. Do you think you'll want to work them into Metacat's unit tests?
--- Bill
-
-
diff --git a/lib/certificateAuthenCode/bbaker_diff_metacat.txt b/lib/certificateAuthenCode/bbaker_diff_metacat.txt
deleted file mode 100644
index ae1c51761..000000000
--- a/lib/certificateAuthenCode/bbaker_diff_metacat.txt
+++ /dev/null
@@ -1,3421 +0,0 @@
-? lib/gsi
-? lib/cog-url-ncsa.jar
-? lib/cog-jglobus-ncsa.jar
-? src/gsimap.properties
-? src/edu/ucsb/nceas/metacat/AuthGsi.java
-? src/edu/ucsb/nceas/metacat/AuthInfo.java
-? src/edu/ucsb/nceas/metacat/GsiMapfile.java
-? src/edu/ucsb/nceas/metacat/client/gsi
-Index: build.properties
-===================================================================
-RCS file: /cvs/metacat/build.properties,v
-retrieving revision 1.16
-diff -r1.16 build.properties
-85a86,100
->
-> # Authentication options -- written into metacat.properties
-> # can be "Gsi" or "Ldap"
-> auth-method=Gsi
-> # can really only be "Ldap" for now
-> auth-delegate=Ldap
-> # can be "true" or "false"
-> auth-delegation-allowed=true
-> # can be "username+password" or "gss"
-> auth-precedence=username+password
->
-> # Should logins from localhost with no password be trusted? Useful
-> # for the GT4 web service wrapper around metacat when it isn't using
-> # GSI delegation.
-> auth-trust-localhost=true
-Index: build.xml
-===================================================================
-RCS file: /cvs/metacat/build.xml,v
-retrieving revision 1.226
-diff -r1.226 build.xml
-273a274,278
->
->
->
->
->
-Index: docs/user/metacatinstall.html
-===================================================================
-RCS file: /cvs/metacat/docs/user/metacatinstall.html,v
-retrieving revision 1.20
-diff -r1.20 metacatinstall.html
-20a21,28
->
-618a627,917
->
->
-> |
-> GSI (Grid Security Infrastructure) Authentication
-> |
->
-> |
->
->
->
-> As an alternative to username/password, Metacat can use Grid
-> Security Infrastructure (GSI) credentials for authentication, if
-> you are programming to the Metacat client API. The advantages
-> are:
->
->
->
-> -
-> Use of security credentials enables sign-on with other grid
-> services.
->
-> -
-> Metacat's plaintext HTTP client-server connection is replaced
-> with encrypted HTTPS+GSI -- that is, SSL HTTP using grid
-> security credentials for the SSL connection.
->
->
->
->
->
->
->
-> -
->
If you don't have any Grid infrastructure available already,
-> you can href="http://grid.ncsa.uiuc.edu/myproxy/fromscratch.html">follow
-> these instructions to get started from scratch.
->
->
-> -
->
Establish host credentials for your Metacat server. The
-> instructions above will suffice for experimentation, or you can
-> get host credentials from your own Certificate Authority. You
-> can always replace them in the future.
->
->
->
->
->
->
-> -
->
->
Add JARs to Tomcat's $CATALINA_HOME/common/lib
-> directory. You can find them in the Metacat distribution in
-> metacat/lib/gsi:
->
->
-> cog-jglobus-ncsa.jar, commons-pool-1.2.jar,
-> cryptix32.jar, cryptix-asn1.jar, cryptix.jar,
-> jce-jdk13-125.jar, jgss.jar, log4j-1.2.8.jar, puretls.jar,
-> xml-apis.jar
->
->
-> Note that cog-jglobus-ncsa.jar is a modified
-> version of cog-jglobus.jar which comes with the href="http://www.globus.org/toolkit/">Globus Toolkit. It
-> includes a patch to enable GSI+HTTPS POST actions (only GET was
-> supported previously). The patch has been committed to the CoG
-> source and will be included in a future a version of the Globus
-> Toolkit, but until then you will need this custom JAR.
->
->
->
-> -
->
Modify Tomcat's server.xml to listen for
-> HTTPS+GSI. Note that you can use any available port for this
-> connector; this example uses 8443.
->
-> Host credentials: The HTTPS connection will require host
-> credentials for the Metacat server; the example below assumes
-> that the host credentials are in
-> /etc/grid-security, which is the default location
-> for a Globus
-> toolkit installation.
->
-> CA Cert Dir: Any Certificate Authorities (CAs) that you
-> depend on -- for example, the one that issued your host
-> credential -- should have their public keys and signing policies
-> stored in a location that is accessible to Tomcat
-> (cacertdir, below). The filenames will be
-> something like 4a6cd8b1.0 and
-> 4a6cd8b1.signing_policy.
->
->
->
-> -
->
Add an HTTPSConnector, inside the
-> <Service name="Catalina"> section, near
-> the SSL HTTP/1.1 connector definition, which is commented
-> out by default:
->
->
-> <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
-> <!--
-> <Connector port="8443" maxHttpHeaderSize="8192"
-> maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
-> enableLookups="false" disableUploadTimeout="true"
-> acceptCount="100" scheme="https" secure="true"
-> clientAuth="false" sslProtocol="TLS" />
-> -->
->
-> <!-- Define an HTTPS+GSI (HTTPS with GSI credentials) Connector on port 8443 -->
-> <Connector className="org.globus.tomcat.coyote.net.HTTPSConnector"
-> port="8443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
-> autoFlush="true" disableUploadTimeout="true" scheme="https"
-> enableLookups="true" acceptCount="10" debug="0"
-> cert="/etc/grid-security/hostcert.pem"
-> key="/etc/grid-security/hostkey.pem"
-> cacertdir="/etc/grid-security/certificates"/>
->
->
-> -
->
Add an HTTPSValve, inside the <Engine
-> name="Catalina" ...> section, near the
-> RequestDumperValve definition, which is
-> commented out by default:
->
->
-> <!--
-> <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
-> -->
->
-> <!-- Globus valve for HTTPS+GSI -->
-> <Valve className="org.globus.tomcat.coyote.valves.HTTPSValve"/>
->
->
->
->
->
-> -
->
Restart Tomcat -- it should now be listening for HTTPS+GSI
-> connections. Watch for errors in its log file
-> ($CATALINA_HOME/logs/catalina.out).
->
->
->
->
->
->
-> -
->
You can now use GSI credentials to initiate a Metacat
-> session, replacing Metacat.login(String username, String
-> password) with Metacat.login(GSSCredential
-> credential). There are several ways to get a
-> GSSCredential; one easy method, if you have access
-> to a MyProxy
-> server, is to use the CoG
-> Java libraries to retrieve a proxy credential from MyProxy.
->
-> Here is some sample Java code. Note that it requires, in
-> addition to the JARs listed above,
-> cog-url-ncsa.jar, which can be found in the
-> metacat/lib directory.
->
-> Note: for code comments, mouse over the title="like this one">marked sections below.
->
->
-> // 1. Get a GSI security credential
-> org.globus.myproxy.MyProxy server = new MyProxy(host, 7512);
-> org.ietf.jgss.GSSCredential credential = server.get(username, passphrase, lifetime);
->
-> // 2. Connect to Metacat via GSI+HTTPS
-> String metacatUrl = "https://<metacat server>:8443/metacat/metacat/";
-> edu.ucsb.nceas.metacat.client.Metacat client
-> = edu.ucsb.nceas.metacat.client.MetacatFactory.createMetacatConnection(metacatUrl);
-> String loginResult = client.login(credential);
->
->
-> The String returned by
-> Metacat.login(GSSCredential) will let you know
-> whether your attempt succeeded.
->
->
-> -
->
In order to give GSI users privileges in Metacat, you must
-> Map GSI user IDs, or Distinguished Names (DNs), to Metacat user
-> IDs, which will generally be LDAP DNs. You can do this in a map
-> file that starts out in
-> metacat/src/gsimap.properties and gets deployed to
-> Tomcat's webapps/metacat/WEB-INF/ directory. You can
-> modify it in either place, but it will be overwritten each time
-> you redeploy Metacat if you change it in Tomcat's installation
-> directory.
->
->
->
->
->
-> Detailed explanation: The sample code above will work in a simple
-> testing situation such as in an IDE's debugger, and it may work in a
-> desktop application, but it won't work inside Tomcat. It has two
-> problems, both of which have to do with Java's protocol handling
-> facilities. The first problem is a protocol handler collision --
-> Tomcat has already instantiated an HTTPS handler, and it is not the
-> one we need for GSI+HTTPS. The second problem has to do with class
-> loading: our special HTTPS protocol handlers are not accessible to
-> Java's protocol handling code because they are not loaded by
-> Tomcat's root classloader.
->
->
-> -
-> You will most likely need to install the Metacat server and the
-> Metacat client application in separate instances of
-> Tomcat in order for them to successfully connect.
->
->
-> -
->
Protocol Handler Collisions:
->
-> Short answer: replace "https" with
-> "httpg".
-> https://<metacat server>:8443/metacat/metacat/
-> becomes
-> httpg://<metacat server>:8443/metacat/metacat/
->
-> Long answer: The URL in the example above,
-> https://<metacat server>:8443/metacat/metacat/,
-> uses the HTTPS protocol. However, actually
-> handling that connection requires a protocol handler that
-> understands GSI+HTTPS instead of plain HTTPS. Fortunately,
-> static initialization code in
-> MetacatGsiClient specifies a non-default
-> HTTPS handler before the default handler is
-> instantiated by Java.
->
-> Unfortunately, in Tomcat, an HTTPS handler has
-> already been instantiated by the time Metacat code runs, and we
-> don't have a chance to instantiate our special handler.
-> Besides, other applications also running in Tomcat may need to
-> use the default handler.
->
-> Instead, we can define a new protocol. It could be named
-> anything -- HTTPGSI, METACAT_GSI,
-> GRID_HTTP, etc. Metacat defines a handler for a
-> protocol named HTTPG. To see how it works, see the
-> static initialization code in
-> edu.ucsb.nceas.metacat.client.MetacatGsiClient and
-> the very simple class
-> edu.ucsb.nceas.protocols.httpg.Handler.
->
-> -
->
Tomcat Classloading: Since the Java classes that dynamically
-> load protocol handlers are in Tomcat's root classloader, the
-> protocol handlers themselves must be there also.
->
->
-> -
-> Copy
metacat.jar, utilities.jar,
-> and cog-url-ncsa.jar from Metacat's lib
-> directory to a place that is accessible by Tomcat, such as
-> $CATALINA_HOME/common/lib.
->
-> -
->
Modify the definition of CLASSPATH in
-> Tomcat's startup script,
-> $CATALINA_HOME/bin/catalina.sh (In Windows,
-> catalina.bat, which has slightly different
-> syntax).
->
-> Replace:
->
-> CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/bootstrap.jar:\
-> "$CATALINA_HOME"/bin/commons-logging-api.jar
->
-> With:
->
-> CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/bootstrap.jar:\
-> "$CATALINA_HOME"/bin/commons-logging-api.jar:"$CATALINA_HOME"/common/lib/metacat.jar:\
-> "$CATALINA_HOME"/common/lib/utilities.jar:"$CATALINA_HOME"/common/lib/cog-url-ncsa.jar
->
->
->
->
->
->
->
-> |
->
->
->
-Index: lib/metacat.properties
-===================================================================
-RCS file: /cvs/metacat/lib/metacat.properties,v
-retrieving revision 1.100
-diff -r1.100 metacat.properties
-43,54d42
-< moderators=@moderators@
-<
-< allowedSubmitters=@allowedSubmitters@
-<
-< deniedSubmitters=@deniedSubmitters@
-<
-< timedreplication=@timedreplication@
-<
-< firsttimedreplication=@firsttimedreplication@
-<
-< timedreplicationinterval=@timedreplicationinterval@
-<
-89,90d76
-< indexed_paths=organizationName,originator/individualName/surName,originator/individualName/givenName,originator/organizationName,creator/individualName/surName,creator/individualName/givenName,creator/organizationName,dataset/title,keyword,northBoundingCoordinate,southBoundingCoordinate,westBoundingCoordinate,eastBoundingCoordinate,title,entityName,individualName/surName,abstract/para,surName,givenName,para,geographicDescription,literalLayout
-<
-97c83,88
-< authclass=edu.ucsb.nceas.metacat.AuthLdap
----
-> authclass=edu.ucsb.nceas.metacat.Auth@auth-method@
-> authDelegateClass=edu.ucsb.nceas.metacat.Auth@auth-delegate@
-> authDelegationAllowed=@auth-delegation-allowed@
-> authPrecedence=@auth-precedence@
-> gsiMapClass=edu.ucsb.nceas.metacat.GsiMapfile
-> trustLocalHost=@auth-trust-localhost@
-99c90,91
-< ldapurl=ldap://ldap.ecoinformatics.org:389/
----
-> #ldapurl=ldap://ldap.ecoinformatics.org:389/
-> ldapurl=ldap://ldap.lternet.edu/
-101c93,94
-< ldapsurl=ldap://ldap.ecoinformatics.org:389/
----
-> #ldapsurl=ldap://ldap.ecoinformatics.org:389/
-> ldapsurl=ldap://ldap.lternet.edu/
-103c96,97
-< ldapbase=dc=ecoinformatics,dc=org
----
-> #ldapbase=dc=ecoinformatics,dc=org
-> ldapbase=o=lter,dc=ecoinformatics,dc=org
-Index: src/edu/ucsb/nceas/metacat/AuthInterface.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/AuthInterface.java,v
-retrieving revision 1.7
-diff -r1.7 AuthInterface.java
-46,48c46,47
-< * @param user the name of the principal to authenticate
-< * @param password the password to use for authentication
-< * @returns boolean true if authentication successful, false otherwise
----
-> * @param info authentication information from the user
-> * @return true if authentication successful, false otherwise
-50c49
-< public boolean authenticate(String user, String password)
----
-> public boolean authenticate(AuthInfo info)
-56c55
-< public String[][] getUsers(String user, String password)
----
-> public String[][] getUsers(AuthInfo info)
-62c61
-< public String[] getUsers(String user, String password, String group)
----
-> public String[] getUsers(AuthInfo info, String group)
-68c67
-< public String[][] getGroups(String user, String password)
----
-> public String[][] getGroups(AuthInfo info)
-74c73
-< public String[][] getGroups(String user, String password, String foruser)
----
-> public String[][] getGroups(AuthInfo info, String foruser)
-80,81c79,80
-< * @param user the user for which the attribute list is requested
-< * @returns HashMap a map of attribute name to a Vector of values
----
-> * @param foruser the user for which the attribute list is requested
-> * @return a map of attribute name to a Vector of values
-89,92c88,90
-< * @param user the user for which the attribute list is requested
-< * @param authuser the user for authenticating against the service
-< * @param password the password for authenticating against the service
-< * @returns HashMap a map of attribute name to a Vector of values
----
-> * @param info authentication information to use to access the directory
-> * @param foruser the user for which the attribute list is requested
-> * @return a map of attribute name to a Vector of values
-94c92
-< public HashMap getAttributes(String user, String password, String foruser)
----
-> public HashMap getAttributes(AuthInfo info, String foruser)
-100,101c98,99
-< * @param user the user which requests the information
-< * @param password the user's password
----
-> * @param info authentication information about the user who requests the
-> * information
-103c101
-< public String getPrincipals(String user, String password)
----
-> public String getPrincipals(AuthInfo info)
-Index: src/edu/ucsb/nceas/metacat/AuthLdap.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/AuthLdap.java,v
-retrieving revision 1.55
-diff -r1.55 AuthLdap.java
-31a32,33
-> import javax.naming.*;
-> import javax.naming.directory.*;
-33,49d34
-< import javax.naming.AuthenticationException;
-< import javax.naming.Context;
-< import javax.naming.NamingEnumeration;
-< import javax.naming.NamingException;
-< import javax.naming.SizeLimitExceededException;
-< import javax.naming.InitialContext;
-< import javax.naming.directory.InvalidSearchFilterException;
-< import javax.naming.directory.Attribute;
-< import javax.naming.directory.Attributes;
-< import javax.naming.directory.BasicAttribute;
-< import javax.naming.directory.BasicAttributes;
-< import javax.naming.directory.DirContext;
-< import javax.naming.directory.InitialDirContext;
-< import javax.naming.directory.SearchResult;
-< import javax.naming.directory.SearchControls;
-< import javax.naming.ReferralException;
-< import javax.naming.ldap.*;
-51,56c36
-< import java.util.Iterator;
-< import java.util.HashMap;
-< import java.util.Hashtable;
-< import java.util.Enumeration;
-< import java.util.Set;
-< import java.util.Vector;
----
-> import java.util.*;
-66d45
-< private MetaCatUtil util = new MetaCatUtil();
-73,76c52
-< private Context rContext;
-< private String userName;
-< private String userPassword;
-< ReferralException refExc;
----
-> ReferralException refExc;
-94,96c70,71
-< * @param user the name of the principal to authenticate
-< * @param password the password to use for authentication
-< * @returns boolean true if authentication successful, false otherwise
----
-> * @param info authentication information from the user
-> * @return true if authentication successful, false otherwise
-98c73
-< public boolean authenticate(String user, String password) throws
----
-> public boolean authenticate(AuthInfo info) throws
-100,104c75,76
-< String ldapUrl = this.ldapUrl;
-< String ldapsUrl = this.ldapsUrl;
-< String ldapBase = this.ldapBase;
-< boolean authenticated = false;
-< String identifier = user;
----
-> boolean authenticated = false;
-> String identifier = info.getUsername();
-110c82
-< authenticated = ldapAuthenticate(identifier, password);
----
-> authenticated = ldapAuthenticate(identifier, info.getPassword());
-119,120c91,92
-< String refUrl = "";
-< String refBase = "";
----
-> String refUrl;
-> String refBase;
-132c104
-< authenticated = ldapAuthenticate(identifier, password,
----
-> authenticated = ldapAuthenticate(identifier, info.getPassword(),
-138c110
-< authenticated = ldapAuthenticate(identifier, password);
----
-> authenticated = ldapAuthenticate(identifier, info.getPassword());
-171c143
-< ConnectException, NamingException, NullPointerException {
----
-> NamingException, NullPointerException {
-185c157
-< ConnectException, NamingException, NullPointerException {
----
-> NamingException, NullPointerException {
-191,192c163,164
-< userName = identifier;
-< userPassword = password;
----
-> // userName = identifier;
-> // userPassword = password;
-220,221d191
-< this.ldapUrl = ldapUrl;
-< this.ldapBase = ldapBase;
-272c242
-< * @returns String the identifying name for the user,
----
-> * @return String the identifying name for the user,
-423,425c393
-< * @param user the user for authenticating against the service
-< * @param password the password for authenticating against the service
-< * @returns string array of all of the user names
----
-> * @return string array of all of the user names
-427c395
-< public String[][] getUsers(String user, String password) throws
----
-> public String[][] getUsers(AuthInfo info) throws
-429c397
-< String[][] users = null;
----
-> String[][] users;
-460c428
-< Attributes tempAttr = null;
----
-> Attributes tempAttr;
-508c476
-< users[i][3] = (String) uorg.elementAt(i);
----
-> users[i][3] = (String) uou.elementAt(i);
-529,530c497
-< * @param user the user for authenticating against the service
-< * @param password the password for authenticating against the service
----
-> * @param info represents the user making the request
-532c499
-< * @returns string array of the user names belonging to the group
----
-> * @return string array of the user names belonging to the group
-534c501
-< public String[] getUsers(String user, String password, String group) throws
----
-> public String[] getUsers(AuthInfo info, String group) throws
-536c503
-< String[] users = null;
----
-> String[] users;
-564d530
-< ;
-596,598c562,563
-< * @param user the user for authenticating against the service
-< * @param password the password for authenticating against the service
-< * @returns string array of the group names
----
-> * @param info represents the user making the request
-> * @return string array of the group names
-600c565
-< public String[][] getGroups(String user, String password) throws
----
-> public String[][] getGroups(AuthInfo info) throws
-602c567
-< return getGroups(user, password, null);
----
-> return getGroups(info, null);
-608,609c573
-< * @param user the user for authenticating against the service
-< * @param password the password for authenticating against the service
----
-> * @param info represents the user making the request
-611c575
-< * @returns string array of the group names
----
-> * @return string array of the group names
-613c577
-< public String[][] getGroups(String user, String password, String foruser) throws
----
-> public String[][] getGroups(AuthInfo info, String foruser) throws
-617c581
-< Attributes tempAttr = null;
----
-> Attributes tempAttr;
-620,621c584,585
-< userName = user;
-< userPassword = password;
----
-> // userName = user;
-> // userPassword = password;
-640c604
-< String filter = null;
----
-> String filter;
-708c672
-< String filter = null;
----
-> String filter;
-768c732
-< * @returns HashMap a map of attribute name to a Vector of values
----
-> * @return HashMap a map of attribute name to a Vector of values
-771c735
-< return getAttributes(null, null, foruser);
----
-> return getAttributes(null, foruser);
-777,778c741
-< * @param user the user for authenticating against the service
-< * @param password the password for authenticating against the service
----
-> * @param info represents the user making the request
-780c743
-< * @returns HashMap a map of attribute name to a Vector of values
----
-> * @return HashMap a map of attribute name to a Vector of values
-782c745
-< public HashMap getAttributes(String user, String password, String foruser) throws
----
-> public HashMap getAttributes(AuthInfo info, String foruser) throws
-785,787d747
-< String ldapUrl = this.ldapUrl;
-< String ldapBase = this.ldapBase;
-< String userident = foruser;
-837,838c797
-< private Hashtable getSubtrees(String user, String password,
-< String ldapUrl, String ldapBase) throws
----
-> private Hashtable getSubtrees(String ldapUrl, String ldapBase) throws
-886c845
-< String attrName = (String) attr.getID();
----
-> String attrName = attr.getID();
-891c850
-< String refName = (String) attr.getID();
----
-> String refName = attr.getID();
-946,947c905
-< * @param user the user which requests the information
-< * @param password the user's password
----
-> * @param info Auth Info representing the user who requests the information
-949c907
-< public String getPrincipals(String user, String password) throws
----
-> public String getPrincipals(AuthInfo info) throws
-952c910,911
-<
----
-> Vector usersIn = new Vector();
->
-960,961c919
-< Hashtable subtrees = getSubtrees(user, password, this.ldapUrl,
-< this.ldapBase);
----
-> Hashtable subtrees = getSubtrees(this.ldapUrl, this.ldapBase);
-963c921
-< Enumeration keyEnum = subtrees.keys();
----
-> Enumeration keyEnum = subtrees.keys();
-1002,1003c960,961
-< String[][] groups = getGroups(user, password);
-< String[][] users = getUsers(user, password);
----
-> String[][] groups = getGroups(info);
-> String[][] users = getUsers(info);
-1012c970
-< String[] usersForGroup = getUsers(user, password, groups[i][0]);
----
-> String[] usersForGroup = getUsers(info, groups[i][0]);
-1014c972,973
-<
----
-> usersIn.addElement(usersForGroup[j]);
->
-1042a1002
-> if (!usersIn.contains(users[j][0])) {
-1053a1014
-> }
-1056a1018,1022
-> if (!usersIn.isEmpty()) {
-> usersIn.removeAllElements();
-> usersIn.trimToSize();
-> }
->
-1087c1053
-< boolean isValid = false;
----
-> boolean isValid;
-1090c1056,1057
-< isValid = authservice.authenticate(user, password);
----
-> AuthInfo info = new AuthInfo(user, password);
-> isValid = authservice.authenticate(info);
-1101c1068
-< HashMap userInfo = authservice.getAttributes(user, password, user);
----
-> HashMap userInfo = authservice.getAttributes(info, user);
-1103c1070
-< Iterator attList = (Iterator) ( ( (Set) userInfo.keySet()).iterator());
----
-> Iterator attList = userInfo.keySet().iterator();
-1118c1085
-< String[][] groups = authservice.getGroups(user, password);
----
-> String[][] groups = authservice.getGroups(info);
-1129c1096
-< String[][] groups = authservice.getGroups(user, password, user);
----
-> String[][] groups = authservice.getGroups(info, user);
-1141c1108
-< String[] users = authservice.getUsers(user, password, savedGroup);
----
-> String[] users = authservice.getUsers(info, savedGroup);
-1151c1118
-< String[][] users = authservice.getUsers(user, password);
----
-> String[][] users = authservice.getUsers(info);
-1160c1127
-< String out = authservice.getPrincipals(user, password);
----
-> String out = authservice.getPrincipals(info);
-1186c1153
-< DirContext refDirContext = null;
----
-> DirContext refDirContext;
-1188c1155
-< String referralInfo = null;
----
-> String referralInfo;
-1202,1203c1169,1170
-< //MetaCatUtil.logMetacat.info("Processing referral (pr1.info): " + userName,35);
-< //MetaCatUtil.logMetacat.info("Processing referral (pr2)",35);
----
-> //MetaCatUtil.logMetacat.info("Processing referral (pr1.info): " + userName);
-> //MetaCatUtil.logMetacat.info("Processing referral (pr2)");
-1205c1172
-< rContext = refExc.getReferralContext();
----
-> Context rContext = refExc.getReferralContext();
-1251d1217
-< DirContext refDirContext = null;
-1258,1259c1224
-< String refInfo = null;
-< refInfo = (String) refExc.getReferralInfo();
----
-> String refInfo = (String) refExc.getReferralInfo();
-1262c1227
-< refInfo.toString());
----
-> refInfo);
-Index: src/edu/ucsb/nceas/metacat/AuthSession.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/AuthSession.java,v
-retrieving revision 1.18
-diff -r1.18 AuthSession.java
-30,31d29
-< import java.net.ConnectException;
-< import javax.servlet.http.HttpSession;
-32a31,32
-> import javax.servlet.http.HttpSession;
-> import java.net.ConnectException;
-41c41
-< private String authClass = null;
----
->
-52,54c52,86
-< MetaCatUtil util = new MetaCatUtil();
-< this.authClass = util.getOption("authclass");
-< this.authService = (AuthInterface)createObject(authClass);
----
-> String authClass = MetaCatUtil.getOption("authclass");
-> this.authService = (AuthInterface)createObject(authClass);
->
-> if (authService instanceof AuthGsi) {
-> // special config options for GSI authentication
-> String authDelegateClass = MetaCatUtil.getOption("authDelegateClass");
-> String gsiMapClass = MetaCatUtil.getOption("gsiMapClass");
-> String authDelegationAllowed = MetaCatUtil.getOption("authDelegationAllowed");
-> String authPrecedence = MetaCatUtil.getOption("authPrecedence");
->
-> AuthGsi gsi = (AuthGsi) authService;
-> if (authDelegateClass != null) {
-> AuthInterface delegate = (AuthInterface) createObject(authDelegateClass);
-> gsi.setDelegate(delegate);
-> }
-> if (gsiMapClass != null) {
-> GsiToUsernameMap map = (GsiToUsernameMap) createObject(gsiMapClass);
-> gsi.setDnMap(map);
-> }
-> if (authDelegationAllowed != null) {
-> boolean allowed = Boolean.valueOf(authDelegationAllowed).booleanValue();
-> if (!allowed && !authDelegationAllowed.trim().toLowerCase().equals("false"))
-> MetaCatUtil.logMetacat.warn("Config error: authDelegationAllowed "
-> + "must be \"true\" or \"false\" (found \""
-> + authDelegationAllowed + "\".");
-> gsi.setAuthnDelegationAllowed(allowed);
-> }
-> if (authPrecedence != null) {
-> try {
-> gsi.setPrecedence(authPrecedence);
-> } catch(IllegalArgumentException e) {
-> System.err.println(e.getMessage());
-> }
-> }
-> }
-70,71c102
-< * @param username the username entered when login
-< * @param password the password entered when login
----
-> * @param info info from the user, such as username and password
-73,77c104,107
-< public boolean authenticate(HttpServletRequest request,
-< String username, String password) {
-< String message = null;
-< try {
-< if ( authService.authenticate(username, password) ) {
----
-> public boolean authenticate(HttpServletRequest request, AuthInfo info) {
-> String message;
-> try {
-> if ( authService.authenticate(info) ) {
-79c109
-< // getGroups returns groupname along with their description.
----
-> // getGroups returns groupname along with their description.
-82c112
-< authService.getGroups(username,password,username);
----
-> authService.getGroups(info,info.getUsername());
-93c123
-< this.session = createSession(request, username, password, groups);
----
-> this.session = createSession(request, info, groups);
-95c125
-< message = "Authentication successful for user: " + username;
----
-> message = "Authentication successful for user: " + info;
-99c129
-< message = "Authentication failed for user: " + username;
----
-> message = "Authentication failed for user: " + info;
-104c134,135
-< message = "Connection to the authentication service failed in " +
----
-> ce.printStackTrace();
-> message = "Connection to the authentication service failed in " +
-107c138,139
-< message = ise.getMessage();
----
-> ise.printStackTrace();
-> message = ise.getMessage();
-114,116c146,147
-< /** Get new HttpSession and store username & password in it */
-< private HttpSession createSession(HttpServletRequest request,
-< String username, String password,
----
-> /** Get new HttpSession and store authentication info in it */
-> private HttpSession createSession(HttpServletRequest request, AuthInfo info,
-129c160
-< session.getAttribute("username"));
----
-> session.getAttribute("auth"));
-137,138c168
-< session.setAttribute("username", username);
-< session.setAttribute("password", password);
----
-> session.setAttribute("auth", info);
-145c175
-< session.getAttribute("username"));
----
-> session.getAttribute("auth"));
-161,162c191
-< * @param user the user which requests the information
-< * @param password the user's password
----
-> * @param info represents the user who requests the information
-164c193
-< public String getPrincipals(String user, String password)
----
-> public String getPrincipals(AuthInfo info)
-167c196
-< return authService.getPrincipals(user, password);
----
-> return authService.getPrincipals(info);
-210c239
-< Object object = null;
----
-> Object object;
-Index: src/edu/ucsb/nceas/metacat/MetaCatServlet.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/MetaCatServlet.java,v
-retrieving revision 1.226
-diff -r1.226 MetaCatServlet.java
-30,35c30
-< import java.io.BufferedInputStream;
-< import java.io.File;
-< import java.io.FileInputStream;
-< import java.io.IOException;
-< import java.io.PrintWriter;
-< import java.io.StringReader;
----
-> import java.io.*;
-37a33,34
-> import java.net.URLEncoder;
-> import java.net.URLDecoder;
-44,48c41,42
-< import java.util.Enumeration;
-< import java.util.Hashtable;
-< import java.util.Iterator;
-< import java.util.PropertyResourceBundle;
-< import java.util.Vector;
----
-> import java.util.*;
-> import java.util.regex.PatternSyntaxException;
-52,55c46
-< import javax.servlet.ServletConfig;
-< import javax.servlet.ServletContext;
-< import javax.servlet.ServletException;
-< import javax.servlet.ServletOutputStream;
----
-> import javax.servlet.*;
-60a52,53
-> import edu.ucsb.nceas.utilities.Options;
->
-61a55
-> import org.ietf.jgss.GSSContext;
-68,69d61
-< import edu.ucsb.nceas.utilities.Options;
-<
-164c156
-<
----
->
-166c158
-<
----
->
-181,182c173,174
-< LOG_CONFIG_NAME = dirPath + "/log4j.properties";
-<
----
-> LOG_CONFIG_NAME = dirPath + "/log4j.properties";
->
-213,214d204
-< } catch (ServletException ex) {
-< throw ex;
-235d224
-<
-237c226,230
-< handleGetOrPost(request, response);
----
-> try { handleGetOrPost(request, response); }
-> catch(RuntimeException e) {
-> e.printStackTrace();
-> throw new ServletException(e);
-> }
-244d236
-<
-246c238,242
-< handleGetOrPost(request, response);
----
-> try { handleGetOrPost(request, response); }
-> catch(RuntimeException e) {
-> e.printStackTrace();
-> throw new ServletException(e);
-> }
-248a245,255
-> /** Copied from org.globus.axis.gsi.GSIConstants, so that we can avoid
-> * a compile dependency. */
-> private static final String
-> GSI_CREDENTIALS = "org.globus.gsi.credentials",
-> GSI_AUTHORIZATION = "org.globus.gsi.authorization",
-> GSI_MODE = "org.globus.gsi.mode",
-> GSI_AUTH_USERNAME = "org.globus.gsi.authorized.user.name",
-> GSI_USER_DN = "org.globus.gsi.authorized.user.dn",
-> GSI_ANONYMOUS = "org.globus.gsi.anonymous",
-> GSI_CONTEXT = "org.globus.gsi.context";
->
-388d394
-<
-403c409,410
-< connPool.printMethodNameHavingBusyDBConnection();
----
-> if (connPool != null)
-> connPool.printMethodNameHavingBusyDBConnection();
-415a423,438
-> Enumeration headerNames = request.getHeaderNames();
-> while (headerNames.hasMoreElements()) {
-> String headerName = (String) headerNames.nextElement();
-> }
->
-> // check whether incoming connection is via GSI-enabled HTTPS
-> // these constants are available from GSIConstants, but not imported
-> // here, to avoid dependencies
-> GSSContext gsiContext = (GSSContext) request.getAttribute(GSI_CONTEXT);
-> String gsiUserDN = (String) request.getAttribute(GSI_USER_DN);
-> boolean gssConnection = gsiContext != null && gsiUserDN != null;
-> if (gssConnection) {
-> params.put(GSI_CONTEXT, gsiContext);
-> params.put(GSI_USER_DN, gsiUserDN);
-> }
->
-433a457,499
-> // Get extra POST parameters because when the incoming request uses
-> // chunked coding, they don't automatically get parsed.
-> if ("application/x-www-form-urlencoded".equals(ctype)) {
-> try {
-> String post = readInputStreamAsString(request);
-> if (post != null && post.length() > 0) {
-> String[] posts = post.split("&");
-> for (int i = 0; i < posts.length; ++i) {
-> String[] a = posts[i].split("=");
-> for (int j = 0; j < a.length; ++j)
-> a[j] = URLDecoder.decode(a[j]);
-> if (a.length >= 2) {
-> addToMultimap(params, a[0], a[1]);
-> }
-> }
-> }
-> } catch(Exception e) {
-> String msg = "Exception reading remaining POST data: "
-> + e.getMessage();
-> if (MetaCatUtil.debugMessage(msg, 20))
-> e.printStackTrace();
-> }
-> }
->
-> String[] usernames = (String[]) params.get("username"),
-> passwords = (String[]) params.get("password");
-> String username = (usernames != null && usernames.length > 0)
-> ? usernames[0] : null;
-> String password = (passwords != null && passwords.length > 0)
-> ? passwords[0] : null;
-> // if no other identifiers, set username to "public" (anonymous)
-> if (username == null && gsiUserDN == null)
-> username = "public";
-> AuthInfo authInfo = new AuthInfo
-> (username, password, gsiContext, gsiUserDN, request);
->
-> for (Iterator i = params.keySet().iterator(); i.hasNext();) {
-> String key = (String) i.next();
-> Object val = params.get(key);
-> if (val == null) val = "null";
-> if (!(val instanceof Object[])) val = new Object[] { val };
-> }
->
-456,457d521
-< String username = null;
-< String password = null;
-464c528
-< handleLoginAction(out, params, request, response);
----
-> handleLoginAction(out, authInfo, params, request, response);
-476d539
-< boolean success = false;
-478,482c541,543
-< // pool
-< //size is greater than initial value, shrink the connection
-< // pool
-< //size to initial value
-< success = DBConnectionPool.shrinkConnectionPoolSize();
----
-> //pool size is greater than initial value, shrink the
-> //connection pool size to initial value
-> boolean success = DBConnectionPool.shrinkConnectionPoolSize();
-496c557,573
-< if (sess.isNew() && !params.containsKey("sessionid")) {
----
-> boolean newSession = sess.isNew() && !params.containsKey("sessionid");
-> if (authInfo.getGssContext() != null) {
-> // if using GSI for authentication, we lose our session
-> // each time, so we have to re-authenticate; fortunately
-> // we have enough information to do so
-> try {
-> AuthSession authSession = new AuthSession();
-> authSession.authenticate(request, authInfo);
-> // call it a continuing session
-> newSession = false;
-> } catch (Exception e) {
-> throw new ServletException
-> ("Unable to authenticate based on GSI credentials: "
-> + e.getMessage(), e);
-> }
-> }
-> if (newSession) {
-499a577
-> authInfo = new AuthInfo("public", null);
-501c579
-< sess.setAttribute("username", username);
----
-> sess.setAttribute("auth", authInfo);
-524c602
-< * sess.getAttribute("username") + " into session
----
-> * sess.getAttribute("auth") + " into session
-535c613,614
-< username = (String) sess.getAttribute("username");
----
-> authInfo = (AuthInfo) sess.getAttribute("auth");
-> username = authInfo.getUsername();
-538d616
-< password = (String) sess.getAttribute("password");
-543c621,623
-< if (username == null || (username.trim().equals(""))) {
----
-> if ((username == null || (username.trim().equals("")))
-> && gsiContext == null )
-> {
-544a625,626
-> authInfo = new AuthInfo(username, null);
-> sess.setAttribute("auth", authInfo);
-619c701
-< handleGetPrincipalsAction(out, username, password);
----
-> handleGetPrincipalsAction(out, authInfo);
-646c728
-< } else if (action.equals("login") || action.equals("logout")) {
----
-> } else if (action.equals("login") || action.equals("logout"), groupnames) {
-709a792,841
-> /** Assume that map's keys are objects and its values are arrays. */
-> private void addToMultimap(Map params, String key, String value) {
-> if (params.containsKey(key)) {
-> String[] oldArray;
-> Object old = params.get(key);
-> if (old instanceof String[]) oldArray = (String[]) old;
-> else {
-> oldArray = new String[1];
-> oldArray[0] = String.valueOf(old);
-> }
-> String[] newArray = new String[oldArray.length + 1];
-> System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
-> newArray[newArray.length - 1] = value;
-> params.put(key, newArray);
-> }
-> else params.put(key, new String[] { value });
-> }
->
-> private static final int READ_CHUNK_SIZE = 1024;
->
-> private String readInputStreamAsString(HttpServletRequest request)
-> throws IOException
-> {
-> ServletInputStream in = request.getInputStream();
-> List byteses = new ArrayList();
-> int total = 0;
-> while(true) {
-> byte[] k = new byte[READ_CHUNK_SIZE];
-> int n = in.read(k);
-> if (n <= 0) break;
-> else {
-> if (n < READ_CHUNK_SIZE) {
-> byte[] k2 = new byte[n];
-> System.arraycopy(k, 0, k2, 0, n);
-> k = k2;
-> }
-> total += n;
-> byteses.add(k);
-> }
-> }
-> byte[] all = new byte[total];
-> int start = 0;
-> for (int i = 0; i < byteses.size(); i++) {
-> byte[] k = (byte[]) byteses.get(i);
-> System.arraycopy(k, 0, all, start, k.length);
-> start += k.length;
-> }
-> return new String(all);
-> }
->
-715c847
-< private void handleLoginAction(PrintWriter out, Hashtable params,
----
-> private void handleLoginAction(PrintWriter out, AuthInfo info, Hashtable params,
-719c851
-< AuthSession sess = null;
----
-> AuthSession sess;
-721c853
-< if(params.get("username") == null){
----
-> if(info.getUsername() == null && info.getGssContext() == null){
-730c862,864
-< if(params.get("password") == null){
----
-> if(info.getPassword() == null && info.getGssContext() == null
-> && !info.isLocal())
-> {
-739,741c873,874
-< String un = ((String[]) params.get("username"))[0];
-< MetaCatUtil.debugMessage("user " + un + " try to login", 20);
-< String pw = ((String[]) params.get("password"))[0];
----
-> MetaCatUtil.debugMessage
-> ("user " + info.toString(true) + " try to login", 20);
-756c889,890
-< boolean isValid = sess.authenticate(request, un, pw);
----
->
-> boolean isValid = sess.authenticate(request, info);
-763c897
-< + "which has username" + session.getAttribute("username")
----
-> + "which has username" + session.getAttribute("auth")
-807c941
-< + sess.getAttribute("username")
----
-> + sess.getAttribute("auth")
-941,943c1075,1077
-< "attachment; filename="
-< + docId + ".zip"); // Set the name of the zip file
-<
----
-> "attachment; filename="
-> + docId + ".zip"); // Set the name of the zip file
->
-1357c1491
-<
----
->
-1642c1776
-<
----
->
-1644c1778
-< out.println("");
----
-> out.println("");
-2169,2170c2303
-< private void handleGetPrincipalsAction(PrintWriter out, String user,
-< String password)
----
-> private void handleGetPrincipalsAction(PrintWriter out, AuthInfo info)
-2174c2307
-< String principals = auth.getPrincipals(user, password);
----
-> String principals = auth.getPrincipals(info);
-2478,2479c2611
-< String username = null;
-< String password = null;
----
-> AuthInfo auth = null;
-2485a2618
-> // TODO: don't do this if GSI, since session will be new every time
-2487,2488c2620,2621
-< username = "public";
-< sess.setAttribute("username", username);
----
-> auth = new AuthInfo("public", null);
-> sess.setAttribute("auth", auth);
-2490,2491c2623
-< username = (String) sess.getAttribute("username");
-< password = (String) sess.getAttribute("password");
----
-> auth = (AuthInfo) sess.getAttribute("auth");
-2512,2513c2644,2645
-< if (username != null && !username.equals("public")) {
-< handleUploadAction(request, out, params, fileList, username,
----
-> if (auth != null && !"public".equals(auth.getUsername())) {
-> handleUploadAction(request, out, params, fileList, auth.getUsername(),
-2909c3041
-<
----
->
-Index: src/edu/ucsb/nceas/metacat/client/Metacat.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/client/Metacat.java,v
-retrieving revision 1.14
-diff -r1.14 Metacat.java
-26a27,28
-> import org.ietf.jgss.GSSCredential;
->
-54c56,88
-< /**
----
-> /**
-> * Log in to a Metacat server using a Grid Security Infrastructure (GSI)
-> * credential to establish an HTTPS connection. Instead of authenticating
-> * the user via username and password, the user's identity will be
-> * extracted from the credential's Distinguished Name (DN).
-> *
-> * Note that some installations will not have the necessary libraries to
-> * run GSI, and therefore we need to be able to run the non-GSI Metacat
-> * client even if those JARs are absent -- catching NoClassDefFoundError,
-> * etc.
-> *
-> * @return the response string from metacat in XML format
-> * @throws MetacatAuthException when the client certificate is missing or
-> * is not trusted or represents a user who is unknown or not authorized to
-> * log in, or if the underlying connection is HTTP instead of HTTPS.
-> * @throws UnsupportedOperationException if this client does not support
-> * GSI-HTTPS.
-> */
-> public String login(GSSCredential credential)
-> throws MetacatAuthException, MetacatInaccessibleException;
->
-> /**
-> * Log in over a trusted connection (usually localhost HTTP) with just
-> * a username to identify the user. The server will only allow this login
-> * method if it is configured to fully trust incoming connections from this
-> * client.
-> *
-> * This may be used, depending on the server's configuration, with
-> * a PKI Distinguished Name (DN), or with an LDAP name.
-> */
-> public String login(String username) throws MetacatInaccessibleException, MetacatAuthException;
->
-> /**
-148d181
-< * @param xmlDocument a Reader for accessing the document to be inserted
-238c271
-< * @returns the sessionId as a String, or null if the session is invalid
----
-> * @return the sessionId as a String, or null if the session is invalid
-248c281
-< * @param String the sessionId from a previously established session
----
-> * @param sessionId the session ID from a previously established session
-Index: src/edu/ucsb/nceas/metacat/client/MetacatClient.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/client/MetacatClient.java,v
-retrieving revision 1.18
-diff -r1.18 MetacatClient.java
-27,36d26
-< import java.io.BufferedReader;
-< import java.io.InputStream;
-< import java.io.InputStreamReader;
-< import java.io.PushbackReader;
-< import java.io.IOException;
-< import java.io.StringWriter;
-< import java.io.Reader;
-< import java.net.URL;
-< import java.util.Properties;
-<
-39c29,34
-< import java.io.File;
----
-> import edu.ucsb.nceas.metacat.client.gsi.MetacatGsiClient;
-> import org.ietf.jgss.GSSCredential;
->
-> import java.io.*;
-> import java.net.URL;
-> import java.util.Properties;
-49,50c44,45
-< /** The URL string for the metacat server */
-< private String metacatUrl;
----
-> /** The URL string for the metacat server */
-> private String metacatUrl;
-52,53c47,48
-< /** The session identifier for the session */
-< private String sessionId;
----
-> /** The session identifier for the session */
-> private String sessionId;
-55,802c50,839
-< /**
-< * Constructor to create a new instance. Protected because instances
-< * should only be created by the factory MetacatFactory.
-< */
-< protected MetacatClient()
-< {
-< this.metacatUrl = null;
-< this.sessionId = null;
-< }
-<
-< /**
-< * Method used to log in to a metacat server. Implementations will need
-< * to cache a cookie value to make the session persistent. Each time a
-< * call is made to one of the other methods (e.g., read), the cookie will
-< * need to be passed back to the metacat server along with the request.
-< *
-< * @param username the username of the user, like an LDAP DN
-< * @param password the password for that user for authentication
-< * @return the response string from metacat in XML format
-< * @throws MetacatAuthException when the username/password could
-< * not be authenticated
-< */
-< public String login(String username, String password)
-< throws MetacatAuthException, MetacatInaccessibleException
-< {
-< Properties prop = new Properties();
-< prop.put("action", "login");
-< prop.put("qformat", "xml");
-< prop.put("username", username);
-< prop.put("password", password);
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< if (response.indexOf("") == -1) {
-< setSessionId("");
-< throw new MetacatAuthException(response);
-< } else {
-< int start = response.indexOf("") + 11;
-< int end = response.indexOf("");
-< if ((start != -1) && (end != -1)) {
-< setSessionId(response.substring(start,end));
-< }
-< }
-< return response;
-< }
-<
-< /**
-< * Method used to log out a metacat server. The Metacat server will end
-< * the session when this call is invoked.
-< *
-< * @return the response string from metacat in XML format
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< */
-< public String logout() throws MetacatInaccessibleException, MetacatException
-< {
-< Properties prop = new Properties();
-< prop.put("action", "logout");
-< prop.put("qformat", "xml");
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< if (response.indexOf("") == -1) {
-< throw new MetacatException(response);
-< }
-< setSessionId("");
-< return response;
-< }
-<
-< /**
-< * Read an XML document from the metacat server session, accessed by docid,
-< * and returned as a Reader.
-< *
-< * @param docid the identifier of the document to be read
-< * @return a Reader for accessing the document
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< */
-< public Reader read(String docid) throws InsufficientKarmaException,
-< MetacatInaccessibleException, MetacatException
-< {
-< PushbackReader pbr = null;
-<
-< Properties prop = new Properties();
-< prop.put("action", "read");
-< prop.put("qformat", "xml");
-< prop.put("docid", docid);
-<
-< InputStream response = null;
-< try {
-< response = sendData(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< pbr = new PushbackReader(new InputStreamReader(response), 512);
-< try {
-< char[] characters = new char[512];
-< int len = pbr.read(characters, 0, 512);
-< StringWriter sw = new StringWriter();
-< sw.write(characters, 0, len);
-< String message = sw.toString();
-< sw.close();
-< pbr.unread(characters, 0, len);
-<
-< if (message.indexOf("") != -1) {
-< if (message.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(message);
-< } else {
-< throw new MetacatException(message);
-< }
-< }
-< } catch (IOException ioe) {
-< throw new MetacatException(
-< "MetacatClient: Error converting Reader to String."
-< + ioe.getMessage());
-< }
-<
-< return pbr;
-< }
-<
-<
-< /**
-< * Read inline data from the metacat server session, accessed by
-< * inlinedataid and returned as a Reader.
-< *
-< * @param inlinedataid the identifier of the data to be read
-< * @return a Reader for accessing the document
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< */
-< public Reader readInlineData(String inlinedataid)
-< throws InsufficientKarmaException,
-< MetacatInaccessibleException, MetacatException
-< {
-< PushbackReader pbr = null;
-<
-< Properties prop = new Properties();
-< prop.put("action", "readinlinedata");
-< prop.put("inlinedataid", inlinedataid);
-<
-< InputStream response = null;
-< try {
-< response = sendData(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< pbr = new PushbackReader(new InputStreamReader(response), 512);
-< try {
-< char[] characters = new char[512];
-< int len = pbr.read(characters, 0, 512);
-< StringWriter sw = new StringWriter();
-< sw.write(characters, 0, len);
-< String message = sw.toString();
-< sw.close();
-< pbr.unread(characters, 0, len);
-<
-< if (message.indexOf("") != -1) {
-< if (message.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(message);
-< } else {
-< throw new MetacatException(message);
-< }
-< }
-< } catch (IOException ioe) {
-< throw new MetacatException(
-< "MetacatClient: Error converting Reader to String."
-< + ioe.getMessage());
-< }
-<
-< return pbr;
-< }
-<
-< /**
-< * Query the metacat document store with the given metacat-compatible
-< * query document, and return the result set as a Reader.
-< *
-< * @param xmlQuery a Reader for accessing the XML version of the query
-< * @return a Reader for accessing the result set
-< */
-< public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
-< IOException
-< {
-< Reader reader = null;
-< String query = null;
-< try {
-< query = IOUtil.getAsString(xmlQuery, true);
-< } catch (IOException ioE) {
-< throw ioE;
-< }
-<
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "squery");
-< prop.put("qformat", "xml");
-< prop.put("query", query);
-<
-< InputStream response = null;
-< try {
-< response = sendData(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-< reader = new InputStreamReader(response);
-< return reader;
-< }
-<
-< /**
-< * Insert an XML document into the repository.
-< *
-< * @param docid the docid to insert the document
-< * @param xmlDocument a Reader for accessing the XML document to be inserted
-< * @param schema a Reader for accessing the DTD or XML Schema for
-< * the document
-< * @return the metacat response message
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< * @throws IOException when there is an error reading the xml document
-< */
-< public String insert(String docid, Reader xmlDocument, Reader schema)
-< throws InsufficientKarmaException, MetacatException, IOException,
-< MetacatInaccessibleException
-< {
-< Reader reader = null;
-< String doctext = null;
-< String schematext = null;
-< try {
-< doctext = IOUtil.getAsString(xmlDocument, true);
-< if (schema != null) {
-< schematext = IOUtil.getAsString(schema, true);
-< }
-< } catch (IOException ioE) {
-< throw ioE;
-< }
-<
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "insert");
-< prop.put("docid", docid);
-< prop.put("doctext", doctext);
-< if (schematext != null) {
-< prop.put("dtdtext", schematext);
-< }
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-<
-< return response;
-< }
-<
-< /**
-< * Update an XML document in the repository.
-< *
-< * @param docid the docid to update
-< * @param xmlDocument a Reader for accessing the XML text to be updated
-< * @param schema a Reader for accessing the DTD or XML Schema for
-< * the document
-< * @return the metacat response message
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< * @throws IOException when there is an error reading the xml document
-< */
-< public String update(String docid, Reader xmlDocument, Reader schema)
-< throws InsufficientKarmaException, MetacatException, IOException,
-< MetacatInaccessibleException
-< {
-< Reader reader = null;
-< String doctext = null;
-< String schematext = null;
-< try {
-< doctext = IOUtil.getAsString(xmlDocument, true);
-< if (schema != null) {
-< schematext = IOUtil.getAsString(schema, true);
-< }
-< } catch (IOException ioE) {
-< throw ioE;
-< }
-<
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "update");
-< prop.put("docid", docid);
-< prop.put("doctext", doctext);
-< if (schematext != null) {
-< prop.put("dtdtext", schematext);
-< }
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-<
-< return response;
-< }
-<
-< /**
-< * Upload a data document into the repository.
-< *
-< * @param docid the docid to insert the document
-< * @param document a Reader for accessing the document to be uploaded
-< * @return the metacat response message
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< * @throws IOException when there is an error reading the xml document
-< */
-< public String upload(String docid, File file)
-< throws InsufficientKarmaException, MetacatException, IOException,
-< MetacatInaccessibleException
-< {
-<
-< URL url = new URL(metacatUrl.trim());
-< HttpMessage msg = new HttpMessage(url);
-< //set up properties
-< Properties arg = new Properties();
-< arg.put("action", "upload");
-< arg.put("docid", docid);
-<
-< Properties filenames = new Properties();
-< String filename = file.getAbsolutePath();
-< filenames.put("datafile", filename);
-<
-< String response = null;
-< try {
-< response = sendDataForString(arg, filenames, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-<
-< return response;
-< }
-<
-< /**
-< * Upload a data document into the repository.
-< *
-< * @param docid the docid to insert the document
-< * @param document a Reader for accessing the document to be uploaded
-< * @return the metacat response message
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< * @throws IOException when there is an error reading the xml document
-< */
-<
-<
-< public String upload(String docid, String filename, InputStream fileData,
-< int size)
-< throws InsufficientKarmaException, MetacatException, IOException,
-< MetacatInaccessibleException {
-<
-< URL url = new URL(metacatUrl.trim());
-< HttpMessage msg = new HttpMessage(url);
-< //set up properties
-< Properties arg = new Properties();
-< arg.put("action", "upload");
-< arg.put("docid", docid);
-<
-< Properties filenames = new Properties();
-< filenames.put("datafile", filename);
-<
-< String response = null;
-< try {
-< response = sendDataForString(arg, filenames, fileData, size);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-<
-< return response;
-< }
-<
-< /**
-< * Delete an XML document in the repository.
-< *
-< * @param docid the docid to delete
-< * @return the metacat response message
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< */
-< public String delete(String docid)
-< throws InsufficientKarmaException, MetacatException,
-< MetacatInaccessibleException
-< {
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "delete");
-< prop.put("docid", docid);
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-< return response;
-< }
-<
-<
-< /**
-< * set the access on an XML document in the repository.
-< *
-< * @param _docid the docid of the document for which the access should be applied.
-< *
-< * @param _principal the document's principal
-< *
-< * @param _permission the access permission to be applied to the docid
-< * {e.g. read,write,all}
-< *
-< * @param _permType the permission type to be applied to the document
-< * {e.g. allow or deny}
-< *
-< * @param _permOrder the order that the document's permissions should be
-< * processed {e.g. denyFirst or allowFirst}
-< *
-< *
-< * @return the metacat response message
-< *
-< * @throws InsufficientKarmaException when the user has insufficent rights
-< * for the operation
-< * @throws MetacatInaccessibleException when the metacat server can not be
-< * reached or does not respond
-< * @throws MetacatException when the metacat server generates another error
-< */
-< public String setAccess(String _docid, String _principal, String
-< _permission, String _permType,
-< String _permOrder )
-< throws InsufficientKarmaException, MetacatException,
-< MetacatInaccessibleException
-< {
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "setaccess");
-< prop.put("docid", _docid);
-< prop.put("principal", _principal);
-< prop.put("permission", _permission);
-< prop.put("permType", _permType);
-< prop.put("permOrder", _permOrder);
-<
-< String response = null;
-< try {
-< response = sendDataForString(prop, null, null, 0);
-< } catch (Exception e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
-<
-< // Check for an error condition
-< if (response.indexOf("") != -1) {
-< if (response.indexOf("does not have permission") != -1) {
-< throw new InsufficientKarmaException(response);
-< } else {
-< throw new MetacatException(response);
-< }
-< }
-< return response;
-< }
-<
-< /**
-< * When the MetacatFactory creates an instance it needs to set the
-< * MetacatUrl to which connections should be made.
-< *
-< * @param metacatUrl the URL for the metacat server
-< */
-< public void setMetacatUrl(String metacatUrl)
-< {
-< this.metacatUrl = metacatUrl;
-< }
-<
-< /**
-< * Get the session identifier for this session. This is only valid if
-< * the login methods has been called successfully for this Metacat object
-< * beforehand.
-< *
-< * @returns the sessionId as a String, or null if the session is invalid
-< */
-< public String getSessionId()
-< {
-< return this.sessionId;
-< }
-<
-< /**
-< * Set the session identifier for this session. This identifier was
-< * previously established with a call to login. To continue to use the
-< * same session, set the session id before making a call to one of the
-< * metacat access methods (e.g., read, query, insert, etc.).
-< *
-< * @param String the sessionId from a previously established session
-< */
-< public void setSessionId(String sessionId)
-< {
-< this.sessionId = sessionId;
-< }
-<
-< /**
-< * The method will return the lasted revision in metacat server
-< * for a given document id. If some error happent, this method will throw
-< * a exception.
-< * @param docId String the given docid you want to use. the docid it self
-< * can have or haven't revision number
-< * @throws MetacatException
-< */
-< public int getNewestDocRevision(String docId) throws MetacatException
-< {
-< int rev = 0;
-< //set up properties
-< Properties prop = new Properties();
-< prop.put("action", "getrevisionanddoctype");
-< prop.put("docid", docId);
-<
-< String response = null;
-< try
-< {
-< response = sendDataForString(prop, null, null, 0);
-< String revStr = parserRevisionResponse(response);
-< Integer revObj = new Integer(revStr);
-< rev = revObj.intValue();
-< // Check for an error condition
-< if (response.indexOf("") != -1)
-< {
-< throw new MetacatException(response);
-< }
-<
-< }
-< catch (Exception e)
-< {
-< throw new MetacatException(e.getMessage());
-< }
-< return rev;
-< }
-<
-<
-< /************************************************************************
-< * PRIVATE METHODS
-< ************************************************************************/
-<
-< /**
-< * Send a request to metacat.
-< *
-< * @param prop the properties to be URL encoded and sent
-< * @param filename the properties to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param fileData the inputStream for the file data to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param size the size of the data being sent to Metacat
-< * in case of upload, otherwise 0
-< */
-< synchronized private InputStream sendDataOnce(Properties args,
-< Properties filename,
-< InputStream fileData,
-< int size)
-< throws Exception
-< {
-< InputStream returnStream = null;
-< URL url = new URL(metacatUrl);
-< HttpMessage msg = new HttpMessage(url);
-< msg.setCookie("JSESSIONID="+this.sessionId);
-< if (filename == null){
-< returnStream = msg.sendPostData(args);
-< } else if (fileData == null){
-< returnStream = msg.sendPostData(args, filename);
-< } else if (size > 0) {
-< returnStream = msg.sendPostData(args, filename, fileData, size);
-< } else {
-< throw new MetacatException("Invalid size specified for " +
-< "the input stream being passed");
-< }
-< return returnStream;
-< }
-<
-< /**
-< * Send a request to Metacat
-< *
-< * @param args the properties to be sent to Metacat
-< * @param filename the properties to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param fileData the inputStream for the file data to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param size the size of the data being sent to Metacat
-< * in case of upload, otherwise 0
-< * @return InputStream as returned by Metacat
-< */
-< synchronized private InputStream sendData(Properties args,
-< Properties filename,
-< InputStream fileData,
-< int size)
-< throws Exception
-< {
-< InputStream returnStream = null;
-<
-< /*
-< Note: The reason that there are three try statements all executing
-< the same code is that there is a problem with the initial connection
-< using the HTTPClient protocol handler. These try statements make
-< sure that a connection is made because it gives each connection a
-< 2nd and 3rd chance to work before throwing an error.
-< THIS IS A TOTAL HACK. THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
-< RELEASE OF MORPHO!!! cwb (7/24/01)
-< */
-< try {
-< return sendDataOnce(args, filename, fileData, size);
-< } catch (Exception e) {
-< try {
-< return sendDataOnce(args, filename, fileData, size);
-< } catch (Exception e2) {
-< try {
-< return sendDataOnce(args, filename, fileData, size);
-< } catch (Exception e3) {
-< System.err.println(
-< "Failed to send data to metacat 3 times.");
-< throw e3;
-< }
-< }
-< }
-< }
-<
-< /**
-< * Send a request to Metacat
-< *
-< * @param args the properties to be sent to Metacat
-< * @param filename the properties to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param fileData the inputStream for the file data to be sent to Metacat
-< * in case of upload, otherwise null
-< * @param size the size of the data being sent to Metacat
-< * in case of upload, otherwise 0
-< * @return a string as returned by Metacat
-< */
-< synchronized private String sendDataForString(Properties args,
-< Properties filename,
-< InputStream fileData,
-< int size)
-< throws Exception
-< {
-< String response = null;
-<
-< try {
-< InputStreamReader returnStream =
-< new InputStreamReader(sendData(args, filename,
-< fileData, size));
-< StringWriter sw = new StringWriter();
-< int len;
-< char[] characters = new char[512];
-< while ((len = returnStream.read(characters, 0, 512)) != -1) {
-< sw.write(characters, 0, len);
-< }
-< returnStream.close();
-< response = sw.toString();
-< sw.close();
-< } catch (Exception e) {
-< throw e;
-< }
-< return response;
-< }
-<
-< /*
-< * "getversionanddoctype" action will return a string from metacat server.
-< * The string format is "revision;doctype"(This is bad idea, we should use xml)
-< * This method will get revision string from the response string
-< */
-< private String parserRevisionResponse(String response) throws Exception
-< {
-< String revision = null;
-< if (response != null)
-< {
-< int firstSemiCol = response.indexOf(";");
-< revision = response.substring(0, firstSemiCol);
-< }
-< return revision;
-< }
----
-> /**
-> * Constructor to create a new instance. Protected because instances
-> * should only be created by the factory MetacatFactory.
-> */
-> protected MetacatClient()
-> {
-> this.metacatUrl = null;
-> this.sessionId = null;
-> }
->
-> /**
-> * Method used to log in to a metacat server. Implementations will need
-> * to cache a cookie value to make the session persistent. Each time a
-> * call is made to one of the other methods (e.g., read), the cookie will
-> * need to be passed back to the metacat server along with the request.
-> *
-> * @param username the username of the user, like an LDAP DN
-> * @param password the password for that user for authentication
-> * @return the response string from metacat in XML format
-> * @throws MetacatAuthException when the username/password could
-> * not be authenticated
-> */
-> public String login(String username, String password)
-> throws MetacatAuthException, MetacatInaccessibleException
-> {
-> Properties prop = new Properties();
-> prop.put("action", "login");
-> prop.put("qformat", "xml");
-> if (username != null) prop.put("username", username);
-> if (password != null) prop.put("password", password);
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> if (response.indexOf("") == -1) {
-> setSessionId("");
-> throw new MetacatAuthException(response);
-> } else {
-> int start = response.indexOf("") + 11;
-> int end = response.indexOf("");
-> if ((start != -1) && (end != -1)) {
-> setSessionId(response.substring(start,end));
-> }
-> }
-> return response;
-> }
->
-> /**
-> * Not implemented -- use {@link MetacatGsiClient} instead.
-> * @throws UnsupportedOperationException every time
-> */
-> public String login(GSSCredential credential)
-> throws MetacatAuthException, MetacatInaccessibleException
-> {
-> // be careful not to do any class-loading here, since some clients will
-> // not have the JARs needed to run the GSI-enabled Metacat client.
-> throw new UnsupportedOperationException
-> ("Not implemented -- use MetacatGsiClient instead.");
-> }
->
-> /**
-> * Log in over a trusted connection (usually localhost HTTP) with just
-> * a username to identify the user. The server will only allow this login
-> * method if it is configured to fully trust incoming connections from this
-> * client.
-> *
-> * This may be used, depending on the server's configuration, with
-> * a PKI Distinguished Name (DN), or with an LDAP name.
-> */
-> public String login(String username)
-> throws MetacatInaccessibleException, MetacatAuthException
-> {
-> return login(username, null);
-> }
->
-> /**
-> * Method used to log out a metacat server. The Metacat server will end
-> * the session when this call is invoked.
-> *
-> * @return the response string from metacat in XML format
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> */
-> public String logout() throws MetacatInaccessibleException, MetacatException
-> {
-> Properties prop = new Properties();
-> prop.put("action", "logout");
-> prop.put("qformat", "xml");
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> if (response.indexOf("") == -1) {
-> throw new MetacatException(response);
-> }
-> setSessionId("");
-> return response;
-> }
->
-> /**
-> * Read an XML document from the metacat server session, accessed by docid,
-> * and returned as a Reader.
-> *
-> * @param docid the identifier of the document to be read
-> * @return a Reader for accessing the document
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> */
-> public Reader read(String docid) throws InsufficientKarmaException,
-> MetacatInaccessibleException, MetacatException
-> {
-> PushbackReader pbr = null;
->
-> Properties prop = new Properties();
-> prop.put("action", "read");
-> prop.put("qformat", "xml");
-> prop.put("docid", docid);
->
-> InputStream response = null;
-> try {
-> response = sendData(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> pbr = new PushbackReader(new InputStreamReader(response), 512);
-> try {
-> char[] characters = new char[512];
-> int len = pbr.read(characters, 0, 512);
-> StringWriter sw = new StringWriter();
-> sw.write(characters, 0, len);
-> String message = sw.toString();
-> sw.close();
-> pbr.unread(characters, 0, len);
->
-> if (message.indexOf("") != -1) {
-> if (message.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(message);
-> } else {
-> throw new MetacatException(message);
-> }
-> }
-> } catch (IOException ioe) {
-> throw new MetacatException(
-> "MetacatClient: Error converting Reader to String: "
-> + ioe.getMessage(), ioe);
-> }
->
-> return pbr;
-> }
->
->
-> /**
-> * Read inline data from the metacat server session, accessed by
-> * inlinedataid and returned as a Reader.
-> *
-> * @param inlinedataid the identifier of the data to be read
-> * @return a Reader for accessing the document
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> */
-> public Reader readInlineData(String inlinedataid)
-> throws InsufficientKarmaException,
-> MetacatInaccessibleException, MetacatException
-> {
-> PushbackReader pbr = null;
->
-> Properties prop = new Properties();
-> prop.put("action", "readinlinedata");
-> prop.put("inlinedataid", inlinedataid);
->
-> InputStream response = null;
-> try {
-> response = sendData(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> pbr = new PushbackReader(new InputStreamReader(response), 512);
-> try {
-> char[] characters = new char[512];
-> int len = pbr.read(characters, 0, 512);
-> StringWriter sw = new StringWriter();
-> sw.write(characters, 0, len);
-> String message = sw.toString();
-> sw.close();
-> pbr.unread(characters, 0, len);
->
-> if (message.indexOf("") != -1) {
-> if (message.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(message);
-> } else {
-> throw new MetacatException(message);
-> }
-> }
-> } catch (IOException ioe) {
-> throw new MetacatException(
-> "MetacatClient: Error converting Reader to String: "
-> + ioe.getMessage(), ioe);
-> }
->
-> return pbr;
-> }
->
-> /**
-> * Query the metacat document store with the given metacat-compatible
-> * query document, and return the result set as a Reader.
-> *
-> * @param xmlQuery a Reader for accessing the XML version of the query
-> * @return a Reader for accessing the result set
-> */
-> public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
-> IOException
-> {
-> Reader reader = null;
-> String query = null;
-> try {
-> query = IOUtil.getAsString(xmlQuery, true);
-> } catch (IOException ioE) {
-> throw ioE;
-> }
->
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "squery");
-> prop.put("qformat", "xml");
-> prop.put("query", query);
->
-> InputStream response = null;
-> try {
-> response = sendData(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
-> reader = new InputStreamReader(response);
-> return reader;
-> }
->
-> /**
-> * Insert an XML document into the repository.
-> *
-> * @param docid the docid to insert the document
-> * @param xmlDocument a Reader for accessing the XML document to be inserted
-> * @param schema a Reader for accessing the DTD or XML Schema for
-> * the document
-> * @return the metacat response message
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> * @throws IOException when there is an error reading the xml document
-> */
-> public String insert(String docid, Reader xmlDocument, Reader schema)
-> throws InsufficientKarmaException, MetacatException, IOException,
-> MetacatInaccessibleException
-> {
-> Reader reader = null;
-> String doctext = null;
-> String schematext = null;
-> try {
-> doctext = IOUtil.getAsString(xmlDocument, true);
-> if (schema != null) {
-> schematext = IOUtil.getAsString(schema, true);
-> }
-> } catch (IOException ioE) {
-> throw ioE;
-> }
->
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "insert");
-> prop.put("docid", docid);
-> prop.put("doctext", doctext);
-> if (schematext != null) {
-> prop.put("dtdtext", schematext);
-> }
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
->
-> return response;
-> }
->
-> /**
-> * Update an XML document in the repository.
-> *
-> * @param docid the docid to update
-> * @param xmlDocument a Reader for accessing the XML text to be updated
-> * @param schema a Reader for accessing the DTD or XML Schema for
-> * the document
-> * @return the metacat response message
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> * @throws IOException when there is an error reading the xml document
-> */
-> public String update(String docid, Reader xmlDocument, Reader schema)
-> throws InsufficientKarmaException, MetacatException, IOException,
-> MetacatInaccessibleException
-> {
-> Reader reader = null;
-> String doctext = null;
-> String schematext = null;
-> try {
-> doctext = IOUtil.getAsString(xmlDocument, true);
-> if (schema != null) {
-> schematext = IOUtil.getAsString(schema, true);
-> }
-> } catch (IOException ioE) {
-> throw ioE;
-> }
->
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "update");
-> prop.put("docid", docid);
-> prop.put("doctext", doctext);
-> if (schematext != null) {
-> prop.put("dtdtext", schematext);
-> }
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
->
-> return response;
-> }
->
-> /**
-> * Upload a data document into the repository.
-> *
-> * @param docid the docid to insert the document
-> * @param document a Reader for accessing the document to be uploaded
-> * @return the metacat response message
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> * @throws IOException when there is an error reading the xml document
-> */
-> public String upload(String docid, File file)
-> throws InsufficientKarmaException, MetacatException, IOException,
-> MetacatInaccessibleException
-> {
->
-> //set up properties
-> Properties arg = new Properties();
-> arg.put("action", "upload");
-> arg.put("docid", docid);
->
-> Properties filenames = new Properties();
-> String filename = file.getAbsolutePath();
-> filenames.put("datafile", filename);
->
-> String response = null;
-> try {
-> response = sendDataForString(arg, filenames, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
->
-> return response;
-> }
->
-> /**
-> * Upload a data document into the repository.
-> *
-> * @param docid the docid to insert the document
-> * @param document a Reader for accessing the document to be uploaded
-> * @return the metacat response message
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> * @throws IOException when there is an error reading the xml document
-> */
->
->
-> public String upload(String docid, String filename, InputStream fileData,
-> int size)
-> throws InsufficientKarmaException, MetacatException, IOException,
-> MetacatInaccessibleException {
->
-> //set up properties
-> Properties arg = new Properties();
-> arg.put("action", "upload");
-> arg.put("docid", docid);
->
-> Properties filenames = new Properties();
-> filenames.put("datafile", filename);
->
-> String response;
-> try {
-> response = sendDataForString(arg, filenames, fileData, size);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
->
-> return response;
-> }
->
-> /**
-> * Delete an XML document in the repository.
-> *
-> * @param docid the docid to delete
-> * @return the metacat response message
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> */
-> public String delete(String docid)
-> throws InsufficientKarmaException, MetacatException,
-> MetacatInaccessibleException
-> {
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "delete");
-> prop.put("docid", docid);
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
-> return response;
-> }
->
->
-> /**
-> * set the access on an XML document in the repository.
-> *
-> * @param _docid the docid of the document for which the access should be applied.
-> *
-> * @param _principal the document's principal
-> *
-> * @param _permission the access permission to be applied to the docid
-> * {e.g. read,write,all}
-> *
-> * @param _permType the permission type to be applied to the document
-> * {e.g. allow or deny}
-> *
-> * @param _permOrder the order that the document's permissions should be
-> * processed {e.g. denyFirst or allowFirst}
-> *
-> *
-> * @return the metacat response message
-> *
-> * @throws InsufficientKarmaException when the user has insufficent rights
-> * for the operation
-> * @throws MetacatInaccessibleException when the metacat server can not be
-> * reached or does not respond
-> * @throws MetacatException when the metacat server generates another error
-> */
-> public String setAccess(String _docid, String _principal, String
-> _permission, String _permType,
-> String _permOrder )
-> throws InsufficientKarmaException, MetacatException,
-> MetacatInaccessibleException
-> {
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "setaccess");
-> prop.put("docid", _docid);
-> prop.put("principal", _principal);
-> prop.put("permission", _permission);
-> prop.put("permType", _permType);
-> prop.put("permOrder", _permOrder);
->
-> String response = null;
-> try {
-> response = sendDataForString(prop, null, null, 0);
-> } catch (Exception e) {
-> throw new MetacatInaccessibleException(e);
-> }
->
-> // Check for an error condition
-> if (response.indexOf("") != -1) {
-> if (response.indexOf("does not have permission") != -1) {
-> throw new InsufficientKarmaException(response);
-> } else {
-> throw new MetacatException(response);
-> }
-> }
-> return response;
-> }
->
-> /**
-> * When the MetacatFactory creates an instance it needs to set the
-> * MetacatUrl to which connections should be made.
-> *
-> * @param metacatUrl the URL for the metacat server
-> */
-> public void setMetacatUrl(String metacatUrl)
-> {
-> this.metacatUrl = metacatUrl;
-> }
->
-> /**
-> * The value set via {@link #setMetacatUrl(String)}.
-> */
-> public String getMetacatUrl()
-> {
-> return metacatUrl;
-> }
->
-> /**
-> * Get the session identifier for this session. This is only valid if
-> * the login methods has been called successfully for this Metacat object
-> * beforehand.
-> *
-> * @return the sessionId as a String, or null if the session is invalid
-> */
-> public String getSessionId()
-> {
-> return this.sessionId;
-> }
->
-> /**
-> * Set the session identifier for this session. This identifier was
-> * previously established with a call to login. To continue to use the
-> * same session, set the session id before making a call to one of the
-> * metacat access methods (e.g., read, query, insert, etc.).
-> *
-> * @param sessionId the sessionId from a previously established session
-> */
-> public void setSessionId(String sessionId)
-> {
-> this.sessionId = sessionId;
-> }
->
-> /**
-> * The method will return the lasted revision in metacat server
-> * for a given document id. If some error happent, this method will throw
-> * a exception.
-> * @param docId String the given docid you want to use. the docid it self
-> * can have or haven't revision number
-> * @throws MetacatException
-> */
-> public int getNewestDocRevision(String docId) throws MetacatException
-> {
-> int rev = 0;
-> //set up properties
-> Properties prop = new Properties();
-> prop.put("action", "getrevisionanddoctype");
-> prop.put("docid", docId);
->
-> String response = null;
-> try
-> {
-> response = sendDataForString(prop, null, null, 0);
-> String revStr = parserRevisionResponse(response);
-> Integer revObj = new Integer(revStr);
-> rev = revObj.intValue();
-> // Check for an error condition
-> if (response.indexOf("") != -1)
-> {
-> throw new MetacatException(response);
-> }
->
-> }
-> catch (Exception e)
-> {
-> throw new MetacatException(e);
-> }
-> return rev;
-> }
->
->
-> /************************************************************************
-> * PRIVATE METHODS
-> ************************************************************************/
->
-> /**
-> * Send a request to metacat.
-> *
-> * @param args the properties to be URL encoded and sent
-> * @param filename the properties to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param fileData the inputStream for the file data to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param size the size of the data being sent to Metacat
-> * in case of upload, otherwise 0
-> */
-> synchronized protected InputStream sendDataOnce(Properties args,
-> Properties filename,
-> InputStream fileData,
-> int size)
-> throws Exception
-> {
-> HttpMessage msg = createHttpMessage();
-> msg.setCookie("JSESSIONID="+this.sessionId);
-> return xmit(msg, args, filename, fileData, size);
-> }
->
-> /** Create an HttpMessage that can send messages to the server.
-> * Designed to be overrideable in case a subclass connects to a server
-> * differently. */
-> protected HttpMessage createHttpMessage()
-> throws IOException, MetacatInaccessibleException, MetacatAuthException
-> {
-> URL url = new URL(metacatUrl.trim());
-> return new HttpMessage(url);
-> }
->
-> /** Helper function to {@link #sendDataOnce}. */
-> protected static InputStream xmit
-> (HttpMessage msg, Properties args, Properties filename,
-> InputStream fileData, int size)
-> throws MetacatException, IOException
-> {
-> if (filename == null){
-> return msg.sendPostData(args);
-> } else if (fileData == null){
-> return msg.sendPostData(args, filename);
-> } else if (size > 0) {
-> return msg.sendPostData(args, filename, fileData, size);
-> } else {
-> throw new MetacatException("Invalid size specified for " +
-> "the input stream being passed");
-> }
-> }
->
-> /**
-> * Send a request to Metacat
-> *
-> * @param args the properties to be sent to Metacat
-> * @param filename the properties to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param fileData the inputStream for the file data to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param size the size of the data being sent to Metacat
-> * in case of upload, otherwise 0
-> * @return InputStream as returned by Metacat
-> */
-> synchronized private InputStream sendData(Properties args,
-> Properties filename,
-> InputStream fileData,
-> int size)
-> throws Exception
-> {
-> InputStream returnStream = null;
->
-> /*
-> Note: The reason that there are three try statements all executing
-> the same code is that there is a problem with the initial connection
-> using the HTTPClient protocol handler. These try statements make
-> sure that a connection is made because it gives each connection a
-> 2nd and 3rd chance to work before throwing an error.
-> THIS IS A TOTAL HACK. THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
-> RELEASE OF MORPHO!!! cwb (7/24/01)
-> */
-> try {
-> return sendDataOnce(args, filename, fileData, size);
-> } catch (Exception e) {
-> try {
-> return sendDataOnce(args, filename, fileData, size);
-> } catch (Exception e2) {
-> try {
-> return sendDataOnce(args, filename, fileData, size);
-> } catch (Exception e3) {
-> System.err.println(
-> "Failed to send data to metacat 3 times.");
-> throw e3;
-> }
-> }
-> }
-> }
->
-> /**
-> * Send a request to Metacat
-> *
-> * @param args the properties to be sent to Metacat
-> * @param filename the properties to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param fileData the inputStream for the file data to be sent to Metacat
-> * in case of upload, otherwise null
-> * @param size the size of the data being sent to Metacat
-> * in case of upload, otherwise 0
-> * @return a string as returned by Metacat
-> */
-> synchronized protected String sendDataForString(Properties args,
-> Properties filename,
-> InputStream fileData,
-> int size)
-> throws Exception
-> {
-> InputStreamReader returnStream =
-> new InputStreamReader(sendData(args, filename,
-> fileData, size));
-> StringWriter sw = new StringWriter();
-> int len;
-> char[] characters = new char[512];
-> while ((len = returnStream.read(characters, 0, 512)) != -1) {
-> sw.write(characters, 0, len);
-> }
-> returnStream.close();
-> String response = sw.toString();
-> sw.close();
-> return response;
-> }
->
-> /*
-> * "getversionanddoctype" action will return a string from metacat server.
-> * The string format is "revision;doctype"(This is bad idea, we should use xml)
-> * This method will get revision string from the response string
-> */
-> private String parserRevisionResponse(String response) throws Exception
-> {
-> String revision = null;
-> if (response != null)
-> {
-> int firstSemiCol = response.indexOf(";");
-> revision = response.substring(0, firstSemiCol);
-> }
-> return revision;
-> }
-Index: src/edu/ucsb/nceas/metacat/client/MetacatException.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/client/MetacatException.java,v
-retrieving revision 1.1
-diff -r1.1 MetacatException.java
-39a40,47
->
-> public MetacatException(Throwable cause) {
-> super(cause);
-> }
->
-> public MetacatException(String message, Throwable cause) {
-> super(message, cause);
-> }
-Index: src/edu/ucsb/nceas/metacat/client/MetacatFactory.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/client/MetacatFactory.java,v
-retrieving revision 1.1
-diff -r1.1 MetacatFactory.java
-27c27,31
-< import java.io.Reader;
----
-> import java.net.URL;
-> import java.net.MalformedURLException;
-> import java.util.Map;
-> import java.util.HashMap;
-> import java.util.Collections;
-35,36c39,84
-< private static final String metacatClientClass =
-< "edu.ucsb.nceas.metacat.client.MetacatClient";
----
-> /** Register Metacat client classes by protocol. */
-> private static final Map PROTOCOL_CLIENT_CLASS_MAP;
-> static {
-> Map protoMap = new HashMap();
-> protoMap.put("http", "edu.ucsb.nceas.metacat.client.MetacatClient");
-> // protoMap.put("https", "edu.ucsb.nceas.metacat.client.MetacatClient");
-> protoMap.put("https", "edu.ucsb.nceas.metacat.client.gsi.MetacatGsiClient");
-> // httpg requires a special class that requires GSI libraries
-> protoMap.put("httpg", "edu.ucsb.nceas.metacat.client.gsi.MetacatGsiClient");
-> PROTOCOL_CLIENT_CLASS_MAP = Collections.unmodifiableMap(protoMap);
-> }
->
-> /**
-> * Create a new instance of a Metacat object of raccessing a server.
-> *
-> * @param metacatUrl the url location of the metacat server
-> * @throws MetacatInaccessibleException when the metacat server can not
-> * be reached
-> */
-> public static Metacat createMetacatConnection(String metacatUrl)
-> throws MetacatInaccessibleException
-> {
-> Metacat m;
-> try {
-> URL url = new URL(metacatUrl);
-> String clientClass = (String)
-> PROTOCOL_CLIENT_CLASS_MAP.get(url.getProtocol().toLowerCase());
-> if (clientClass == null)
-> clientClass = (String) PROTOCOL_CLIENT_CLASS_MAP.get("http");
-> Class c = Class.forName(clientClass);
-> m = (Metacat)c.newInstance();
-> } catch (InstantiationException e) {
-> throw new MetacatInaccessibleException(e);
-> } catch (IllegalAccessException e) {
-> throw new MetacatInaccessibleException(e);
-> } catch (ClassNotFoundException e) {
-> throw new MetacatInaccessibleException
-> ("Unable to instantiate metacat client for server URL \""
-> + metacatUrl + "\".", e);
-> } catch (NoClassDefFoundError e) {
-> throw new MetacatInaccessibleException
-> ("Unable to instantiate metacat client for server URL \""
-> + metacatUrl + "\".", e);
-> } catch (MalformedURLException e) {
-> throw new MetacatInaccessibleException(e);
-> }
-38,58c86
-< /**
-< * Create a new instance of a Metacat object of raccessing a server.
-< *
-< * @param metacatUrl the url location of the metacat server
-< * @throws MetacatInaccessibleException when the metacat server can not
-< * be reached
-< */
-< public static Metacat createMetacatConnection(String metacatUrl)
-< throws MetacatInaccessibleException
-< {
-< Metacat m = null;
-< try {
-< Class c = Class.forName(metacatClientClass);
-< m = (Metacat)c.newInstance();
-< } catch (InstantiationException e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< } catch (IllegalAccessException e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< } catch (ClassNotFoundException e) {
-< throw new MetacatInaccessibleException(e.getMessage());
-< }
----
-> m.setMetacatUrl(metacatUrl);
-60,63c88,89
-< m.setMetacatUrl(metacatUrl);
-<
-< return m;
-< }
----
-> return m;
-> }
-Index: src/edu/ucsb/nceas/metacat/client/MetacatInaccessibleException.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/client/MetacatInaccessibleException.java,v
-retrieving revision 1.1
-diff -r1.1 MetacatInaccessibleException.java
-40a41,48
->
-> public MetacatInaccessibleException(Throwable cause) {
-> super(cause);
-> }
->
-> public MetacatInaccessibleException(String message, Throwable cause) {
-> super(message, cause);
-> }
-Index: src/edu/ucsb/nceas/metacat/harvesterClient/HarvesterRegistration.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/harvesterClient/HarvesterRegistration.java,v
-retrieving revision 1.13
-diff -r1.13 HarvesterRegistration.java
-28a29,30
-> import edu.ucsb.nceas.metacat.AuthInfo;
->
-362c364
-< String ldapDN;
----
-> String ldapDN;
-379,380c381,383
-< ldapDN = (String) httpSession.getAttribute("username");
-< ldapPwd = (String) httpSession.getAttribute("password");
----
-> AuthInfo auth = (AuthInfo) httpSession.getAttribute("auth");
-> ldapDN = auth.getUsername();
-> ldapPwd = auth.getPassword();
-531,532c534,536
-< ldapDN = (String) httpSession.getAttribute("username");
-< ldapPwd = (String) httpSession.getAttribute("password");
----
-> AuthInfo auth = (AuthInfo) httpSession.getAttribute("auth");
-> ldapDN = auth.getUsername();
-> ldapPwd = auth.getPassword();
-Index: src/edu/ucsb/nceas/metacat/harvesterClient/HarvesterRegistrationLogin.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/harvesterClient/HarvesterRegistrationLogin.java,v
-retrieving revision 1.7
-diff -r1.7 HarvesterRegistrationLogin.java
-33a34
-> import edu.ucsb.nceas.metacat.AuthInfo;
-42c43
-< final String LDAP_DOMAIN = ",dc=ecoinformatics,dc=org";
----
-> final String LDAP_DOMAIN = ",dc=ecoinformatics,dc=org";
-44,129c45,130
-< /**
-< * Handle "GET" method requests from HTTP clients
-< *
-< * @param req The request
-< * @param res The response
-< * @throws ServletException, java.io.IOException
-< */
-< public void doGet(HttpServletRequest req, HttpServletResponse res)
-< throws ServletException, java.io.IOException {
-< handleGetOrPost(req, res);
-< }
-<
-<
-< /**
-< * Handle "POST" method requests from HTTP clients
-< *
-< * @param req The request
-< * @param res The response
-< * @throws ServletException, java.io.IOException
-< */
-< public void doPost(HttpServletRequest req, HttpServletResponse res)
-< throws ServletException, java.io.IOException {
-< handleGetOrPost(req, res);
-< }
-<
-<
-< /**
-< * Handle "GET" or "POST" method requests from HTTP clients
-< *
-< * @param req The request
-< * @param res The response
-< * @throws ServletException, java.io.IOException
-< */
-< private void handleGetOrPost(HttpServletRequest req,
-< HttpServletResponse res)
-< throws ServletException, java.io.IOException {
-< AuthSession authSession;
-< String authSessionMessage;
-< HttpSession httpSession;
-< boolean isValid;
-< String o = req.getParameter("o");
-< String organization;
-< String passwd = req.getParameter("passwd");
-< PrintWriter out = res.getWriter();
-< String uid = req.getParameter("uid");
-< String user;
-<
-< if ((uid == null) || (uid.equals(""))) {
-< out.println("Invalid login: no Username specified.");
-< return;
-< }
-< else if ((o == null) || (o.equals(""))) {
-< out.println("Invalid login: no Organization selected.");
-< return;
-< }
-< else if ((passwd == null) || (passwd.equals(""))) {
-< out.println("Invalid login: no Password specified.");
-< return;
-< }
-< else {
-< user = "uid=" + uid + ",o=" + o + LDAP_DOMAIN;
-< }
-<
-< res.setContentType("text/plain");
-<
-< try {
-< authSession = new AuthSession();
-< isValid = authSession.authenticate(req, user, passwd);
-< authSessionMessage = authSession.getMessage();
-< System.out.println("authSession.authenticate(): "+authSessionMessage);
-< out.println("authSession.authenticate(): " + authSessionMessage);
-<
-< if (isValid) {
-< httpSession = req.getSession(true);
-< httpSession.setAttribute("username", user);
-< httpSession.setAttribute("password", passwd);
-< res.sendRedirect("harvesterRegistration");
-< }
-< else {
-< out.println("Invalid login");
-< }
-< }
-< catch (Exception e) {
-< System.out.println("Error in AuthSession()" + e.getMessage());
-< }
-< }
----
-> /**
-> * Handle "GET" method requests from HTTP clients
-> *
-> * @param req The request
-> * @param res The response
-> * @throws ServletException, java.io.IOException
-> */
-> public void doGet(HttpServletRequest req, HttpServletResponse res)
-> throws ServletException, java.io.IOException {
-> handleGetOrPost(req, res);
-> }
->
->
-> /**
-> * Handle "POST" method requests from HTTP clients
-> *
-> * @param req The request
-> * @param res The response
-> * @throws ServletException, java.io.IOException
-> */
-> public void doPost(HttpServletRequest req, HttpServletResponse res)
-> throws ServletException, java.io.IOException {
-> handleGetOrPost(req, res);
-> }
->
->
-> /**
-> * Handle "GET" or "POST" method requests from HTTP clients
-> *
-> * @param req The request
-> * @param res The response
-> * @throws ServletException, java.io.IOException
-> */
-> private void handleGetOrPost(HttpServletRequest req,
-> HttpServletResponse res)
-> throws ServletException, java.io.IOException {
-> AuthSession authSession;
-> String authSessionMessage;
-> HttpSession httpSession;
-> boolean isValid;
-> String o = req.getParameter("o");
-> String organization;
-> String passwd = req.getParameter("passwd");
-> PrintWriter out = res.getWriter();
-> String uid = req.getParameter("uid");
-> String user;
->
-> if ((uid == null) || (uid.equals(""))) {
-> out.println("Invalid login: no Username specified.");
-> return;
-> }
-> else if ((o == null) || (o.equals(""))) {
-> out.println("Invalid login: no Organization selected.");
-> return;
-> }
-> else if ((passwd == null) || (passwd.equals(""))) {
-> out.println("Invalid login: no Password specified.");
-> return;
-> }
-> else {
-> user = "uid=" + uid + ",o=" + o + LDAP_DOMAIN;
-> }
->
-> res.setContentType("text/plain");
->
-> try {
-> authSession = new AuthSession();
-> AuthInfo auth = new AuthInfo(user, passwd);
-> isValid = authSession.authenticate(req, auth);
-> authSessionMessage = authSession.getMessage();
-> System.out.println("authSession.authenticate(): "+authSessionMessage);
-> out.println("authSession.authenticate(): " + authSessionMessage);
->
-> if (isValid) {
-> httpSession = req.getSession(true);
-> httpSession.setAttribute("auth", auth);
-> res.sendRedirect("harvesterRegistration");
-> }
-> else {
-> out.println("Invalid login");
-> }
-> }
-> catch (Exception e) {
-> System.out.println("Error in AuthSession()" + e.getMessage());
-> }
-> }
-Index: src/edu/ucsb/nceas/metacat/harvesterClient/LoginServlet.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/harvesterClient/LoginServlet.java,v
-retrieving revision 1.6
-diff -r1.6 LoginServlet.java
-38a39
-> import edu.ucsb.nceas.metacat.AuthInfo;
-46,47c47,48
-< // Close all connections
-< System.out.println("Destroying LoginServlet");
----
-> // Close all connections
-> System.out.println("Destroying LoginServlet");
-58,60c59,61
-< throws ServletException, java.io.IOException {
-< // Process the data and send back the response
-< handleGetOrPost(request, response);
----
-> throws ServletException, java.io.IOException {
-> // Process the data and send back the response
-> handleGetOrPost(request, response);
-71,73c72,74
-< throws ServletException, java.io.IOException {
-< // Process the data and send back the response
-< handleGetOrPost(request, response);
----
-> throws ServletException, java.io.IOException {
-> // Process the data and send back the response
-> handleGetOrPost(request, response);
-84,115c85,116
-< HttpServletResponse response)
-< throws ServletException, java.io.IOException {
-< AuthSession authSession = null;
-< HttpSession httpSession;
-< boolean isValid;
-< PrintWriter out = response.getWriter();
-< String passwd = request.getParameter("passwd");
-< String user = request.getParameter("user");
-<
-< response.setContentType("text/plain");
-<
-< try {
-< authSession = new AuthSession();
-< }
-< catch (Exception e) {
-< out.println("Error creating AuthSession: " + e.getMessage());
-< return;
-< }
-<
-< isValid = authSession.authenticate(request, user, passwd);
-<
-< if (isValid) {
-< System.out.println(authSession.getMessage());
-< httpSession = request.getSession(true);
-< httpSession.setAttribute("username", user);
-< httpSession.setAttribute("password", passwd);
-< response.sendRedirect("../style/skins/dev/harvesterUpload.html");
-< }
-< else {
-< out.println("Error authenticating Metacat login: " +
-< authSession.getMessage());
-< }
----
-> HttpServletResponse response)
-> throws ServletException, java.io.IOException {
-> AuthSession authSession = null;
-> HttpSession httpSession;
-> boolean isValid;
-> PrintWriter out = response.getWriter();
-> String passwd = request.getParameter("passwd");
-> String user = request.getParameter("user");
-> AuthInfo auth = new AuthInfo(user, passwd);
->
-> response.setContentType("text/plain");
->
-> try {
-> authSession = new AuthSession();
-> }
-> catch (Exception e) {
-> out.println("Error creating AuthSession: " + e.getMessage());
-> return;
-> }
->
-> isValid = authSession.authenticate(request, auth);
->
-> if (isValid) {
-> System.out.println(authSession.getMessage());
-> httpSession = request.getSession(true);
-> httpSession.setAttribute("auth", auth);
-> response.sendRedirect("../style/skins/dev/harvesterUpload.html");
-> }
-> else {
-> out.println("Error authenticating Metacat login: " +
-> authSession.getMessage());
-> }
-Index: src/edu/ucsb/nceas/metacat/harvesterClient/MetUpload.java
-===================================================================
-RCS file: /cvs/metacat/src/edu/ucsb/nceas/metacat/harvesterClient/MetUpload.java,v
-retrieving revision 1.8
-diff -r1.8 MetUpload.java
-44a45
-> import edu.ucsb.nceas.metacat.AuthInfo;
-84c85
-< String password = (String) sess.getAttribute("password");
----
-> AuthInfo auth = (AuthInfo) sess.getAttribute("auth");
-87,88c88
-< String username = (String) sess.getAttribute("username");
-<
----
->
-124c124
-< upload(out, docid, sr, username, password);
----
-> upload(out, docid, sr, auth);
-127c127
-< delete(out, docid, username, password);
----
-> delete(out, docid, auth);
-137,138c137
-< * @param username the Metacat username
-< * @param password the Metacat password
----
-> * @param auth the Metacat username and password
-142,143c141
-< String username,
-< String password
----
-> AuthInfo auth
-155c153
-< metacat.login(username, password);
----
-> metacat.login(auth.getUsername(), auth.getPassword());
-220,221c218
-< * @param username the Metacat username
-< * @param password the Metacat password
----
-> * @param auth the Metacat username and password
-226,227c223
-< String username,
-< String password
----
-> AuthInfo auth
-247c243
-< metacat.login(username, password);
----
-> metacat.login(auth.getUsername(), auth.getPassword());
diff --git a/lib/certificateAuthenCode/bbaker_diff_utilities.txt b/lib/certificateAuthenCode/bbaker_diff_utilities.txt
deleted file mode 100644
index b7b322f6f..000000000
--- a/lib/certificateAuthenCode/bbaker_diff_utilities.txt
+++ /dev/null
@@ -1,138 +0,0 @@
-Index: build.xml
-===================================================================
-RCS file: /cvs/utilities/build.xml,v
-retrieving revision 1.8
-diff -w -r1.8 build.xml
-66a67,68
->
->
-69c71
-< value="${xercesjar}:${log4jjar}:${httpjar}" />
----
-> value="${xercesjar}:${log4jjar}:${httpjar}:${jglobusjar}:${jgssjar}:${xalanjar}" />
-97a100
-> source="1.4" target="1.4"
-164a168
-> source="1.4" target="1.4"
-Index: src/java/edu/ucsb/nceas/utilities/HttpMessage.java
-===================================================================
-RCS file: /cvs/utilities/src/java/edu/ucsb/nceas/utilities/HttpMessage.java,v
-retrieving revision 1.3
-diff -w -r1.3 HttpMessage.java
-27,30d26
-< import java.io.*;
-< import java.net.*;
-< import java.util.*;
-<
-32a29,36
-> import java.io.*;
-> import java.net.HttpURLConnection;
-> import java.net.URL;
-> import java.net.URLConnection;
-> import java.net.URLEncoder;
-> import java.util.Enumeration;
-> import java.util.Properties;
->
-35,39c39,42
-< private URL servlet = null;
-< private String argString = null;
-< private static String cookie = null;
-< private OutputStream out = null;
-< private URLConnection con = null;
----
-> protected URL servlet = null;
-> protected String cookie = null;
-> protected OutputStream out = null;
-> protected URLConnection con = null;
-61c64
-< argString = "";//default
----
-> String argString = "";
-79c82
-< private void openPostConnection() throws IOException
----
-> protected void openPostConnection() throws IOException
-153c156
-< ((HttpURLConnection)con).setRequestProperty("Content-Type", ctype);
----
-> con.setRequestProperty("Content-Type", ctype);
-155,156c158
-< ((HttpURLConnection)con).setRequestProperty("Content-Length",
-< new Long(contentLength).toString());
----
-> con.setRequestProperty("Content-Length", Long.toString(contentLength));
-159c161
-< out = con.getOutputStream();
----
-> out = getConOutputStream();
-214c216
-< ((HttpURLConnection)con).setRequestProperty("Content-Type", ctype);
----
-> con.setRequestProperty("Content-Type", ctype);
-216,217c218
-< ((HttpURLConnection)con).setRequestProperty("Content-Length",
-< new Long(contentLength).toString());
----
-> con.setRequestProperty("Content-Length", Long.toString(contentLength));
-220c221
-< out = con.getOutputStream();
----
-> out = getConOutputStream();
-227a229,256
-> /** If true, ignore all flush() calls during POST connections
-> * (that is, during sendPostData calls) until the connection is
-> * closed. Useful because flush() calls on a GSS SSL stream close the
-> * stream. */
-> protected boolean ignoreOutputStreamFlushes = false;
->
-> /** If ignoreOutputStreamFlushes is true, wrap o in a
-> * stream that simply ignores all flush()es until it is closed.
-> * No effect if ignoreOutputStreamFlushes is false. */
-> private OutputStream wrapFlushes(OutputStream o) {
-> if (!ignoreOutputStreamFlushes) return o;
-> else return new FilterOutputStream(o) {
-> public void flush() {} // ignore flushes
-> public void close() throws IOException {
-> //noinspection EmptyCatchBlock
-> try { super.flush(); } // but make sure they happen on close
-> catch (IOException ignored) {}
-> super.close();
-> }
-> };
-> }
->
-> /** Call this rather than calling con.getOutputStream() directly,
-> * to properly wrap a GSI SSL output stream. */
-> protected OutputStream getConOutputStream() throws IOException {
-> return wrapFlushes(con.getOutputStream());
-> }
->
-240c269
-< out = new DataOutputStream(con.getOutputStream());
----
-> out = new DataOutputStream(getConOutputStream());
-272c301
-< private InputStream closePostConnection() throws IOException
----
-> protected InputStream closePostConnection() throws IOException
-297c326
-< return sendPostMessage(null);
----
-> return sendPostData(null);
-319c348
-< private String toEncodedString(Properties args)
----
-> protected static String toEncodedString(Properties args)
-327c356,357
-< buf.append(URLEncoder.encode(name) + "=" + URLEncoder.encode(value));
----
-> buf.append(URLEncoder.encode(name)).append("=")
-> .append(URLEncoder.encode(value));
-337c367
-< public static String getCookie()
----
-> public String getCookie()
-345c375
-< public static void setCookie(String newCookie)
----
-> public void setCookie(String newCookie)
diff --git a/lib/certificateAuthenCode/metacat_additions.tar.gz b/lib/certificateAuthenCode/metacat_additions.tar.gz
deleted file mode 100644
index 451d5b242950c92c15cb79e88eece47943077920..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3412744
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%0A{&cQ#m(JNowY(-Ccgc){
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