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: */package org.apache.openejb.core.security;
018:
019: import org.apache.openejb.spi.SecurityService;
020: import org.apache.openejb.core.ThreadContextListener;
021: import org.apache.openejb.core.ThreadContext;
022: import org.apache.openejb.core.CoreDeploymentInfo;
023: import org.apache.openejb.core.security.jacc.BasicJaccProvider;
024: import org.apache.openejb.core.security.jacc.BasicPolicyConfiguration;
025: import org.apache.openejb.InterfaceType;
026: import org.apache.openejb.loader.SystemInstance;
027:
028: import javax.security.auth.Subject;
029: import javax.security.auth.login.LoginException;
030: import javax.security.jacc.PolicyContext;
031: import javax.security.jacc.EJBRoleRefPermission;
032: import javax.security.jacc.EJBMethodPermission;
033: import javax.security.jacc.PolicyConfigurationFactory;
034: import java.security.AccessControlContext;
035: import java.security.PrivilegedAction;
036: import java.security.AccessController;
037: import java.security.Principal;
038: import java.security.AccessControlException;
039: import java.security.Permission;
040: import java.security.Policy;
041: import java.util.UUID;
042: import java.util.List;
043: import java.util.ArrayList;
044: import java.util.Enumeration;
045: import java.util.Collections;
046: import java.util.Properties;
047: import java.util.Set;
048: import java.util.HashSet;
049: import java.util.Map;
050: import java.util.LinkedHashSet;
051: import java.util.concurrent.ConcurrentHashMap;
052: import java.lang.reflect.Method;
053:
054: /**
055: * This security service chooses a UUID as its token as this can be serialized
056: * to clients, is mostly secure, and can be deserialized in a client vm without
057: * addition openejb-core classes.
058: */
059: public abstract class AbstractSecurityService implements
060: SecurityService<UUID>, ThreadContextListener,
061: BasicPolicyConfiguration.RoleResolver {
062: static private final Map<Object, Identity> identities = new ConcurrentHashMap<Object, Identity>();
063: static protected final ThreadLocal<Identity> clientIdentity = new ThreadLocal<Identity>();
064: protected final String defaultUser = "guest";
065: protected final Subject defaultSubject;
066: protected final SecurityContext defaultContext;
067: private String realmName = "PropertiesLogin";
068:
069: public AbstractSecurityService() {
070: System.setProperty(JaccProvider.class.getName(),
071: BasicJaccProvider.class.getName());
072:
073: installJacc();
074:
075: ThreadContext.addThreadContextListener(this );
076:
077: defaultSubject = createSubject(defaultUser);
078: defaultContext = new SecurityContext(defaultSubject);
079:
080: SystemInstance.get().setComponent(
081: BasicPolicyConfiguration.RoleResolver.class, this );
082: }
083:
084: public String getRealmName() {
085: return realmName;
086: }
087:
088: public void setRealmName(String realmName) {
089: this .realmName = realmName;
090: }
091:
092: public void init(Properties props) throws Exception {
093: }
094:
095: public UUID login(String username, String password)
096: throws LoginException {
097: return login(realmName, username, password);
098: }
099:
100: public Set<String> getLogicalRoles(Principal[] principals,
101: Set<String> logicalRoles) {
102: LinkedHashSet<String> roles = new LinkedHashSet<String>(
103: principals.length);
104: for (Principal principal : principals) {
105: String name = principal.getName();
106: if (logicalRoles.contains(name)) {
107: roles.add(name);
108: }
109: }
110: return roles;
111: }
112:
113: public void contextEntered(ThreadContext oldContext,
114: ThreadContext newContext) {
115: String moduleID = newContext.getDeploymentInfo().getModuleID();
116: PolicyContext.setContextID(moduleID);
117:
118: SecurityContext securityContext = (oldContext != null) ? oldContext
119: .get(SecurityContext.class)
120: : null;
121:
122: CoreDeploymentInfo callingDeploymentInfo = (oldContext != null) ? oldContext
123: .getDeploymentInfo()
124: : null;
125: Subject runAsSubject = getRunAsSubject(callingDeploymentInfo);
126: if (runAsSubject != null) {
127:
128: securityContext = new SecurityContext(runAsSubject);
129:
130: } else if (securityContext == null) {
131:
132: Identity identity = clientIdentity.get();
133: if (identity != null) {
134: securityContext = new SecurityContext(identity.subject);
135: } else {
136: securityContext = defaultContext;
137: }
138: }
139:
140: newContext.set(SecurityContext.class, securityContext);
141: }
142:
143: protected Subject getRunAsSubject(
144: CoreDeploymentInfo callingDeploymentInfo) {
145: if (callingDeploymentInfo == null)
146: return null;
147:
148: String runAsRole = callingDeploymentInfo.getRunAs();
149: Subject runAs = createRunAsSubject(runAsRole);
150: return runAs;
151: }
152:
153: protected Subject createRunAsSubject(String runAsRole) {
154: return createSubject(runAsRole);
155: }
156:
157: public void contextExited(ThreadContext exitedContext,
158: ThreadContext reenteredContext) {
159: if (reenteredContext == null) {
160: PolicyContext.setContextID(null);
161: } else {
162: PolicyContext.setContextID(reenteredContext
163: .getDeploymentInfo().getModuleID());
164: }
165: }
166:
167: protected UUID registerSubject(Subject subject) {
168: Identity identity = new Identity(subject);
169: UUID token = identity.getToken();
170: identities.put(token, identity);
171: return token;
172: }
173:
174: public void logout(UUID securityIdentity) throws LoginException {
175: Identity identity = identities.get(securityIdentity);
176: if (identity == null)
177: throw new LoginException(
178: "Identity is not currently logged in: "
179: + securityIdentity);
180: identities.remove(securityIdentity);
181: }
182:
183: protected void unregisterSubject(Object securityIdentity) {
184: identities.remove(securityIdentity);
185: }
186:
187: public void associate(UUID securityIdentity) throws LoginException {
188: if (clientIdentity.get() != null)
189: throw new LoginException(
190: "Thread already associated with a client identity. Refusing to overwrite.");
191: if (securityIdentity == null)
192: throw new NullPointerException(
193: "The security token passed in is null");
194:
195: // The securityIdentity token must associated with a logged in Identity
196: Identity identity = identities.get(securityIdentity);
197: if (identity == null)
198: throw new LoginException(
199: "Identity is not currently logged in: "
200: + securityIdentity);
201:
202: clientIdentity.set(identity);
203: }
204:
205: public UUID disassociate() {
206: try {
207: Identity identity = clientIdentity.get();
208: return (identity == null) ? null : identity.getToken();
209: } finally {
210: clientIdentity.remove();
211: }
212: }
213:
214: public boolean isCallerInRole(String role) {
215: if (role == null)
216: throw new IllegalArgumentException("Role must not be null");
217:
218: ThreadContext threadContext = ThreadContext.getThreadContext();
219: SecurityContext securityContext = threadContext
220: .get(SecurityContext.class);
221:
222: try {
223: CoreDeploymentInfo deployment = threadContext
224: .getDeploymentInfo();
225:
226: securityContext.acc
227: .checkPermission(new EJBRoleRefPermission(
228: deployment.getEjbName(), role));
229: } catch (AccessControlException e) {
230: return false;
231: }
232: return true;
233: }
234:
235: public Principal getCallerPrincipal() {
236: ThreadContext threadContext = ThreadContext.getThreadContext();
237: SecurityContext securityContext = threadContext
238: .get(SecurityContext.class);
239: Set<Principal> principals = securityContext.subject
240: .getPrincipals();
241: if (!principals.isEmpty()) {
242: return principals.iterator().next();
243: }
244: return null;
245: }
246:
247: public boolean isCallerAuthorized(Method method,
248: InterfaceType interfaceType) {
249: ThreadContext threadContext = ThreadContext.getThreadContext();
250: SecurityContext securityContext = threadContext
251: .get(SecurityContext.class);
252:
253: try {
254:
255: CoreDeploymentInfo deploymentInfo = threadContext
256: .getDeploymentInfo();
257:
258: String ejbName = deploymentInfo.getEjbName();
259:
260: InterfaceType type = deploymentInfo.getInterfaceType(method
261: .getDeclaringClass());
262:
263: String name = (type == null) ? null : type.getSpecName();
264:
265: Permission permission = new EJBMethodPermission(ejbName,
266: name, method);
267:
268: if (permission != null)
269: securityContext.acc.checkPermission(permission);
270:
271: } catch (AccessControlException e) {
272: return false;
273: }
274: return true;
275: }
276:
277: protected static void installJacc() {
278: ClassLoader contextClassLoader = Thread.currentThread()
279: .getContextClassLoader();
280:
281: final String providerKey = "javax.security.jacc.PolicyConfigurationFactory.provider";
282: try {
283: if (System.getProperty(providerKey) == null) {
284: System.setProperty(providerKey,
285: JaccProvider.Factory.class.getName());
286: ClassLoader cl = JaccProvider.Factory.class
287: .getClassLoader();
288: Thread.currentThread().setContextClassLoader(cl);
289: }
290:
291: // Force the loading of the javax.security.jacc.PolicyConfigurationFactory.provider
292: // Hopefully it will be cached thereafter and ClassNotFoundExceptions thrown
293: // from the equivalent call in JaccPermissionsBuilder can be avoided.
294: PolicyConfigurationFactory.getPolicyConfigurationFactory();
295: } catch (Exception e) {
296: throw new IllegalStateException(
297: "Could not install JACC Policy Configuration Factory: "
298: + System.getProperty(providerKey), e);
299: } finally {
300: Thread.currentThread().setContextClassLoader(
301: contextClassLoader);
302: }
303:
304: String policyProvider = System.getProperty(
305: "javax.security.jacc.policy.provider",
306: JaccProvider.Policy.class.getName());
307: try {
308: ClassLoader classLoader = Thread.currentThread()
309: .getContextClassLoader();
310: Class policyClass = Class.forName(policyProvider, true,
311: classLoader);
312: Policy policy = (Policy) policyClass.newInstance();
313: policy.refresh();
314: Policy.setPolicy(policy);
315: } catch (Exception e) {
316: throw new IllegalStateException(
317: "Could not install JACC Policy Provider: "
318: + policyProvider, e);
319: }
320: }
321:
322: protected Subject createSubject(String name) {
323: if (name == null)
324: return null;
325:
326: User user = new User(name);
327: Group group = new Group(name);
328: group.addMember(user);
329:
330: HashSet<Principal> principals = new HashSet<Principal>();
331: principals.add(user);
332: principals.add(group);
333:
334: return new Subject(true, principals, new HashSet(),
335: new HashSet());
336: }
337:
338: protected final static class SecurityContext {
339:
340: private final Subject subject;
341: private final AccessControlContext acc;
342:
343: public SecurityContext(Subject subject) {
344: this .subject = subject;
345: this .acc = (AccessControlContext) Subject.doAsPrivileged(
346: subject, new PrivilegedAction() {
347: public Object run() {
348: return AccessController.getContext();
349: }
350: }, null);
351: }
352: }
353:
354: protected static class Identity {
355: private final Subject subject;
356: private final UUID token;
357:
358: public Identity(Subject subject) {
359: this .subject = subject;
360: this .token = UUID.randomUUID();
361: }
362:
363: public Identity(Subject subject, UUID token) {
364: this .subject = subject;
365: this .token = token;
366: }
367:
368: public Subject getSubject() {
369: return subject;
370: }
371:
372: public UUID getToken() {
373: return token;
374: }
375: }
376:
377: public static class Group implements java.security.acl.Group {
378: private final List<Principal> members = new ArrayList<Principal>();
379: private final String name;
380:
381: public Group(String name) {
382: this .name = name;
383: }
384:
385: public boolean addMember(Principal user) {
386: return members.add(user);
387: }
388:
389: public boolean removeMember(Principal user) {
390: return members.remove(user);
391: }
392:
393: public boolean isMember(Principal member) {
394: return members.contains(member);
395: }
396:
397: public Enumeration<? extends Principal> members() {
398: return Collections.enumeration(members);
399: }
400:
401: public String getName() {
402: return name;
403: }
404: }
405:
406: public static class User implements Principal {
407: private final String name;
408:
409: public User(String name) {
410: this .name = name;
411: }
412:
413: public String getName() {
414: return name;
415: }
416: }
417: }
|