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    }