001 /*
002 * Copyright 2009 Red Hat, Inc.
003 * Red Hat licenses this file to you under the Apache License, version
004 * 2.0 (the "License"); you may not use this file except in compliance
005 * with the License. You may obtain a copy of the License at
006 * http://www.apache.org/licenses/LICENSE-2.0
007 * Unless required by applicable law or agreed to in writing, software
008 * distributed under the License is distributed on an "AS IS" BASIS,
009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
010 * implied. See the License for the specific language governing
011 * permissions and limitations under the License.
012 */
013
014 package org.hornetq.spi.core.security;
015
016 import java.security.Principal;
017 import java.security.acl.Group;
018 import java.util.HashSet;
019 import java.util.Iterator;
020 import java.util.Set;
021
022 import javax.security.auth.Subject;
023 import javax.security.auth.callback.CallbackHandler;
024 import javax.security.auth.login.Configuration;
025 import javax.security.auth.login.LoginContext;
026 import javax.security.auth.login.LoginException;
027
028 import org.hornetq.core.logging.Logger;
029 import org.hornetq.core.security.CheckType;
030 import org.hornetq.core.security.Role;
031 import org.hornetq.core.server.HornetQComponent;
032
033 /**
034 * This implementation delegates to the JAAS security interfaces.
035 *
036 * The {@link Subject} returned by the login context is expecting to have a {@link Group} with the <code>Roles</code> name
037 * containing a set of {@link Principal} for each role of the user.
038 *
039 * @author <a href="ataylor@redhat.com">Andy Taylor</a>
040 * @author <a href="tim.fox@jboss.com">Tim Fox</a>
041 * @author <a href="jmesnil@redhat.com">Jeff Mesnil</a>
042 */
043 public class JAASSecurityManager implements HornetQSecurityManager, HornetQComponent
044 {
045 private static final Logger log = Logger.getLogger(JAASSecurityManager.class);
046
047 // Static --------------------------------------------------------
048
049 // Attributes ----------------------------------------------------
050
051 private final boolean trace = JAASSecurityManager.log.isTraceEnabled();
052
053 private String configurationName;
054
055 private boolean started;
056
057 private CallbackHandler callbackHandler;
058
059 private Configuration config;
060
061 // HornetQSecurityManager implementation -----------------------------
062
063 public boolean validateUser(final String user, final String password)
064 {
065 try
066 {
067 getAuthenticatedSubject(user, password);
068 return true;
069 }
070 catch (LoginException e1)
071 {
072 return false;
073 }
074 }
075
076 public boolean validateUserAndRole(final String user,
077 final String password,
078 final Set<Role> roles,
079 final CheckType checkType)
080 {
081 Subject localSubject = null;
082 try
083 {
084 localSubject = getAuthenticatedSubject(user, password);
085 }
086 catch (LoginException e1)
087 {
088 return false;
089 }
090
091 boolean authenticated = true;
092
093 if (localSubject != null)
094 {
095 Set<Principal> rolePrincipals = getRolePrincipals(checkType, roles);
096
097 // authenticated = realmMapping.doesUserHaveRole(principal, rolePrincipals);
098
099 boolean hasRole = false;
100 // check that the caller is authenticated to the current thread
101
102 // Check the caller's roles
103 Group subjectRoles = getSubjectRoles(localSubject);
104 if (subjectRoles != null)
105 {
106 Iterator<Principal> iter = rolePrincipals.iterator();
107 while (!hasRole && iter.hasNext())
108 {
109 Principal role = iter.next();
110 hasRole = subjectRoles.isMember(role);
111 }
112 }
113
114 authenticated = hasRole;
115
116 if (trace)
117 {
118 JAASSecurityManager.log.trace("user " + user + (authenticated ? " is " : " is NOT ") + "authorized");
119 }
120 }
121 return authenticated;
122 }
123
124 public void addRole(final String user, final String role)
125 {
126 // NO-OP
127 }
128
129 public void addUser(final String user, final String password)
130 {
131 // NO-OP
132 }
133
134 public void removeRole(final String user, final String role)
135 {
136 // NO-OP
137 }
138
139 public void removeUser(final String user)
140 {
141 // NO-OP
142 }
143
144 public void setDefaultUser(final String username)
145 {
146 // NO-OP
147 }
148
149 // HornetQComponent implementation -----------------------------
150
151 /**
152 * lifecycle method, needs to be called
153 *
154 * @throws Exception
155 */
156 public synchronized void start() throws Exception
157 {
158 if (started)
159 {
160 return;
161 }
162
163 started = true;
164 }
165
166 public synchronized void stop()
167 {
168 if (!started)
169 {
170 return;
171 }
172 started = false;
173 }
174
175 public synchronized boolean isStarted()
176 {
177 return started;
178 }
179
180 private Subject getAuthenticatedSubject(final String user, final String password) throws LoginException
181 {
182 SimplePrincipal principal = user == null ? null : new SimplePrincipal(user);
183
184 char[] passwordChars = null;
185
186 if (password != null)
187 {
188 passwordChars = password.toCharArray();
189 }
190
191 Subject subject = new Subject();
192
193 if (user != null)
194 {
195 subject.getPrincipals().add(principal);
196 }
197 subject.getPrivateCredentials().add(passwordChars);
198
199 LoginContext lc = new LoginContext(configurationName, subject, callbackHandler, config);
200 lc.login();
201 return lc.getSubject();
202 }
203
204 private Group getSubjectRoles(final Subject subject)
205 {
206 Set<Group> subjectGroups = subject.getPrincipals(Group.class);
207 Iterator<Group> iter = subjectGroups.iterator();
208 Group roles = null;
209 while (iter.hasNext())
210 {
211 Group grp = iter.next();
212 String name = grp.getName();
213 if (name.equals("Roles"))
214 {
215 roles = grp;
216 }
217 }
218 return roles;
219 }
220
221 private Set<Principal> getRolePrincipals(final CheckType checkType, final Set<Role> roles)
222 {
223 Set<Principal> principals = new HashSet<Principal>();
224 for (Role role : roles)
225 {
226 if (checkType.hasRole(role))
227 {
228 principals.add(new SimplePrincipal(role.getName()));
229 }
230 }
231 return principals;
232 }
233
234 // Public --------------------------------------------------------
235
236 public void setConfigurationName(final String configurationName)
237 {
238 this.configurationName = configurationName;
239 }
240
241 public void setCallbackHandler(final CallbackHandler handler)
242 {
243 callbackHandler = handler;
244 }
245
246 public void setConfiguration(final Configuration config)
247 {
248 this.config = config;
249 }
250
251 // Private -------------------------------------------------------
252
253 // Inner classes -------------------------------------------------
254
255 public static class SimplePrincipal implements Principal, java.io.Serializable
256 {
257 private static final long serialVersionUID = 1L;
258
259 private final String name;
260
261 public SimplePrincipal(final String name)
262 {
263 this.name = name;
264 }
265
266 /** Compare this SimplePrincipal's name against another Principal
267 @return true if name equals another.getName();
268 */
269 @Override
270 public boolean equals(final Object another)
271 {
272 if (!(another instanceof Principal))
273 {
274 return false;
275 }
276 String anotherName = ((Principal)another).getName();
277 boolean equals = false;
278 if (name == null)
279 {
280 equals = anotherName == null;
281 }
282 else
283 {
284 equals = name.equals(anotherName);
285 }
286 return equals;
287 }
288
289 @Override
290 public int hashCode()
291 {
292 return name == null ? 0 : name.hashCode();
293 }
294
295 @Override
296 public String toString()
297 {
298 return name;
299 }
300
301 public String getName()
302 {
303 return name;
304 }
305 }
306
307 }