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