Use the simplified password-service instead of SHA256Calculator.
After all, SHA256Calculator is deprecated since it combines API
and IMPL even in the name!
This is also more configurable and secure.
Change-Id: I471e0fe1d11d6b65ab574c5286ce1a874a2231fb
Signed-off-by: Ryan Goulding <ryandgoulding@gmail.com>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>testutils</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-password-service-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-password-service-impl</artifactId>
+ </dependency>
</dependencies>
<build>
<include>org/h2/constraint/**</include>
</includes>
</filter>
+ <filter>
+ <artifact>org.opendaylight.aaa:aaa-password-service-api</artifact>
+ <includes>
+ <include>org/opendaylight/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>org.opendaylight.aaa:aaa-password-service-impl</artifact>
+ <includes>
+ <include>org/opendaylight/**</include>
+ </includes>
+ </filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
import java.util.stream.Collectors;
import org.opendaylight.aaa.api.IDMStoreException;
import org.opendaylight.aaa.api.IIDMStore;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.StoreBuilder;
import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.datastore.h2.H2Store;
import org.opendaylight.aaa.datastore.h2.IdmLightConfig;
import org.opendaylight.aaa.datastore.h2.IdmLightConfigBuilder;
import org.opendaylight.aaa.datastore.h2.IdmLightSimpleConnectionProvider;
+import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
/**
* AAA CLI interface.
private final IIDMStore identityStore;
private final StoreBuilder storeBuilder;
private static final String DOMAIN = IIDMStore.DEFAULT_DOMAIN;
+ private final PasswordHashService passwordService;
public StandaloneCommandLineInterface(File directoryWithDatabaseFile) throws IOException, IDMStoreException {
IdmLightConfigBuilder configBuider = new IdmLightConfigBuilder();
configBuider.dbDirectory(directoryWithDatabaseFile.getCanonicalPath()).dbUser("foo").dbPwd("bar");
IdmLightConfig config = configBuider.build();
- H2Store h2Store = new H2Store(new IdmLightSimpleConnectionProvider(config));
+ passwordService = new DefaultPasswordHashService();
+
+ H2Store h2Store = new H2Store(new IdmLightSimpleConnectionProvider(config), passwordService);
this.identityStore = h2Store;
this.storeBuilder = new StoreBuilder(h2Store);
return false;
} else {
User user = optUser.get();
- String realPwd = SHA256Calculator.getSHA256(password, user.getSalt());
- return user.getPassword().equals(realPwd);
+ return passwordService.passwordsMatch(password, user.getPassword(), user.getSalt());
}
}
package org.opendaylight.aaa.cli;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Objects;
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.opendaylight.aaa.api.IIDMStore;
import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
private static volatile String authUser = null;
protected IIDMStore identityStore;
+ private final PasswordHashService passwordService;
+
+ public AaaCliAbstractCommand(final PasswordHashService passwordService) {
+ Objects.requireNonNull(this.passwordService = passwordService);
+ }
public void setIdentityStore(IIDMStore identityStore) {
this.identityStore = identityStore;
if (currentUser == null) {
final String userName = CliUtils.readPassword(super.session, "Enter Username:");
final String passwd = CliUtils.readPassword(super.session, "Enter Password:");
- final User usr = DataStoreUtils.isAdminUser(identityStore, userName, passwd);
+ final User usr = DataStoreUtils.isAdminUser(identityStore, passwordService, userName, passwd);
if (usr != null) {
authUser = userName;
SessionsManager.getInstance().addUserSession(userName, usr);
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.model.Domain;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.slf4j.Logger;
"--domainDescription" }, description = "The domain Description", required = true, multiValued = false)
private String domainDesc = "";
- public AddDomain() {
- super();
+ public AddDomain(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.model.Grant;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--roleName" }, description = "The role name", required = false, multiValued = false)
private String roleName = "";
- public AddGrant() {
- super();
+ public AddGrant(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.model.Role;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--roleDescription" }, description = "The role Description", required = true, multiValued = false)
private String roleDesc = "";
- public AddRole() {
- super();
+ public AddRole(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.model.Grant;
import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
multiValued = false)
private String userEmail = "";
- public AddUser() {
- super();
+ public AddUser(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
import org.apache.karaf.shell.console.OsgiCommandSupport;
import org.opendaylight.aaa.api.ClaimCache;
import org.opendaylight.aaa.api.IIDMStore;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.utils.CliUtils;
@Command(name = "change-user-pwd", scope = "aaa", description = "Change the user password.")
"--userName" }, description = "The user name", required = true, multiValued = false)
private String userName;
- public ChangeUserPassword(IIDMStore identityStore, ClaimCache claimCache) {
+ private final PasswordHashService passwordService;
+
+ public ChangeUserPassword(final IIDMStore identityStore, final ClaimCache claimCache,
+ final PasswordHashService passwordService) {
+
this.identityStore = identityStore;
this.claimCache = claimCache;
+ this.passwordService = passwordService;
}
@Override
final String newPwd = CliUtils.readPassword(this.session, "Enter new password:");
final Users users = identityStore.getUsers();
for (User usr : users.getUsers()) {
- final String realPwd = SHA256Calculator.getSHA256(currentPwd, usr.getSalt());
- if (usr.getName().equals(userName) && usr.getPassword().equals(realPwd)) {
+ if (usr.getName().equals(userName)
+ && passwordService.passwordsMatch(currentPwd, usr.getPassword(), usr.getSalt())) {
claimCache.clear();
usr.setPassword(newPwd);
identityStore.updateUser(usr);
package org.opendaylight.aaa.cli.dmstore;
import org.apache.karaf.shell.commands.Command;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
*/
public class ListODLDomains extends AaaCliAbstractCommand {
- public ListODLDomains() {
- super();
+ public ListODLDomains(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
package org.opendaylight.aaa.cli.dmstore;
import org.apache.karaf.shell.commands.Command;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
*/
public class ListODLRoles extends AaaCliAbstractCommand {
- public ListODLRoles() {
- super();
+ public ListODLRoles(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
package org.opendaylight.aaa.cli.dmstore;
import org.apache.karaf.shell.commands.Command;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
*/
public class ListODLUsers extends AaaCliAbstractCommand {
- public ListODLUsers() {
- super();
+ public ListODLUsers(final PasswordHashService passwordService) {
+ super(passwordService);
}
@Override
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.ClaimCache;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--domainName" }, description = "The domain name", required = true, multiValued = false)
private String domainName;
- public RemoveDomain(ClaimCache claimCache) {
+ public RemoveDomain(final ClaimCache claimCache, final PasswordHashService passwordService) {
+ super(passwordService);
this.claimCache = claimCache;
}
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.ClaimCache;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--roleName" }, description = "The role name", required = false, multiValued = false)
private String roleName;
- public RemoveGrant(ClaimCache claimCache) {
+ public RemoveGrant(final ClaimCache claimCache, final PasswordHashService passwordService) {
+ super(passwordService);
this.claimCache = claimCache;
}
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.ClaimCache;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--roleName" }, description = "The role name", required = true, multiValued = false)
private String roleName;
- public RemoveRole(ClaimCache claimCache) {
+ public RemoveRole(final ClaimCache claimCache, final PasswordHashService passwordService) {
+ super(passwordService);
this.claimCache = claimCache;
}
import org.apache.karaf.shell.commands.Command;
import org.apache.karaf.shell.commands.Option;
import org.opendaylight.aaa.api.ClaimCache;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cli.AaaCliAbstractCommand;
import org.opendaylight.aaa.cli.utils.CliUtils;
import org.opendaylight.aaa.cli.utils.DataStoreUtils;
"--userName" }, description = "The user name", required = true, multiValued = false)
private String userName;
- public RemoveUser(ClaimCache claimCache) {
+ public RemoveUser(final ClaimCache claimCache, final PasswordHashService passwordService) {
+ super(passwordService);
this.claimCache = claimCache;
}
import java.util.List;
import org.opendaylight.aaa.api.IDMStoreException;
import org.opendaylight.aaa.api.IIDMStore;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.model.Domain;
import org.opendaylight.aaa.api.model.Domains;
import org.opendaylight.aaa.api.model.Grant;
import org.opendaylight.aaa.api.model.Role;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
return null;
}
- public static String getGrantId(IIDMStore identityStore, String domainName, String roleName, String userName)
+ public static String getGrantId(IIDMStore identityStore, String domainName, String roleName,
+ String userName)
throws IDMStoreException {
final String domainId = getDomainId(identityStore, domainName);
if (domainId == null) {
return null;
}
- public static User isAdminUser(IIDMStore identityStore, String userName, String password) throws IDMStoreException {
+ public static User isAdminUser(IIDMStore identityStore, PasswordHashService passwordService,
+ String userName, String password) throws IDMStoreException {
+
final Users users = identityStore.getUsers();
for (User usr : users.getUsers()) {
- final String realPwd = SHA256Calculator.getSHA256(password, usr.getSalt());
- if (usr.getName().equals(userName) && usr.getPassword().equals(realPwd)) {
+ if (usr.getName().equals(userName)
+ && passwordService.passwordsMatch(password, usr.getPassword(), usr.getSalt())) {
List<Grant> grants = identityStore.getGrants(usr.getUserid()).getGrants();
if (grants != null && !grants.isEmpty()) {
final String adminRoleId = getRoleId(identityStore, ADMIN_ROLE);
<reference id="iIDMStore" interface="org.opendaylight.aaa.api.IIDMStore" />
<reference id="claimCache" interface="org.opendaylight.aaa.api.ClaimCache" />
+ <reference id="passwordService" interface="org.opendaylight.aaa.api.password.service.PasswordHashService" />
<command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
<command>
<action class="org.opendaylight.aaa.cli.dmstore.ListODLUsers">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.ListODLDomains">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.ListODLRoles">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.ChangeUserPassword">
<argument ref="iIDMStore" />
<argument ref="claimCache" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.GetODLSelfSignCert">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.GenerateCertReq">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.GetTrustStoreCert">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.GetCipherSuites">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.GetTlsProtocols">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.ExportDefaultKeystores">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.cert.ImportDefaultKeystores">
<argument ref="KeyStoreConsoleProvider" />
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.AddRole">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.AddDomain">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.AddUser">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.AddGrant">
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.RemoveGrant">
<argument ref="claimCache" />
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.RemoveUser">
<argument ref="claimCache" />
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.RemoveRole">
<argument ref="claimCache" />
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
<command>
<action class="org.opendaylight.aaa.cli.dmstore.RemoveDomain">
<argument ref="claimCache" />
<property name="identityStore" ref="iIDMStore"/>
+ <argument ref="passwordService" />
</action>
</command>
</command-bundle>
class TestAaaCliAbstractCommand extends AaaCliAbstractCommand {
TestAaaCliAbstractCommand() {
- super();
+ super(null);
}
@Override
<build>
<plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <!--
+ DefaultPasswordHashService is intentionally exported here for internal use by aaa-cli-jar. In general,
+ it is bad SOA practice to export implementation bundles. This was done in order to fulfill a specific
+ non-OSGi requirement that uses this implementation directly. Outside consumers should consider
+ getting the Service through querying the OSGi registry for
+ org.opendaylight.aaa.api.password.service.PasswordHashService implementations instead.
+ -->
+ <Export-Package>
+ org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
</goals>
<configuration>
<artifacts>
- <!-- attach aaa-app-config.xml as an artifact -->
<artifact>
<file>${project.build.directory}/classes/initial/aaa-password-service-config.xml</file>
<type>xml</type>
private static final Logger LOG = LoggerFactory.getLogger(DefaultPasswordHashService.class);
public static final String DEFAULT_HASH_ALGORITHM = "SHA-512";
- public static final int DEFAULT_NUM_ITERATIONS = 10000;
+ public static final int DEFAULT_NUM_ITERATIONS = 20000;
private final DefaultHashService hashService = new DefaultHashService();
+ public DefaultPasswordHashService() {
+ hashService.setRandomNumberGenerator(new SecureRandomNumberGenerator());
+ hashService.setGeneratePublicSalt(true);
+ setNumIterations(Optional.of(DEFAULT_NUM_ITERATIONS));
+ setHashAlgorithm(Optional.of(DEFAULT_HASH_ALGORITHM));
+ }
+
public DefaultPasswordHashService(final PasswordServiceConfig passwordServiceConfig) {
+ this();
final Optional<Integer> numIterationsOptional = Optional.ofNullable(passwordServiceConfig.getIterations());
setNumIterations(numIterationsOptional);
final Optional<String> privateSaltOptional = Optional.ofNullable(passwordServiceConfig.getPrivateSalt());
setPrivateSalt(privateSaltOptional);
-
- hashService.setRandomNumberGenerator(new SecureRandomNumberGenerator());
- hashService.setGeneratePublicSalt(true);
}
private void setNumIterations(final Optional<Integer> numIterationsOptional) {
<bean id="passwordService" class="org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService">
<argument ref="passwordServiceConfig" />
</bean>
- <service ref="passwordService" interface="org.opendaylight.aaa.api.password.service.PasswordService"
+ <service ref="passwordService" interface="org.opendaylight.aaa.api.password.service.PasswordHashService"
odl:type="default"/>
</blueprint>
<groupId>org.opendaylight.aaa</groupId>
<artifactId>aaa-filterchain</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-password-service-api</artifactId>
+ </dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-password-service-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
import org.opendaylight.aaa.api.PasswordCredentials;
import org.opendaylight.aaa.api.StoreBuilder;
import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cert.api.ICertificateManager;
import org.opendaylight.aaa.datastore.h2.H2TokenStore;
import org.opendaylight.aaa.shiro.oauth2.OAuth2TokenServlet;
private final String oauth2EndpointPath;
private final TokenAuthenticators tokenAuthenticators;
private final AuthenticationService authenticationService;
+ private final PasswordHashService passwordHashService;
/**
* Constructor.
final String oauth2EndpointPath,
final DatastoreConfig datastoreConfig,
final IIDMStore iidmStore,
- final AuthenticationService authenticationService) {
+ final AuthenticationService authenticationService,
+ final PasswordHashService passwordHashService) {
this.dataBroker = dataBroker;
this.certificateManager = certificateManager;
this.shiroConfiguration = shiroConfiguration;
this.moonEndpointPath = moonEndpointPath;
this.oauth2EndpointPath = oauth2EndpointPath;
this.authenticationService = authenticationService;
+ this.passwordHashService = passwordHashService;
if (datastoreConfig == null || !datastoreConfig.getStore().equals(DatastoreConfig.Store.H2DataStore)) {
LOG.info("AAA Datastore has not been initialized");
public AuthenticationService getAuthenticationService() {
return authenticationService;
}
+
+ public PasswordHashService getPasswordHashService() {
+ return passwordHashService;
+ }
}
import org.opendaylight.aaa.api.model.Roles;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final RoleStore roleStore;
private final GrantStore grantStore;
- public H2Store(String dbUsername, String dbPassword) {
+ public H2Store(final String dbUsername, final String dbPassword, final PasswordHashService passwordService) {
this(new IdmLightSimpleConnectionProvider(
- new IdmLightConfigBuilder().dbUser(dbUsername).dbPwd(dbPassword).build()));
+ new IdmLightConfigBuilder().dbUser(dbUsername).dbPwd(dbPassword).build()), passwordService);
}
- public H2Store(ConnectionProvider connectionFactory) {
+ public H2Store(ConnectionProvider connectionFactory, final PasswordHashService passwordService) {
this.domainStore = new DomainStore(connectionFactory);
- this.userStore = new UserStore(connectionFactory);
+ this.userStore = new UserStore(connectionFactory, passwordService);
this.roleStore = new RoleStore(connectionFactory);
this.grantStore = new GrantStore(connectionFactory);
}
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Objects;
import org.apache.commons.text.StringEscapeUtils;
import org.opendaylight.aaa.api.IDMStoreUtil;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHash;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public static final String SQL_SALT = "salt";
private static final String TABLE_NAME = "USERS";
- public UserStore(ConnectionProvider dbConnectionFactory) {
+ private final PasswordHashService passwordService;
+
+ public UserStore(ConnectionProvider dbConnectionFactory, final PasswordHashService passwordService) {
super(dbConnectionFactory, TABLE_NAME);
+ this.passwordService = Objects.requireNonNull(passwordService);
}
@Override
return "CREATE TABLE users " + "(userid VARCHAR(128) PRIMARY KEY,"
+ "name VARCHAR(128) NOT NULL, " + "domainid VARCHAR(128) NOT NULL, "
+ "email VARCHAR(128) NOT NULL, " + "password VARCHAR(128) NOT NULL, "
- + "description VARCHAR(128) NOT NULL, " + "salt VARCHAR(15) NOT NULL, "
+ + "description VARCHAR(128) NOT NULL, " + "salt VARCHAR(128) NOT NULL, "
+ "enabled INTEGER NOT NULL)";
}
Preconditions.checkNotNull(user.getName());
Preconditions.checkNotNull(user.getDomainid());
- user.setSalt(SHA256Calculator.generateSALT());
+ final PasswordHash passwordHash = passwordService.getPasswordHash(user.getPassword());
+ user.setSalt(passwordHash.getSalt());
String query =
"insert into users"
+ " (userid,domainid,name,email,password,description,enabled,salt) values(?,?,?,?,?,?,?,?)";
statement.setString(2, user.getDomainid());
statement.setString(3, user.getName());
statement.setString(4, user.getEmail());
- statement.setString(5, SHA256Calculator.getSHA256(user.getPassword(), user.getSalt()));
+ statement.setString(5, passwordHash.getHashedPassword());
statement.setString(6, user.getDescription());
statement.setInt(7, user.isEnabled() ? 1 : 0);
statement.setString(8, user.getSalt());
if (salt == null) {
salt = savedUser.getSalt();
}
- savedUser.setPassword(SHA256Calculator.getSHA256(user.getPassword(), salt));
+ final PasswordHash passwordHash = passwordService.getPasswordHash(user.getPassword(), salt);
+ savedUser.setPassword(passwordHash.getHashedPassword());
}
String query = "UPDATE users SET email = ?, password = ?, description = ?, enabled = ? WHERE userid = ?";
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nonnull;
import org.opendaylight.aaa.api.AuthenticationException;
import org.opendaylight.aaa.api.IdMService;
import org.opendaylight.aaa.api.IdMServiceImpl;
import org.opendaylight.aaa.api.PasswordCredentials;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.model.Domain;
import org.opendaylight.aaa.api.model.Grant;
import org.opendaylight.aaa.api.model.Grants;
import org.opendaylight.aaa.api.model.Role;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.shiro.tokenauthrealm.auth.ClaimBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Map<String, Map<PasswordCredentials, Claim>> claimCache = new ConcurrentHashMap<>();
private final IIDMStore idmStore;
+ private final PasswordHashService passwordService;
- public IdmLightProxy(IIDMStore idmStore) {
+ public IdmLightProxy(IIDMStore idmStore, PasswordHashService passwordService) {
this.idmStore = idmStore;
+ this.passwordService = Objects.requireNonNull(passwordService);
}
@Override
+ " does not exist in domain " + credsDomain);
}
user = userList.get(0);
- if (!SHA256Calculator.getSHA256(creds.password(), user.getSalt()).equals(
- user.getPassword())) {
+ if (!passwordService.passwordsMatch(creds.password(), user.getPassword(), user.getSalt())) {
throw new AuthenticationException("UserName / Password not found");
}
if (!user.isEnabled()) {
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
-import org.opendaylight.aaa.api.SHA256Calculator;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.api.shiro.principal.ODLPrincipal;
import org.opendaylight.aaa.shiro.principal.ODLPrincipalImpl;
import org.opendaylight.aaa.shiro.realm.util.TokenUtils;
InstanceIdentifier.builder(Authentication.class).build();
private final DataBroker dataBroker;
+ private final PasswordHashService passwordHashService;
public MdsalRealm() {
this.dataBroker = Objects.requireNonNull(ThreadLocals.DATABROKER_TL.get());
+ this.passwordHashService = Objects.requireNonNull(ThreadLocals.PASSWORD_HASH_SERVICE_TL.get());
LOG.info("MdsalRealm created");
}
}
if (userEnabled && u.getUserid().equals(inputUserId)) {
final String inputPassword = TokenUtils.extractPassword(authenticationToken);
- final String hashedInputPassword = SHA256Calculator.getSHA256(inputPassword, u.getSalt());
- if (hashedInputPassword.equals(u.getPassword())) {
+ if (passwordHashService.passwordsMatch(inputPassword, u.getPassword(), u.getSalt())) {
final ODLPrincipal odlPrincipal = ODLPrincipalImpl
.createODLPrincipal(inputUsername, domainId, inputUserId);
return new SimpleAuthenticationInfo(odlPrincipal, inputPassword, getName());
import org.apache.shiro.web.env.IniWebEnvironment;
import org.opendaylight.aaa.api.AuthenticationService;
import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cert.api.ICertificateManager;
import org.opendaylight.aaa.shiro.tokenauthrealm.auth.TokenAuthenticators;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
private final AuthenticationService authenticationService;
private final TokenAuthenticators tokenAuthenticators;
private final TokenStore tokenStore;
+ private final PasswordHashService passwordHashService;
AAAIniWebEnvironment(ShiroConfiguration shiroConfiguration, DataBroker dataBroker,
- ICertificateManager certificateManager, AuthenticationService authenticationService,
- TokenAuthenticators tokenAuthenticators, TokenStore tokenStore) {
+ ICertificateManager certificateManager, AuthenticationService authenticationService,
+ TokenAuthenticators tokenAuthenticators, TokenStore tokenStore,
+ PasswordHashService passwordHashService) {
this.shiroConfiguration = shiroConfiguration;
this.dataBroker = dataBroker;
this.certificateManager = certificateManager;
this.authenticationService = authenticationService;
this.tokenAuthenticators = tokenAuthenticators;
this.tokenStore = tokenStore;
+ this.passwordHashService = passwordHashService;
LOG.debug("AAAIniWebEnvironment created");
}
ThreadLocals.AUTH_SETVICE_TL.set(authenticationService);
ThreadLocals.TOKEN_AUTHENICATORS_TL.set(tokenAuthenticators);
ThreadLocals.TOKEN_STORE_TL.set(tokenStore);
+ ThreadLocals.PASSWORD_HASH_SERVICE_TL.set(passwordHashService);
try {
// Initialize the Shiro environment from clustered-app-config
final Ini ini = createIniFromClusteredAppConfig(shiroConfiguration);
ThreadLocals.AUTH_SETVICE_TL.remove();
ThreadLocals.TOKEN_AUTHENICATORS_TL.remove();
ThreadLocals.TOKEN_STORE_TL.remove();
+ ThreadLocals.PASSWORD_HASH_SERVICE_TL.remove();
}
}
}
ThreadLocals.AUTH_SETVICE_TL.set(provider.getAuthenticationService());
ThreadLocals.TOKEN_AUTHENICATORS_TL.set(provider.getTokenAuthenticators());
ThreadLocals.TOKEN_STORE_TL.set(provider.getTokenStore());
+ ThreadLocals.PASSWORD_HASH_SERVICE_TL.set(provider.getPasswordHashService());
// Initialize the Shiro environment from clustered-app-config
final Ini ini = AAAIniWebEnvironment.createIniFromClusteredAppConfig(provider.getShiroConfiguration());
ThreadLocals.AUTH_SETVICE_TL.remove();
ThreadLocals.TOKEN_AUTHENICATORS_TL.remove();
ThreadLocals.TOKEN_STORE_TL.remove();
+ ThreadLocals.PASSWORD_HASH_SERVICE_TL.remove();
}
}
}
import org.apache.shiro.web.env.WebEnvironment;
import org.opendaylight.aaa.api.AuthenticationService;
import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cert.api.ICertificateManager;
import org.opendaylight.aaa.shiro.tokenauthrealm.auth.TokenAuthenticators;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
private final AuthenticationService authenticationService;
private final TokenAuthenticators tokenAuthenticators;
private final TokenStore tokenStore;
+ private final PasswordHashService passwordHashService;
public ShiroWebEnvironmentLoaderListener(ShiroConfiguration shiroConfiguration, DataBroker dataBroker,
- ICertificateManager certificateManager, AuthenticationService authenticationService,
- TokenAuthenticators tokenAuthenticators, TokenStore tokenStore) {
+ ICertificateManager certificateManager,
+ AuthenticationService authenticationService,
+ TokenAuthenticators tokenAuthenticators, TokenStore tokenStore,
+ PasswordHashService passwordHashService) {
this.shiroConfiguration = shiroConfiguration;
this.dataBroker = dataBroker;
this.certificateManager = certificateManager;
this.authenticationService = authenticationService;
this.tokenAuthenticators = tokenAuthenticators;
this.tokenStore = tokenStore;
+ this.passwordHashService = passwordHashService;
LOG.debug("ShiroWebEnvironmentLoaderListenerImpl created");
}
@Override
protected WebEnvironment createEnvironment(ServletContext sc) {
MutableWebEnvironment environment = new AAAIniWebEnvironment(shiroConfiguration, dataBroker,
- certificateManager, authenticationService, tokenAuthenticators, tokenStore);
+ certificateManager, authenticationService, tokenAuthenticators, tokenStore, passwordHashService);
// in newer Shiro version, there is a determineWebEnvironment() which should be
// used instead of createEnvironment() but for 1.3.x we just copy/paste from parent and do:
import org.opendaylight.aaa.api.AuthenticationService;
import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
import org.opendaylight.aaa.cert.api.ICertificateManager;
import org.opendaylight.aaa.shiro.tokenauthrealm.auth.TokenAuthenticators;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
ThreadLocal<TokenStore> TOKEN_STORE_TL = new ThreadLocal<>();
ThreadLocal<TokenAuthenticators> TOKEN_AUTHENICATORS_TL = new ThreadLocal<>();
+
+ ThreadLocal<PasswordHashService> PASSWORD_HASH_SERVICE_TL = new ThreadLocal<>();
}
default-config-file-name="aaa-datastore-config.xml"
binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.aaa.app.config.rev170619.DatastoreConfig" />
+ <reference id="passwordService" interface="org.opendaylight.aaa.api.password.service.PasswordHashService"
+ odl:type="default"/>
+
<bean id="idmStore" class="org.opendaylight.aaa.datastore.h2.H2Store">
<argument value="${dbUsername}" />
<argument value="${dbPassword}" />
+ <argument ref="passwordService" />
</bean>
<bean id="idmLightProxy" class="org.opendaylight.aaa.shiro.idm.IdmLightProxy">
<argument ref="idmStore" />
+ <argument ref="passwordService" />
</bean>
<bean id="authService" class="org.opendaylight.aaa.shiro.tokenauthrealm.auth.AuthenticationManager"/>
<argument ref="datastoreConfig" />
<argument ref="idmStore" />
<argument ref="authService" />
+ <argument ref="passwordService" />
</bean>
<bean id="authenticator" class="org.opendaylight.aaa.authenticator.ODLAuthenticator" />
<argument>
<bean factory-ref="provider" factory-method="getTokenStore"/>
</argument>
+ <argument ref="passwordService" />
</bean>
<reference id="customFilterAdapterConfig"
import org.opendaylight.aaa.api.model.Grant;
import org.opendaylight.aaa.api.model.Role;
import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
+import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
public class H2StoreTest {
}
private H2Store h2Store;
+ private PasswordHashService passwordService = new DefaultPasswordHashService();
@Before
public void before() throws StoreException, SQLException {
IdmLightSimpleConnectionProvider dbConnectionFactory = new IdmLightSimpleConnectionProvider(
new IdmLightConfigBuilder().dbUser("foo").dbPwd("bar").build());
- UserStore us = new UserStore(dbConnectionFactory);
+ UserStore us = new UserStore(dbConnectionFactory, passwordService);
us.dbClean();
DomainStore ds = new DomainStore(dbConnectionFactory);
ds.dbClean();
GrantStore gs = new GrantStore(dbConnectionFactory);
gs.dbClean();
- h2Store = new H2Store("foo", "bar");
+ h2Store = new H2Store("foo", "bar", passwordService);
}
@Test
@Test
public void testCreateUser() throws StoreException {
- User user = h2Store.createUser("test", "pass", "domain", "desc", "email", true, "SALT");
+ User user = h2Store.createUser("test", "pass", "domain", "desc",
+ "email",true, "SALT");
Assert.assertEquals(true, user != null);
}
public void testCreateGrant() throws StoreException {
Domain domain = h2Store.createDomain("sdn", true);
Role role = h2Store.createRole("temp", "temp domain", "Temp Testing role");
- User user = h2Store.createUser("test", "pass", "domain", "desc", "email", true, "SALT");
+ User user = h2Store.createUser("test", "pass", "domain", "desc",
+ "email", true, "SALT");
Grant grant = h2Store.createGrant(domain.getDomainid(), user.getUserid(), role.getRoleid());
Assert.assertEquals(true, grant != null);
}
@Test
public void testUpdatingUserEmail() throws StoreException {
UserStore us = new UserStore(
- new IdmLightSimpleConnectionProvider(new IdmLightConfigBuilder().dbUser("foo").dbPwd("bar").build()));
+ new IdmLightSimpleConnectionProvider(
+ new IdmLightConfigBuilder().dbUser("foo").dbPwd("bar").build()), passwordService);
Domain domain = h2Store.createDomain("sdn", true);
- User user = h2Store.createUser("test", "pass", domain.getDomainid(), "desc", "email", true, "SALT");
+ User user = h2Store.createUser("test", "pass", domain.getDomainid(), "desc",
+ "email", true, "SALT");
user.setName("test");
user = us.putUser(user);
import org.junit.Test;
import org.mockito.Mockito;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
public class UserStoreTest {
private final ConnectionProvider connectionFactoryMock = () -> connectionMock;
- private final UserStore userStoreUnderTest = new UserStore(connectionFactoryMock);
+ private final UserStore userStoreUnderTest = new UserStore(connectionFactoryMock,
+ new DefaultPasswordHashService());
@Test
public void getUsersTest() throws SQLException, Exception {
import org.opendaylight.aaa.api.IDMStoreException;
import org.opendaylight.aaa.api.IIDMStore;
import org.opendaylight.aaa.api.PasswordCredentials;
-import org.opendaylight.aaa.api.SHA256Calculator;
import org.opendaylight.aaa.api.model.Domain;
import org.opendaylight.aaa.api.model.Grant;
import org.opendaylight.aaa.api.model.Grants;
import org.opendaylight.aaa.api.model.Role;
import org.opendaylight.aaa.api.model.User;
import org.opendaylight.aaa.api.model.Users;
+import org.opendaylight.aaa.api.password.service.PasswordHashService;
+import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
import org.opendaylight.aaa.shiro.idm.IdmLightProxy;
/*
public class PasswordHashTest {
private IIDMStore store;
+ private PasswordHashService passwordService = new DefaultPasswordHashService();
@Before
public void before() throws IDMStoreException {
user.setUserid(creds.username());
user.setDomainid("sdn");
user.setSalt("ABCD");
- user.setPassword(SHA256Calculator.getSHA256(creds.password(), user.getSalt()));
+ user.setPassword(passwordService.getPasswordHash(creds.password(), user.getSalt()).getHashedPassword());
List<User> lu = new LinkedList<>();
lu.add(user);
Users users = new Users();
@Test
public void testPasswordHash() {
- IdmLightProxy proxy = new IdmLightProxy(store);
+ IdmLightProxy proxy = new IdmLightProxy(store, passwordService);
proxy.authenticate(new Creds());
}
import org.junit.Before;
import org.opendaylight.aaa.api.IIDMStore;
import org.opendaylight.aaa.api.StoreBuilder;
+import org.opendaylight.aaa.impl.password.service.DefaultPasswordHashService;
import org.opendaylight.aaa.provider.GsonProvider;
import org.opendaylight.aaa.shiro.idm.IdmLightApplication;
import org.opendaylight.aaa.shiro.idm.IdmLightProxy;
@Override
protected Application configure() {
testStore = new IDMTestStore();
- return ResourceConfig.forApplication(new IdmLightApplication(testStore, new IdmLightProxy(testStore)));
+ return ResourceConfig.forApplication(new IdmLightApplication(testStore,
+ new IdmLightProxy(testStore, new DefaultPasswordHashService())));
}
@Override
</dependencyManagement>
<dependencies>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>odl-aaa-password-service</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+
<!-- OSGI -->
<dependency>
<groupId>org.apache.felix</groupId>
<dependency>
<groupId>org.opendaylight.aaa</groupId>
<artifactId>odl-aaa-web</artifactId>
- <version>0.8.0-SNAPSHOT</version>
+ <version>${project.version}</version>
<classifier>features</classifier>
<type>xml</type>
</dependency>
<configfile finalname="etc/opendaylight/datastore/initial/config/aaa-datastore-config.xml">
mvn:org.opendaylight.aaa/aaa-shiro/${project.version}/xml/aaa-datastore-config
</configfile>
+ <configfile finalname="etc/opendaylight/datastore/initial/config/aaa-password-service-config.xml">
+ mvn:org.opendaylight.aaa/aaa-password-service-impl/${project.version}/xml/aaa-password-service-config
+ </configfile>
<configfile finalname="bin/idmtool">
mvn:org.opendaylight.aaa/aaa-shiro/${project.version}/py/idmtool
</configfile>