/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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.
*/
package org.jasig.portal.security.provider;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Vector;
import org.jasig.portal.properties.PropertiesManager;
import org.jasig.portal.security.IAdditionalDescriptor;
import org.jasig.portal.security.IOpaqueCredentials;
import org.jasig.portal.security.IParentAwareSecurityContext;
import org.jasig.portal.security.IPrincipal;
import org.jasig.portal.security.ISecurityContext;
import org.jasig.portal.security.PortalSecurityException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <p>This is the basic abstract class for all security contexts that should
* chain to children security contexts.</p>
*
* @author Andrew Newman, newman@yale.edu
* @version $Revision: 20532 $
* @author Don Fracapane (df7@columbia.edu)
* Added a new method named getSubContextNames() that returns an Enumeration of names
* for the subcontexts.
*/
public abstract class ChainingSecurityContext implements ISecurityContext
{
protected final Log log = LogFactory.getLog(getClass());
/**
* Default value for stopWhenAuthenticated.
* This value will be used when the corresponding property cannot be loaded.
*/
private static final boolean DEFAULT_STOP_WHEN_AUTHENTICATED = true;
protected static boolean stopWhenAuthenticated = PropertiesManager.getPropertyAsBoolean("org.jasig.portal.security.provider.ChainingSecurityContext.stopWhenAuthenticated", DEFAULT_STOP_WHEN_AUTHENTICATED);
protected boolean isauth = false;
protected Vector mySubContexts;
protected ChainingPrincipal myPrincipal;
protected ChainingOpaqueCredentials myOpaqueCredentials;
protected IAdditionalDescriptor myAdditionalDescriptor;
protected Comparator myOrder;
public ChainingSecurityContext() {
myPrincipal = new ChainingPrincipal();
myOpaqueCredentials = new ChainingOpaqueCredentials();
myAdditionalDescriptor = new ChainingAdditionalDescriptor();
mySubContexts = new Vector();
}
public IPrincipal getPrincipalInstance() {
if (this.isauth)
return new ChainingPrincipal();
else
return this.myPrincipal;
}
public IOpaqueCredentials getOpaqueCredentialsInstance() {
if (this.isauth)
return new ChainingOpaqueCredentials();
else
return this.myOpaqueCredentials;
}
/**
* We walk the chain of subcontext assigning principals and opaquecredentials
* from the parent. Note that the contexts themselves should resist
* actually performing the assignment if an assignment has already been made
* to either the credentials or the UID.
*/
public synchronized void authenticate() throws PortalSecurityException {
int i;
Enumeration e = mySubContexts.elements();
boolean error = false;
while (e.hasMoreElements()) {
ISecurityContext sctx = ((Entry) e.nextElement()).getCtx();
// The principal and credential are now set for all subcontexts in Authentication
try {
if (sctx instanceof IParentAwareSecurityContext) {
((IParentAwareSecurityContext) sctx).authenticate(this);
} else {
sctx.authenticate();
}
} catch (Exception ex) {
error = true;
log.error("Exception authenticating subcontext " + sctx, ex);
}
// Stop attempting to authenticate if authenticated and if the property flag is set
if(stopWhenAuthenticated && sctx.isAuthenticated()) {
break;
}
}
// Zero out the actual credentials if it isn't already null
if (this.myOpaqueCredentials.credentialstring != null){
for (i = 0; i < this.myOpaqueCredentials.credentialstring.length; i++)
this.myOpaqueCredentials.credentialstring[i] = 0;
myOpaqueCredentials.credentialstring = null;
}
if (error && !this.isauth) throw new PortalSecurityException("One of the security subcontexts threw an exception");
return;
}
public IPrincipal getPrincipal() {
if (this.isauth)
return this.myPrincipal;
else
return null;
}
public IOpaqueCredentials getOpaqueCredentials() {
if (this.isauth)
return this.myOpaqueCredentials;
else
return null;
}
public IAdditionalDescriptor getAdditionalDescriptor() {
if (this.isauth)
return this.myAdditionalDescriptor;
else
return null;
}
public boolean isAuthenticated() {
return this.isauth;
}
public synchronized ISecurityContext getSubContext(String name) {
for (int i = 0; i < mySubContexts.size(); i++)
{
Entry entry = (Entry) mySubContexts.get(i);
if (entry.getKey() != null && entry.getKey().equals(name))
{
return(entry.getCtx());
}
}
PortalSecurityException ep = new PortalSecurityException("No such subcontext: " + name);
if (log.isDebugEnabled())
log.debug("No such subcontext as " + name, ep);
return(null);
}
public synchronized boolean doesSubContextExist(String name) {
for (int i = 0; i < mySubContexts.size(); i++)
{
Entry entry = (Entry)mySubContexts.get(i);
if (entry.getKey() != null && entry.getKey().equals(name))
{
return(true);
}
}
return(false);
}
// Return an enumeration of subcontexts by running the vector and
// creating the enumeration. All this so the subcontexts will
// be returned in the order they appeared in the properties file.
public synchronized Enumeration getSubContexts() {
Enumeration e = mySubContexts.elements();
class Adapter implements Enumeration {
Enumeration base;
public Adapter(Enumeration e) {
this.base = e;
}
public boolean hasMoreElements() {
return base.hasMoreElements();
}
public Object nextElement() {
return ((Entry) base.nextElement()).getCtx();
}
}
return new Adapter(e);
}
public synchronized void addSubContext(String name, ISecurityContext ctx)
throws PortalSecurityException {
// Make sure the subcontext does not already exist in the chain
if(doesSubContextExist(name))
{
PortalSecurityException ep = new PortalSecurityException("Subcontext already exists: " + name);
log.error("Subcontext already exists:" + name, ep);
throw(ep);
}
else
{
mySubContexts.add(new Entry(name, ctx));
}
}
// I 'spose the public class could just implement all of these interfaces
// but I prefer member classes. -ADN
protected class ChainingPrincipal implements IPrincipal {
protected String globalUID;
protected String UID;
protected String FullName;
/**
* Original, no-arg constructor used by the <code>ChainingSecurityContext</code>.
*
*/
public ChainingPrincipal() {}
/**
* Creates a new <code>ChainingPrincipal</code> from the specified <code>IPrincipal</code>.
*
*/
public ChainingPrincipal(IPrincipal p) {
this.globalUID = p.getGlobalUID();
this.UID = p.getUID();
this.FullName = p.getFullName();
}
public String getUID() {
return this.UID;
}
public String getGlobalUID() {
return this.globalUID;
}
// This is supposed to be the person's "human readable" name. We should
// probably do an account lookup at the very least to return this.
public String getFullName() {
return this.FullName;
}
public void setUID(String UID) {
if (this.UID == null)
this.UID = UID;
}
public void setFullName(String FullName) {
if(this.FullName == null)
this.FullName = FullName;
}
}
protected class ChainingOpaqueCredentials implements IOpaqueCredentials {
public byte[] credentialstring;
// Since we want to explicitly zero our credentials after authenticate,
// copy the credentials here in case a sub-authenticator doesn't want
// to perform the operation immediately.
public void setCredentials(byte[] credentials) {
int i;
if (this.credentialstring == null) {
this.credentialstring = new byte[credentials.length];
for (i = 0; i < credentials.length; i++)
this.credentialstring[i] = credentials[i];
}
}
public void setCredentials(String credentials) {
if (this.credentialstring == null && credentials!=null)
setCredentials(credentials.getBytes());
}
}
// Returns an Enumeration of the names of the subcontexts.
public synchronized Enumeration getSubContextNames() {
Vector scNames = new Vector();
for (int i = 0; i < mySubContexts.size(); i++)
{
Entry entry = (Entry) mySubContexts.get(i);
if (entry.getKey() != null){
scNames.add(entry.getKey());
}
}
return scNames.elements();
}
/**
* A default, placeholder implementation of IAdditionalDescriptor an instance of which
* is the default value for the instance variable "myAdditionalDescriptor" of instances of
* this class.
*/
public class ChainingAdditionalDescriptor implements IAdditionalDescriptor {
// do nothing
}
// entries in our subcontext list
private static class Entry implements Serializable {
String key;
ISecurityContext ctx;
public Entry(String key, ISecurityContext ctx) {
this.key = key;
this.ctx = ctx;
}
public ISecurityContext getCtx() {
return this.ctx;
}
public String getKey() {
return this.key;
}
}
}