Allow abilitiy to boot controller with non-default admin credentials
[controller.git] / opendaylight / usermanager / implementation / src / main / java / org / opendaylight / controller / usermanager / internal / UserManager.java
index 8d9e34717e31651cc4086b5a70798e11aceae9e5..3f6ac4a8c786af11cb88bf16e4ab0c44ae8b3097 100644 (file)
@@ -8,9 +8,10 @@
 
 package org.opendaylight.controller.usermanager.internal;
 
+import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.util.ArrayList;
@@ -31,17 +32,16 @@ import org.opendaylight.controller.clustering.services.CacheConfigException;
 import org.opendaylight.controller.clustering.services.CacheExistException;
 import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
 import org.opendaylight.controller.clustering.services.IClusterServices;
+import org.opendaylight.controller.configuration.ConfigurationObject;
 import org.opendaylight.controller.configuration.IConfigurationAware;
+import org.opendaylight.controller.configuration.IConfigurationService;
 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.sal.utils.StatusCode;
 import org.opendaylight.controller.usermanager.AuthResponse;
 import org.opendaylight.controller.usermanager.AuthenticatedUser;
 import org.opendaylight.controller.usermanager.AuthorizationConfig;
@@ -52,8 +52,8 @@ 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.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -77,11 +77,12 @@ public class UserManager implements IUserManager, IObjectReader,
     private static final String DEFAULT_ADMIN = "admin";
     private static final String DEFAULT_ADMIN_PASSWORD = "admin";
     private static final String DEFAULT_ADMIN_ROLE = UserLevel.NETWORKADMIN.toString();
-    private static final String ROOT = GlobalConstants.STARTUPHOME.toString();
-    private static final String USERS_FILE_NAME = ROOT + "users.conf";
-    private static final String SERVERS_FILE_NAME = ROOT + "servers.conf";
-    private static final String AUTH_FILE_NAME = ROOT + "authorization.conf";
-    private static final String RECOVERY_FILE = ROOT + "NETWORK_ADMIN_PASSWORD_RECOVERY";
+    private static final String USERS_FILE_NAME = "users.conf";
+    private static final String SERVERS_FILE_NAME = "servers.conf";
+    private static final String AUTH_FILE_NAME = "authorization.conf";
+    private static final String RECOVERY_FILE = "NETWORK_ADMIN_PASSWORD_RECOVERY";
+    private static final boolean DISALLOW_DEFAULT_ADMIN_PASSWORD =
+        Boolean.getBoolean("usermanager.disable-default-admin-password");
     private ConcurrentMap<String, UserConfig> localUserConfigList;
     private ConcurrentMap<String, ServerConfig> remoteServerConfigList;
     // local authorization info for remotely authenticated users
@@ -89,10 +90,30 @@ public class UserManager implements IUserManager, IObjectReader,
     private ConcurrentMap<String, AuthenticatedUser> activeUsers;
     private ConcurrentMap<String, IAAAProvider> authProviders;
     private IClusterGlobalServices clusterGlobalService = null;
+    private IConfigurationService configurationService;
     private SecurityContextRepository securityContextRepo = new UserSecurityContextRepository();
     private IContainerAuthorization containerAuthorizationClient;
     private Set<IResourceAuthorization> applicationAuthorizationClients;
     private ISessionManager sessionMgr = new SessionManager();
