/* * Copyright 2001-2005 Internet2 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * File: SAMLArtifact.java * */ package gov.nih.nci.cagrid.opensaml.artifact; import gov.nih.nci.cagrid.opensaml.artifact.Artifact; import gov.nih.nci.cagrid.opensaml.artifact.ArtifactParserException; import gov.nih.nci.cagrid.opensaml.artifact.InvalidArgumentException; import gov.nih.nci.cagrid.opensaml.artifact.NullArgumentException; import gov.nih.nci.cagrid.opensaml.artifact.SAMLArtifactChecking; import gov.nih.nci.cagrid.opensaml.artifact.TwoByteSequence; import gov.nih.nci.cagrid.opensaml.artifact.Util; import java.util.Arrays; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; /** * <p>The <code>SAMLArtifact</code> abstract class is a partial * implementation of the <code>Artifact</code> interface. * In particular, this class provides a final * implementation of the <code>Artifact.TypeCode</code> * interface. To complete the implementation, a subclass * <strong>must</strong> implement the * <code>Artifact.RemainingArtifact</code> * and <code>Artifact.Parser</code> interfaces.</p> * * <p>A <em>SAML artifact</em> has three components: * 1) a two-byte type code, 2) a precise * definition of "remaining artifact," and * 3) an encoding scheme. * The encoding is fixed (base64) whereas the type code * and "remaining artifact" vary from artifact to * artifact.</p> * * <p>The formal definition of a <em>SAML artifact</em> * is given by the following productions:</p> * * <pre>SAMLArtifact := base64(TypeCode RemainingArtifact) *TypeCode := Byte1 Byte2</pre> * * <p>An implementation must specify a type code value * and provide a definition for <code>RemainingArtifact</code> * to complete the grammar.</p> * * <p>Subclasses of <code>SAMLArtifact</code> * <strong>must</strong> adhere to certain naming conventions. * First of all, the name of the subclass is always * "SAMLArtifactType" followed by the hex encoding of the type * code. For instance, a subclass that implements a type * 0x0001 artifact is called <code>SAMLArtifactType0001</code>. * Moreover, each class <strong>must</strong> contain an * implementation of <code>Artifact.Parser</code> called * (simply) <code>Parser</code>. In this way, the * {@link SAMLArtifact.TypeCode#getParser()} method can locate * the appropriate parser on-demand (based on type code).</p> * * @author Tom Scavo */ public abstract class SAMLArtifact implements Artifact, SAMLArtifactChecking { /** * The <code>typeCode</code> property * of this <code>SAMLArtifact</code> object. */ protected Artifact.TypeCode typeCode = null; /** * The <code>remainingArtifact</code> property * of this <code>SAMLArtifact</code> object. */ protected Artifact.RemainingArtifact remainingArtifact = null; protected SAMLArtifact() {} public Artifact.TypeCode getTypeCode() { return this.typeCode; } public Artifact.RemainingArtifact getRemainingArtifact() { return this.remainingArtifact; } public int size() { return this.typeCode.size() + this.remainingArtifact.size(); } public byte[] getBytes() { byte[] bytes0 = this.typeCode.getBytes(); byte[] bytes1 = this.remainingArtifact.getBytes(); return Util.concat( bytes0, bytes1 ); } /** * Encode this <code>SAMLArtifact</code> object using the * base64 encoding method. * * @return the encoded artifact * * @see org.apache.commons.codec.binary.Base64 */ public String encode() { return new String( Base64.encodeBase64( this.getBytes() ) ); } /** * Encode this <code>SAMLArtifact</code> object using a * simple hex encoding method. * * @return the encoded artifact * * @see org.apache.commons.codec.binary.Hex */ public String toString() { return new String( Hex.encodeHex( this.getBytes() ) ); } public boolean equals( Object o ) { if ( !( o instanceof SAMLArtifact ) ) { return false; } SAMLArtifact artifact = (SAMLArtifact) o; return Arrays.equals( this.getBytes(), artifact.getBytes() ); } public int hashCode() { return this.typeCode.hashCode() & this.remainingArtifact.hashCode(); } /* nested classes */ /** * A <code>TypeCode</code> is an arbitrary two-byte sequence. * The most important method defined by this class is * the {@link SAMLArtifact.TypeCode#getParser()} method. */ public static final class TypeCode extends TwoByteSequence implements Artifact.TypeCode { // constants: private static final String CLASS_NAME_PREFIX = "gov.nih.nci.cagrid.opensaml.artifact.SAMLArtifactType"; private static final String CLASS_NAME_SUFFIX = "$Parser"; /** * The workhorse constructor. */ public TypeCode( byte b0, byte b1 ) { super( b0, b1 ); } /** * A convenience constructor. */ public TypeCode( short tc ) { super( tc ); } /** * Converts this <code>TypeCode</code> object to a string. * The two bytes are hex-encoded and prefixed by "0x". * The result is the string equivalent of a hex integer. * * @return a string version of this type code */ public String toString() { return "0x" + super.toString(); } /** * Gets the <code>Artifact.Parser</code> object corresponding * to this <code>TypeCode</code>. * * @return an artifact parser */ public Artifact.Parser getParser() throws ArtifactParserException { String typeCodeStr = super.toString(); String className = CLASS_NAME_PREFIX + typeCodeStr + CLASS_NAME_SUFFIX; Artifact.Parser parser; try { parser = (Artifact.Parser) Class.forName( className ).newInstance(); } catch ( Exception e ) { throw new ArtifactParserException( e.getMessage() ); } return parser; } } /** * Subclasses of <code>SAMLArtifact</code> must extend this abstract * class or implement <code>Artifact.RemainingArtifact</code> from * scratch. */ public abstract static class RemainingArtifact implements Artifact.RemainingArtifact { /** * Encode this <code>Artifact.RemainingArtifact</code> * object using a simple hex encoding method. * * @return the encoded <code>remainingArtifact</code> * * @see org.apache.commons.codec.binary.Hex */ public String toString() { return new String( Hex.encodeHex( this.getBytes() ) ); } /** * Compares this <code>Artifact.RemainingArtifact</code> * object to the given object. If the latter is not an * instance of <code>Artifact.RemainingArtifact</code>, * the method immediately returns false. * * @return true if and only if the given object is equivalent * to this <code>Artifact.RemainingArtifact</code> * object */ public boolean equals( Object o ) { if ( !( o instanceof Artifact.RemainingArtifact ) ) { return false; } Artifact.RemainingArtifact ra = (Artifact.RemainingArtifact) o; return Arrays.equals( this.getBytes(), ra.getBytes() ); } } /** * Subclasses of <code>SAMLArtifact</code> must extend this * (trivial) abstract class or implement * <code>Artifact.Parser</code> from scratch. */ public abstract static class Parser implements Artifact.Parser {} /* static methods */ /** * Pre-parses an encoded artifact. This method * determines the type code of the encoded artifact. Knowing * the type code, the corresponding parser may be obtained * with {@link SAMLArtifact.TypeCode#getParser()}, for * instance. * * @param s the string to be parsed * * @return the type code of the encoded artifact */ public static Artifact.TypeCode getTypeCode( String s ) { byte[] bytes = Base64.decodeBase64( s.getBytes() ); return new TypeCode( (byte) bytes[0], (byte) bytes[1] ); } public static void checkHandleArg( byte[] handle ) { checkNullArg( handle ); int n = handle.length; if ( n != HANDLE_LENGTH ) { throw new InvalidArgumentException( n, HANDLE_LENGTH ); } } public static void checkIdentifierArg( byte[] identifier ) { checkNullArg( identifier ); int n = identifier.length; if ( n != IDENTIFIER_LENGTH ) { throw new InvalidArgumentException( n, IDENTIFIER_LENGTH ); } } public static void checkNullArg( Object obj ) { if ( obj == null ) { throw new NullArgumentException(); } } }