/* * Copyright (c) 2013-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.systemservices.impl.resource; import static com.emc.storageos.security.password.Constants.PASSWORD_CHANGED_NUMBER; import static com.emc.storageos.security.password.Constants.PASSWORD_CHANGE_INTERVAL; import static com.emc.storageos.security.password.Constants.PASSWORD_LOWERCASE_NUMBER; import static com.emc.storageos.security.password.Constants.PASSWORD_MIN_LENGTH; import static com.emc.storageos.security.password.Constants.PASSWORD_NUMERIC_NUMBER; import static com.emc.storageos.security.password.Constants.PASSWORD_PREVENT_DICTIONARY; import static com.emc.storageos.security.password.Constants.PASSWORD_REPEATING_NUMBER; import static com.emc.storageos.security.password.Constants.PASSWORD_REUSE_NUMBER; import static com.emc.storageos.security.password.Constants.PASSWORD_UPPERCASE_NUMBER; import java.net.URI; import java.net.URISyntaxException; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.TreeMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.SecurityContext; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.coordinator.client.model.PropertyInfoExt; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.coordinator.client.service.impl.CoordinatorClientImpl; import com.emc.storageos.coordinator.common.impl.ZkConnection; import com.emc.storageos.coordinator.exceptions.CoordinatorException; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.model.password.PasswordResetParam; import com.emc.storageos.model.password.PasswordUpdateParam; import com.emc.storageos.model.property.PropertiesMetadata; import com.emc.storageos.model.property.PropertyInfo; import com.emc.storageos.model.property.PropertyInfoUpdate; import com.emc.storageos.model.property.PropertyMetadata; import com.emc.storageos.security.audit.AuditLogManager; import com.emc.storageos.security.audit.RecordableAuditLog; import com.emc.storageos.security.authentication.StorageOSUser; import com.emc.storageos.security.password.Constants; import com.emc.storageos.security.password.PasswordUtils; import com.emc.storageos.services.OperationTypeEnum; import com.emc.storageos.svcs.errorhandling.resources.BadRequestException; import com.emc.storageos.svcs.errorhandling.resources.ForbiddenException; import com.emc.storageos.systemservices.exceptions.CoordinatorClientException; import com.emc.storageos.systemservices.exceptions.LocalRepositoryException; import com.emc.storageos.systemservices.impl.util.LocalPasswordHandler; import com.emc.storageos.util.DummyDbClient; // Requires coordinator to be running on the local host, which build and external servers may not have running, therefore ignoring by default. // This class can be modified to instantiate coordinator and other dependencies so the test is self-contained. See DbsvcTestBase and other unit // tests that accomplish this. @Ignore public class PasswordServiceTest { private static final Logger log = LoggerFactory.getLogger(PasswordServiceTest.class); private DummyConfigService _cfg = new DummyConfigService(); public PropertyInfoExt _passwordProps = new PropertyInfoExt(); public PropertiesMetadata _propertiesMetadata = new PropertiesMetadata(); private DummyEncryptionProvider provider; private static final String SYSTEM_ENCPASSWORD_FORMAT = "system_%s_encpassword"; // NOSONAR // ("squid:S2068 Suppressing sonar violation of hard-coded password") Map<String, String> propertiesMap; /** * local user sysmonitor */ public static final String LOCAL_SYSMON = "sysmonitor"; /** * local user root */ public static final String LOCAL_ROOT = "root"; /** * local user svcuser */ public static final String LOCAL_SVCUSER = "svcuser"; /** * local user proxyuser */ public static final String LOCAL_PROXYUSER = "proxyuser"; public LocalPasswordHandler getPasswordHandler() { LocalPasswordHandler ph = new LocalPasswordHandler(); initPasswordHandler(ph); return ph; } public DummyLocalPasswordHandler getDummyLocalPasswordHandler() { DummyLocalPasswordHandler ph = new DummyLocalPasswordHandler(); initPasswordHandler(ph); return ph; } private void initPasswordHandler(LocalPasswordHandler ph) { Properties properties = generateProperties(); PasswordUtils.setDefaultProperties(properties); PasswordUtils passUtils = new PasswordUtils(); try { CoordinatorClient client = getCoordinatorClient(); passUtils.setCoordinator(client); } catch (Exception e) { log.error("Failed to set coordinator client e=", e); throw new RuntimeException(e); } passUtils.setEncryptionProvider(provider); passUtils.setDbClient(new DummyDbClient()); ph.setPasswordUtils(passUtils); ph.setConfigService(_cfg); } private Properties generateProperties() { Properties properties = new Properties(); properties.setProperty(Constants.PASSWORD_EXPIRE_DAYS, "20"); String encryptPassword = provider.getEncryptedString("changeMe"); properties.setProperty("system_root_encpassword", encryptPassword); properties.setProperty("system_" + LOCAL_PROXYUSER + "_encpassword", encryptPassword); properties.setProperty(PASSWORD_MIN_LENGTH, "1"); properties.setProperty(PASSWORD_LOWERCASE_NUMBER, "1"); properties.setProperty(PASSWORD_UPPERCASE_NUMBER, "1"); properties.setProperty(PASSWORD_NUMERIC_NUMBER, "0"); properties.setProperty(PASSWORD_REPEATING_NUMBER, "1"); properties.setProperty(PASSWORD_CHANGE_INTERVAL, "1"); properties.setProperty(PASSWORD_CHANGED_NUMBER, "1"); properties.setProperty(PASSWORD_REUSE_NUMBER, "1"); properties.setProperty(PASSWORD_PREVENT_DICTIONARY, "yes"); return properties; } public PasswordUpdateParam getDummyPasswordUpdate(String password, String encpassword) { PasswordUpdateParam passwordUpdate = new PasswordUpdateParam(); passwordUpdate.setPassword(password); passwordUpdate.setEncPassword(encpassword); return passwordUpdate; } public PasswordResetParam getDummyPasswordReset(String username, String password, String encpassword) { PasswordResetParam passwordUpdate = new PasswordResetParam(); passwordUpdate.setUsername(username); passwordUpdate.setPassword(password); passwordUpdate.setEncPassword(encpassword); return passwordUpdate; } public Map<String, PropertyMetadata> getPropsMetaData() { Map<String, PropertyMetadata> metadata = new TreeMap(); PropertyMetadata proxyuser_metadata = setPropMetaData("Encrypted password for the 'proxyuser' account", "Encrypted (SHA-512) password for the local 'proxyuser' account.", "encryptedstring", 255, "Security", true, true, false, true, false, "", true); PropertyMetadata sysmonitor_metadata = setPropMetaData("Encrypted password for the 'sysmonitor' account", "Encrypted password for the 'sysmonitor' account.", "string", 255, "Security", true, true, false, true, false, "$6$BIu9aQ6$wBnn9Tn.CUuuoi/JZe.oAOmUDIVCqHpXeem7ZHO5R7dPg2hul8tNCBzwumKrFw8A0qm.LH8YvMJUaN2AL1JVc0", true); PropertyMetadata root_metadata = setPropMetaData("Encrypted password for the 'root' account", "Encrypted (SHA-512) password for the local 'root' account.", "string", 255, "Security", true, true, false, true, false, "$6$eBIu9aQ6$wBnn9Tn.CUuuoi/JZe.oAOmUDIVCqHpXeem7ZHO5R7dPg2hul8tNCBzwumKrFw8A0qm.LH8YvMJUaN2AL1JVc0", false); PropertyMetadata svcuser_metadata = setPropMetaData("Encrypted password for the 'svcuser' account", "Encrypted (SHA-512) password for the local 'svcuser' account.", "string", 255, "Security", true, true, false, true, false, "$6$eBIu9aQ6$wBnn9Tn.CUuuoi/JZe.oAOmUDIVCqHpXeem7ZHO5R7dPg2hul8tNCBzwumKrFw8A0qm.LH8YvMJUaN2AL1JVc0", false); metadata.put("system_proxyuser_encpassword", proxyuser_metadata); metadata.put("system_sysmonitor_encpassword", sysmonitor_metadata); metadata.put("system_root_encpassword", root_metadata); metadata.put("system_svcuser_encpassword", svcuser_metadata); return metadata; } public PropertyMetadata setPropMetaData(String label, String description, String type, int maxLen, String tag, Boolean advanced, Boolean userMutable, Boolean userConfigurable, Boolean reconfigRequired, Boolean rebootRequired, String value, Boolean controlNodeOnly) { PropertyMetadata metaData = new PropertyMetadata(); metaData.setLabel(label); metaData.setDescription(description); metaData.setType(type); metaData.setMaxLen(maxLen); metaData.setTag(tag); metaData.setAdvanced(advanced); metaData.setUserMutable(userMutable); metaData.setUserConfigurable(userConfigurable); metaData.setReconfigRequired(reconfigRequired); metaData.setRebootRequired(rebootRequired); metaData.setValue(value); metaData.setControlNodeOnly(controlNodeOnly); return metaData; } @Before public void setUp() { // fill in the fake ovf repository _passwordProps.addProperty(String.format(SYSTEM_ENCPASSWORD_FORMAT, LOCAL_ROOT), ""); _passwordProps.addProperty(String.format(SYSTEM_ENCPASSWORD_FORMAT, LOCAL_SYSMON), ""); _passwordProps.addProperty(String.format(SYSTEM_ENCPASSWORD_FORMAT, LOCAL_PROXYUSER), ""); _passwordProps.addProperty(String.format(SYSTEM_ENCPASSWORD_FORMAT, LOCAL_SVCUSER), ""); _propertiesMetadata.setMetadata(getPropsMetaData()); provider = new DummyEncryptionProvider(); provider.start(); String encryptPassword = provider.getEncryptedString("changeMe"); propertiesMap = new HashMap(); propertiesMap.put("system_root_encpassword", encryptPassword); propertiesMap.put("system_proxyuser_encpassword", encryptPassword); propertiesMap.put(PASSWORD_MIN_LENGTH, "1"); propertiesMap.put(PASSWORD_LOWERCASE_NUMBER, "1"); propertiesMap.put(PASSWORD_UPPERCASE_NUMBER, "1"); propertiesMap.put(PASSWORD_NUMERIC_NUMBER, "0"); propertiesMap.put(PASSWORD_REPEATING_NUMBER, "1"); propertiesMap.put(PASSWORD_CHANGE_INTERVAL, "1"); propertiesMap.put(PASSWORD_CHANGED_NUMBER, "1"); propertiesMap.put(PASSWORD_REUSE_NUMBER, "1"); propertiesMap.put(PASSWORD_PREVENT_DICTIONARY, "yes"); } @Test public void testUpdatePassword() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("!changeMe3", null); passwordUpdate.setOldPassword("changeMe"); LocalPasswordHandler ph = getDummyLocalPasswordHandler(); ph.setLocalUsers(createLocalUsers()); ph.setDbClient(new DummyDbClient()); ph.setEncryptionProvider(provider); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); int statusCode = res.getStatus(); Assert.assertTrue("updatePassword failed with code " + statusCode + ": " + res.getEntity().toString(), statusCode == Status.OK.getStatusCode()); sc = new DummySecurityContext(LOCAL_PROXYUSER); passwordResource.setSecurityContext(sc); res = passwordResource.updatePassword(null, null, passwordUpdate, false); statusCode = res.getStatus(); Assert.assertTrue("updatePassword failed with code " + statusCode + ": " + res.getEntity().toString(), statusCode == Status.OK.getStatusCode()); } @Test(expected = BadRequestException.class) public void testUpdateSamePassword() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("ChangeMe", null); LocalPasswordHandler ph = new DummyLocalPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); // The following should fail with exception Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); } @Test(expected = ForbiddenException.class) public void testUpdatePasswordNoSecurityContext() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("!changeme", null); LocalPasswordHandler ph = getPasswordHandler(); passwordResource.setPasswordHandler(ph); Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); res.getStatus(); } @Test(expected = ForbiddenException.class) public void testUpdatePasswordNoPrincipal() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("!changeme", null); LocalPasswordHandler ph = getPasswordHandler(); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext("noprincipal"); passwordResource.setSecurityContext(sc); Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); res.getStatus(); } @Test(expected = BadRequestException.class) public void testUpdatePasswordEmptyParams() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("", ""); LocalPasswordHandler ph = getPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); } @Test(expected = BadRequestException.class) public void testUpdatePasswordTooManyParams() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordUpdateParam passwordUpdate = getDummyPasswordUpdate("clearTextPwd", "HashedPassword"); LocalPasswordHandler ph = getPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updatePassword(null, null, passwordUpdate, false); } @Test public void testUpdateUserPassword() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset(LOCAL_ROOT, "!changeMe3", ""); LocalPasswordHandler ph = getDummyLocalPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updateUserPassword(passwordUpdate, false); int statusCode = res.getStatus(); Assert.assertTrue("updatePassword failed with code " + statusCode + ": " + res.getEntity().toString(), statusCode == Status.OK.getStatusCode()); sc = new DummySecurityContext(LOCAL_PROXYUSER); passwordResource.setSecurityContext(sc); passwordUpdate.setUsername(LOCAL_PROXYUSER); res = passwordResource.updateUserPassword(passwordUpdate, false); statusCode = res.getStatus(); Assert.assertTrue("updatePassword failed with code " + statusCode + ": " + res.getEntity().toString(), statusCode == Status.OK.getStatusCode()); } @Test(expected = BadRequestException.class) public void testUpdateUserPasswordNonExistingUser() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset("user123", "!changeme", ""); LocalPasswordHandler ph = getDummyLocalPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext("root"); passwordResource.setSecurityContext(sc); Response res = passwordResource.updateUserPassword(passwordUpdate, false); } @Test(expected = ForbiddenException.class) public void testUpdateUserPasswordNoSecurtyContext() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset("user123", "!changeme", ""); Response res = passwordResource.updateUserPassword(passwordUpdate, false); Assert.assertTrue("Should throw exception, but returned " + res.getStatus(), false); } @Test(expected = BadRequestException.class) public void testUpdateUserPasswordEmptyParams() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset(LOCAL_ROOT, "", ""); LocalPasswordHandler ph = getPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updateUserPassword(passwordUpdate, false); } @Test(expected = BadRequestException.class) public void testUpdateUserPasswordTooManyParams() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset(LOCAL_ROOT, "clearTextPwd", "HashedPassword"); LocalPasswordHandler ph = getPasswordHandler(); ph.setLocalUsers(createLocalUsers()); passwordResource.setPasswordHandler(ph); SecurityContext sc = new DummySecurityContext(LOCAL_ROOT); passwordResource.setSecurityContext(sc); Response res = passwordResource.updateUserPassword(passwordUpdate, false); } @Test(expected = ForbiddenException.class) public void testUpdateUserPasswordNoPrincipal() { PasswordService passwordResource = new PasswordService(); passwordResource.setPropertiesMetadata(_propertiesMetadata); passwordResource.setAuditLogManager(new DummyAuditLogManager()); PasswordResetParam passwordUpdate = getDummyPasswordReset("user123", "!changeme", ""); SecurityContext sc = new DummySecurityContext("noprincipal"); passwordResource.setSecurityContext(sc); Response res = passwordResource.updateUserPassword(passwordUpdate, false); Assert.assertTrue("Should throw exception, but returned " + res.getStatus(), false); } private class DummyLocalPasswordHandler extends LocalPasswordHandler { @Override public void setUserPassword(String username, String clearTextPassword, boolean bReset) { } @Override public void setUserEncryptedPassword(String username, String clearTextPassword, boolean bReset) { } @Override public String getUserPassword(String userName) { // return a string with prefix '$6$' return "$6$eBIu9aQ6$wBnn9Tn.CUuuoi/JZe.oAOmUDIVCqHpXeem7ZHO5R7dPg2hul8tNCBzwumKrFw8A0qm.LH8YvMJUaN2AL1JVc0"; } } private class DummySecurityContext implements SecurityContext { private DummyPrincipal _principal; public DummySecurityContext(String username) { if (!username.equalsIgnoreCase("noprincipal")) { _principal = new DummyPrincipal(username); } } @Override public String getAuthenticationScheme() { return null; } @Override public Principal getUserPrincipal() { return _principal; } @Override public boolean isSecure() { return false; } @Override public boolean isUserInRole(String arg0) { return false; } } private class DummyPrincipal extends StorageOSUser { public DummyPrincipal(String name) { super(name, ""); super.setIsLocal(true); } } private Map<String, StorageOSUser> createLocalUsers() { Map<String, StorageOSUser> locals = new HashMap<String, StorageOSUser>(); locals.put(LOCAL_ROOT, new StorageOSUser( LOCAL_ROOT, "")); locals.put(LOCAL_PROXYUSER, new StorageOSUser( LOCAL_PROXYUSER, "")); return locals; } private class DummyConfigService extends ConfigService { @Override public Response setProperties(PropertyInfoUpdate setProperty) throws LocalRepositoryException, CoordinatorClientException, URISyntaxException { _passwordProps.addProperties(setProperty.getAllProperties()); return Response.ok().build(); } } private class DummyAuditLogManager extends AuditLogManager { public void setDbClient(DbClient dbClient) { } public void recordAuditLogs(RecordableAuditLog... auditlogs) { } public void recordAuditLog(URI tenantId, URI userId, String serviceType, OperationTypeEnum auditType, long timestamp, String operationalStatus, String operationStage, Object... descparams) { } } private CoordinatorClient getCoordinatorClient() throws Exception { DummyCoordinatorClient client = new DummyCoordinatorClient(); ZkConnection zkConn = new ZkConnection(); List<URI> uris = new ArrayList(); uris.add(new URI("coordinator://localhost:2181")); zkConn.setServer(uris); zkConn.setTimeoutMs(10000); zkConn.build(); client.setZkConnection(zkConn); return client; } private class DummyCoordinatorClient extends CoordinatorClientImpl { public DummyCoordinatorClient() { Properties props = generateProperties(); setDefaultProperties(props); Properties ovfProps = new Properties(); setOvfProperties(ovfProps); } public PropertyInfoExt getTargetInfo(final Class clazz) throws CoordinatorException { return _passwordProps; } @Override public PropertyInfo getPropertyInfo() { PropertyInfo info = new PropertyInfo(); info.setProperties(propertiesMap); return info; } } }