1 /*
2 * JBoss, the OpenSource WebOS
3 *
4 * Distributable under LGPL license.
5 * See terms of license at gnu.org.
6 */
7 package org.jboss.security.auth.spi;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.net.URL;
12 import java.util.ArrayList;
13 import java.util.Enumeration;
14 import java.util.Map;
15 import java.util.Properties;
16 import java.util.StringTokenizer;
17
18 import java.security.acl.Group;
19 import javax.security.auth.Subject;
20 import javax.security.auth.callback.CallbackHandler;
21 import javax.security.auth.login.LoginException;
22
23 import org.jboss.security.SimpleGroup;
24 import org.jboss.security.SimplePrincipal;
25 import org.jboss.security.auth.spi.UsernamePasswordLoginModule;
26
27 /** A simple properties file based login module that consults two Java Properties
28 formatted text files for username to password("users.properties") and
29 username to roles("roles.properties") mapping. The names of the properties
30 files may be overriden by the usersProperties and rolesProperties options.
31 The properties files are loaded during initialization using the thread context
32 class loader. This means that these files can be placed into the J2EE
33 deployment jar or the JBoss config directory.
34
35 The users.properties file uses a format:
36 username1=password1
37 username2=password2
38 ...
39
40 to define all valid usernames and their corresponding passwords.
41
42 The roles.properties file uses a format:
43 username1=role1,role2,...
44 username1.RoleGroup1=role3,role4,...
45 username2=role1,role3,...
46
47 to define the sets of roles for valid usernames. The "username.XXX" form of
48 property name is used to assign the username roles to a particular named
49 group of roles where the XXX portion of the property name is the group name.
50 The "username=..." form is an abbreviation for "username.Roles=...".
51 The following are therefore equivalent:
52 jduke=TheDuke,AnimatedCharacter
53 jduke.Roles=TheDuke,AnimatedCharacter
54
55 @author <a href="edward.kenworthy@crispgroup.co.uk">Edward Kenworthy</a>, 12th Dec 2000
56 @author Scott.Stark@jboss.org
57 */
58 public class UsersRolesLoginModule extends UsernamePasswordLoginModule
59 {
60 /** The name of the properties resource containing user/passwords */
61 private String usersRsrcName = "users.properties";
62 /** The name of the properties resource containing user/roles */
63 private String rolesRsrcName = "roles.properties";
64 /** The users.properties values */
65 private Properties users;
66 /** The roles.properties values */
67 private Properties roles;
68
69 /** Initialize this LoginModule.
70 *@param options, the login module option map. Supported options include:
71 *usersProperties: The name of the properties resource containing
72 user/passwords. The default is "users.properties"
73 *rolesProperties: The name of the properties resource containing user/roles
74 The default is "roles.properties".
75 */
76 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
77 {
78 super.initialize(subject, callbackHandler, sharedState, options);
79 try
80 {
81 // Check for usersProperties & rolesProperties
82 String option = (String) options.get("usersProperties");
83 if( option != null )
84 usersRsrcName = option;
85 option = (String) options.get("rolesProperties");
86 if( option != null )
87 rolesRsrcName = option;
88 // Load the properties file that contains the list of users and passwords
89 loadUsers();
90 loadRoles();
91 }
92 catch(Exception e)
93 {
94 // Note that although this exception isn't passed on, users or roles will be null
95 // so that any call to login will throw a LoginException.
96 super.log.error("Failed to load users/passwords/role files", e);
97 }
98 }
99
100 /** Method to authenticate a Subject (phase 1). This validates that the
101 *users and roles properties files were loaded and then calls
102 *super.login to perform the validation of the password.
103 *@exception LoginException, thrown if the users or roles properties files
104 *were not found or the super.login method fails.
105 */
106 public boolean login() throws LoginException
107 {
108 if( users == null )
109 throw new LoginException("Missing users.properties file.");
110 if( roles == null )
111 throw new LoginException("Missing roles.properties file.");
112
113 return super.login();
114 }
115
116 /** Create the set of roles the user belongs to by parsing the roles.properties
117 data for username=role1,role2,... and username.XXX=role1,role2,...
118 patterns.
119 @return Group[] containing the sets of roles
120 */
121 protected Group[] getRoleSets() throws LoginException
122 {
123 String targetUser = getUsername();
124 Enumeration users = roles.propertyNames();
125 SimpleGroup rolesGroup = new SimpleGroup("Roles");
126 ArrayList groups = new ArrayList();
127 groups.add(rolesGroup);
128 while( users.hasMoreElements() && targetUser != null )
129 {
130 String user = (String) users.nextElement();
131 String value = roles.getProperty(user);
132 // See if this entry is of the form targetUser[.GroupName]=roles
133 int index = user.indexOf('.');
134 boolean isRoleGroup = false;
135 boolean userMatch = false;
136 if( index > 0 && targetUser.regionMatches(0, user, 0, index) == true )
137 isRoleGroup = true;
138 else
139 userMatch = targetUser.equals(user);
140
141 // Check for username.RoleGroup pattern
142 if( isRoleGroup == true )
143 {
144 String groupName = user.substring(index+1);
145 if( groupName.equals("Roles") )
146 parseGroupMembers(rolesGroup, value);
147 else
148 {
149 SimpleGroup group = new SimpleGroup(groupName);
150 parseGroupMembers(group, value);
151 groups.add(group);
152 }
153 }
154 else if( userMatch == true )
155 {
156 // Place these roles into the Default "Roles" group
157 parseGroupMembers(rolesGroup, value);
158 }
159 }
160 Group[] roleSets = new Group[groups.size()];
161 groups.toArray(roleSets);
162 return roleSets;
163 }
164 protected String getUsersPassword()
165 {
166 String username = getUsername();
167 String password = null;
168 if( username != null )
169 password = users.getProperty(username , null);
170 return password;
171 }
172
173 // utility methods
174 private void parseGroupMembers(Group group, String value)
175 {
176 StringTokenizer tokenizer = new StringTokenizer(value, ",");
177 while( tokenizer.hasMoreTokens() )
178 {
179 String token = tokenizer.nextToken();
180 SimplePrincipal p = new SimplePrincipal(token);
181 group.addMember(p);
182 }
183 }
184
185 private void loadUsers() throws IOException
186 {
187 users = loadProperties(usersRsrcName);
188 }
189
190 private void loadRoles() throws IOException
191 {
192 roles = loadProperties(rolesRsrcName);
193 }
194
195 /**
196 * Loads the given properties file and returns a Properties object containing the
197 * key,value pairs in that file.
198 * The properties files should be in the class path.
199 */
200 private Properties loadProperties(String propertiesName) throws IOException
201 {
202 Properties bundle = null;
203 ClassLoader loader = Thread.currentThread().getContextClassLoader();
204 URL url = loader.getResource(propertiesName);
205 if( url == null )
206 throw new IOException("Properties file " + propertiesName + " not found");
207 super.log.trace("Properties file="+url);
208 InputStream is = url.openStream();
209 if( is != null )
210 {
211 bundle = new Properties();
212 bundle.load(is);
213 }
214 else
215 {
216 throw new IOException("Properties file " + propertiesName + " not avilable");
217 }
218 return bundle;
219 }
220 }