/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.security.provider; import java.io.Serializable; import java.util.Enumeration; import java.util.Vector; import org.apereo.portal.properties.PropertiesManager; import org.apereo.portal.security.IAdditionalDescriptor; import org.apereo.portal.security.IOpaqueCredentials; import org.apereo.portal.security.IParentAwareSecurityContext; import org.apereo.portal.security.IPrincipal; import org.apereo.portal.security.ISecurityContext; import org.apereo.portal.security.PortalSecurityException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the basic abstract class for all security contexts that should chain to children security * contexts. * * returns an Enumeration of names for the subcontexts. */ public abstract class ChainingSecurityContext implements ISecurityContext { protected final Logger log = LoggerFactory.getLogger(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.apereo.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; 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 subcontexts 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(); 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) { 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; } 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 suppose 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; } } }