package net.deterlab.abac;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
/**
* A class for parsing and generating Credentials inside a Context. All
* credential parsing should use a credential factory inside a context uses a
* CredentialFactory.
* @author ISI ABAC team
* @version 1.5
*/
public class CredentialFactory implements Cloneable {
protected List spec;
/**
* The xmlparsing routines helpfully close input streams when they
* successfully parse a document from one. It's possible for a correctly
* parsed XML document to need to be reparsed by later specialization, for
* example because the XML was fine, but it was the wrong type of
* credential. This makes the close operation a NOOP.
*/
static protected class UnclosableBufferedInputStream extends
BufferedInputStream {
public UnclosableBufferedInputStream(InputStream is) {
super(is);
}
public UnclosableBufferedInputStream(InputStream is, int s) {
super(is, s);
}
public void close() throws IOException { }
};
/** The classes understood by the default CredentialFactory (in order) */
static public String[] defNames = new String[] {
"net.deterlab.abac.GENICredentialv1_1",
"net.deterlab.abac.GENICredentialv1_0",
"net.deterlab.abac.X509Credential",
"net.deterlab.abac.GENIPrivCredential",
};
/** Maximum credential size that can be rewound for reparsing */
public static final int maxSize = 50 * 1024;
/**
* Create a Credential Factory that parses the default type(s)
* @throws ABACException if the object cannot be created
*/
public CredentialFactory() throws ABACException {
spec = new ArrayList();
for ( String name: defNames)
registerClass(name);
}
/**
* Create a Credential Factory that parses the given type(s). Each String
* should be the binary name for a class that exports a static
* getCredentialParser method that returns a CredentialParser for the
* class.
* @param names a Collection of Strings naming the classes to parse
* @throws ABACException if the object cannot be created
*/
public CredentialFactory(Collection names) throws ABACException {
spec = new ArrayList();
for (String n : names )
registerClass(n);
}
/**
* Create a Credential Factory that parses the given type(s). Each String
* should be the binary name for a class that exports a static
* getCredentialParser method that returns a CredentialParser for the
* class.
* @param names an Array of Strings naming the classes to parse
* @throws ABACException if the object cannot be created
*/
public CredentialFactory(String[] names) throws ABACException {
spec = new ArrayList();
for (String n : names )
registerClass(n);
}
/**
* Create a Credential Factory that is a clone of the given
* CredentialFactory.
* @param cf the CredentialFactory to copy
* @throws ABACException if the object cannot be created
*/
public CredentialFactory(CredentialFactory cf) throws ABACException {
this();
spec = new ArrayList();
for ( int i = 0; i < spec.size(); i++)
spec.add(cf.spec.get(i));
}
/**
* Make a copy of this CredentialFactory
* @return a CredentialFactory, a copy of this one
*/
public Object clone() throws CloneNotSupportedException {
CredentialFactory cf = null;
try {
cf = new CredentialFactory(this);
}
catch (ABACException ae) {
return null;
}
return cf;
}
/**
* Parse an input stream using each possible credential format and the
* available identities for validation. Return the credentials found or
* throw an ABACException with the problem. It wraps the input stream in a
* BufferedInputStream in order to retry is a parser fails. Credentials
* larger than maxSize will nor be able to be reparsed.
* @param is an InputStream to parse
* @param ids a Collection of Identities for validation
* @return an Array of Credentials parsed
* @throws CertInvalidException if the stream is unparsable
* @throws MissingIssuerException if none of the Identities can validate the
* @throws BadSignatureException if the signature check fails
*/
public Credential[] parseCredential(InputStream is,
Collection ids) throws ABACException {
Credential[] rv = null;
ABACException err = null;
UnclosableBufferedInputStream bs =
new UnclosableBufferedInputStream(is, maxSize);
bs.mark(maxSize);
for (CredentialFactorySpecialization c : spec ) {
try {
rv = c.parseCredential(bs, ids);
break;
}
catch (ABACException e ) {
err = e;
rv = null;
}
try {
if (spec.size() > 1) bs.reset();
}
catch (IOException ie) {
break;
}
}
if ( rv != null )
return rv;
else
throw (err != null) ? err :
new ABACException("null exception and failed construction??");
}
/**
* Parse a File using each possible credential format and the
* available identities for validation. Return the credentials found or
* throw an ABACException with the problem. Calls the InputStream version
* internally.
* @param f a File to parse
* @param ids a Collection of Identities for validation
* @return an Array of Credentials parsed
* @throws CertInvalidException if the stream is unparsable
* @throws MissingIssuerException if none of the Identities can validate the
* @throws BadSignatureException if the signature check fails
*/
public Credential[] parseCredential(File f,
Collection ids) throws ABACException {
try {
return parseCredential(new FileInputStream(f), ids);
}
catch (FileNotFoundException e) {
throw new CertInvalidException(e.getMessage(), e);
}
}
/**
* Parse a file named fn using each possible credential format and the
* available identities for validation. Return the credentials found or
* throw an ABACException with the problem. Calls the InputStream version
* internally.
* @param fn a String holding the filename to parse
* @param ids a Collection of Identities for validation
* @return an Array of Credentials parsed
* @throws CertInvalidException if the stream is unparsable
* @throws MissingIssuerException if none of the Identities can validate the
* @throws BadSignatureException if the signature check fails
*/
public Credential[] parseCredential(String fn,
Collection ids) throws ABACException {
return parseCredential(new File(fn), ids);
}
/**
* Return a credential from the first class registered in
* the factory.
* @param head a Role, the head of the encoded ABAC statement
* @param tail a Role, the tail of the decoded ABAC statement
* @param aliases a KeyIDMap holding aliases for this creation
* @return a Credential encoding that ABAC statement
*/
public Credential generateCredential(Role head, Role tail,
KeyIDMap aliases) {
if (spec.isEmpty()) return null;
return spec.get(0).generateCredential(head, tail, aliases);
}
/**
* Add the named class to the list of usable specializations. The class
* passed in must have a static getCredentialFactorySpecialization() method
* that returns a CredentialFactorySpecialization to use.
* @param name a String containing the binary name of the class to register
* @throws ABACException if there is a problem. The cause field of this
* exception is set to the classloading exception, if any.
*/
public void registerClass(String name)
throws ABACException {
CredentialFactorySpecialization cs = null;
try {
/* > doesn't guarantee much, but shuts the compiler up */
Class> c = Class.forName(name);
Method m = c.getMethod("getCredentialFactorySpecialization");
cs = (CredentialFactorySpecialization) m.invoke(null);
}
catch (Exception e) {
throw new ABACException("Could not register credential type" +
e.getMessage(), e);
}
spec.add(cs);
}
}