001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    package org.apache.geronimo.security;
020    
021    import java.io.Serializable;
022    import java.security.AccessControlContext;
023    import java.security.AccessControlException;
024    import java.security.AccessController;
025    import java.security.InvalidKeyException;
026    import java.security.NoSuchAlgorithmException;
027    import java.security.Principal;
028    import java.security.PrivilegedAction;
029    import java.util.Hashtable;
030    import java.util.IdentityHashMap;
031    import java.util.Map;
032    import java.util.Set;
033    import javax.crypto.Mac;
034    import javax.crypto.SecretKey;
035    import javax.crypto.spec.SecretKeySpec;
036    import javax.security.auth.Subject;
037    import javax.security.jacc.EJBRoleRefPermission;
038    
039    import org.apache.geronimo.security.realm.providers.GeronimoCallerPrincipal;
040    
041    
042    /**
043     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
044     */
045    public class ContextManager {
046        private static ThreadLocal currentCallerId = new ThreadLocal();
047        private static final ThreadLocal callers = new ThreadLocal();
048        private static Map subjectContexts = new IdentityHashMap();
049        private static Map subjectIds = new Hashtable();
050        private static long nextSubjectId = System.currentTimeMillis();
051    
052        private static SecretKey key;
053        private static String algorithm;
054        private static String password;
055    
056        public static final GeronimoSecurityPermission GET_CONTEXT = new GeronimoSecurityPermission("getContext");
057        public static final GeronimoSecurityPermission SET_CONTEXT = new GeronimoSecurityPermission("setContext");
058    
059        static {
060            password = "secret";
061            ContextManager.setAlgorithm("HmacSHA1");
062        }
063    
064        /**
065         * After a login, the client is left with a relatively empty Subject, while
066         * the Subject used by the server has more important contents.  This method
067         * lets a server-side component acting as an authentication client (such
068         * as Tocmat/Jetty) access the fully populated server-side Subject.
069         */
070        public static Subject getServerSideSubject(Subject clientSideSubject) {
071            Set set = clientSideSubject.getPrincipals(IdentificationPrincipal.class);
072            if(set == null || set.size() == 0) {
073                return null;
074            }
075            IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
076            return getRegisteredSubject(idp.getId());
077        }
078    
079        public static void setCurrentCallerId(Serializable id) {
080            SecurityManager sm = System.getSecurityManager();
081            if (sm != null) sm.checkPermission(SET_CONTEXT);
082    
083            currentCallerId.set(id);
084        }
085    
086        public static Serializable getCurrentCallerId() {
087            SecurityManager sm = System.getSecurityManager();
088            if (sm != null) sm.checkPermission(GET_CONTEXT);
089    
090            return (Serializable) currentCallerId.get();
091        }
092    
093        public static void setCallers(Subject currentCaller, Subject nextCaller) {
094            SecurityManager sm = System.getSecurityManager();
095            if (sm != null) sm.checkPermission(SET_CONTEXT);
096            assert currentCaller != null;
097            assert nextCaller != null;
098            Callers newCallers = new Callers(currentCaller, nextCaller);
099            callers.set(newCallers);
100        }
101    
102        public static void clearCallers() {
103            callers.set(null);
104        }
105    
106        public static Callers getCallers() {
107            SecurityManager sm = System.getSecurityManager();
108            if (sm != null) sm.checkPermission(GET_CONTEXT);
109            return (Callers) callers.get();
110        }
111    
112        public static Callers setNextCaller(Subject nextCaller) {
113            SecurityManager sm = System.getSecurityManager();
114            if (sm != null) sm.checkPermission(SET_CONTEXT);
115            assert nextCaller != null;
116            Callers oldCallers = (Callers) callers.get();
117            assert oldCallers != null;
118            Callers newCallers = new Callers(oldCallers.getNextCaller(), nextCaller);
119            callers.set(newCallers);
120            return oldCallers;
121        }
122    
123        public static Callers pushNextCaller(Subject nextCaller) {
124            SecurityManager sm = System.getSecurityManager();
125            if (sm != null) sm.checkPermission(SET_CONTEXT);
126            Callers oldCallers = (Callers) callers.get();
127            Subject oldNextCaller = oldCallers == null? null: oldCallers.getNextCaller();
128            Subject newNextCaller = nextCaller == null? oldNextCaller : nextCaller;
129            Callers newCallers = new Callers(oldNextCaller, newNextCaller);
130            callers.set(newCallers);
131            return oldCallers;
132        }
133    
134        public static void popCallers(Callers oldCallers) {
135            SecurityManager sm = System.getSecurityManager();
136            if (sm != null) sm.checkPermission(SET_CONTEXT);
137            callers.set(oldCallers);
138        }
139    
140        public static Subject getCurrentCaller() {
141            SecurityManager sm = System.getSecurityManager();
142            if (sm != null) sm.checkPermission(GET_CONTEXT);
143    
144            Callers callers = (Callers) ContextManager.callers.get();
145            return callers == null? null: callers.getCurrentCaller();
146        }
147    
148        public static Subject getNextCaller() {
149            SecurityManager sm = System.getSecurityManager();
150            if (sm != null) sm.checkPermission(GET_CONTEXT);
151    
152            Callers callers = (Callers) ContextManager.callers.get();
153            return callers == null? null: callers.getNextCaller();
154        }
155    
156        public static AccessControlContext getCurrentContext() {
157            SecurityManager sm = System.getSecurityManager();
158            if (sm != null) sm.checkPermission(GET_CONTEXT);
159    
160            Callers threadLocalCallers = (Callers) callers.get();
161            assert threadLocalCallers != null : "No current callers";
162            Subject currentSubject = threadLocalCallers.getCurrentCaller();
163            assert currentSubject != null : "No current caller";
164            Context context = (Context) subjectContexts.get(currentSubject);
165    
166            assert context != null : "No registered context";
167    
168            return context.context;
169        }
170    
171        public static Principal getCurrentPrincipal(Subject callerSubject) {
172            SecurityManager sm = System.getSecurityManager();
173            if (sm != null) sm.checkPermission(GET_CONTEXT);
174    
175            if (callerSubject == null) {
176                return new Principal() {
177                    public String getName() {
178                        return "";
179                    }
180                };
181            }
182            Context context = (Context) subjectContexts.get(callerSubject);
183    
184            assert context != null : "No registered context";
185    
186            return context.principal;
187        }
188    
189        public static SubjectId getCurrentId() {
190            SecurityManager sm = System.getSecurityManager();
191            if (sm != null) sm.checkPermission(GET_CONTEXT);
192    
193            Callers threadLocalCallers = (Callers) callers.get();
194            assert threadLocalCallers != null : "No current callers";
195            Subject currentSubject = threadLocalCallers.getCurrentCaller();
196            assert currentSubject != null : "No current caller";
197            Context context = (Context) subjectContexts.get(currentSubject);
198    
199            assert context != null : "No registered context";
200    
201            return context.id;
202        }
203    
204        public static SubjectId getSubjectId(Subject subject) {
205            SecurityManager sm = System.getSecurityManager();
206            if (sm != null) sm.checkPermission(GET_CONTEXT);
207    
208            Context context = (Context) subjectContexts.get(subject);
209    
210            return (context != null ? context.id : null);
211        }
212    
213        public static boolean isCallerInRole(String EJBName, String role) {
214            if (EJBName == null) throw new IllegalArgumentException("EJBName must not be null");
215            if (role == null) throw new IllegalArgumentException("Role must not be null");
216    
217            try {
218                Callers currentCallers = (Callers)callers.get();
219                if (currentCallers == null) {
220                    return false;
221                }
222                Subject currentSubject = currentCallers.getCurrentCaller();
223                if (currentSubject == null) {
224                    return false;
225                }
226    
227                Context context = (Context) subjectContexts.get(currentSubject);
228    
229                assert context != null : "No registered context";
230    
231                context.context.checkPermission(new EJBRoleRefPermission(EJBName, role));
232            } catch (AccessControlException e) {
233                return false;
234            }
235            return true;
236        }
237    
238        public static Subject getRegisteredSubject(SubjectId id) {
239            return (Subject) subjectIds.get(id);
240        }
241    
242        public static synchronized SubjectId registerSubject(Subject subject) {
243            SecurityManager sm = System.getSecurityManager();
244            if (sm != null) sm.checkPermission(SET_CONTEXT);
245    
246            if (subject == null) throw new IllegalArgumentException("Subject must not be null");
247    
248            AccessControlContext acc = (AccessControlContext) Subject.doAsPrivileged(subject, new PrivilegedAction() {
249                public Object run() {
250                    return AccessController.getContext();
251                }
252            }, null);
253    
254            Context context = new Context();
255            context.subject = subject;
256            context.context = acc;
257            Set principals = subject.getPrincipals(GeronimoCallerPrincipal.class);
258            if (!principals.isEmpty()) {
259                context.principal = (Principal) principals.iterator().next();
260            } else if (!(principals = subject.getPrincipals(PrimaryRealmPrincipal.class)).isEmpty()) {
261                context.principal = (PrimaryRealmPrincipal) principals.iterator().next();
262            } else if (!(principals = subject.getPrincipals(RealmPrincipal.class)).isEmpty()) {
263                context.principal = (RealmPrincipal) principals.iterator().next();
264            } else if (!(principals = subject.getPrincipals()).isEmpty()) {
265                context.principal = (Principal) principals.iterator().next();
266            }
267            Long id = new Long(nextSubjectId++);
268            context.id = new SubjectId(id, hash(id));
269    
270            subjectIds.put(context.id, subject);
271            subjectContexts.put(subject, context);
272    
273            return context.id;
274        }
275    
276        public static synchronized void unregisterSubject(Subject subject) {
277            SecurityManager sm = System.getSecurityManager();
278            if (sm != null) sm.checkPermission(SET_CONTEXT);
279    
280            if (subject == null) throw new IllegalArgumentException("Subject must not be null");
281    
282            Context context = (Context) subjectContexts.get(subject);
283            if (context == null) return;
284    
285            subjectIds.remove(context.id);
286            subjectContexts.remove(subject);
287        }
288    
289        /**
290         * Obtain the thread's identifying principal.
291         * <p/>
292         * Clients should use <code>Subject.doAs*</code> to associate a Subject
293         * with the thread's call stack.  It is this Subject that will be used for
294         * authentication checks.
295         * <p/>
296         * Return a <code>IdentificationPrincipal</code>.  This kind of principal
297         * is inserted into a subject if one uses one of the Geronimo LoginModules.
298         * It is a secure id that identifies the Subject.
299         *
300         * @return the principal that identifies the Subject of this thread.
301         * @see Subject#doAs(javax.security.auth.Subject, java.security.PrivilegedAction)
302         * @see Subject#doAs(javax.security.auth.Subject, java.security.PrivilegedExceptionAction)
303         * @see Subject#doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedAction, java.security.AccessControlContext)
304         * @see Subject#doAsPrivileged(javax.security.auth.Subject, java.security.PrivilegedExceptionAction, java.security.AccessControlContext)
305         */
306        public static IdentificationPrincipal getThreadPrincipal() {
307            SecurityManager sm = System.getSecurityManager();
308            if (sm != null) sm.checkPermission(GET_CONTEXT);
309    
310            Subject subject = Subject.getSubject(AccessController.getContext());
311            if (subject != null) {
312                Set set = subject.getPrincipals(IdentificationPrincipal.class);
313                if (!set.isEmpty()) return (IdentificationPrincipal) set.iterator().next();
314            }
315            return null;
316        }
317    
318        public static String getAlgorithm() {
319            SecurityManager sm = System.getSecurityManager();
320            if (sm != null) sm.checkPermission(GET_CONTEXT);
321    
322            return algorithm;
323        }
324    
325        public static void setAlgorithm(String algorithm) {
326            SecurityManager sm = System.getSecurityManager();
327            if (sm != null) sm.checkPermission(SET_CONTEXT);
328    
329            ContextManager.algorithm = algorithm;
330    
331            key = new SecretKeySpec(password.getBytes(), algorithm);
332    
333            /**
334             * Make sure that we can generate the  Mac.
335             */
336            try {
337                Mac mac = Mac.getInstance(algorithm);
338                mac.init(key);
339            } catch (NoSuchAlgorithmException e) {
340                assert false : "Should never have reached here";
341            } catch (InvalidKeyException e) {
342                assert false : "Should never have reached here";
343            }
344        }
345    
346        public static String getPassword() {
347            SecurityManager sm = System.getSecurityManager();
348            if (sm != null) sm.checkPermission(GET_CONTEXT);
349    
350            return password;
351        }
352    
353        public static void setPassword(String password) {
354            SecurityManager sm = System.getSecurityManager();
355            if (sm != null) sm.checkPermission(SET_CONTEXT);
356    
357            ContextManager.password = password;
358    
359            key = new SecretKeySpec(password.getBytes(), algorithm);
360        }
361    
362        private static byte[] hash(Long id) {
363            long n = id.longValue();
364            byte[] bytes = new byte[8];
365            for (int i = 7; i >= 0; i--) {
366                bytes[i] = (byte) (n);
367                n >>>= 8;
368            }
369    
370            try {
371                Mac mac = Mac.getInstance(algorithm);
372                mac.init(key);
373                mac.update(bytes);
374    
375                return mac.doFinal();
376            } catch (NoSuchAlgorithmException e) {
377            } catch (InvalidKeyException e) {
378            }
379            assert false : "Should never have reached here";
380            return null;
381        }
382    
383        private static class Context {
384            SubjectId id;
385            AccessControlContext context;
386            Subject subject;
387            Principal principal;
388        }
389    
390    }