001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.security.provider;
007:
008: import java.io.Serializable;
009: import java.util.Comparator;
010: import java.util.Enumeration;
011: import java.util.Vector;
012:
013: import org.jasig.portal.properties.PropertiesManager;
014: import org.jasig.portal.security.IAdditionalDescriptor;
015: import org.jasig.portal.security.IOpaqueCredentials;
016: import org.jasig.portal.security.IPrincipal;
017: import org.jasig.portal.security.ISecurityContext;
018: import org.jasig.portal.security.PortalSecurityException;
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: /**
023: * <p>This is the basic abstract class for all security contexts that should
024: * chain to children security contexts.</p>
025: *
026: * @author Andrew Newman, newman@yale.edu
027: * @version $Revision: 36777 $
028: * @author Don Fracapane (df7@columbia.edu)
029: * Added a new method named getSubContextNames() that returns an Enumeration of names
030: * for the subcontexts.
031: */
032: public abstract class ChainingSecurityContext implements
033: ISecurityContext {
034: protected final Log log = LogFactory.getLog(getClass());
035:
036: /**
037: * Default value for stopWhenAuthenticated.
038: * This value will be used when the corresponding property cannot be loaded.
039: */
040: private static final boolean DEFAULT_STOP_WHEN_AUTHENTICATED = true;
041: protected static boolean stopWhenAuthenticated = PropertiesManager
042: .getPropertyAsBoolean(
043: "org.jasig.portal.security.provider.ChainingSecurityContext.stopWhenAuthenticated",
044: DEFAULT_STOP_WHEN_AUTHENTICATED);
045:
046: protected boolean isauth = false;
047: protected Vector mySubContexts;
048: protected ChainingPrincipal myPrincipal;
049: protected ChainingOpaqueCredentials myOpaqueCredentials;
050: protected IAdditionalDescriptor myAdditionalDescriptor;
051: protected Comparator myOrder;
052:
053: public ChainingSecurityContext() {
054: myPrincipal = new ChainingPrincipal();
055: myOpaqueCredentials = new ChainingOpaqueCredentials();
056: myAdditionalDescriptor = new ChainingAdditionalDescriptor();
057: mySubContexts = new Vector();
058: }
059:
060: public IPrincipal getPrincipalInstance() {
061: if (this .isauth)
062: return new ChainingPrincipal();
063: else
064: return this .myPrincipal;
065: }
066:
067: public IOpaqueCredentials getOpaqueCredentialsInstance() {
068: if (this .isauth)
069: return new ChainingOpaqueCredentials();
070: else
071: return this .myOpaqueCredentials;
072: }
073:
074: /**
075: * We walk the chain of subcontext assigning principals and opaquecredentials
076: * from the parent. Note that the contexts themselves should resist
077: * actually performing the assignment if an assignment has already been made
078: * to either the credentials or the UID.
079: */
080:
081: public synchronized void authenticate()
082: throws PortalSecurityException {
083: int i;
084: Enumeration e = mySubContexts.elements();
085: boolean error = false;
086:
087: while (e.hasMoreElements()) {
088: ISecurityContext sctx = ((Entry) e.nextElement()).getCtx();
089: // The principal and credential are now set for all subcontexts in Authentication
090: try {
091: sctx.authenticate();
092: } catch (Exception ex) {
093: error = true;
094: log.error(
095: "Exception authenticating subcontext " + sctx,
096: ex);
097: }
098: // Stop attempting to authenticate if authenticated and if the property flag is set
099: if (stopWhenAuthenticated && sctx.isAuthenticated()) {
100: break;
101: }
102: }
103:
104: // Zero out the actual credentials if it isn't already null
105: if (this .myOpaqueCredentials.credentialstring != null) {
106: for (i = 0; i < this .myOpaqueCredentials.credentialstring.length; i++)
107: this .myOpaqueCredentials.credentialstring[i] = 0;
108: myOpaqueCredentials.credentialstring = null;
109: }
110: if (error && !this .isauth)
111: throw new PortalSecurityException(
112: "One of the security subcontexts threw an exception");
113: return;
114: }
115:
116: public IPrincipal getPrincipal() {
117: if (this .isauth)
118: return this .myPrincipal;
119: else
120: return null;
121: }
122:
123: public IOpaqueCredentials getOpaqueCredentials() {
124: if (this .isauth)
125: return this .myOpaqueCredentials;
126: else
127: return null;
128: }
129:
130: public IAdditionalDescriptor getAdditionalDescriptor() {
131: if (this .isauth)
132: return this .myAdditionalDescriptor;
133: else
134: return null;
135: }
136:
137: public boolean isAuthenticated() {
138: return this .isauth;
139: }
140:
141: public synchronized ISecurityContext getSubContext(String name) {
142: for (int i = 0; i < mySubContexts.size(); i++) {
143: Entry entry = (Entry) mySubContexts.get(i);
144: if (entry.getKey() != null && entry.getKey().equals(name)) {
145: return (entry.getCtx());
146: }
147: }
148: PortalSecurityException ep = new PortalSecurityException(
149: "No such subcontext: " + name);
150: if (log.isDebugEnabled())
151: log.debug("No such subcontext as " + name, ep);
152: return (null);
153: }
154:
155: public synchronized boolean doesSubContextExist(String name) {
156: for (int i = 0; i < mySubContexts.size(); i++) {
157: Entry entry = (Entry) mySubContexts.get(i);
158: if (entry.getKey() != null && entry.getKey().equals(name)) {
159: return (true);
160: }
161: }
162: return (false);
163: }
164:
165: // Return an enumeration of subcontexts by running the vector and
166: // creating the enumeration. All this so the subcontexts will
167: // be returned in the order they appeared in the properties file.
168: public synchronized Enumeration getSubContexts() {
169: Enumeration e = mySubContexts.elements();
170: class Adapter implements Enumeration {
171: Enumeration base;
172:
173: public Adapter(Enumeration e) {
174: this .base = e;
175: }
176:
177: public boolean hasMoreElements() {
178: return base.hasMoreElements();
179: }
180:
181: public Object nextElement() {
182: return ((Entry) base.nextElement()).getCtx();
183: }
184: }
185: return new Adapter(e);
186: }
187:
188: public synchronized void addSubContext(String name,
189: ISecurityContext ctx) throws PortalSecurityException {
190: // Make sure the subcontext does not already exist in the chain
191: if (doesSubContextExist(name)) {
192: PortalSecurityException ep = new PortalSecurityException(
193: "Subcontext already exists: " + name);
194: log.error("Subcontext already exists:" + name, ep);
195: throw (ep);
196: } else {
197: mySubContexts.add(new Entry(name, ctx));
198: }
199: }
200:
201: // I 'spose the public class could just implement all of these interfaces
202: // but I prefer member classes. -ADN
203:
204: protected class ChainingPrincipal implements IPrincipal {
205: protected String globalUID;
206: protected String UID;
207: protected String FullName;
208:
209: /**
210: * Original, no-arg constructor used by the <code>ChainingSecurityContext</code>.
211: *
212: */
213: public ChainingPrincipal() {
214: }
215:
216: /**
217: * Creates a new <code>ChainingPrincipal</code> from the specified <code>IPrincipal</code>.
218: *
219: */
220: public ChainingPrincipal(IPrincipal p) {
221: this .globalUID = p.getGlobalUID();
222: this .UID = p.getUID();
223: this .FullName = p.getFullName();
224: }
225:
226: public String getUID() {
227: return this .UID;
228: }
229:
230: public String getGlobalUID() {
231: return this .globalUID;
232: }
233:
234: // This is supposed to be the person's "human readable" name. We should
235: // probably do an account lookup at the very least to return this.
236:
237: public String getFullName() {
238: return this .FullName;
239: }
240:
241: public void setUID(String UID) {
242: if (this .UID == null)
243: this .UID = UID;
244: }
245:
246: public void setFullName(String FullName) {
247: if (this .FullName == null)
248: this .FullName = FullName;
249: }
250:
251: }
252:
253: protected class ChainingOpaqueCredentials implements
254: IOpaqueCredentials {
255:
256: public byte[] credentialstring;
257:
258: // Since we want to explicitly zero our credentials after authenticate,
259: // copy the credentials here in case a sub-authenticator doesn't want
260: // to perform the operation immediately.
261:
262: public void setCredentials(byte[] credentials) {
263: int i;
264:
265: if (this .credentialstring == null) {
266: this .credentialstring = new byte[credentials.length];
267: for (i = 0; i < credentials.length; i++)
268: this .credentialstring[i] = credentials[i];
269: }
270: }
271:
272: public void setCredentials(String credentials) {
273: if (this .credentialstring == null && credentials != null)
274: setCredentials(credentials.getBytes());
275: }
276: }
277:
278: // Returns an Enumeration of the names of the subcontexts.
279: public synchronized Enumeration getSubContextNames() {
280: Vector scNames = new Vector();
281: for (int i = 0; i < mySubContexts.size(); i++) {
282: Entry entry = (Entry) mySubContexts.get(i);
283: if (entry.getKey() != null) {
284: scNames.add(entry.getKey());
285: }
286: }
287: return scNames.elements();
288: }
289:
290: /**
291: * A default, placeholder implementation of IAdditionalDescriptor an instance of which
292: * is the default value for the instance variable "myAdditionalDescriptor" of instances of
293: * this class.
294: */
295: public class ChainingAdditionalDescriptor implements
296: IAdditionalDescriptor {
297: // do nothing
298: }
299:
300: // entries in our subcontext list
301: private static class Entry implements Serializable {
302: String key;
303: ISecurityContext ctx;
304:
305: public Entry(String key, ISecurityContext ctx) {
306: this .key = key;
307: this .ctx = ctx;
308: }
309:
310: public ISecurityContext getCtx() {
311: return this .ctx;
312: }
313:
314: public String getKey() {
315: return this.key;
316: }
317: }
318: }
|