[3797bbe] | 1 | package net.deterlab.abac; |
---|
| 2 | |
---|
| 3 | import java.io.*; |
---|
| 4 | import java.util.*; |
---|
[3612811] | 5 | import java.lang.reflect.*; |
---|
[3797bbe] | 6 | |
---|
| 7 | |
---|
| 8 | /** |
---|
[a7f73b5] | 9 | * A class for parsing and generating Credentials inside a Context. All |
---|
| 10 | * credential parsing should use a credential factory inside a context uses a |
---|
| 11 | * CredentialFactory. |
---|
[3797bbe] | 12 | * @author <a href="http://abac.deterlab.net">ISI ABAC team</a> |
---|
[a1a9a47] | 13 | * @version 1.5 |
---|
[3797bbe] | 14 | */ |
---|
[d06c453] | 15 | public class CredentialFactory implements Cloneable { |
---|
[aaadefd] | 16 | protected List<CredentialFactorySpecialization> spec; |
---|
[6bf703b0] | 17 | |
---|
| 18 | /** |
---|
| 19 | * The xmlparsing routines helpfully close input streams when they |
---|
| 20 | * successfully parse a document from one. It's possible for a correctly |
---|
| 21 | * parsed XML document to need to be reparsed by later specialization, for |
---|
| 22 | * example because the XML was fine, but it was the wrong type of |
---|
| 23 | * credential. This makes the close operation a NOOP. |
---|
| 24 | */ |
---|
| 25 | static protected class UnclosableBufferedInputStream extends |
---|
| 26 | BufferedInputStream { |
---|
| 27 | public UnclosableBufferedInputStream(InputStream is) { |
---|
| 28 | super(is); |
---|
| 29 | } |
---|
| 30 | public UnclosableBufferedInputStream(InputStream is, int s) { |
---|
| 31 | super(is, s); |
---|
| 32 | } |
---|
| 33 | |
---|
| 34 | public void close() throws IOException { } |
---|
| 35 | }; |
---|
[a7f73b5] | 36 | /** The classes understood by the default CredentialFactory (in order) */ |
---|
[1a1acd9] | 37 | static public String[] defNames = new String[] { |
---|
[c1736fe] | 38 | "net.deterlab.abac.GENICredentialv1_1", |
---|
| 39 | "net.deterlab.abac.GENICredentialv1_0", |
---|
[6bf703b0] | 40 | "net.deterlab.abac.X509Credential", |
---|
| 41 | "net.deterlab.abac.GENIPrivCredential", |
---|
[1a1acd9] | 42 | }; |
---|
[6bf703b0] | 43 | |
---|
[a7f73b5] | 44 | /** Maximum credential size that can be rewound for reparsing */ |
---|
[3612811] | 45 | public static final int maxSize = 50 * 1024; |
---|
| 46 | |
---|
[f84d71e] | 47 | /** |
---|
[d06c453] | 48 | * Create a Credential Factory that parses the default type(s) |
---|
| 49 | * @throws ABACException if the object cannot be created |
---|
[f84d71e] | 50 | */ |
---|
[d06c453] | 51 | public CredentialFactory() throws ABACException { |
---|
[aaadefd] | 52 | spec = new ArrayList<CredentialFactorySpecialization>(); |
---|
[1a1acd9] | 53 | for ( String name: defNames) |
---|
| 54 | registerClass(name); |
---|
[d06c453] | 55 | } |
---|
| 56 | |
---|
| 57 | /** |
---|
| 58 | * Create a Credential Factory that parses the given type(s). Each String |
---|
| 59 | * should be the binary name for a class that exports a static |
---|
| 60 | * getCredentialParser method that returns a CredentialParser for the |
---|
| 61 | * class. |
---|
| 62 | * @param names a Collection of Strings naming the classes to parse |
---|
| 63 | * @throws ABACException if the object cannot be created |
---|
| 64 | */ |
---|
| 65 | public CredentialFactory(Collection<String> names) throws ABACException { |
---|
[aaadefd] | 66 | spec = new ArrayList<CredentialFactorySpecialization>(); |
---|
[d06c453] | 67 | for (String n : names ) |
---|
| 68 | registerClass(n); |
---|
| 69 | } |
---|
| 70 | |
---|
| 71 | /** |
---|
| 72 | * Create a Credential Factory that parses the given type(s). Each String |
---|
| 73 | * should be the binary name for a class that exports a static |
---|
| 74 | * getCredentialParser method that returns a CredentialParser for the |
---|
| 75 | * class. |
---|
| 76 | * @param names an Array of Strings naming the classes to parse |
---|
| 77 | * @throws ABACException if the object cannot be created |
---|
| 78 | */ |
---|
| 79 | public CredentialFactory(String[] names) throws ABACException { |
---|
[aaadefd] | 80 | spec = new ArrayList<CredentialFactorySpecialization>(); |
---|
[d06c453] | 81 | for (String n : names ) |
---|
| 82 | registerClass(n); |
---|
| 83 | } |
---|
[f84d71e] | 84 | |
---|
[d06c453] | 85 | /** |
---|
| 86 | * Create a Credential Factory that is a clone of the given |
---|
| 87 | * CredentialFactory. |
---|
| 88 | * @param cf the CredentialFactory to copy |
---|
| 89 | * @throws ABACException if the object cannot be created |
---|
| 90 | */ |
---|
| 91 | public CredentialFactory(CredentialFactory cf) throws ABACException { |
---|
| 92 | this(); |
---|
| 93 | |
---|
[aaadefd] | 94 | spec = new ArrayList<CredentialFactorySpecialization>(); |
---|
[d06c453] | 95 | |
---|
[aaadefd] | 96 | for ( int i = 0; i < spec.size(); i++) |
---|
| 97 | spec.add(cf.spec.get(i)); |
---|
[d06c453] | 98 | } |
---|
| 99 | |
---|
| 100 | /** |
---|
| 101 | * Make a copy of this CredentialFactory |
---|
[a7f73b5] | 102 | * @return a CredentialFactory, a copy of this one |
---|
[d06c453] | 103 | */ |
---|
| 104 | public Object clone() throws CloneNotSupportedException { |
---|
| 105 | CredentialFactory cf = null; |
---|
| 106 | try { |
---|
| 107 | cf = new CredentialFactory(this); |
---|
[3797bbe] | 108 | } |
---|
[d06c453] | 109 | catch (ABACException ae) { |
---|
| 110 | return null; |
---|
[3797bbe] | 111 | } |
---|
[d06c453] | 112 | return cf; |
---|
[3612811] | 113 | } |
---|
| 114 | |
---|
[d06c453] | 115 | |
---|
[f84d71e] | 116 | /** |
---|
| 117 | * Parse an input stream using each possible credential format and the |
---|
| 118 | * available identities for validation. Return the credentials found or |
---|
| 119 | * throw an ABACException with the problem. It wraps the input stream in a |
---|
| 120 | * BufferedInputStream in order to retry is a parser fails. Credentials |
---|
| 121 | * larger than maxSize will nor be able to be reparsed. |
---|
| 122 | * @param is an InputStream to parse |
---|
| 123 | * @param ids a Collection of Identities for validation |
---|
| 124 | * @return an Array of Credentials parsed |
---|
| 125 | * @throws CertInvalidException if the stream is unparsable |
---|
| 126 | * @throws MissingIssuerException if none of the Identities can validate the |
---|
| 127 | * @throws BadSignatureException if the signature check fails |
---|
| 128 | */ |
---|
[d06c453] | 129 | public Credential[] parseCredential(InputStream is, |
---|
[f84d71e] | 130 | Collection<Identity> ids) throws ABACException { |
---|
| 131 | Credential[] rv = null; |
---|
[3612811] | 132 | ABACException err = null; |
---|
[6bf703b0] | 133 | UnclosableBufferedInputStream bs = |
---|
| 134 | new UnclosableBufferedInputStream(is, maxSize); |
---|
[3612811] | 135 | |
---|
[6bf703b0] | 136 | bs.mark(maxSize); |
---|
[aaadefd] | 137 | for (CredentialFactorySpecialization c : spec ) { |
---|
[3612811] | 138 | try { |
---|
[6bf703b0] | 139 | rv = c.parseCredential(bs, ids); |
---|
[f84d71e] | 140 | break; |
---|
[3612811] | 141 | } |
---|
[f84d71e] | 142 | catch (ABACException e ) { |
---|
| 143 | err = e; |
---|
[3612811] | 144 | rv = null; |
---|
| 145 | } |
---|
| 146 | try { |
---|
[6bf703b0] | 147 | if (spec.size() > 1) bs.reset(); |
---|
[3612811] | 148 | } |
---|
| 149 | catch (IOException ie) { |
---|
| 150 | break; |
---|
| 151 | } |
---|
[3797bbe] | 152 | } |
---|
[3612811] | 153 | |
---|
[3797bbe] | 154 | if ( rv != null ) |
---|
[f84d71e] | 155 | return rv; |
---|
[3797bbe] | 156 | else |
---|
[3612811] | 157 | throw (err != null) ? err : |
---|
| 158 | new ABACException("null exception and failed construction??"); |
---|
| 159 | } |
---|
| 160 | |
---|
| 161 | |
---|
[f84d71e] | 162 | /** |
---|
| 163 | * Parse a File using each possible credential format and the |
---|
| 164 | * available identities for validation. Return the credentials found or |
---|
| 165 | * throw an ABACException with the problem. Calls the InputStream version |
---|
| 166 | * internally. |
---|
| 167 | * @param f a File to parse |
---|
| 168 | * @param ids a Collection of Identities for validation |
---|
| 169 | * @return an Array of Credentials parsed |
---|
| 170 | * @throws CertInvalidException if the stream is unparsable |
---|
| 171 | * @throws MissingIssuerException if none of the Identities can validate the |
---|
| 172 | * @throws BadSignatureException if the signature check fails |
---|
| 173 | */ |
---|
[d06c453] | 174 | public Credential[] parseCredential(File f, |
---|
[f84d71e] | 175 | Collection<Identity> ids) throws ABACException { |
---|
[3612811] | 176 | try { |
---|
[f84d71e] | 177 | return parseCredential(new FileInputStream(f), ids); |
---|
[3612811] | 178 | } |
---|
| 179 | catch (FileNotFoundException e) { |
---|
| 180 | throw new CertInvalidException(e.getMessage(), e); |
---|
| 181 | } |
---|
[3797bbe] | 182 | } |
---|
[f84d71e] | 183 | /** |
---|
| 184 | * Parse a file named fn using each possible credential format and the |
---|
| 185 | * available identities for validation. Return the credentials found or |
---|
| 186 | * throw an ABACException with the problem. Calls the InputStream version |
---|
| 187 | * internally. |
---|
[327a740] | 188 | * @param fn a String holding the content to parse |
---|
[f84d71e] | 189 | * @param ids a Collection of Identities for validation |
---|
| 190 | * @return an Array of Credentials parsed |
---|
| 191 | * @throws CertInvalidException if the stream is unparsable |
---|
| 192 | * @throws MissingIssuerException if none of the Identities can validate the |
---|
| 193 | * @throws BadSignatureException if the signature check fails |
---|
| 194 | */ |
---|
[327a740] | 195 | public Credential[] parseCredential(String s, |
---|
[f84d71e] | 196 | Collection<Identity> ids) throws ABACException { |
---|
[327a740] | 197 | ByteArrayInputStream bs = new ByteArrayInputStream(s.getBytes()); |
---|
| 198 | |
---|
| 199 | return parseCredential(bs, ids); |
---|
[3797bbe] | 200 | } |
---|
[3612811] | 201 | |
---|
[f84d71e] | 202 | /** |
---|
[a7f73b5] | 203 | * Return a credential from the first class registered in |
---|
| 204 | * the factory. |
---|
[aaadefd] | 205 | * @param head a Role, the head of the encoded ABAC statement |
---|
| 206 | * @param tail a Role, the tail of the decoded ABAC statement |
---|
[79f516b] | 207 | * @param aliases a KeyIDMap holding aliases for this creation |
---|
[aaadefd] | 208 | * @return a Credential encoding that ABAC statement |
---|
| 209 | */ |
---|
[79f516b] | 210 | public Credential generateCredential(Role head, Role tail, |
---|
| 211 | KeyIDMap aliases) { |
---|
[aaadefd] | 212 | if (spec.isEmpty()) return null; |
---|
[79f516b] | 213 | return spec.get(0).generateCredential(head, tail, aliases); |
---|
[aaadefd] | 214 | } |
---|
| 215 | |
---|
| 216 | /** |
---|
| 217 | * Add the named class to the list of usable specializations. The class |
---|
| 218 | * passed in must have a static getCredentialFactorySpecialization() method |
---|
| 219 | * that returns a CredentialFactorySpecialization to use. |
---|
[f84d71e] | 220 | * @param name a String containing the binary name of the class to register |
---|
| 221 | * @throws ABACException if there is a problem. The cause field of this |
---|
| 222 | * exception is set to the classloading exception, if any. |
---|
| 223 | */ |
---|
[d06c453] | 224 | public void registerClass(String name) |
---|
[f84d71e] | 225 | throws ABACException { |
---|
[aaadefd] | 226 | CredentialFactorySpecialization cs = null; |
---|
[f84d71e] | 227 | |
---|
| 228 | try { |
---|
[152673d] | 229 | /* <?> doesn't guarantee much, but shuts the compiler up */ |
---|
| 230 | Class<?> c = Class.forName(name); |
---|
[aaadefd] | 231 | Method m = c.getMethod("getCredentialFactorySpecialization"); |
---|
[3612811] | 232 | |
---|
[aaadefd] | 233 | cs = (CredentialFactorySpecialization) m.invoke(null); |
---|
[f84d71e] | 234 | } |
---|
| 235 | catch (Exception e) { |
---|
| 236 | throw new ABACException("Could not register credential type" + |
---|
| 237 | e.getMessage(), e); |
---|
| 238 | } |
---|
[3612811] | 239 | |
---|
[aaadefd] | 240 | spec.add(cs); |
---|
[3612811] | 241 | } |
---|
[3797bbe] | 242 | } |
---|