1 /*
2 * JBoss, the OpenSource J2EE webOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7
8 // $Id: JBossAuthorizationHandler.java,v 1.1 2002/03/15 10:04:24 cgjung Exp $
9
10 package org.jboss.net.axis.server;
11
12 import org.apache.axis.AxisFault;
13 import org.apache.axis.handlers.BasicHandler;
14 import org.apache.axis.MessageContext;
15
16 import org.jboss.security.SimplePrincipal;
17 import org.jboss.security.AnybodyPrincipal;
18 import org.jboss.security.NobodyPrincipal;
19 import org.jboss.security.RealmMapping;
20
21 import javax.naming.InitialContext;
22 import javax.naming.NamingException;
23
24 import java.security.Principal;
25 import javax.security.auth.Subject;
26
27 import java.util.StringTokenizer;
28 import java.util.Set;
29 import java.util.Iterator;
30 import java.util.Collection;
31 import java.util.Collections;
32
33 /**
34 * AuthorizationHandler that checks allowed and denied roles against the active
35 * subject using a given realmMapping. Is somehow redundant to what, e.g., the JBoss EJB invocation handler
36 * does, but maybe we need this to shield access to other container resources
37 * such as MBeans for which we will expose security-agnostic providers.
38 * <br>
39 * <h3>Change History</h3>
40 * <ul>
41 * <li> jung, 15.03.2002: Added security domain option. </li>
42 * </ul>
43 * <br>
44 * <h3>To Do</h3>
45 * <ul>
46 * <li> jung, 14.03.2002: Cache simple principals. Principal factory for
47 * interacting with various security domains.
48 * </ul>
49 * @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a>
50 * @created 14.03.2002
51 * @version $Revision: 1.1 $
52 */
53
54 public class JBossAuthorizationHandler extends BasicHandler {
55
56 //
57 // Attributes
58 //
59
60 /** the security domain against which we call */
61 protected RealmMapping realmMapping;
62 /** the roles that we want to let through */
63 final protected Set rolesAllowed = new java.util.HashSet();
64 /** the roles that we want to deny access */
65 final protected Set rolesDenied = new java.util.HashSet();
66 /** whether this handler has been initialized */
67 protected boolean isInitialised;
68
69 //
70 // Constructors
71 //
72
73 public JBossAuthorizationHandler() {
74 }
75
76 //
77 // Protected helpers
78 //
79
80 /** initializes the roles checked by this handler */
81 protected void initialise() throws AxisFault {
82 // bind against the jboss security subsystem
83 isInitialised = true;
84 realmMapping = null;
85 String securityDomain = (String) getOption(Constants.SECURITY_DOMAIN_OPTION);
86 if (securityDomain != null) {
87 try {
88 realmMapping =
89 (RealmMapping) new InitialContext().lookup(securityDomain);
90 } catch (NamingException e) {
91 throw new AxisFault("Could not lookup security domain " + securityDomain, e);
92 }
93 }
94
95 // parse role options
96 String allowedRoles = (String) getOption(Constants.ALLOWED_ROLES_OPTION);
97
98 // default:let all through
99 if (allowedRoles == null) {
100 allowedRoles = "*";
101 }
102
103 StringTokenizer tokenizer = new StringTokenizer(allowedRoles, ",");
104 while (tokenizer.hasMoreTokens()) {
105 rolesAllowed.add(getPrincipal(tokenizer.nextToken()));
106 }
107
108 String deniedRoles = (String) getOption(Constants.DENIED_ROLES_OPTION);
109 if (deniedRoles != null) {
110 tokenizer = new StringTokenizer(deniedRoles, ",");
111 while (tokenizer.hasMoreTokens()) {
112 rolesDenied.add(getPrincipal(tokenizer.nextToken()));
113 }
114 }
115 }
116
117 /**
118 * creates a new principal belonging to the given username,
119 * override to adapt to specific security domains.
120 */
121 protected Principal getPrincipal(String userName) {
122 if (userName.equals("*")) {
123 return AnybodyPrincipal.ANYBODY_PRINCIPAL;
124 } else {
125 return new SimplePrincipal(userName);
126 }
127 }
128
129 /** returns a collection of principals that the context subject
130 * is associated with
131 */
132 protected Collection getAssociatedPrincipals(MessageContext msgContext) {
133 // get the active subject
134 Subject activeSubject =
135 (Subject) msgContext.getProperty(MessageContext.AUTHUSER);
136 if (activeSubject == null) {
137 return Collections.singleton(NobodyPrincipal.NOBODY_PRINCIPAL);
138 } else {
139 return activeSubject.getPrincipals();
140 }
141 }
142
143 /** return whether the given Principal has the given roles */
144 protected boolean doesUserHaveRole(Principal principal, Set roles) {
145 return realmMapping.doesUserHaveRole(principal, roles);
146 }
147
148 //
149 // API
150 //
151
152 /**
153 * Authenticate the user and password from the msgContext. Note that
154 * we do not disassociate the subject here, since that would have
155 * to be done by a separate handler in the response chain and we
156 * currently expect Jetty or the WebContainer to do that for us
157 */
158
159 public void invoke(MessageContext msgContext) throws AxisFault {
160
161 // initialize the handler
162 if (!isInitialised) {
163 synchronized (this) {
164 if (!isInitialised) {
165 initialise();
166 }
167 }
168 }
169
170 // check association
171 if (realmMapping == null) {
172 throw new AxisFault("No security domain associated.");
173 }
174
175 Iterator allPrincipals = getAssociatedPrincipals(msgContext).iterator();
176 boolean accessAllowed = false;
177 while (allPrincipals.hasNext()) {
178 Principal nextPrincipal = (Principal) allPrincipals.next();
179 // a single denied is enough to exclude the access
180 if (doesUserHaveRole(nextPrincipal, rolesDenied)) {
181 accessAllowed = false;
182 break;
183 // allowed
184 } else if (!accessAllowed && doesUserHaveRole(nextPrincipal, rolesAllowed)) {
185 accessAllowed = true;
186 }
187 }
188
189 if (!accessAllowed) {
190 throw new AxisFault("Access denied.");
191 }
192 }
193 }