View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  
19  package org.apache.geronimo.security;
20  
21  import java.io.Serializable;
22  import java.security.AccessControlContext;
23  import java.security.AccessControlException;
24  import java.security.AccessController;
25  import java.security.InvalidKeyException;
26  import java.security.NoSuchAlgorithmException;
27  import java.security.Principal;
28  import java.security.PrivilegedAction;
29  import java.util.Hashtable;
30  import java.util.IdentityHashMap;
31  import java.util.Map;
32  import java.util.Set;
33  import javax.crypto.Mac;
34  import javax.crypto.SecretKey;
35  import javax.crypto.spec.SecretKeySpec;
36  import javax.security.auth.Subject;
37  import javax.security.jacc.EJBRoleRefPermission;
38  
39  import org.apache.geronimo.security.realm.providers.GeronimoCallerPrincipal;
40  
41  
42  /**
43   * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
44   */
45  public class ContextManager {
46      private static ThreadLocal currentCallerId = new ThreadLocal();
47      private static final ThreadLocal callers = new ThreadLocal();
48      private static Map subjectContexts = new IdentityHashMap();
49      private static Map subjectIds = new Hashtable();
50      private static long nextSubjectId = System.currentTimeMillis();
51  
52      private static SecretKey key;
53      private static String algorithm;
54      private static String password;
55  
56      public static final GeronimoSecurityPermission GET_CONTEXT = new GeronimoSecurityPermission("getContext");
57      public static final GeronimoSecurityPermission SET_CONTEXT = new GeronimoSecurityPermission("setContext");
58  
59      static {
60          password = "secret";
61          ContextManager.setAlgorithm("HmacSHA1");
62      }
63  
64      /**
65       * After a login, the client is left with a relatively empty Subject, while
66       * the Subject used by the server has more important contents.  This method
67       * lets a server-side component acting as an authentication client (such
68       * as Tocmat/Jetty) access the fully populated server-side Subject.
69       */
70      public static Subject getServerSideSubject(Subject clientSideSubject) {
71          Set set = clientSideSubject.getPrincipals(IdentificationPrincipal.class);
72          if(set == null || set.size() == 0) {
73              return null;
74          }
75          IdentificationPrincipal idp = (IdentificationPrincipal)set.iterator().next();
76          return getRegisteredSubject(idp.getId());
77      }
78  
79      public static void setCurrentCallerId(Serializable id) {
80          SecurityManager sm = System.getSecurityManager();
81          if (sm != null) sm.checkPermission(SET_CONTEXT);
82  
83          currentCallerId.set(id);
84      }
85  
86      public static Serializable getCurrentCallerId() {
87          SecurityManager sm = System.getSecurityManager();
88          if (sm != null) sm.checkPermission(GET_CONTEXT);
89  
90          return (Serializable) currentCallerId.get();
91      }
92  
93      public static void setCallers(Subject currentCaller, Subject nextCaller) {
94          SecurityManager sm = System.getSecurityManager();
95          if (sm != null) sm.checkPermission(SET_CONTEXT);
96          assert currentCaller != null;
97          assert nextCaller != null;
98          Callers newCallers = new Callers(currentCaller, nextCaller);
99          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 }