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 5d0cbb62862d6f0e7f61be02a7d72153e0780284..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,15 +32,14 @@ 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.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;
@@ -53,6 +53,7 @@ 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;
@@ -76,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
@@ -88,6 +90,7 @@ 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;
@@ -96,8 +99,8 @@ public class UserManager implements IUserManager, IObjectReader,
         ADD("add", "added"),
         MODIFY("modify", "modified"),
         REMOVE("remove", "removed");
-        private String action;
-        private String postAction;
+        private final String action;
+        private final String postAction;
         private Command(String action, String postAction) {
             this.action = action;
             this.postAction = postAction;
@@ -204,57 +207,103 @@ public class UserManager implements IUserManager, IObjectReader,
         /*
          * Do not load local startup file if we are not the coordinator
          */
-        if ((clusterGlobalService != null) && (clusterGlobalService.amICoordinator())) {
-            loadUserConfig();
-            loadServerConfig();
-            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
@@ -276,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() });
@@ -298,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);
         }
 
@@ -339,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();
 
@@ -365,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);
         }
 
@@ -374,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();
@@ -408,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
@@ -419,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
@@ -430,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
@@ -444,48 +489,21 @@ 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);
         }
     }
 
@@ -684,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;
     }
@@ -694,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);
     }
 
     /*
@@ -705,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
@@ -817,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;
@@ -867,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);