View Javadoc

1   /*
2    * Copyright (c) 2006-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.danube.auth.jaas.memory;
14  
15  import java.io.IOException;
16  import java.io.InputStream;
17  import java.io.OutputStream;
18  import java.lang.reflect.Method;
19  import java.net.URI;
20  import java.net.URISyntaxException;
21  import java.net.URLConnection;
22  import java.security.MessageDigest;
23  import java.security.NoSuchAlgorithmException;
24  import java.security.Security;
25  import java.security.cert.CertificateException;
26  import java.util.ArrayList;
27  import java.util.Enumeration;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Properties;
33  import java.util.Set;
34  
35  import javax.security.auth.login.AppConfigurationEntry;
36  import javax.security.auth.login.Configuration;
37  
38  import org.bouncycastle.jce.provider.BouncyCastleProvider;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  
43  /**
44   * This is keystore login module service. This class sets up keystore login module
45   *
46   * @author Daniel Sendula
47   */
48  public class PropertiesModuleService {
49  
50      protected Logger logger = LoggerFactory.getLogger(getClass());
51  
52      /** Login context name */
53      private String loginContext;
54  
55      /** Control flag defaulted to "requrired"*/
56      private String controlFlag = "required";
57  
58      /** Configuration */
59      private Configuration configuration;
60  
61      /** Options */
62      private Map<String, Object> options = new HashMap<String, Object>();
63  
64      /** Properties URI */
65      private String propertiesURI;
66      
67      /** Properties */
68      private Properties properties = new Properties();
69      
70      static {
71          Security.addProvider(new BouncyCastleProvider());
72      }
73  
74      /**
75       * Default constructor
76       */
77      public PropertiesModuleService() {
78      }
79  
80      /**
81       * Starts the service - adding keystore login module to system application configuration entry
82       * @throws Exception
83       */
84      public void start() throws Exception {
85  
86          AppConfigurationEntry.LoginModuleControlFlag flag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
87  
88          if (AppConfigurationEntry.LoginModuleControlFlag.REQUIRED.toString().indexOf(controlFlag) > 0 ) {
89              flag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
90          } else if( AppConfigurationEntry.LoginModuleControlFlag.REQUISITE.toString().indexOf(controlFlag) > 0 ) {
91              flag = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
92          } else if( AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT.toString().indexOf(controlFlag) > 0 ) {
93              flag = AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
94          }else if( AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL.toString().indexOf(controlFlag) > 0 ) {
95              flag = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
96          }
97  
98          
99          options.put("properties", properties);
100         AppConfigurationEntry entry = new AppConfigurationEntry(PropertiesLoginModule.class.getName(), flag, options);
101 
102         AppConfigurationEntry[] list = new AppConfigurationEntry[1];
103         list[0] = entry;
104         Method method = configuration.getClass().getMethod("setAppConfigurationEntry", new Class[]{String.class, list.getClass()});
105         Object[] args = {loginContext, list};
106         method.invoke(configuration, args);
107         logger.info("Set up login context '" + loginContext + "'");
108     }
109 
110 
111     public void stop() throws Exception {
112         Method method = configuration.getClass().getMethod("removeAppConfigurationEntry", new Class[]{String.class});
113         Object[] args = {loginContext};
114         method.invoke(configuration, args);
115         logger.info("Removed login context '" + loginContext + "'");
116     }
117 
118     /**
119      * Lists certificates (users) from the keystore
120      * @return users as a string
121      * @throws Exception
122      */
123     public String listUsersString() throws Exception {
124         List<String> u = listUsers();
125         String[] users = new String[u.size()];
126         users = (String[])u.toArray(users);
127 
128         StringBuffer res = new StringBuffer();
129         res.append("[");
130         for (int i=0; i<users.length; i++) {
131             if (i > 0) {
132                 res.append(", ");
133             }
134             res.append(users[i]);
135         }
136         res.append("]");
137         return res.toString();
138     }
139 
140     /**
141      * Returns a list of all users in the keystore
142      * @return list of all users in the keystore
143      * @throws Exception
144      */
145     public List<String> listUsers() throws Exception {
146         ArrayList<String> users = new ArrayList<String>();
147         Enumeration<?> en = properties.propertyNames();
148         while (en.hasMoreElements()) {
149             users.add((String)en.nextElement());
150         }
151         return users;
152     }
153 
154     /**
155      * Changes the password of the user
156      * @param user user
157      * @param oldPassword old password
158      * @param newPassword new password
159      * @throws Exception
160      */
161     public void changePassword(String user, String oldPassword, String newPassword) throws Exception {
162         newPassword = generateMD5Hash(newPassword);
163         String line = properties.getProperty(user);
164         if (line != null) {
165             int i = line.indexOf(',');
166             if (i < 0) {
167                 properties.setProperty(user, newPassword);
168             } else {
169                 properties.setProperty(user, newPassword + line.substring(i));
170             }
171         }
172         storeProperties();
173     }
174 
175     /**
176      * Adds new user to the store
177      * @param user user
178      * @param passwd password
179      * @throws Exception
180      */
181     public void addUser(String user, String passwd) throws Exception {
182         properties.setProperty(user, generateMD5Hash(passwd));
183         storeProperties();
184     }
185 
186     /**
187      * Removes certificate  - user
188      * @param user username
189      * @throws Exception
190      */
191     public void removeUser(String user) throws Exception {
192         properties.remove(user);
193         storeProperties();
194     }
195 
196     public Set<String> getUserRoles(String user) {
197         String line = properties.getProperty(user);
198         if (line == null) {
199             return null;
200         }
201         Set<String> res = new HashSet<String>();
202         String[] roles = line.split(",");
203         for (int i = 1; i < roles.length; i++) {
204             res.add(roles[i]);
205         }
206         return res;
207     }
208     
209     public String[] getUserRolesAsArray(String user) {
210         String line = properties.getProperty(user);
211         if (line == null) {
212             return null;
213         }
214         String[] roles = line.split(",");
215         String[] res = new String[roles.length - 1];
216         if (roles.length > 1) {
217             System.arraycopy(roles, 1, res, 0, roles.length - 1);
218         }
219         return res;
220     }
221     
222     public void loadProperties() throws CertificateException, IOException, URISyntaxException {
223         if (propertiesURI != null) {
224             logger.info("Loading properties from " + propertiesURI.toString() + " for login context '" + loginContext + "'");
225             InputStream keystoreInputStream = new URI(propertiesURI).toURL().openStream();
226             try {
227                 properties.load(keystoreInputStream);
228             } finally {
229                 keystoreInputStream.close();
230             }
231         }
232     }
233 
234     public void storeProperties() throws IOException, URISyntaxException {
235 
236         if ((properties != null) && (propertiesURI != null)) {
237             
238             logger.info("Storing properties to " + propertiesURI.toString() + " for login context '" + loginContext + "'");
239             OutputStream keystoreOutputStream = null;
240             URLConnection connection = new URI(propertiesURI).toURL().openConnection();
241             connection.setDoOutput(true);
242             keystoreOutputStream = connection.getOutputStream();
243     
244             try {
245                 properties.store(keystoreOutputStream, "For login context " + loginContext);
246             } finally {
247                 keystoreOutputStream.close();
248             }
249         }
250     }
251 
252 
253     /**
254      * Returns properties uri
255      * @return properties uri
256      */
257     public String getPropertiesURI() {
258         return propertiesURI;
259     }
260 
261     /**
262      * Sets properties URI
263      * @param uri properties URI
264      */
265     public void setPropertesURI(String uri) {
266         propertiesURI = uri;
267     }
268 
269     /**
270      * Sets login context name
271      * @param loginContext login context name
272      */
273     public void setLoginContext(String loginContext) {
274         this.loginContext = loginContext;
275     }
276 
277     /**
278      * Returns login context name
279      * @return login context name
280      */
281     public String getLoginContext() {
282         return loginContext;
283     }
284 
285     /**
286      * Sets control flag
287      * @param controlFlag control flag
288      */
289     public void setControlFlag(String controlFlag) {
290         this.controlFlag = controlFlag;
291     }
292 
293     /**
294      * Returns control flag
295      * @return control flag
296      */
297     public String getControlFlag() {
298         return controlFlag;
299     }
300 
301     /**
302      * Returns configuration
303      * @return configuration
304      */
305     public Configuration getConfiguration() {
306         return configuration;
307     }
308 
309     /**
310      * Sets configuration
311      * @param configuration configuration
312      */
313     public void setConfiguration(Configuration configuration) {
314         this.configuration = configuration;
315     }
316 
317     public Properties getProperties() {
318         return properties;
319     }
320     
321     public void setProperties(Properties properties) {
322         this.properties = properties;
323     }
324 
325     public static String generateMD5Hash(String password) throws NoSuchAlgorithmException {
326         return generateHash("MD5", password);
327     }
328     public static String generateHash(String algorithm, String password) throws NoSuchAlgorithmException {
329         MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
330         StringBuilder res = new StringBuilder();
331         res.append("{MD5}");
332         byte[] bytes = messageDigest.digest(password.getBytes());
333         String h = null;
334         int i;
335         for (byte b : bytes) {
336             i = (int)b;
337             if (i < 0) {
338                 i = - i + 127;
339             }
340             h = Integer.toString(i, 16);
341             if (h.length() < 2) {
342                 res.append('0').append(h);
343             } else {
344                 res.append(h);
345             }
346         }
347         return res.toString();
348 
349     }
350 }