1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.security;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.Path;
24 import java.security.Principal;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.Set;
33
34 import javax.security.auth.Subject;
35
36 import org.eclipse.jetty.security.MappedLoginService.KnownUser;
37 import org.eclipse.jetty.security.MappedLoginService.RolePrincipal;
38 import org.eclipse.jetty.server.UserIdentity;
39 import org.eclipse.jetty.util.PathWatcher;
40 import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
41 import org.eclipse.jetty.util.StringUtil;
42 import org.eclipse.jetty.util.component.AbstractLifeCycle;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45 import org.eclipse.jetty.util.resource.PathResource;
46 import org.eclipse.jetty.util.resource.Resource;
47 import org.eclipse.jetty.util.security.Credential;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.Listener
64 {
65 private static final Logger LOG = Log.getLogger(PropertyUserStore.class);
66
67 private Path _configPath;
68 private Resource _configResource;
69
70 private PathWatcher pathWatcher;
71 private boolean hotReload = false;
72
73 private IdentityService _identityService = new DefaultIdentityService();
74 private boolean _firstLoad = true;
75 private final List<String> _knownUsers = new ArrayList<String>();
76 private final Map<String, UserIdentity> _knownUserIdentities = new HashMap<String, UserIdentity>();
77 private List<UserListener> _listeners;
78
79
80
81
82
83
84 @Deprecated
85 public String getConfig()
86 {
87 return _configPath.toString();
88 }
89
90
91
92
93
94
95 @Deprecated
96 public void setConfig(String configFile)
97 {
98 setConfigPath(configFile);
99 }
100
101
102
103
104
105 public Path getConfigPath()
106 {
107 return _configPath;
108 }
109
110
111
112
113
114 public void setConfigPath(String configFile)
115 {
116 if (configFile == null)
117 {
118 _configPath = null;
119 }
120 else
121 {
122 _configPath = new File(configFile).toPath();
123 }
124 }
125
126
127
128
129
130 public void setConfigPath(File configFile)
131 {
132 _configPath = configFile.toPath();
133 }
134
135
136
137
138
139 public void setConfigPath(Path configPath)
140 {
141 _configPath = configPath;
142 }
143
144
145 public UserIdentity getUserIdentity(String userName)
146 {
147 return _knownUserIdentities.get(userName);
148 }
149
150
151
152
153
154
155 public Resource getConfigResource() throws IOException
156 {
157 if (_configResource == null)
158 {
159 _configResource = new PathResource(_configPath);
160 }
161
162 return _configResource;
163 }
164
165
166
167
168
169
170 public boolean isHotReload()
171 {
172 return hotReload;
173 }
174
175
176
177
178
179
180 public void setHotReload(boolean enable)
181 {
182 if (isRunning())
183 {
184 throw new IllegalStateException("Cannot set hot reload while user store is running");
185 }
186 this.hotReload = enable;
187 }
188
189
190
191
192
193
194
195 @Deprecated
196 public void setRefreshInterval(int sec)
197 {
198 }
199
200
201
202
203
204
205 @Deprecated
206 public int getRefreshInterval()
207 {
208 return (hotReload)?1:0;
209 }
210
211 @Override
212 public String toString()
213 {
214 StringBuilder s = new StringBuilder();
215 s.append(this.getClass().getName());
216 s.append("[");
217 s.append("users.count=").append(this._knownUsers.size());
218 s.append("identityService=").append(this._identityService);
219 s.append("]");
220 return s.toString();
221 }
222
223
224 private void loadUsers() throws IOException
225 {
226 if (_configPath == null)
227 return;
228
229 if (LOG.isDebugEnabled())
230 {
231 LOG.debug("Loading " + this + " from " + _configPath);
232 }
233
234 Properties properties = new Properties();
235 if (getConfigResource().exists())
236 properties.load(getConfigResource().getInputStream());
237
238 Set<String> known = new HashSet<String>();
239
240 for (Map.Entry<Object, Object> entry : properties.entrySet())
241 {
242 String username = ((String)entry.getKey()).trim();
243 String credentials = ((String)entry.getValue()).trim();
244 String roles = null;
245 int c = credentials.indexOf(',');
246 if (c > 0)
247 {
248 roles = credentials.substring(c + 1).trim();
249 credentials = credentials.substring(0,c).trim();
250 }
251
252 if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0)
253 {
254 String[] roleArray = IdentityService.NO_ROLES;
255 if (roles != null && roles.length() > 0)
256 {
257 roleArray = StringUtil.csvSplit(roles);
258 }
259 known.add(username);
260 Credential credential = Credential.getCredential(credentials);
261
262 Principal userPrincipal = new KnownUser(username,credential);
263 Subject subject = new Subject();
264 subject.getPrincipals().add(userPrincipal);
265 subject.getPrivateCredentials().add(credential);
266
267 if (roles != null)
268 {
269 for (String role : roleArray)
270 {
271 subject.getPrincipals().add(new RolePrincipal(role));
272 }
273 }
274
275 subject.setReadOnly();
276
277 _knownUserIdentities.put(username,_identityService.newUserIdentity(subject,userPrincipal,roleArray));
278 notifyUpdate(username,credential,roleArray);
279 }
280 }
281
282 synchronized (_knownUsers)
283 {
284
285
286
287 if (!_firstLoad)
288 {
289 Iterator<String> users = _knownUsers.iterator();
290 while (users.hasNext())
291 {
292 String user = users.next();
293 if (!known.contains(user))
294 {
295 _knownUserIdentities.remove(user);
296 notifyRemove(user);
297 }
298 }
299 }
300
301
302
303
304
305 _knownUsers.clear();
306 _knownUsers.addAll(known);
307
308 }
309
310
311
312
313 _firstLoad = false;
314
315 if (LOG.isDebugEnabled())
316 {
317 LOG.debug("Loaded " + this + " from " + _configPath);
318 }
319 }
320
321
322
323
324
325
326
327
328
329 protected void doStart() throws Exception
330 {
331 super.doStart();
332
333 loadUsers();
334 if ( isHotReload() && (_configPath != null) )
335 {
336 this.pathWatcher = new PathWatcher();
337 this.pathWatcher.watch(_configPath);
338 this.pathWatcher.addListener(this);
339 this.pathWatcher.setNotifyExistingOnStart(false);
340 this.pathWatcher.start();
341 }
342
343 }
344
345 @Override
346 public void onPathWatchEvent(PathWatchEvent event)
347 {
348 try
349 {
350 loadUsers();
351 }
352 catch (IOException e)
353 {
354 LOG.warn(e);
355 }
356 }
357
358
359
360
361
362 protected void doStop() throws Exception
363 {
364 super.doStop();
365 if (this.pathWatcher != null)
366 this.pathWatcher.stop();
367 this.pathWatcher = null;
368 }
369
370
371
372
373
374
375
376
377 private void notifyUpdate(String username, Credential credential, String[] roleArray)
378 {
379 if (_listeners != null)
380 {
381 for (Iterator<UserListener> i = _listeners.iterator(); i.hasNext();)
382 {
383 i.next().update(username,credential,roleArray);
384 }
385 }
386 }
387
388
389
390
391
392
393 private void notifyRemove(String username)
394 {
395 if (_listeners != null)
396 {
397 for (Iterator<UserListener> i = _listeners.iterator(); i.hasNext();)
398 {
399 i.next().remove(username);
400 }
401 }
402 }
403
404
405
406
407
408 public void registerUserListener(UserListener listener)
409 {
410 if (_listeners == null)
411 {
412 _listeners = new ArrayList<UserListener>();
413 }
414 _listeners.add(listener);
415 }
416
417
418
419
420 public interface UserListener
421 {
422 public void update(String username, Credential credential, String[] roleArray);
423
424 public void remove(String username);
425 }
426 }