LockOutManager.java 8.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/**
 * $Revision$
 * $Date$
 *
 * Copyright (C) 2006 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */
package org.jivesoftware.openfire.lockout;

import org.jivesoftware.util.*;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;

import java.util.Map;
import java.util.Date;

/**
Daniel Henninger's avatar
Daniel Henninger committed
20
 * The LockOutManager manages the LockOutProvider configured for this server, caches knowledge of
Daniel Henninger's avatar
Daniel Henninger committed
21
 * whether accounts are disabled or enabled, and provides a single point of entry for handling
22 23 24 25 26 27 28 29 30 31 32
 * locked/disabled accounts.
 *
 * The provider can be specified in <tt>openfire.xml</tt> by adding:
 *  ...
 *    <provider>
 *       <lockout>
 *          <className>my.lock.out.provider</className>
 *       </lockout>
 *    </provider>
 *  ...
 *
33 34 35 36
 * @author Daniel Henninger
 */
public class LockOutManager {

37
    // Wrap this guy up so we can mock out the LockOutManager class.
38 39
    private static class LockOutManagerContainer {
        private static LockOutManager instance = new LockOutManager();
40 41 42 43 44 45 46 47 48 49 50
    }

    /**
     * Returns the currently-installed LockOutProvider. <b>Warning:</b> in virtually all
     * cases the lockout provider should not be used directly. Instead, the appropriate
     * methods in LockOutManager should be called. Direct access to the lockout provider is
     * only provided for special-case logic.
     *
     * @return the current LockOutProvider.
     */
    public static LockOutProvider getLockOutProvider() {
51
        return LockOutManagerContainer.instance.provider;
52 53 54 55 56 57 58 59 60 61 62 63 64
    }

    /**
     * Returns a singleton instance of LockOutManager.
     *
     * @return a LockOutManager instance.
     */
    public static LockOutManager getInstance() {
        return LockOutManagerContainer.instance;
    }

    /* Cache of locked out accounts */
    private Cache<String,LockOutFlag> lockOutCache;
65
    private LockOutProvider provider;
66

67 68 69
    /**
     * Constructs a LockOutManager, setting up it's cache, propery listener, and setting up the provider.
     */
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    private LockOutManager() {
        // Initialize the lockout cache.
        lockOutCache = CacheFactory.createCache("Locked Out Accounts");

        // Load an lockout provider.
        initProvider();

        // Detect when a new lockout provider class is set 
        PropertyEventListener propListener = new PropertyEventListener() {
            public void propertySet(String property, Map params) {
                //Ignore
            }

            public void propertyDeleted(String property, Map params) {
                //Ignore
            }

            public void xmlPropertySet(String property, Map params) {
                if ("provider.lockout.className".equals(property)) {
                    initProvider();
                }
            }

            public void xmlPropertyDeleted(String property, Map params) {
                //Ignore
            }
        };
        PropertyEventDispatcher.addListener(propListener);
    }

100 101 102 103
    /**
     * Initializes the server's lock out provider, based on configuration and defaults to
     * DefaultLockOutProvider if the specified provider is not valid or not specified.
     */
104
    private void initProvider() {
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        String className = JiveGlobals.getXMLProperty("provider.lockout.className",
                "org.jivesoftware.openfire.lockout.DefaultLockOutProvider");
        // Check if we need to reset the provider class
        if (provider == null || !className.equals(provider.getClass().getName())) {
            try {
                Class c = ClassUtils.forName(className);
                provider = (LockOutProvider) c.newInstance();
            }
            catch (Exception e) {
                Log.error("Error loading lockout provider: " + className, e);
                provider = new DefaultLockOutProvider();
            }
        }
    }

    /**
     * Returns a LockOutFlag for a given username, which contains information about the time
     * period that the specified account is going to be disabled.
     *
     * @param username Username of account to request status of.
     * @return The LockOutFlag instance describing the accounts disabled status.
     * @throws NotLockedOutException if user account specified is not currently locked out (disabled).
     */
    public LockOutFlag getDisabledStatus(String username) throws NotLockedOutException {
129 130 131
        if (username == null) {
            throw new UnsupportedOperationException("Null username not allowed!");
        }
132 133 134
        if (!provider.shouldNotBeCached()) {
            return provider.getDisabledStatus(username);
        }
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
        LockOutFlag flag = lockOutCache.get(username);
        // If ID wan't found in cache, load it up and put it there.
        if (flag == null) {
            synchronized (username.intern()) {
                flag = lockOutCache.get(username);
                // If group wan't found in cache, load it up and put it there.
                if (flag == null) {
                    flag = provider.getDisabledStatus(username);
                    lockOutCache.put(username, flag);
                }
            }
        }
        return flag;
    }

    /**
     * Returns true or false if an account is currently locked out.
     *
     * @param username Username of account to check on.
     * @return True or false if the account is currently locked out.
     */
    public boolean isAccountDisabled(String username) {
        try {
            LockOutFlag flag = getDisabledStatus(username);
            if (flag == null) {
                return false;
            }
            Date curDate = new Date();
            if (flag.getStartTime() != null && curDate.before(flag.getStartTime())) {
                return false;
            }
            if (flag.getEndTime() != null && curDate.after(flag.getEndTime())) {
                return false;
            }
            return true;
        }
        catch (NotLockedOutException e) {
            return false;
        }
    }

    /**
     * Sets an account to disabled, starting at an optional time and ending at an optional time.
     * If either times are set to null, the lockout is considered "forever" in that direction.
     * For example, if you had a start time of 2 hours from now, and a null end time, then the account
     * would be locked out in two hours, and never unlocked until someone manually unlcoked the account.
     *
     * @param username User whose account will be disabled.
     * @param startTime When to start the lockout, or null if immediately.
     * @param endTime When to end the lockout, or null if forever.
     * @throws UnsupportedOperationException if the provider is readonly.
     */
    public void disableAccount(String username, Date startTime, Date endTime) throws UnsupportedOperationException {
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException();
        }
        LockOutFlag flag = new LockOutFlag(username, startTime, endTime);
        provider.setDisabledStatus(flag);
193 194 195 196
        if (!provider.shouldNotBeCached()) {
            // Add lockout data to cache.
            lockOutCache.put(username, flag);
        }
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
        // Fire event.
        LockOutEventDispatcher.accountLocked(flag);
    }

    /**
     * Enables an account that may or may not have previously been disabled.  This erases any
     * knowledge of a lockout, including one that wasn't necessarily in effect at the time the
     * method was called.
     *
     * @param username User to enable.
     * @throws UnsupportedOperationException if the provider is readonly.
     */
    public void enableAccount(String username) throws UnsupportedOperationException {
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException();
        }
        provider.unsetDisabledStatus(username);
214 215 216 217
        if (!provider.shouldNotBeCached()) {
            // Remove lockout data from cache.
            lockOutCache.remove(username);
        }
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
        // Fire event.
        LockOutEventDispatcher.accountUnlocked(username);
    }

    /**
     * "Records" (notifies all listeners) that a failed login occurred.
     *
     * @param username Locked out user that attempted to login.
     */
    public void recordFailedLogin(String username) {
        // Fire event.
        LockOutEventDispatcher.lockedAccountDenied(username);
    }

}