Separate UserManager into api and implementation bundles
[controller.git] / opendaylight / usermanager / implementation / src / main / java / org / opendaylight / controller / usermanager / internal / UserManagerImpl.java
diff --git a/opendaylight/usermanager/implementation/src/main/java/org/opendaylight/controller/usermanager/internal/UserManagerImpl.java b/opendaylight/usermanager/implementation/src/main/java/org/opendaylight/controller/usermanager/internal/UserManagerImpl.java
new file mode 100644 (file)
index 0000000..faa6e3b
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.usermanager.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.clustering.services.CacheConfigException;
+import org.opendaylight.controller.clustering.services.CacheExistException;
+import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
+import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
+import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.containermanager.IContainerAuthorization;
+import org.opendaylight.controller.sal.authorization.AuthResultEnum;
+import org.opendaylight.controller.sal.authorization.IResourceAuthorization;
+import org.opendaylight.controller.sal.authorization.UserLevel;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.opendaylight.controller.sal.utils.GlobalConstants;
+import org.opendaylight.controller.sal.utils.IObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectReader;
+import org.opendaylight.controller.sal.utils.ObjectWriter;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.usermanager.AuthResponse;
+import org.opendaylight.controller.usermanager.AuthorizationConfig;
+import org.opendaylight.controller.usermanager.IAAAProvider;
+import org.opendaylight.controller.usermanager.ISessionManager;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.opendaylight.controller.usermanager.ServerConfig;
+import org.opendaylight.controller.usermanager.UserConfig;
+import org.opendaylight.controller.usermanager.security.SessionManager;
+import org.opendaylight.controller.usermanager.security.UserSecurityContextRepository;
+import org.opendaylight.controller.usermanager.internal.AuthenticatedUser;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.web.context.SecurityContextRepository;
+
+/**
+ * The internal implementation of the User Manager.
+ */
+public class UserManagerImpl implements IUserManager, IObjectReader,
+        IConfigurationAware, ICacheUpdateAware<Long, String>, CommandProvider,
+        AuthenticationProvider {
+    private static final Logger logger = LoggerFactory
+            .getLogger(UserManagerImpl.class);
+    private static final String defaultAdmin = "admin";
+    private static final String defaultAdminPassword = "admin";
+    private static final String defaultAdminRole = UserLevel.NETWORKADMIN
+            .toString();
+    private static final String ROOT = GlobalConstants.STARTUPHOME.toString();
+    private static final String SAVE = "save";
+    private static final String usersFileName = ROOT + "users.conf";
+    private static final String serversFileName = ROOT + "servers.conf";
+    private static final String authFileName = ROOT + "authorization.conf";
+    private ConcurrentMap<String, UserConfig> localUserConfigList;
+    private ConcurrentMap<String, ServerConfig> remoteServerConfigList;
+    // local authorization info for remotely authenticated users
+    private ConcurrentMap<String, AuthorizationConfig> authorizationConfList;
+    private ConcurrentMap<String, AuthenticatedUser> activeUsers;
+    private ConcurrentMap<String, IAAAProvider> authProviders;
+    private ConcurrentMap<Long, String> localUserListSaveConfigEvent,
+    remoteServerSaveConfigEvent, authorizationSaveConfigEvent;
+    private IClusterGlobalServices clusterGlobalService = null;
+    private SecurityContextRepository securityContextRepo = new UserSecurityContextRepository();
+    private IContainerAuthorization containerAuthorizationClient;
+    private Set<IResourceAuthorization> applicationAuthorizationClients;
+    private ISessionManager sessionMgr = new SessionManager();
+
+    public boolean addAAAProvider(IAAAProvider provider) {
+        if (provider == null || provider.getName() == null
+                || provider.getName().trim().isEmpty()) {
+            return false;
+        }
+        if (authProviders.get(provider.getName()) != null) {
+            return false;
+        }
+
+        authProviders.put(provider.getName(), provider);
+        return true;
+    }
+
+    public void removeAAAProvider(IAAAProvider provider) {
+        authProviders.remove(provider.getName());
+    }
+
+    public IAAAProvider getAAAProvider(String name) {
+        return authProviders.get(name);
+    }
+
+    @Override
+    public Set<String> getAAAProviderNames() {
+        return authProviders.keySet();
+    }
+
+    @SuppressWarnings("deprecation")
+    private void allocateCaches() {
+        this.applicationAuthorizationClients = Collections
+                .synchronizedSet(new HashSet<IResourceAuthorization>());
+        if (clusterGlobalService == null) {
+            logger.error("un-initialized clusterGlobalService, can't create cache");
+            return;
+        }
+
+        try {
+            clusterGlobalService.createCache("usermanager.localUserConfigList",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache(
+                    "usermanager.remoteServerConfigList",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache(
+                    "usermanager.authorizationConfList",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache("usermanager.activeUsers",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache(
+                    "usermanager.localUserSaveConfigEvent",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache(
+                    "usermanager.remoteServerSaveConfigEvent",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+
+            clusterGlobalService.createCache(
+                    "usermanager.authorizationSaveConfigEvent",
+                    EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL));
+        } catch (CacheConfigException cce) {
+            logger.error("Cache configuration invalid - check cache mode");
+        } catch (CacheExistException ce) {
+            logger.debug("Skipping cache creation as already present");
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "deprecation" })
+    private void retrieveCaches() {
+        if (clusterGlobalService == null) {
+            logger.error("un-initialized clusterService, can't retrieve cache");
+            return;
+        }
+
+        activeUsers = (ConcurrentMap<String, AuthenticatedUser>) clusterGlobalService
+                .getCache("usermanager.activeUsers");
+        if (activeUsers == null) {
+            logger.error("Failed to get cache for activeUsers");
+        }
+
+        localUserConfigList = (ConcurrentMap<String, UserConfig>) clusterGlobalService
+                .getCache("usermanager.localUserConfigList");
+        if (localUserConfigList == null) {
+            logger.error("Failed to get cache for localUserConfigList");
+        }
+
+        remoteServerConfigList = (ConcurrentMap<String, ServerConfig>) clusterGlobalService
+                .getCache("usermanager.remoteServerConfigList");
+        if (remoteServerConfigList == null) {
+            logger.error("Failed to get cache for remoteServerConfigList");
+        }
+
+        authorizationConfList = (ConcurrentMap<String, AuthorizationConfig>) clusterGlobalService
+                .getCache("usermanager.authorizationConfList");
+        if (authorizationConfList == null) {
+            logger.error("Failed to get cache for authorizationConfList");
+        }
+
+        localUserListSaveConfigEvent = (ConcurrentMap<Long, String>) clusterGlobalService
+                .getCache("usermanager.localUserSaveConfigEvent");
+        if (localUserListSaveConfigEvent == null) {
+            logger.error("Failed to get cache for localUserSaveConfigEvent");
+        }
+
+        remoteServerSaveConfigEvent = (ConcurrentMap<Long, String>) clusterGlobalService
+                .getCache("usermanager.remoteServerSaveConfigEvent");
+        if (remoteServerSaveConfigEvent == null) {
+            logger.error("Failed to get cache for remoteServerSaveConfigEvent");
+        }
+
+        authorizationSaveConfigEvent = (ConcurrentMap<Long, String>) clusterGlobalService
+                .getCache("usermanager.authorizationSaveConfigEvent");
+        if (authorizationSaveConfigEvent == null) {
+            logger.error("Failed to get cache for authorizationSaveConfigEvent");
+        }
+    }
+
+    private void loadConfigurations() {
+        // To encode and decode user and server configuration objects
+        loadSecurityKeys();
+
+        /*
+         * Do not load local startup file if we already got the configurations
+         * synced from another cluster node
+         */
+        if (localUserConfigList.isEmpty()) {
+            loadUserConfig();
+        }
+        if (remoteServerConfigList.isEmpty()) {
+            loadServerConfig();
+        }
+        if (authorizationConfList.isEmpty()) {
+            loadAuthConfig();
+        }
+    }
+
+    private void loadSecurityKeys() {
+
+    }
+
+    private void checkDefaultNetworkAdmin() {
+        // If startup config is not there, it's old or it was deleted,
+        // need to add Default Admin
+        if (!localUserConfigList.containsKey(defaultAdmin)) {
+            List<String> roles = new ArrayList<String>(1);
+            roles.add(defaultAdminRole);
+            localUserConfigList.put(defaultAdmin, new UserConfig(defaultAdmin,
+                    defaultAdminPassword, roles));
+        }
+    }
+
+    @Override
+    public AuthResultEnum authenticate(String userName, String password) {
+        IAAAProvider aaaClient;
+        AuthResponse rcResponse = null;
+        AuthenticatedUser result;
+        boolean remotelyAuthenticated = false;
+        boolean authorizationInfoIsPresent = false;
+        boolean authorized = false;
+
+        /*
+         * Attempt remote authentication first if server is configured
+         */
+        for (ServerConfig aaaServer : remoteServerConfigList.values()) {
+            String protocol = aaaServer.getProtocol();
+            aaaClient = this.getAAAProvider(protocol);
+            if (aaaClient != null) {
+                rcResponse = aaaClient.authService(userName, password,
+                        aaaServer.getAddress(), aaaServer.getSecret());
+                if (rcResponse.getStatus() == AuthResultEnum.AUTH_ACCEPT) {
+                    logger.info(
+                            "Remote Authentication Succeeded for User: \"{}\", by Server: {}",
+                            userName, aaaServer.getAddress());
+                    remotelyAuthenticated = true;
+                    break;
+                } else if (rcResponse.getStatus() == AuthResultEnum.AUTH_REJECT) {
+                    logger.info(
+                            "Remote Authentication Rejected User: \"{}\", from Server: {}, Reason:{}",
+                            new Object[] { userName, aaaServer.getAddress(),
+                                    rcResponse.getStatus().toString() });
+                } else {
+                    logger.info(
+                            "Remote Authentication Failed for User: \"{}\", from Server: {}, Reason:{}",
+                            new Object[] { userName, aaaServer.getAddress(),
+                                    rcResponse.getStatus().toString() });
+                }
+            }
+        }
+
+        if (!remotelyAuthenticated) {
+            UserConfig localUser = this.localUserConfigList.get(userName);
+            if (localUser == null) {
+                logger.info(
+                        "Local Authentication Failed for User:\"{}\", Reason: "
+                                + "user not found in Local Database", userName);
+                return (AuthResultEnum.AUTH_INVALID_LOC_USER);
+            }
+            rcResponse = localUser.authenticate(password);
+            if (rcResponse.getStatus() != AuthResultEnum.AUTH_ACCEPT_LOC) {
+                logger.info(
+                        "Local Authentication Failed for User: \"{}\", Reason: {}",
+                        userName, rcResponse.getStatus().toString());
+
+                return (rcResponse.getStatus());
+            }
+            logger.info("Local Authentication Succeeded for User: \"{}\"",
+                    userName);
+        }
+
+        /*
+         * Authentication succeeded
+         */
+        result = new AuthenticatedUser(userName);
+
+        /*
+         * Extract attributes from response All the information we are
+         * interested in is in the first Cisco VSA (vendor specific attribute).
+         * Just process the first VSA and return
+         */
+        String attributes = (rcResponse.getData() != null && !rcResponse
+                .getData().isEmpty()) ? rcResponse.getData().get(0) : null;
+
+        /*
+         * Check if the authorization information is present
+         */
+        authorizationInfoIsPresent = checkAuthorizationInfo(attributes);
+
+        /*
+         * The AAA server was only used to perform the authentication Look for
+         * locally stored authorization info for this user If found, add the
+         * data to the rcResponse
+         */
+        if (remotelyAuthenticated && !authorizationInfoIsPresent) {
+            logger.info(
+                    "No Remote Authorization Info provided by Server for User: \"{}\"",
+                    userName);
+            logger.info(
+                    "Looking for Local Authorization Info for User: \"{}\"",
+                    userName);
+
+            AuthorizationConfig resource = authorizationConfList.get(userName);
+            if (resource != null) {
+                logger.info("Found Local Authorization Info for User: \"{}\"",
+                        userName);
+                attributes = resource.getRolesString();
+
+            }
+            authorizationInfoIsPresent = checkAuthorizationInfo(attributes);
+        }
+
+        /*
+         * Common response parsing for local & remote authenticated user Looking
+         * for authorized resources, detecting attributes' validity
+         */
+        if (authorizationInfoIsPresent) {
+            // Identifying the administrative role
+            result.setRoleList(attributes.split(" "));
+            authorized = true;
+        } else {
+            logger.info("Not able to find Authorization Info for User: \"{}\"",
+                    userName);
+        }
+
+        /*
+         * Add profile for authenticated user
+         */
+        putUserInActiveList(userName, result);
+        if (authorized) {
+            logger.info("User \"{}\" authorized for the following role(s): {}",
+                    userName, result.getUserRoles());
+        } else {
+            logger.info("User \"{}\" Not Authorized for any role ", userName);
+        }
+
+        return rcResponse.getStatus();
+    }
+
+    // Check in the attributes string whether or not authorization information
+    // is present
+    private boolean checkAuthorizationInfo(String attributes) {
+        return (attributes != null && !attributes.isEmpty());
+    }
+
+    private void putUserInActiveList(String user, AuthenticatedUser result) {
+        activeUsers.put(user, result);
+    }
+
+    private void removeUserFromActiveList(String user) {
+        if (!activeUsers.containsKey(user)) {
+            // as cookie persists in cache, we can get logout for unexisting
+            // active users
+            return;
+        }
+        activeUsers.remove(user);
+    }
+
+    @Override
+    public Status saveLocalUserList() {
+        // Publish the save config event to the cluster nodes
+        localUserListSaveConfigEvent.put(new Date().getTime(), SAVE);
+        return saveLocalUserListInternal();
+    }
+
+    private Status saveLocalUserListInternal() {
+        ObjectWriter objWriter = new ObjectWriter();
+        return objWriter.write(new ConcurrentHashMap<String, UserConfig>(
+                localUserConfigList), usersFileName);
+    }
+
+    @Override
+    public Status saveAAAServerList() {
+        // Publish the save config event to the cluster nodes
+        remoteServerSaveConfigEvent.put(new Date().getTime(), SAVE);
+        return saveAAAServerListInternal();
+    }
+
+    private Status saveAAAServerListInternal() {
+        ObjectWriter objWriter = new ObjectWriter();
+        return objWriter.write(new ConcurrentHashMap<String, ServerConfig>(
+                remoteServerConfigList), serversFileName);
+    }
+
+    @Override
+    public Status saveAuthorizationList() {
+        // Publish the save config event to the cluster nodes
+        authorizationSaveConfigEvent.put(new Date().getTime(), SAVE);
+        return saveAuthorizationListInternal();
+    }
+
+    private Status saveAuthorizationListInternal() {
+        ObjectWriter objWriter = new ObjectWriter();
+        return objWriter.write(
+                new ConcurrentHashMap<String, AuthorizationConfig>(
+                        authorizationConfList), authFileName);
+    }
+
+    @Override
+    public Object readObject(ObjectInputStream ois)
+            throws FileNotFoundException, IOException, ClassNotFoundException {
+        // Perform the class deserialization locally, from inside the package
+        // where the class is defined
+        return ois.readObject();
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadUserConfig() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<String, UserConfig> confList = (ConcurrentMap<String, UserConfig>) objReader
+                .read(this, usersFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (UserConfig conf : confList.values()) {
+            addLocalUser(conf);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadServerConfig() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<String, ServerConfig> confList = (ConcurrentMap<String, ServerConfig>) objReader
+                .read(this, serversFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (ServerConfig conf : confList.values()) {
+            addAAAServer(conf);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void loadAuthConfig() {
+        ObjectReader objReader = new ObjectReader();
+        ConcurrentMap<String, AuthorizationConfig> confList = (ConcurrentMap<String, AuthorizationConfig>) objReader
+                .read(this, authFileName);
+
+        if (confList == null) {
+            return;
+        }
+
+        for (AuthorizationConfig conf : confList.values()) {
+            addAuthInfo(conf);
+        }
+    }
+
+    /*
+     * Interaction with GUI START
+     */
+    public Status addRemoveLocalUser(UserConfig AAAconf, boolean delete) {
+        // UserConfig Validation check
+        Status validCheck = AAAconf.validate();
+        if (!validCheck.isSuccess()) {
+            return validCheck;
+        }
+
+        // Update Config database
+        if (delete) {
+            if (AAAconf.getUser().equals(UserManagerImpl.defaultAdmin)) {
+                String msg = "Invalid Request: Default Network Admin  User "
+                        + "cannot be deleted";
+                logger.debug(msg);
+                return new Status(StatusCode.NOTALLOWED, msg);
+            }
+            localUserConfigList.remove(AAAconf.getUser());
+            /*
+             * A user account has been removed form local database, we assume
+             * admin does not want this user to stay connected, in case he has
+             * an open session. So we clean the active list as well.
+             */
+            removeUserFromActiveList(AAAconf.getUser());
+        } else {
+            if (AAAconf.getUser().equals(UserManagerImpl.defaultAdmin)) {
+                String msg = "Invalid Request: Default Network Admin  User "
+                        + "cannot be added";
+                logger.debug(msg);
+                return new Status(StatusCode.NOTALLOWED, msg);
+            }
+            localUserConfigList.put(AAAconf.getUser(), AAAconf);
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    private Status addRemoveAAAServer(ServerConfig AAAconf, boolean delete) {
+        // Validation check
+        if (!AAAconf.isValid()) {
+            String msg = "Invalid Server configuration";
+            logger.warn(msg);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+
+        // Update configuration database
+        if (delete) {
+            remoteServerConfigList.remove(AAAconf.getAddress());
+        } else {
+            remoteServerConfigList.put(AAAconf.getAddress(), AAAconf);
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    private Status addRemoveAuthInfo(AuthorizationConfig AAAconf, boolean delete) {
+        Status configCheck = AAAconf.validate();
+        if (!configCheck.isSuccess()) {
+            String msg = "Invalid Authorization configuration: "
+                    + configCheck.getDescription();
+            logger.warn(msg);
+            return new Status(StatusCode.BADREQUEST, msg);
+        }
+
+        // Update configuration database
+        if (delete) {
+            authorizationConfList.remove(AAAconf.getUser());
+        } else {
+            authorizationConfList.put(AAAconf.getUser(), AAAconf);
+        }
+
+        return new Status(StatusCode.SUCCESS, null);
+    }
+
+    @Override
+    public Status addLocalUser(UserConfig AAAconf) {
+        return addRemoveLocalUser(AAAconf, false);
+    }
+
+    @Override
+    public Status removeLocalUser(UserConfig AAAconf) {
+        return addRemoveLocalUser(AAAconf, true);
+    }
+
+    @Override
+    public Status removeLocalUser(String userName) {
+        if (userName == null || userName.trim().isEmpty()) {
+            return new Status(StatusCode.BADREQUEST, "Invalid user name");
+        }
+        if (!localUserConfigList.containsKey(userName)) {
+            return new Status(StatusCode.NOTFOUND, "User does not exist");
+        }
+        return addRemoveLocalUser(localUserConfigList.get(userName), true);
+    }
+
+    @Override
+    public Status addAAAServer(ServerConfig AAAconf) {
+        return addRemoveAAAServer(AAAconf, false);
+    }
+
+    @Override
+    public Status removeAAAServer(ServerConfig AAAconf) {
+        return addRemoveAAAServer(AAAconf, true);
+    }
+
+    @Override
+    public Status addAuthInfo(AuthorizationConfig AAAconf) {
+        return addRemoveAuthInfo(AAAconf, false);
+    }
+
+    @Override
+    public Status removeAuthInfo(AuthorizationConfig AAAconf) {
+        return addRemoveAuthInfo(AAAconf, true);
+    }
+
+    @Override
+    public List<UserConfig> getLocalUserList() {
+        return new ArrayList<UserConfig>(localUserConfigList.values());
+    }
+
+    @Override
+    public List<ServerConfig> getAAAServerList() {
+        return new ArrayList<ServerConfig>(remoteServerConfigList.values());
+    }
+
+    @Override
+    public List<AuthorizationConfig> getAuthorizationList() {
+        return new ArrayList<AuthorizationConfig>(
+                authorizationConfList.values());
+    }
+
+    @Override
+    public Status changeLocalUserPassword(String user, String curPassword,
+            String newPassword) {
+        UserConfig targetConfigEntry = null;
+
+        // update configuration entry
+        targetConfigEntry = localUserConfigList.get(user);
+        if (targetConfigEntry == null) {
+            return new Status(StatusCode.NOTFOUND, "User not found");
+        }
+        Status status = targetConfigEntry
+                .update(curPassword, newPassword, null);
+        if (!status.isSuccess()) {
+            return status;
+        }
+        // Trigger cluster update
+        localUserConfigList.put(user, targetConfigEntry);
+
+        logger.info("Password changed for User \"{}\"", user);
+
+        return status;
+    }
+
+    @Override
+    public void userLogout(String userName) {
+        // TODO: if user was authenticated through AAA server, send
+        // Acct-Status-Type=stop message to server with logout as reason
+        removeUserFromActiveList(userName);
+        logger.info("User \"{}\" logged out", userName);
+    }
+
+    /*
+     * This function will get called by http session mgr when session times out
+     */
+    @Override
+    public void userTimedOut(String userName) {
+        // TODO: if user was authenticated through AAA server, send
+        // Acct-Status-Type=stop message to server with timeout as reason
+        removeUserFromActiveList(userName);
+        logger.info("User \"{}\" timed out", userName);
+    }
+
+    @Override
+    public String getAccessDate(String user) {
+        return this.activeUsers.get(user).getAccessDate();
+    }
+
+    @Override
+    public synchronized Map<String, List<String>> getUserLoggedIn() {
+        Map<String, List<String>> loggedInList = new HashMap<String, List<String>>();
+        for (Map.Entry<String, AuthenticatedUser> user : activeUsers.entrySet()) {
+            String userNameShow = user.getKey();
+            loggedInList.put(userNameShow, user.getValue().getUserRoles());
+        }
+        return loggedInList;
+    }
+
+    /*
+     * Interaction with GUI END
+     */
+
+    /*
+     * Cluster notifications
+     */
+
+    @Override
+    public void entryCreated(Long key, String cacheName, boolean originLocal) {
+        // don't react on this event
+    }
+
+    @Override
+    public void entryUpdated(Long key, String new_value, String cacheName,
+            boolean originLocal) {
+        if (cacheName.equals("localUserSaveConfigEvent")) {
+            this.saveLocalUserListInternal();
+        } else if (cacheName.equals("remoteServerSaveConfigEvent")) {
+            this.saveAAAServerListInternal();
+        } else if (cacheName.equals("authorizationSaveConfigEvent")) {
+            this.saveAuthorizationListInternal();
+        }
+    }
+
+    @Override
+    public void entryDeleted(Long key, String cacheName, boolean originLocal) {
+        // don't react on this event
+    }
+
+    public void _umAddUser(CommandInterpreter ci) {
+        String userName = ci.nextArgument();
+        String password = ci.nextArgument();
+        String role = ci.nextArgument();
+
+        List<String> roles = new ArrayList<String>();
+        while (role != null) {
+            if (!role.trim().isEmpty()) {
+                roles.add(role);
+            }
+            role = ci.nextArgument();
+        }
+
+        if (userName == null || userName.trim().isEmpty() || password == null
+                || password.trim().isEmpty() || roles == null
+                || roles.isEmpty()) {
+            ci.println("Invalid Arguments");
+            ci.println("umAddUser <user_name> <password> <user_role>");
+            return;
+        }
+        ci.print(this.addLocalUser(new UserConfig(userName, password, roles)));
+    }
+
+    public void _umRemUser(CommandInterpreter ci) {
+        String userName = ci.nextArgument();
+
+        if (userName == null || userName.trim().isEmpty()) {
+            ci.println("Invalid Arguments");
+            ci.println("umRemUser <user_name>");
+            return;
+        }
+        UserConfig target = localUserConfigList.get(userName);
+        if (target == null) {
+            ci.println("User not found");
+            return;
+        }
+        ci.println(this.removeLocalUser(target));
+    }
+
+    public void _umGetUsers(CommandInterpreter ci) {
+        for (UserConfig conf : this.getLocalUserList()) {
+            ci.println(conf.getUser() + " " + conf.getRoles());
+        }
+    }
+
+    public void _addAAAServer(CommandInterpreter ci) {
+        String server = ci.nextArgument();
+        String secret = ci.nextArgument();
+        String protocol = ci.nextArgument();
+
+        if (server == null || secret == null || protocol == null) {
+            ci.println("Usage : addAAAServer <server> <secret> <protocol>");
+            return;
+        }
+        ServerConfig s = new ServerConfig(server, secret, protocol);
+        addAAAServer(s);
+    }
+
+    public void _removeAAAServer(CommandInterpreter ci) {
+        String server = ci.nextArgument();
+        String secret = ci.nextArgument();
+        String protocol = ci.nextArgument();
+
+        if (server == null || secret == null || protocol == null) {
+            ci.println("Usage : addAAAServer <server> <secret> <protocol>");
+            return;
+        }
+        ServerConfig s = new ServerConfig(server, secret, protocol);
+        removeAAAServer(s);
+    }
+
+    public void _printAAAServers(CommandInterpreter ci) {
+        for (ServerConfig aaaServer : remoteServerConfigList.values()) {
+            ci.println(aaaServer.getAddress() + "-" + aaaServer.getProtocol());
+        }
+    }
+
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        return help.toString();
+    }
+
+    void setClusterGlobalService(IClusterGlobalServices s) {
+        logger.debug("Cluster Service Global set");
+        this.clusterGlobalService = s;
+    }
+
+    void unsetClusterGlobalService(IClusterGlobalServices s) {
+        if (this.clusterGlobalService == s) {
+            logger.debug("Cluster Service Global removed!");
+            this.clusterGlobalService = null;
+        }
+    }
+
+    void unsetContainerAuthClient(IContainerAuthorization s) {
+        if (this.containerAuthorizationClient == s) {
+            this.containerAuthorizationClient = null;
+        }
+    }
+
+    void setContainerAuthClient(IContainerAuthorization s) {
+        this.containerAuthorizationClient = s;
+    }
+
+    void setAppAuthClient(IResourceAuthorization s) {
+        this.applicationAuthorizationClients.add(s);
+    }
+
+    void unsetAppAuthClient(IResourceAuthorization s) {
+        this.applicationAuthorizationClients.remove(s);
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init() {
+    }
+
+    /**
+     * Function called by the dependency manager when at least one dependency
+     * become unsatisfied or when the component is shutting down because for
+     * example bundle is being stopped.
+     *
+     */
+    void destroy() {
+    }
+
+    /**
+     * Function called by dependency manager after "init ()" is called and after
+     * the services provided by the class are registered in the service registry
+     *
+     */
+    void start() {
+        authProviders = new ConcurrentHashMap<String, IAAAProvider>();
+        // Instantiate cluster synced variables
+        allocateCaches();
+        retrieveCaches();
+
+        // Read startup configuration and populate databases
+        loadConfigurations();
+
+        // Make sure default Network Admin account is there
+        checkDefaultNetworkAdmin();
+        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+                .getBundleContext();
+        bundleContext.registerService(CommandProvider.class.getName(), this,
+                null);
+    }
+
+    /**
+     * Function called by the dependency manager before the services exported by
+     * the component are unregistered, this will be followed by a "destroy ()"
+     * calls
+     *
+     */
+    void stop() {
+    }
+
+    @Override
+    public List<String> getUserRoles(String userName) {
+        List<String> roles = null;
+        if (userName != null) {
+            /*
+             * First look in active users then in local configured users,
+             * finally in local authorized users
+             */
+            if (activeUsers.containsKey(userName)) {
+                roles = activeUsers.get(userName).getUserRoles();
+            } else if (localUserConfigList.containsKey(userName)) {
+                roles = localUserConfigList.get(userName).getRoles();
+            } else if (authorizationConfList.containsKey(userName)) {
+                roles = authorizationConfList.get(userName).getRoles();
+            }
+        }
+        return (roles == null) ? new ArrayList<String>(0) : roles;
+    }
+
+    @Override
+    public UserLevel getUserLevel(String username) {
+        // Returns the highest controller user level for the passed user
+        List<String> rolesNames = getUserRoles(username);
+
+        if (rolesNames.isEmpty()) {
+            return UserLevel.NOUSER;
+        }
+
+        // Check against the well known controller roles first
+        if (rolesNames.contains(UserLevel.SYSTEMADMIN.toString())) {
+            return UserLevel.SYSTEMADMIN;
+        }
+        if (rolesNames.contains(UserLevel.NETWORKADMIN.toString())) {
+            return UserLevel.NETWORKADMIN;
+        }
+        if (rolesNames.contains(UserLevel.NETWORKOPERATOR.toString())) {
+            return UserLevel.NETWORKOPERATOR;
+        }
+        // Check if container user now
+        if (containerAuthorizationClient != null) {
+            for (String roleName : rolesNames) {
+                if (containerAuthorizationClient.isApplicationRole(roleName)) {
+                    return UserLevel.CONTAINERUSER;
+                }
+            }
+        }
+        // Finally check if application user
+        if (applicationAuthorizationClients != null) {
+            for (String roleName : rolesNames) {
+                for (IResourceAuthorization client : this.applicationAuthorizationClients) {
+                    if (client.isApplicationRole(roleName)) {
+                        return UserLevel.APPUSER;
+                    }
+                }
+            }
+        }
+        return UserLevel.NOUSER;
+    }
+
+
+    @Override
+    public List<UserLevel> getUserLevels(String username) {
+        // Returns the controller user levels for the passed user
+        List<String> rolesNames =  getUserRoles(username);
+        List<UserLevel> levels = new ArrayList<UserLevel>();
+
+        if (rolesNames.isEmpty()) {
+            return levels;
+        }
+
+        // Check against the well known controller roles first
+        if (rolesNames.contains(UserLevel.SYSTEMADMIN.toString())) {
+            levels.add(UserLevel.SYSTEMADMIN);
+        }
+        if (rolesNames.contains(UserLevel.NETWORKADMIN.toString())) {
+            levels.add(UserLevel.NETWORKADMIN);
+        }
+        if (rolesNames.contains(UserLevel.NETWORKOPERATOR.toString())) {
+            levels.add(UserLevel.NETWORKOPERATOR);
+        }
+        // Check if container user now
+        if (containerAuthorizationClient != null) {
+            for (String roleName : rolesNames) {
+                if (containerAuthorizationClient.isApplicationRole(roleName)) {
+                    levels.add(UserLevel.CONTAINERUSER);
+                    break;
+                }
+            }
+        }
+        // Finally check if application user
+        if (applicationAuthorizationClients != null) {
+            for (String roleName : rolesNames) {
+                for (IResourceAuthorization client : this.applicationAuthorizationClients) {
+                    if (client.isApplicationRole(roleName)) {
+                        levels.add(UserLevel.APPUSER);
+                        break;
+                    }
+                }
+            }
+        }
+        return levels;
+    }
+
+    @Override
+    public Status saveConfiguration() {
+        boolean success = true;
+        Status ret = saveLocalUserList();
+        if (!ret.isSuccess()) {
+            success = false;
+        }
+        ret = saveAAAServerList();
+        if (!ret.isSuccess()) {
+            success = false;
+        }
+        ret = saveAuthorizationList();
+        if (!ret.isSuccess()) {
+            success = false;
+        }
+
+        if (success) {
+            return new Status(StatusCode.SUCCESS, null);
+        }
+
+        return new Status(StatusCode.INTERNALERROR,
+                "Failed to save user configurations");
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String username)
+            throws UsernameNotFoundException {
+        AuthenticatedUser user = activeUsers.get(username);
+
+        if (user != null) {
+            boolean enabled = true;
+            boolean accountNonExpired = true;
+            boolean credentialsNonExpired = true;
+            boolean accountNonLocked = true;
+
+            return new User(username, localUserConfigList.get(username)
+                    .getPassword(), enabled, accountNonExpired,
+                    credentialsNonExpired, accountNonLocked,
+                    user.getGrantedAuthorities(getUserLevel(username)));
+        } else {
+            throw new UsernameNotFoundException("User not found " + username);
+        }
+    }
+
+    @Override
+    public boolean supports(Class<?> authentication) {
+        return UsernamePasswordAuthenticationToken.class
+                .isAssignableFrom(authentication);
+
+    }
+
+    @Override
+    public SecurityContextRepository getSecurityContextRepo() {
+        return securityContextRepo;
+    }
+
+    public void setSecurityContextRepo(
+            SecurityContextRepository securityContextRepo) {
+        this.securityContextRepo = securityContextRepo;
+    }
+
+    @Override
+    public Authentication authenticate(Authentication authentication)
+            throws AuthenticationException {
+
+        if (StringUtils.isBlank((String) authentication.getCredentials())
+                || StringUtils.isBlank((String) authentication.getPrincipal())) {
+            throw new BadCredentialsException(
+                    "Username or credentials did not match");
+        }
+
+        AuthResultEnum result = authenticate(
+                (String) authentication.getPrincipal(),
+                (String) authentication.getCredentials());
+        if (result.equals(AuthResultEnum.AUTHOR_PASS)
+                || result.equals(AuthResultEnum.AUTH_ACCEPT_LOC)
+                || result.equals(AuthResultEnum.AUTH_ACCEPT)) {
+
+            AuthenticatedUser user = activeUsers.get(authentication
+                    .getPrincipal().toString());
+
+            if (user == null) {
+                throw new AuthenticationServiceException(
+                        "Authentication Failure");
+            }
+
+            authentication = new UsernamePasswordAuthenticationToken(
+                    authentication.getPrincipal(),
+                    authentication.getCredentials(),
+                    user.getGrantedAuthorities(getUserLevel(authentication
+                            .getName())));
+            return authentication;
+
+        } else {
+            throw new BadCredentialsException(
+                    "Username or credentials did not match");
+        }
+
+    }
+
+    // Following are setters for use in unit testing
+    void setLocalUserConfigList(ConcurrentMap<String, UserConfig> ucl) {
+        if (ucl != null) {
+            this.localUserConfigList = ucl;
+        }
+    }
+
+    void setRemoteServerConfigList(ConcurrentMap<String, ServerConfig> scl) {
+        if (scl != null) {
+            this.remoteServerConfigList = scl;
+        }
+    }
+
+    void setAuthorizationConfList(ConcurrentMap<String, AuthorizationConfig> acl) {
+        if (acl != null) {
+            this.authorizationConfList = acl;
+        }
+    }
+
+    void setActiveUsers(ConcurrentMap<String, AuthenticatedUser> au) {
+        if (au != null) {
+            this.activeUsers = au;
+        }
+    }
+
+    void setAuthProviders(ConcurrentMap<String, IAAAProvider> ap) {
+        if (ap != null) {
+            this.authProviders = ap;
+        }
+    }
+
+    @Override
+    public ISessionManager getSessionManager() {
+        return this.sessionMgr;
+    }
+
+    public void setSessionMgr(ISessionManager sessionMgr) {
+        this.sessionMgr = sessionMgr;
+    }
+
+    @Override
+    public String getPassword(String username) {
+        return localUserConfigList.get(username).getPassword();
+    }
+
+    @Override
+    public boolean isRoleInUse(String role) {
+        if (role == null || role.isEmpty()) {
+            return false;
+        }
+        // Check against controller roles
+        if (role.equals(UserLevel.SYSTEMADMIN.toString())
+                || role.equals(UserLevel.NETWORKADMIN.toString())
+                || role.equals(UserLevel.NETWORKOPERATOR.toString())) {
+            return true;
+        }
+        // Check if container roles
+        if (containerAuthorizationClient != null) {
+            if (containerAuthorizationClient.isApplicationRole(role)) {
+                return true;
+            }
+        }
+        // Finally if application role
+        if (applicationAuthorizationClients != null) {
+            for (IResourceAuthorization client : this.applicationAuthorizationClients) {
+                if (client.isApplicationRole(role)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}