/*
* 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: SAMLArtifactType0002.java
*
*/
package gov.nih.nci.cagrid.opensaml.artifact;
import gov.nih.nci.cagrid.opensaml.SAMLConfig;
import gov.nih.nci.cagrid.opensaml.artifact.Artifact;
import gov.nih.nci.cagrid.opensaml.artifact.ArtifactParseException;
import gov.nih.nci.cagrid.opensaml.artifact.SAMLArtifact;
import gov.nih.nci.cagrid.opensaml.artifact.Util;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
/**
* <p>This class implements a type 0x0002 artifact as
* specified by SAML V1.1.</p>
*
* <pre>TypeCode := 0x0002
*RemainingArtifact := AssertionHandle SourceLocation
*AssertionHandle := 20-byte_sequence
*SourceLocation := URI</pre>
*
* <p>Since the URI is arbitrary, a type 0x0002
* artifact is of indeterminate size.</p>
*
* <p>The <code>AssertionHandle</code> is a sequence
* of random bytes that points to an
* authentication assertion at the IdP.</p>
*
* <p>Before the artifact is base64-encoded, the URI
* is converted to a sequence of bytes based on UTF-8.
* While parsing an encoded artifact, this encoding
* process is reversed.</p>
*
* @author Tom Scavo
*/
public class SAMLArtifactType0002 extends SAMLArtifact {
/**
* The type code of this <code>Artifact</code> object.
*/
public static final Artifact.TypeCode TYPE_CODE =
new TypeCode( (byte) 0x00, (byte) 0x02 );
/**
* This constructor initializes the
* <code>remainingArtifact</code> property by calling
* the corresponding constructor of this implementation
* of <code>Artifact.RemainingArtifact</code>.
* <p>
* This constructor throws an (unchecked)
* <code>NullArgumentException</code> if its argument is null.
*
* @param sourceLocation the desired source location
* of this <code>SAMLArtifactType0002</code> object
*
* @see SAMLArtifactType0002.RemainingArtifact
* @see gov.nih.nci.cagrid.opensaml.artifact.NullArgumentException
*/
public SAMLArtifactType0002( URI sourceLocation ) {
checkNullArg( sourceLocation );
this.typeCode = TYPE_CODE;
this.remainingArtifact = new RemainingArtifact( sourceLocation );
}
/**
* This constructor initializes the
* <code>remainingArtifact</code> property by calling
* the corresponding constructor of this implementation
* of <code>Artifact.RemainingArtifact</code>.
* <p>
* This constructor throws a <code>NullArgumentException</code>
* or <code>InvalidArgumentException</code> if any of its
* arguments are null or invalid, respectively.
* These exceptions are unchecked.
*
* @param assertionHandle the desired assertion handle
* of this <code>SAMLArtifactType0002</code> object
*
* @param sourceLocation the desired source location
* of this <code>SAMLArtifactType0002</code> object
*
* @see SAMLArtifactType0002.RemainingArtifact
* @see gov.nih.nci.cagrid.opensaml.artifact.NullArgumentException
* @see gov.nih.nci.cagrid.opensaml.artifact.InvalidArgumentException
*/
public SAMLArtifactType0002( byte[] assertionHandle, URI sourceLocation ) {
checkHandleArg( assertionHandle );
checkNullArg( sourceLocation );
this.typeCode = TYPE_CODE;
this.remainingArtifact =
new RemainingArtifact( assertionHandle, sourceLocation );
}
/**
* This constructor initializes the
* <code>remainingArtifact</code> property to the
* given value.
* <p>
* This constructor throws an (unchecked)
* <code>NullArgumentException</code> if its argument is null.
*
* @param remainingArtifact the desired value of
* the <code>remainingArtifact</code> property
* of this <code>SAMLArtifactType0002</code> object
*
* @see SAMLArtifactType0002.RemainingArtifact
* @see gov.nih.nci.cagrid.opensaml.artifact.NullArgumentException
*/
public SAMLArtifactType0002( Artifact.RemainingArtifact remainingArtifact ) {
checkNullArg( remainingArtifact );
this.typeCode = TYPE_CODE;
this.remainingArtifact = remainingArtifact;
}
/**
* A convenience method that returns the
* <code>assertionHandle</code> property of this implementation
* of <code>Artifact.RemainingArtifact</code>.
*
* @return the <code>assertionHandle</code> property
*
* @see SAMLArtifactType0002.RemainingArtifact
*/
public byte[] getAssertionHandle() {
return ((RemainingArtifact) this.remainingArtifact).getAssertionHandle();
}
/**
* A convenience method that returns the
* <code>sourceLocation</code> property of this implementation
* of <code>Artifact.RemainingArtifact</code>.
*
* @return the <code>sourceLocation</code> property
*
* @see SAMLArtifactType0002.RemainingArtifact
*/
public URI getSourceLocation() {
return ((RemainingArtifact) this.remainingArtifact).getSourceLocation();
}
/**
* An implementation of <code>Artifact.RemainingArtifact</code>
* for type 0x0002 artifacts (via extension of
* <code>SAMLArtifact.RemainingArtifact</code>).
* This class defines two properties
* (<code>assertionHandle</code> and <code>sourceLocation</code>).
*/
public static final class RemainingArtifact
extends SAMLArtifact.RemainingArtifact {
private byte[] assertionHandle;
private URI sourceLocation;
private byte[] sourceLocationBytes;
/**
* This constructor initializes the <code>sourceLocation</code>
* property of this <code>RemainingArtifact</code>
* object to the given value. The <code>assertionHandle</code>
* property is initialized to a sequence of random bytes.
*
* @param sourceLocation a source location
*/
public RemainingArtifact( URI sourceLocation ) {
byte[] assertionHandle = SAMLConfig.instance().getDefaultIDProvider().generateRandomBytes( HANDLE_LENGTH );
RemainingArtifact ra;
ra = new RemainingArtifact( assertionHandle, sourceLocation );
this.assertionHandle = ra.assertionHandle;
this.sourceLocation = ra.sourceLocation;
this.sourceLocationBytes = ra.sourceLocationBytes;
}
/**
* This constructor initializes the properties
* of this <code>RemainingArtifact</code>
* object to the given values.
* <p>
* This constructor throws a <code>NullArgumentException</code>
* or <code>InvalidArgumentException</code> if any of its
* arguments are null or invalid, respectively.
* These exceptions are unchecked.
*
* @param assertionHandle an assertion handle
*
* @param sourceLocation a source location
*
* @see gov.nih.nci.cagrid.opensaml.artifact.NullArgumentException
* @see gov.nih.nci.cagrid.opensaml.artifact.InvalidArgumentException
*/
public RemainingArtifact( byte[] assertionHandle, URI sourceLocation ) {
checkHandleArg( assertionHandle );
checkNullArg( sourceLocation );
this.assertionHandle = assertionHandle;
this.sourceLocation = sourceLocation;
this.sourceLocationBytes = sourceLocation.toBytes();
}
/**
* Get the <code>assertionHandle</code> property of this
* <code>Artifact.RemainingArtifact</code> object.
*
* return the <code>assertionHandle</code> property
*/
public byte[] getAssertionHandle() { return this.assertionHandle; }
/**
* Get the <code>sourceLocation</code> property of this
* <code>Artifact.RemainingArtifact</code> object.
*
* return the <code>sourceLocation</code> property
*/
public URI getSourceLocation() { return this.sourceLocation; }
public int size() {
return this.assertionHandle.length + this.sourceLocationBytes.length;
}
public byte[] getBytes() {
byte[] bytes0 = this.assertionHandle;
byte[] bytes1 = this.sourceLocationBytes;
return Util.concat( bytes0, bytes1 );
}
public int hashCode() {
return this.assertionHandle.hashCode() &
this.sourceLocationBytes.hashCode();
}
}
/**
* An implementation of <code>Artifact.Parser</code>
* for type 0x0002 artifacts.
*/
public static final class Parser implements Artifact.Parser {
/**
* Parse the given encoded string.
*
* @param s the encoded string
*
* @return an artifact that may be cast to type
* <code>SAMLArtifactType0002</code>
*
* @exception gov.nih.nci.cagrid.opensaml.artifact.ArtifactParseException
* if the length of the decoded string is
* less than the minimum length, or the
* type code is incorrect, or
* the tail portion of the parsed string
* is not a valid URI
*
* @see org.apache.commons.codec.binary.Base64
*/
public Artifact parse( String s ) throws ArtifactParseException {
// check total length:
byte[] bytes = Base64.decodeBase64( s.getBytes() );
int minLength = 2 + HANDLE_LENGTH;
if ( bytes.length < minLength ) {
throw new ArtifactParseException( bytes.length, minLength );
}
// check type code:
TypeCode typeCode =
new TypeCode( bytes[0], bytes[1] );
if ( ! typeCode.equals( TYPE_CODE ) ) {
throw new ArtifactParseException( typeCode, TYPE_CODE );
}
// extract the assertion handle:
byte[] assertionHandle = new byte[ HANDLE_LENGTH ];
System.arraycopy( bytes, 2, assertionHandle, 0, HANDLE_LENGTH );
// extract the remaining bytes:
int length = bytes.length - minLength;
byte[] remainingBytes = new byte[ length ];
System.arraycopy( bytes, minLength, remainingBytes, 0, length );
// convert the remaining bytes to a string:
URI uri;
try {
uri = new URI( remainingBytes, "UTF-8" );
}
catch (UnsupportedEncodingException e) {
throw new ArtifactParseException("UTF-8 unsupported string format, can not create artifact URI");
}
return new SAMLArtifactType0002( assertionHandle, uri );
}
}
}