103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.security;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.File;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.FilenameFilter;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.security.Principal;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.ArrayList;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.HashMap;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.HashSet;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Iterator;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.List;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Map;
3103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Properties;
3203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Set;
3303928aee4356845252ac6b662d5c72c29903813eJake Slack
3403928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.security.auth.Subject;
3503928aee4356845252ac6b662d5c72c29903813eJake Slack
3603928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.security.MappedLoginService.KnownUser;
3703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.security.MappedLoginService.RolePrincipal;
3803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.server.UserIdentity;
3903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.Scanner;
4003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.Scanner.BulkListener;
4103928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.component.AbstractLifeCycle;
4203928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
4303928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
4403928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.resource.Resource;
4503928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.security.Credential;
4603928aee4356845252ac6b662d5c72c29903813eJake Slack
4703928aee4356845252ac6b662d5c72c29903813eJake Slack/**
4803928aee4356845252ac6b662d5c72c29903813eJake Slack * PropertyUserStore
4903928aee4356845252ac6b662d5c72c29903813eJake Slack *
5003928aee4356845252ac6b662d5c72c29903813eJake Slack * This class monitors a property file of the format mentioned below and notifies registered listeners of the changes to the the given file.
5103928aee4356845252ac6b662d5c72c29903813eJake Slack *
5203928aee4356845252ac6b662d5c72c29903813eJake Slack * <PRE>
5303928aee4356845252ac6b662d5c72c29903813eJake Slack *  username: password [,rolename ...]
5403928aee4356845252ac6b662d5c72c29903813eJake Slack * </PRE>
5503928aee4356845252ac6b662d5c72c29903813eJake Slack *
5603928aee4356845252ac6b662d5c72c29903813eJake Slack * Passwords may be clear text, obfuscated or checksummed. The class com.eclipse.Util.Password should be used to generate obfuscated passwords or password
5703928aee4356845252ac6b662d5c72c29903813eJake Slack * checksums.
5803928aee4356845252ac6b662d5c72c29903813eJake Slack *
5903928aee4356845252ac6b662d5c72c29903813eJake Slack * If DIGEST Authentication is used, the password must be in a recoverable format, either plain text or OBF:.
6003928aee4356845252ac6b662d5c72c29903813eJake Slack */
6103928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class PropertyUserStore extends AbstractLifeCycle
6203928aee4356845252ac6b662d5c72c29903813eJake Slack{
6303928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(PropertyUserStore.class);
6403928aee4356845252ac6b662d5c72c29903813eJake Slack
6503928aee4356845252ac6b662d5c72c29903813eJake Slack    private String _config;
6603928aee4356845252ac6b662d5c72c29903813eJake Slack    private Resource _configResource;
6703928aee4356845252ac6b662d5c72c29903813eJake Slack    private Scanner _scanner;
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    private int _refreshInterval = 0;// default is not to reload
6903928aee4356845252ac6b662d5c72c29903813eJake Slack
7003928aee4356845252ac6b662d5c72c29903813eJake Slack    private IdentityService _identityService = new DefaultIdentityService();
7103928aee4356845252ac6b662d5c72c29903813eJake Slack    private boolean _firstLoad = true; // true if first load, false from that point on
7203928aee4356845252ac6b662d5c72c29903813eJake Slack    private final List<String> _knownUsers = new ArrayList<String>();
7303928aee4356845252ac6b662d5c72c29903813eJake Slack    private final Map<String, UserIdentity> _knownUserIdentities = new HashMap<String, UserIdentity>();
7403928aee4356845252ac6b662d5c72c29903813eJake Slack    private List<UserListener> _listeners;
7503928aee4356845252ac6b662d5c72c29903813eJake Slack
7603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
7703928aee4356845252ac6b662d5c72c29903813eJake Slack    public String getConfig()
7803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7903928aee4356845252ac6b662d5c72c29903813eJake Slack        return _config;
8003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8103928aee4356845252ac6b662d5c72c29903813eJake Slack
8203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8303928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setConfig(String config)
8403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
8503928aee4356845252ac6b662d5c72c29903813eJake Slack        _config = config;
8603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8703928aee4356845252ac6b662d5c72c29903813eJake Slack
8803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8903928aee4356845252ac6b662d5c72c29903813eJake Slack        public UserIdentity getUserIdentity(String userName)
9003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
9103928aee4356845252ac6b662d5c72c29903813eJake Slack            return _knownUserIdentities.get(userName);
9203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
9303928aee4356845252ac6b662d5c72c29903813eJake Slack
9403928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
9503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
9603928aee4356845252ac6b662d5c72c29903813eJake Slack     * returns the resource associated with the configured properties file, creating it if necessary
9703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
9803928aee4356845252ac6b662d5c72c29903813eJake Slack    public Resource getConfigResource() throws IOException
9903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
10003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_configResource == null)
10103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
10203928aee4356845252ac6b662d5c72c29903813eJake Slack            _configResource = Resource.newResource(_config);
10303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
10403928aee4356845252ac6b662d5c72c29903813eJake Slack
10503928aee4356845252ac6b662d5c72c29903813eJake Slack        return _configResource;
10603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
10703928aee4356845252ac6b662d5c72c29903813eJake Slack
10803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
10903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
11003928aee4356845252ac6b662d5c72c29903813eJake Slack     * sets the refresh interval (in seconds)
11103928aee4356845252ac6b662d5c72c29903813eJake Slack     */
11203928aee4356845252ac6b662d5c72c29903813eJake Slack    public void setRefreshInterval(int msec)
11303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
11403928aee4356845252ac6b662d5c72c29903813eJake Slack        _refreshInterval = msec;
11503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
11603928aee4356845252ac6b662d5c72c29903813eJake Slack
11703928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
11803928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
11903928aee4356845252ac6b662d5c72c29903813eJake Slack     * refresh interval in seconds for how often the properties file should be checked for changes
12003928aee4356845252ac6b662d5c72c29903813eJake Slack     */
12103928aee4356845252ac6b662d5c72c29903813eJake Slack    public int getRefreshInterval()
12203928aee4356845252ac6b662d5c72c29903813eJake Slack    {
12303928aee4356845252ac6b662d5c72c29903813eJake Slack        return _refreshInterval;
12403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
12503928aee4356845252ac6b662d5c72c29903813eJake Slack
12603928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
12703928aee4356845252ac6b662d5c72c29903813eJake Slack    private void loadUsers() throws IOException
12803928aee4356845252ac6b662d5c72c29903813eJake Slack    {
12903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_config == null)
13003928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
13103928aee4356845252ac6b662d5c72c29903813eJake Slack
13203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (LOG.isDebugEnabled())
13303928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.debug("Load " + this + " from " + _config);
13403928aee4356845252ac6b662d5c72c29903813eJake Slack        Properties properties = new Properties();
13503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (getConfigResource().exists())
13603928aee4356845252ac6b662d5c72c29903813eJake Slack            properties.load(getConfigResource().getInputStream());
13703928aee4356845252ac6b662d5c72c29903813eJake Slack        Set<String> known = new HashSet<String>();
13803928aee4356845252ac6b662d5c72c29903813eJake Slack
13903928aee4356845252ac6b662d5c72c29903813eJake Slack        for (Map.Entry<Object, Object> entry : properties.entrySet())
14003928aee4356845252ac6b662d5c72c29903813eJake Slack        {
14103928aee4356845252ac6b662d5c72c29903813eJake Slack            String username = ((String)entry.getKey()).trim();
14203928aee4356845252ac6b662d5c72c29903813eJake Slack            String credentials = ((String)entry.getValue()).trim();
14303928aee4356845252ac6b662d5c72c29903813eJake Slack            String roles = null;
14403928aee4356845252ac6b662d5c72c29903813eJake Slack            int c = credentials.indexOf(',');
14503928aee4356845252ac6b662d5c72c29903813eJake Slack            if (c > 0)
14603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
14703928aee4356845252ac6b662d5c72c29903813eJake Slack                roles = credentials.substring(c + 1).trim();
14803928aee4356845252ac6b662d5c72c29903813eJake Slack                credentials = credentials.substring(0,c).trim();
14903928aee4356845252ac6b662d5c72c29903813eJake Slack            }
15003928aee4356845252ac6b662d5c72c29903813eJake Slack
15103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0)
15203928aee4356845252ac6b662d5c72c29903813eJake Slack            {
15303928aee4356845252ac6b662d5c72c29903813eJake Slack                String[] roleArray = IdentityService.NO_ROLES;
15403928aee4356845252ac6b662d5c72c29903813eJake Slack                if (roles != null && roles.length() > 0)
15503928aee4356845252ac6b662d5c72c29903813eJake Slack                {
15603928aee4356845252ac6b662d5c72c29903813eJake Slack                    roleArray = roles.split(",");
15703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
15803928aee4356845252ac6b662d5c72c29903813eJake Slack                known.add(username);
15903928aee4356845252ac6b662d5c72c29903813eJake Slack                Credential credential = Credential.getCredential(credentials);
16003928aee4356845252ac6b662d5c72c29903813eJake Slack
16103928aee4356845252ac6b662d5c72c29903813eJake Slack                Principal userPrincipal = new KnownUser(username,credential);
16203928aee4356845252ac6b662d5c72c29903813eJake Slack                Subject subject = new Subject();
16303928aee4356845252ac6b662d5c72c29903813eJake Slack                subject.getPrincipals().add(userPrincipal);
16403928aee4356845252ac6b662d5c72c29903813eJake Slack                subject.getPrivateCredentials().add(credential);
16503928aee4356845252ac6b662d5c72c29903813eJake Slack
16603928aee4356845252ac6b662d5c72c29903813eJake Slack                if (roles != null)
16703928aee4356845252ac6b662d5c72c29903813eJake Slack                {
16803928aee4356845252ac6b662d5c72c29903813eJake Slack                    for (String role : roleArray)
16903928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
17003928aee4356845252ac6b662d5c72c29903813eJake Slack                        subject.getPrincipals().add(new RolePrincipal(role));
17103928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
17203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
17303928aee4356845252ac6b662d5c72c29903813eJake Slack
17403928aee4356845252ac6b662d5c72c29903813eJake Slack                subject.setReadOnly();
17503928aee4356845252ac6b662d5c72c29903813eJake Slack
17603928aee4356845252ac6b662d5c72c29903813eJake Slack                _knownUserIdentities.put(username,_identityService.newUserIdentity(subject,userPrincipal,roleArray));
17703928aee4356845252ac6b662d5c72c29903813eJake Slack                notifyUpdate(username,credential,roleArray);
17803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
17903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
18003928aee4356845252ac6b662d5c72c29903813eJake Slack
18103928aee4356845252ac6b662d5c72c29903813eJake Slack        synchronized (_knownUsers)
18203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
18303928aee4356845252ac6b662d5c72c29903813eJake Slack            /*
18403928aee4356845252ac6b662d5c72c29903813eJake Slack             * if its not the initial load then we want to process removed users
18503928aee4356845252ac6b662d5c72c29903813eJake Slack             */
18603928aee4356845252ac6b662d5c72c29903813eJake Slack            if (!_firstLoad)
18703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
18803928aee4356845252ac6b662d5c72c29903813eJake Slack                Iterator<String> users = _knownUsers.iterator();
18903928aee4356845252ac6b662d5c72c29903813eJake Slack                while (users.hasNext())
19003928aee4356845252ac6b662d5c72c29903813eJake Slack                {
19103928aee4356845252ac6b662d5c72c29903813eJake Slack                    String user = users.next();
19203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (!known.contains(user))
19303928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
19403928aee4356845252ac6b662d5c72c29903813eJake Slack                        _knownUserIdentities.remove(user);
19503928aee4356845252ac6b662d5c72c29903813eJake Slack                        notifyRemove(user);
19603928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
19703928aee4356845252ac6b662d5c72c29903813eJake Slack                }
19803928aee4356845252ac6b662d5c72c29903813eJake Slack            }
19903928aee4356845252ac6b662d5c72c29903813eJake Slack
20003928aee4356845252ac6b662d5c72c29903813eJake Slack            /*
20103928aee4356845252ac6b662d5c72c29903813eJake Slack             * reset the tracked _users list to the known users we just processed
20203928aee4356845252ac6b662d5c72c29903813eJake Slack             */
20303928aee4356845252ac6b662d5c72c29903813eJake Slack
20403928aee4356845252ac6b662d5c72c29903813eJake Slack            _knownUsers.clear();
20503928aee4356845252ac6b662d5c72c29903813eJake Slack            _knownUsers.addAll(known);
20603928aee4356845252ac6b662d5c72c29903813eJake Slack
20703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
20803928aee4356845252ac6b662d5c72c29903813eJake Slack
20903928aee4356845252ac6b662d5c72c29903813eJake Slack        /*
21003928aee4356845252ac6b662d5c72c29903813eJake Slack         * set initial load to false as there should be no more initial loads
21103928aee4356845252ac6b662d5c72c29903813eJake Slack         */
21203928aee4356845252ac6b662d5c72c29903813eJake Slack        _firstLoad = false;
21303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
21403928aee4356845252ac6b662d5c72c29903813eJake Slack
21503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
21603928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
21703928aee4356845252ac6b662d5c72c29903813eJake Slack     * Depending on the value of the refresh interval, this method will either start up a scanner thread that will monitor the properties file for changes after
21803928aee4356845252ac6b662d5c72c29903813eJake Slack     * it has initially loaded it. Otherwise the users will be loaded and there will be no active monitoring thread so changes will not be detected.
21903928aee4356845252ac6b662d5c72c29903813eJake Slack     *
22003928aee4356845252ac6b662d5c72c29903813eJake Slack     *
22103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
22203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
22303928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void doStart() throws Exception
22403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
22503928aee4356845252ac6b662d5c72c29903813eJake Slack        super.doStart();
22603928aee4356845252ac6b662d5c72c29903813eJake Slack
22703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (getRefreshInterval() > 0)
22803928aee4356845252ac6b662d5c72c29903813eJake Slack        {
22903928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner = new Scanner();
23003928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.setScanInterval(getRefreshInterval());
23103928aee4356845252ac6b662d5c72c29903813eJake Slack            List<File> dirList = new ArrayList<File>(1);
23203928aee4356845252ac6b662d5c72c29903813eJake Slack            dirList.add(getConfigResource().getFile().getParentFile());
23303928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.setScanDirs(dirList);
23403928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.setFilenameFilter(new FilenameFilter()
23503928aee4356845252ac6b662d5c72c29903813eJake Slack            {
23603928aee4356845252ac6b662d5c72c29903813eJake Slack                public boolean accept(File dir, String name)
23703928aee4356845252ac6b662d5c72c29903813eJake Slack                {
23803928aee4356845252ac6b662d5c72c29903813eJake Slack                    File f = new File(dir,name);
23903928aee4356845252ac6b662d5c72c29903813eJake Slack                    try
24003928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
24103928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (f.compareTo(getConfigResource().getFile()) == 0)
24203928aee4356845252ac6b662d5c72c29903813eJake Slack                        {
24303928aee4356845252ac6b662d5c72c29903813eJake Slack                            return true;
24403928aee4356845252ac6b662d5c72c29903813eJake Slack                        }
24503928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
24603928aee4356845252ac6b662d5c72c29903813eJake Slack                    catch (IOException e)
24703928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
24803928aee4356845252ac6b662d5c72c29903813eJake Slack                        return false;
24903928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
25003928aee4356845252ac6b662d5c72c29903813eJake Slack
25103928aee4356845252ac6b662d5c72c29903813eJake Slack                    return false;
25203928aee4356845252ac6b662d5c72c29903813eJake Slack                }
25303928aee4356845252ac6b662d5c72c29903813eJake Slack
25403928aee4356845252ac6b662d5c72c29903813eJake Slack            });
25503928aee4356845252ac6b662d5c72c29903813eJake Slack
25603928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.addListener(new BulkListener()
25703928aee4356845252ac6b662d5c72c29903813eJake Slack            {
25803928aee4356845252ac6b662d5c72c29903813eJake Slack                public void filesChanged(List<String> filenames) throws Exception
25903928aee4356845252ac6b662d5c72c29903813eJake Slack                {
26003928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (filenames == null)
26103928aee4356845252ac6b662d5c72c29903813eJake Slack                        return;
26203928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (filenames.isEmpty())
26303928aee4356845252ac6b662d5c72c29903813eJake Slack                        return;
26403928aee4356845252ac6b662d5c72c29903813eJake Slack                    if (filenames.size() == 1)
26503928aee4356845252ac6b662d5c72c29903813eJake Slack                    {
26603928aee4356845252ac6b662d5c72c29903813eJake Slack                        Resource r = Resource.newResource(filenames.get(0));
26703928aee4356845252ac6b662d5c72c29903813eJake Slack                        if (r.getFile().equals(_configResource.getFile()))
26803928aee4356845252ac6b662d5c72c29903813eJake Slack                            loadUsers();
26903928aee4356845252ac6b662d5c72c29903813eJake Slack                    }
27003928aee4356845252ac6b662d5c72c29903813eJake Slack                }
27103928aee4356845252ac6b662d5c72c29903813eJake Slack
27203928aee4356845252ac6b662d5c72c29903813eJake Slack                public String toString()
27303928aee4356845252ac6b662d5c72c29903813eJake Slack                {
27403928aee4356845252ac6b662d5c72c29903813eJake Slack                    return "PropertyUserStore$Scanner";
27503928aee4356845252ac6b662d5c72c29903813eJake Slack                }
27603928aee4356845252ac6b662d5c72c29903813eJake Slack
27703928aee4356845252ac6b662d5c72c29903813eJake Slack            });
27803928aee4356845252ac6b662d5c72c29903813eJake Slack
27903928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.setReportExistingFilesOnStartup(true);
28003928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.setRecursive(false);
28103928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.start();
28203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28303928aee4356845252ac6b662d5c72c29903813eJake Slack        else
28403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
28503928aee4356845252ac6b662d5c72c29903813eJake Slack            loadUsers();
28603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
28703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
28803928aee4356845252ac6b662d5c72c29903813eJake Slack
28903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
29003928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
29103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
29203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
29303928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void doStop() throws Exception
29403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
29503928aee4356845252ac6b662d5c72c29903813eJake Slack        super.doStop();
29603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_scanner != null)
29703928aee4356845252ac6b662d5c72c29903813eJake Slack            _scanner.stop();
29803928aee4356845252ac6b662d5c72c29903813eJake Slack        _scanner = null;
29903928aee4356845252ac6b662d5c72c29903813eJake Slack    }
30003928aee4356845252ac6b662d5c72c29903813eJake Slack
30103928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
30203928aee4356845252ac6b662d5c72c29903813eJake Slack     * Notifies the registered listeners of potential updates to a user
30303928aee4356845252ac6b662d5c72c29903813eJake Slack     *
30403928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param username
30503928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param credential
30603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param roleArray
30703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
30803928aee4356845252ac6b662d5c72c29903813eJake Slack    private void notifyUpdate(String username, Credential credential, String[] roleArray)
30903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
31003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_listeners != null)
31103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
31203928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Iterator<UserListener> i = _listeners.iterator(); i.hasNext();)
31303928aee4356845252ac6b662d5c72c29903813eJake Slack            {
31403928aee4356845252ac6b662d5c72c29903813eJake Slack                i.next().update(username,credential,roleArray);
31503928aee4356845252ac6b662d5c72c29903813eJake Slack            }
31603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
31703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
31803928aee4356845252ac6b662d5c72c29903813eJake Slack
31903928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
32003928aee4356845252ac6b662d5c72c29903813eJake Slack     * notifies the registered listeners that a user has been removed.
32103928aee4356845252ac6b662d5c72c29903813eJake Slack     *
32203928aee4356845252ac6b662d5c72c29903813eJake Slack     * @param username
32303928aee4356845252ac6b662d5c72c29903813eJake Slack     */
32403928aee4356845252ac6b662d5c72c29903813eJake Slack    private void notifyRemove(String username)
32503928aee4356845252ac6b662d5c72c29903813eJake Slack    {
32603928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_listeners != null)
32703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
32803928aee4356845252ac6b662d5c72c29903813eJake Slack            for (Iterator<UserListener> i = _listeners.iterator(); i.hasNext();)
32903928aee4356845252ac6b662d5c72c29903813eJake Slack            {
33003928aee4356845252ac6b662d5c72c29903813eJake Slack                i.next().remove(username);
33103928aee4356845252ac6b662d5c72c29903813eJake Slack            }
33203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
33303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
33403928aee4356845252ac6b662d5c72c29903813eJake Slack
33503928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
33603928aee4356845252ac6b662d5c72c29903813eJake Slack     * registers a listener to be notified of the contents of the property file
33703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
33803928aee4356845252ac6b662d5c72c29903813eJake Slack    public void registerUserListener(UserListener listener)
33903928aee4356845252ac6b662d5c72c29903813eJake Slack    {
34003928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_listeners == null)
34103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
34203928aee4356845252ac6b662d5c72c29903813eJake Slack            _listeners = new ArrayList<UserListener>();
34303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
34403928aee4356845252ac6b662d5c72c29903813eJake Slack        _listeners.add(listener);
34503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
34603928aee4356845252ac6b662d5c72c29903813eJake Slack
34703928aee4356845252ac6b662d5c72c29903813eJake Slack    /**
34803928aee4356845252ac6b662d5c72c29903813eJake Slack     * UserListener
34903928aee4356845252ac6b662d5c72c29903813eJake Slack     */
35003928aee4356845252ac6b662d5c72c29903813eJake Slack    public interface UserListener
35103928aee4356845252ac6b662d5c72c29903813eJake Slack    {
35203928aee4356845252ac6b662d5c72c29903813eJake Slack        public void update(String username, Credential credential, String[] roleArray);
35303928aee4356845252ac6b662d5c72c29903813eJake Slack
35403928aee4356845252ac6b662d5c72c29903813eJake Slack        public void remove(String username);
35503928aee4356845252ac6b662d5c72c29903813eJake Slack    }
35603928aee4356845252ac6b662d5c72c29903813eJake Slack}
357