1 // ========================================================================
2 // Copyright (c) 1996-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.security;
15
16 import java.io.File;
17 import java.io.FilenameFilter;
18 import java.io.IOException;
19 import java.util.ArrayList;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Properties;
25 import java.util.Set;
26
27 import org.eclipse.jetty.http.security.Credential;
28 import org.eclipse.jetty.server.UserIdentity;
29 import org.eclipse.jetty.util.Scanner;
30 import org.eclipse.jetty.util.Scanner.BulkListener;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.resource.Resource;
33
34 /* ------------------------------------------------------------ */
35 /**
36 * Properties User Realm.
37 *
38 * An implementation of UserRealm that stores users and roles in-memory in
39 * HashMaps.
40 * <P>
41 * Typically these maps are populated by calling the load() method or passing a
42 * properties resource to the constructor. The format of the properties file is:
43 *
44 * <PRE>
45 * username: password [,rolename ...]
46 * </PRE>
47 *
48 * Passwords may be clear text, obfuscated or checksummed. The class
49 * com.eclipse.Util.Password should be used to generate obfuscated passwords or
50 * password checksums.
51 *
52 * If DIGEST Authentication is used, the password must be in a recoverable
53 * format, either plain text or OBF:.
54 *
55 * @see org.eclipse.jetty.security.Password
56 *
57 */
58 public class HashLoginService extends MappedLoginService
59 {
60 private String _config;
61 private Resource _configResource;
62 private Scanner _scanner;
63 private int _refreshInterval = 0;// default is not to reload
64
65 /* ------------------------------------------------------------ */
66 public HashLoginService()
67 {
68 }
69
70 /* ------------------------------------------------------------ */
71 public HashLoginService(String name)
72 {
73 setName(name);
74 }
75
76 /* ------------------------------------------------------------ */
77 public HashLoginService(String name, String config)
78 {
79 setName(name);
80 setConfig(config);
81 }
82
83 /* ------------------------------------------------------------ */
84 public String getConfig()
85 {
86 return _config;
87 }
88
89 /* ------------------------------------------------------------ */
90 public void getConfig(String config)
91 {
92 _config=config;
93 }
94
95 /* ------------------------------------------------------------ */
96 public Resource getConfigResource()
97 {
98 return _configResource;
99 }
100
101 /* ------------------------------------------------------------ */
102 /**
103 * Load realm users from properties file. The property file maps usernames
104 * to password specs followed by an optional comma separated list of role
105 * names.
106 *
107 * @param config Filename or url of user properties file.
108 * @exception java.io.IOException if user properties file could not be
109 * loaded
110 */
111 public void setConfig(String config)
112 {
113 _config = config;
114 }
115
116 /* ------------------------------------------------------------ */
117 public void setRefreshInterval(int msec)
118 {
119 _refreshInterval = msec;
120 }
121
122 /* ------------------------------------------------------------ */
123 public int getRefreshInterval()
124 {
125 return _refreshInterval;
126 }
127
128 /* ------------------------------------------------------------ */
129 @Override
130 protected UserIdentity loadUser(String username)
131 {
132 return null;
133 }
134
135 /* ------------------------------------------------------------ */
136 @Override
137 public void loadUsers() throws IOException
138 {
139 if (_config==null)
140 return;
141 _configResource = Resource.newResource(_config);
142
143 if (Log.isDebugEnabled()) Log.debug("Load " + this + " from " + _config);
144 Properties properties = new Properties();
145 properties.load(_configResource.getInputStream());
146 Set<String> known = new HashSet<String>();
147
148 for (Map.Entry<Object, Object> entry : properties.entrySet())
149 {
150 String username = ((String) entry.getKey()).trim();
151 String credentials = ((String) entry.getValue()).trim();
152 String roles = null;
153 int c = credentials.indexOf(',');
154 if (c > 0)
155 {
156 roles = credentials.substring(c + 1).trim();
157 credentials = credentials.substring(0, c).trim();
158 }
159
160 if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0)
161 {
162 String[] roleArray = IdentityService.NO_ROLES;
163 if (roles != null && roles.length() > 0)
164 roleArray = roles.split(",");
165 known.add(username);
166 putUser(username,Credential.getCredential(credentials),roleArray);
167 }
168 }
169
170 Iterator<String> users = _users.keySet().iterator();
171 while(users.hasNext())
172 {
173 String user=users.next();
174 if (!known.contains(user))
175 users.remove();
176 }
177 }
178
179
180 /* ------------------------------------------------------------ */
181 /**
182 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
183 */
184 protected void doStart() throws Exception
185 {
186 super.doStart();
187
188 if (getRefreshInterval() > 0)
189 {
190 _scanner = new Scanner();
191 _scanner.setScanInterval(getRefreshInterval());
192 List<File> dirList = new ArrayList<File>(1);
193 dirList.add(_configResource.getFile());
194 _scanner.setScanDirs(dirList);
195 _scanner.setFilenameFilter(new FilenameFilter()
196 {
197 public boolean accept(File dir, String name)
198 {
199 File f = new File(dir, name);
200 try
201 {
202 if (f.compareTo(_configResource.getFile()) == 0) return true;
203 }
204 catch (IOException e)
205 {
206 return false;
207 }
208
209 return false;
210 }
211
212 });
213 _scanner.addListener(new BulkListener()
214 {
215 public void filesChanged(List filenames) throws Exception
216 {
217 if (filenames == null) return;
218 if (filenames.isEmpty()) return;
219 if (filenames.size() == 1 && filenames.get(0).equals(_config)) loadUsers();
220 }
221
222 public String toString()
223 {
224 return "HashLoginService$Scanner";
225 }
226
227 });
228 _scanner.setReportExistingFilesOnStartup(false);
229 _scanner.setRecursive(false);
230 _scanner.start();
231 }
232 }
233
234 /* ------------------------------------------------------------ */
235 /**
236 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
237 */
238 protected void doStop() throws Exception
239 {
240 super.doStop();
241 if (_scanner != null) _scanner.stop();
242 _scanner = null;
243 }
244
245
246 }