+    protected enum Command {
+        ADD("add", "added"),
+        MODIFY("modify", "modified"),
+        REMOVE("remove", "removed");
+        private final String action;
+        private final String postAction;
+        private Command(String action, String postAction) {
+            this.action = action;
+            this.postAction = postAction;
+        }
+
+        public String getAction() {
+            return action;
+        }
+
+        public String getPostAction() {
+            return postAction;
+        }
+    }
 
     public boolean addAAAProvider(IAAAProvider provider) {
         if (provider == null || provider.getName() == null
@@ -183,66 +204,106 @@ public class UserManager implements IUserManager, IObjectReader,
     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
+         * Do not load local startup file if we are not the coordinator
          */
-        if (localUserConfigList.isEmpty()) {
-            loadUserConfig();
-        }
-        if (remoteServerConfigList.isEmpty()) {
-            loadServerConfig();
-        }
-        if (authorizationConfList.isEmpty()) {
-            loadAuthConfig();
-        }
+        loadUserConfig();
+        loadServerConfig();
+        loadAuthConfig();
     }
 
     private void loadSecurityKeys() {
 
     }
 
-    private void checkDefaultNetworkAdmin() {
-        /*
-         * If startup config is not there, it's old or it was deleted or if a
-         * password recovery was run, need to add Default Network Admin User
-         */
-        if (!localUserConfigList.containsKey(DEFAULT_ADMIN)) {
-            List<String> roles = new ArrayList<String>(1);
-            roles.add(DEFAULT_ADMIN_ROLE);
-            // Need to skip the strong password check for the default admin
-            UserConfig defaultAdmin = UserConfig.getUncheckedUserConfig(UserManager.DEFAULT_ADMIN,
-                    UserManager.DEFAULT_ADMIN_PASSWORD, roles);
-            localUserConfigList.put(UserManager.DEFAULT_ADMIN, defaultAdmin);
+    private void checkDefaultNetworkAdmin(String newPass) {
+        boolean usingFactoryPassword = false;
+        // network admin already configured.
+        if (localUserConfigList.containsKey(DEFAULT_ADMIN)) {
+            UserConfig uc =  localUserConfigList.get(DEFAULT_ADMIN);
+            if (!uc.isPasswordMatch(DEFAULT_ADMIN_PASSWORD)) {
+                return;
+            } else {
+                usingFactoryPassword = true;
+            }
+        }
+
+        List<String> defaultRoles = new ArrayList<String>(1);
+        defaultRoles.add(DEFAULT_ADMIN_ROLE);
+        if (newPass == null) {
+            if (!localUserConfigList.containsKey(DEFAULT_ADMIN)) {
+              // Need to skip the strong password check for the default admin
+                UserConfig defaultAdmin = UserConfig.getUncheckedUserConfig(
+                    UserManager.DEFAULT_ADMIN, UserManager.DEFAULT_ADMIN_PASSWORD,
+                    defaultRoles);
+                localUserConfigList.put(UserManager.DEFAULT_ADMIN, defaultAdmin);
+                usingFactoryPassword = true;
+            }
+        } else {
+            // use new password for admin
+            Status status = UserConfig.validateClearTextPassword(newPass);
+            if (status.isSuccess()) {
+                localUserConfigList.put(UserManager.DEFAULT_ADMIN,
+                    new UserConfig(UserManager.DEFAULT_ADMIN, newPass, defaultRoles));
+                logger.trace("Network Adminstrator password is reset.");
+                if (newPass.equals(DEFAULT_ADMIN_PASSWORD)) {
+                    usingFactoryPassword = true;
+                }
+            } else {
+                logger.warn("Password is invalid - {}. Network Adminstrator password " +
+                    "cannot be set.", status.getDescription());
+            }
+        }
+
+        if (usingFactoryPassword) {
+            if (DISALLOW_DEFAULT_ADMIN_PASSWORD) {
+                logger.warn("Network Administrator factory default password " +
+                    "is disallowed. Please set the password prior to starting " +
+                    "the controller. Shutting down now.");
+                // shutdown osgi
+                try {
+                    BundleContext bundleContext = FrameworkUtil.getBundle(
+                        getClass()).getBundleContext();
+                    bundleContext.getBundle(0).stop();
+                } catch (BundleException e) {
+                    logger.warn("Cannot stop framework ", e);
+                }
+            } else {
+                logger.warn("Network Administrator password is set to factory default. " +
+                    "Please change the password as soon as possible.");
+            }
         }
     }
 
-    private void checkPasswordRecovery() {
+    private String checkPasswordRecovery() {
         final String fileDescription = "Default Network Administrator password recovery file";
+        File recoveryFile = new File(UserManager.RECOVERY_FILE);
+        if (!recoveryFile.exists()) return null;
+        // read the recovery file
+        String pwd = null;
         try {
-            FileInputStream fis = new FileInputStream(UserManager.RECOVERY_FILE);
+            BufferedReader reader = new BufferedReader(new FileReader(recoveryFile));
+            // read password from recovery file if it has one
+            pwd = reader.readLine();
+            if (pwd != null && pwd.trim().length() == 0) {
+                pwd = null;
+            }
+            reader.close();
             /*
              * Recovery file detected, remove current default network
              * administrator entry from local users configuration list.
              * Warn user and delete recovery file.
              */
             this.localUserConfigList.remove(UserManager.DEFAULT_ADMIN);
-            logger.info("Default Network Administrator password has been reset to factory default.");
-            logger.info("Please change the default Network Administrator password as soon as possible");
-            File filePointer = new File(UserManager.RECOVERY_FILE);
-            boolean status = filePointer.delete();
-            if (!status) {
+            if (!recoveryFile.delete()) {
                 logger.warn("Failed to delete {}", fileDescription);
             } else {
                 logger.trace("{} deleted", fileDescription);
             }
-            fis.close();
-        } catch (FileNotFoundException fnf) {
-            logger.trace("{} not present", fileDescription);
         } catch (IOException e) {
-            logger.warn("Failed to close file stream for {}", fileDescription);
+            logger.warn("Failed to process file {}", fileDescription);
         }
+        return pwd;
     }
 
     @Override
@@ -264,18 +325,18 @@ public class UserManager implements IUserManager, IObjectReader,
                 rcResponse = aaaClient.authService(userName, password,
                         aaaServer.getAddress(), aaaServer.getSecret());
                 if (rcResponse.getStatus() == AuthResultEnum.AUTH_ACCEPT) {
-                    logger.info(
+                    logger.trace(
                             "Remote Authentication Succeeded for User: \"{}\", by Server: {}",
                             userName, aaaServer.getAddress());
                     remotelyAuthenticated = true;
                     break;
                 } else if (rcResponse.getStatus() == AuthResultEnum.AUTH_REJECT) {
-                    logger.info(
+                    logger.trace(
                             "Remote Authentication Rejected User: \"{}\", from Server: {}, Reason:{}",
                             new Object[] { userName, aaaServer.getAddress(),
                                     rcResponse.getStatus().toString() });
                 } else {
-                    logger.info(
+                    logger.trace(
                             "Remote Authentication Failed for User: \"{}\", from Server: {}, Reason:{}",
                             new Object[] { userName, aaaServer.getAddress(),
                                     rcResponse.getStatus().toString() });
@@ -286,20 +347,20 @@ public class UserManager implements IUserManager, IObjectReader,
         if (!remotelyAuthenticated) {
             UserConfig localUser = this.localUserConfigList.get(userName);
             if (localUser == null) {
-                logger.info(
+                logger.trace(
                         "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(
+                logger.trace(
                         "Local Authentication Failed for User: \"{}\", Reason: {}",
                         userName, rcResponse.getStatus().toString());
 
                 return (rcResponse.getStatus());
             }
-            logger.info("Local Authentication Succeeded for User: \"{}\"",
+            logger.trace("Local Authentication Succeeded for User: \"{}\"",
                     userName);
         }
 
@@ -327,16 +388,16 @@ public class UserManager implements IUserManager, IObjectReader,
          * data to the rcResponse
          */
         if (remotelyAuthenticated && !authorizationInfoIsPresent) {
-            logger.info(
+            logger.trace(
                     "No Remote Authorization Info provided by Server for User: \"{}\"",
                     userName);
-            logger.info(
+            logger.trace(
                     "Looking for Local Authorization Info for User: \"{}\"",
                     userName);
 
             AuthorizationConfig resource = authorizationConfList.get(userName);
             if (resource != null) {
-                logger.info("Found Local Authorization Info for User: \"{}\"",
+                logger.trace("Found Local Authorization Info for User: \"{}\"",
                         userName);
                 attributes = resource.getRolesString();
 
@@ -353,7 +414,7 @@ public class UserManager implements IUserManager, IObjectReader,
             result.setRoleList(attributes.split(" "));
             authorized = true;
         } else {
-            logger.info("Not able to find Authorization Info for User: \"{}\"",
+            logger.trace("Not able to find Authorization Info for User: \"{}\"",
                     userName);
         }
 
@@ -362,10 +423,10 @@ public class UserManager implements IUserManager, IObjectReader,
          */
         putUserInActiveList(userName, result);
         if (authorized) {
-            logger.info("User \"{}\" authorized for the following role(s): {}",
+            logger.trace("User \"{}\" authorized for the following role(s): {}",
                     userName, result.getUserRoles());
         } else {
-            logger.info("User \"{}\" Not Authorized for any role ", userName);
+            logger.trace("User \"{}\" Not Authorized for any role ", userName);
         }
 
         return rcResponse.getStatus();
@@ -396,9 +457,8 @@ public class UserManager implements IUserManager, IObjectReader,
     }
 
     private Status saveLocalUserListInternal() {
-        ObjectWriter objWriter = new ObjectWriter();
-        return objWriter.write(new ConcurrentHashMap<String, UserConfig>(
-                localUserConfigList), USERS_FILE_NAME);
+        return configurationService.persistConfiguration(
+                new ArrayList<ConfigurationObject>(localUserConfigList.values()), USERS_FILE_NAME);
     }
 
     @Override
@@ -407,9 +467,8 @@ public class UserManager implements IUserManager, IObjectReader,
     }
 
     private Status saveAAAServerListInternal() {
-        ObjectWriter objWriter = new ObjectWriter();
-        return objWriter.write(new ConcurrentHashMap<String, ServerConfig>(
-                remoteServerConfigList), SERVERS_FILE_NAME);
+        return configurationService.persistConfiguration(
+                new ArrayList<ConfigurationObject>(remoteServerConfigList.values()), SERVERS_FILE_NAME);
     }
 
     @Override
@@ -418,10 +477,8 @@ public class UserManager implements IUserManager, IObjectReader,
     }
 
     private Status saveAuthorizationListInternal() {
-        ObjectWriter objWriter = new ObjectWriter();
-        return objWriter.write(
-                new ConcurrentHashMap<String, AuthorizationConfig>(
-                        authorizationConfList), AUTH_FILE_NAME);
+        return configurationService.persistConfiguration(
+                new ArrayList<ConfigurationObject>(authorizationConfList.values()), AUTH_FILE_NAME);
     }
 
     @Override
@@ -432,55 +489,28 @@ public class UserManager implements IUserManager, IObjectReader,
         return ois.readObject();
     }
 
-    @SuppressWarnings("unchecked")
     private void loadUserConfig() {
-        ObjectReader objReader = new ObjectReader();
-        ConcurrentMap<String, UserConfig> confList = (ConcurrentMap<String, UserConfig>) objReader
-                .read(this, USERS_FILE_NAME);
-
-        if (confList == null) {
-            return;
-        }
-
-        for (UserConfig conf : confList.values()) {
-            addRemoveLocalUserInternal(conf, false);
+        for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, USERS_FILE_NAME)) {
+            addRemoveLocalUserInternal((UserConfig) conf, false);
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void loadServerConfig() {
-        ObjectReader objReader = new ObjectReader();
-        ConcurrentMap<String, ServerConfig> confList = (ConcurrentMap<String, ServerConfig>) objReader
-                .read(this, SERVERS_FILE_NAME);
-
-        if (confList == null) {
-            return;
-        }
-
-        for (ServerConfig conf : confList.values()) {
-            addAAAServer(conf);
+        for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, SERVERS_FILE_NAME)) {
+            addAAAServer((ServerConfig) conf);
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void loadAuthConfig() {
-        ObjectReader objReader = new ObjectReader();
-        ConcurrentMap<String, AuthorizationConfig> confList = (ConcurrentMap<String, AuthorizationConfig>) objReader
-                .read(this, AUTH_FILE_NAME);
-
-        if (confList == null) {
-            return;
-        }
-
-        for (AuthorizationConfig conf : confList.values()) {
-            addAuthInfo(conf);
+        for (ConfigurationObject conf : configurationService.retrieveConfiguration(this, AUTH_FILE_NAME)) {
+            addAuthInfo((AuthorizationConfig) conf);
         }
     }
 
     /*
      * Interaction with GUI START
      */
-    private Status addRemoveLocalUser(UserConfig AAAconf, boolean delete) {
+    private Status changeLocalUser(UserConfig AAAconf, Command command) {
         // UserConfig Validation check
         Status validCheck = AAAconf.validate();
         if (!validCheck.isSuccess()) {
@@ -491,28 +521,51 @@ public class UserManager implements IUserManager, IObjectReader,
 
         // Check default admin user
         if (user.equals(UserManager.DEFAULT_ADMIN)) {
-            String msg = "Invalid Request: Default Network Admin  User cannot be " + ((delete)? "removed" : "added");
+            String msg = String.format("Invalid Request: Default Network Admin  User cannot be %s", command.getPostAction());
             logger.debug(msg);
             return new Status(StatusCode.NOTALLOWED, msg);
         }
 
         // Check user presence/conflict
+        UserConfig currentAAAconf = localUserConfigList.get(user);
         StatusCode statusCode = null;
         String reason = null;
-        if (delete && !localUserConfigList.containsKey(user)) {
-            reason = "not found";
-            statusCode = StatusCode.NOTFOUND;
-        } else if (!delete && localUserConfigList.containsKey(user)) {
-            reason = "already present";
-            statusCode = StatusCode.CONFLICT;
+        switch (command) {
+        case ADD:
+            if (currentAAAconf != null) {
+                reason = "already present";
+                statusCode = StatusCode.CONFLICT;
+            }
+            break;
+        case MODIFY:
+        case REMOVE:
+            if (currentAAAconf == null) {
+                reason = "not found";
+                statusCode = StatusCode.NOTFOUND;
+            }
+            break;
+        default:
+            break;
+
         }
         if (statusCode != null) {
+            String action = String.format("Failed to %s user %s: ", command.getAction(), user);
             String msg = String.format("User %s %s in configuration database", user, reason);
-            logger.debug(msg);
+            logger.debug(action + msg);
             return new Status(statusCode, msg);
         }
 
-        return addRemoveLocalUserInternal(AAAconf, delete);
+        switch (command) {
+        case ADD:
+            return addRemoveLocalUserInternal(AAAconf, false);
+        case MODIFY:
+            addRemoveLocalUserInternal(currentAAAconf, true);
+            return addRemoveLocalUserInternal(AAAconf, false);
+        case REMOVE:
+            return addRemoveLocalUserInternal(AAAconf, true);
+        default:
+            return new Status(StatusCode.INTERNALERROR, "Unknown action");
+        }
     }
 
     private Status addRemoveLocalUserInternal(UserConfig AAAconf, boolean delete) {
@@ -571,12 +624,17 @@ public class UserManager implements IUserManager, IObjectReader,
 
     @Override
     public Status addLocalUser(UserConfig AAAconf) {
-        return addRemoveLocalUser(AAAconf, false);
+        return changeLocalUser(AAAconf, Command.ADD);
+    }
+
+    @Override
+    public Status modifyLocalUser(UserConfig AAAconf) {
+        return changeLocalUser(AAAconf, Command.MODIFY);
     }
 
     @Override
     public Status removeLocalUser(UserConfig AAAconf) {
-        return addRemoveLocalUser(AAAconf, true);
+        return changeLocalUser(AAAconf, Command.REMOVE);
     }
 
     @Override
@@ -589,7 +647,7 @@ public class UserManager implements IUserManager, IObjectReader,
             return new Status(StatusCode.NOTFOUND, "User does not exist");
         }
 
-        return addRemoveLocalUser(localUserConfigList.get(userName), true);
+        return changeLocalUser(localUserConfigList.get(userName), Command.REMOVE);
     }
 
     @Override
@@ -644,7 +702,7 @@ public class UserManager implements IUserManager, IObjectReader,
         // Trigger cluster update
         localUserConfigList.put(user, targetConfigEntry);
 
-        logger.info("Password changed for User \"{}\"", user);
+        logger.trace("Password changed for User \"{}\"", user);
 
         return status;
     }
@@ -654,7 +712,7 @@ public class UserManager implements IUserManager, IObjectReader,
         // 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);
+        logger.trace("User \"{}\" logged out", userName);
     }
 
     /*
@@ -665,7 +723,7 @@ public class UserManager implements IUserManager, IObjectReader,
         // 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);
+        logger.trace("User \"{}\" timed out", userName);
     }
 
     @Override
@@ -777,6 +835,16 @@ public class UserManager implements IUserManager, IObjectReader,
         }
     }
 
+    public void setConfigurationService(IConfigurationService service) {
+        logger.trace("Got configuration service set request {}", service);
+        this.configurationService = service;
+    }
+
+    public void unsetConfigurationService(IConfigurationService service) {
+        logger.trace("Got configuration service UNset request");
+        this.configurationService = null;
+    }
+
     void unsetContainerAuthClient(IContainerAuthorization s) {
         if (this.containerAuthorizationClient == s) {
             this.containerAuthorizationClient = null;
@@ -827,10 +895,10 @@ public class UserManager implements IUserManager, IObjectReader,
         loadConfigurations();
 
         // Check if a password recovery was triggered for default network admin user
-        checkPasswordRecovery();
+        String pwd = checkPasswordRecovery();
 
         // Make sure default Network Admin account is there
-        checkDefaultNetworkAdmin();
+        checkDefaultNetworkAdmin(pwd);
 
         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
         bundleContext.registerService(CommandProvider.class.getName(), this, null);