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
014package org.hornetq.spi.core.security;
015
016import java.security.Principal;
017import java.security.acl.Group;
018import java.util.HashSet;
019import java.util.Iterator;
020import java.util.Set;
021
022import javax.security.auth.Subject;
023import javax.security.auth.callback.CallbackHandler;
024import javax.security.auth.login.Configuration;
025import javax.security.auth.login.LoginContext;
026import javax.security.auth.login.LoginException;
027
028import org.hornetq.core.logging.Logger;
029import org.hornetq.core.security.CheckType;
030import org.hornetq.core.security.Role;
031import 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 */
043public 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}