package org.azzyzt.jee.runtime.meta; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Credentials are used along with the <code>AzzyztGeneratorOption</code> * <code>AddCredentialBasedAuthorization</code> and the annotation * <code>RequiresCredentials</code>. Credentials guard service * invocations. A principal invoking a service has zero or any number of * credentials associated with the particular service. * <p> * Credentials have names and may have zero or any number of properties. * Each property has a name and a value. Property names and values are strings, * the default value for a property is the string "<code>true</code>". * <p> * Thus properties without explicitly given value are meant to be understood as * flags, modifying the semantics of their owning credential. * <p> * Credentials have a string representation. The generic site adapter coming * with Azzyzt JEE Tools takes the value of an HTTP header (by default * "<code>x-authorize-roles</code>", but that can be set in the server per * application or server-wide via JNDI string resources), and if the header * is set, interprets it as string representation of the invoking principal's * credentials. * <p> * Properties of credentials are specified after the name of the credential in parenthesis. An empty * property list results in empty parenthesis. Empty parenthesis can be omitted. * <p> * If more than one credential is specified, credentials must be separated with semicolons. * White space is not significant. * <p> * When using <code>AddCredentialBasedAuthorization</code>, a <code>RequiresCredentials</code> * annotation is generated on the <code>ModifyMultiBean</code> and the store and delete methods of the * entity-specific service beans. The string representation of the generated <code>RequiresCredentials</code> * annotation is "<code>modify()</code>", meaning a credential "<code>modify</code>" without properties. * * The <code>RequiresCredentials</code> annotation can be used in user-written beans. It can be set * on the whole service bean and/or on methods. For any particular invocation the required * credentials are cumulative, with credentials on Methods overriding those on service bean classes. * * @see org.azzyzt.jee.runtime.annotation.RequiresCredentials * @see org.azzyzt.jee.runtime.meta.AzzyztGeneratorOption */ public class Credentials { private Map<String, Credential> creds = new HashMap<String, Credential>(); public static Credentials fromString(String in) { Credentials result = new Credentials(); if (in == null || in.isEmpty()) { return result; } String[] credStatements = in.split(";"); for (String credStatement : credStatements) { // credential name credStatement = credStatement.trim(); if (credStatement.isEmpty()) { continue; } String[] credStatementParts = credStatement.split("[()]"); if (!credStatementParts[0].matches("^\\w+$")) { continue; } String credentialName = credStatementParts[0]; Credential c = new Credential(credentialName); result.add(credentialName, c); if (credStatementParts.length == 1) { continue; } // credential properties in parens String propsPart = credStatementParts[1]; String[] props = propsPart.split(","); for (String prop : props) { // isolate name/value pairs prop = prop.trim(); if (prop.isEmpty()) { continue; } String[] p = prop.split("="); String pName = p[0].trim(); if (pName.isEmpty()) { continue; } String pValue = Credential.PROPVAL_TRUE; // default if no value is given if (p.length > 1) { String p1 = p[1].trim(); if (!p1.isEmpty()) { // has a value pValue = p1; } } c.addProperty(pName, pValue); } } return result; } /** * @param required * @return <code>true</code> if this set of credentials * contains all required credentials, and if a required * credential has properties, all required properties are present and * have the required property values. Additional credentials and * additional property values are accepted. */ public boolean satisfy(Credentials required) { if (required == null || required.isEmpty()) { // null requirement is satisfied by any mix of credentials return true; } for (String credentialName : required.credentialNameSet()) { if (!hasCredential(credentialName)) { return false; } if (!getCredential(credentialName).satisfies(required.getCredential(credentialName))) { return false; } } return true; } /** * Merges this credentials with other credentials. As a result, this credentials are equal to * (if originally empty) or a superset of the specified <code>other</code> credentials. * <p> * This credentials' properties are updated with <code>other</code> credentials' properties. * In case of equal property names, <code>other</code>'s values override this' values. * * @param other */ public void mergeFrom(Credentials other) { if (other == null || other.isEmpty()) { return; } for (String credentialName : other.credentialNameSet()) { Credential c = other.getCredential(credentialName); if (hasCredential(credentialName)) { getCredential(credentialName).mergeFrom(c); } else { add(credentialName, c); } } } public boolean hasCredential(String credentialName) { return creds.containsKey(credentialName); } public Credential getCredential(String credentialName) { return creds.get(credentialName); } public boolean isEmpty() { return creds.isEmpty(); } public Set<String> credentialNameSet() { return creds.keySet(); } public int numberOfCredentials() { return creds.size(); } public Credential add(String credentialName, Credential c) { return creds.put(credentialName, c); } }