Skip to content
Permalink
Browse files
Adapting identity manager to changes in anubis in preparation for app…
…lication specific keys and key rotation.
  • Loading branch information
mifosio-04-04-2018 committed Apr 11, 2017
1 parent 1327b71 commit e43288413414a2b7d2be9be0fa168fa42db67a9c
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 197 deletions.
@@ -15,7 +15,6 @@
*/
package io.mifos.identity.api.v1.client;

import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.core.api.annotation.ThrowsException;
import io.mifos.core.api.util.CustomFeignClientsConfiguration;
import io.mifos.identity.api.v1.domain.*;
@@ -122,12 +121,7 @@ public interface IdentityManager {

@RequestMapping(value = "/initialize", method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
produces = {MediaType.APPLICATION_JSON_VALUE})
@ThrowsException(status = HttpStatus.CONFLICT, exception = TenantAlreadyInitializedException.class)
Signature initialize(@RequestParam("password") String password);

@RequestMapping(value = "/signature", method = RequestMethod.GET,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
Signature getSignature();
String initialize(@RequestParam("password") String password);
}
@@ -14,6 +14,7 @@
* limitations under the License.
*/

import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.domain.*;
import io.mifos.anubis.test.v1.SystemSecurityEnvironment;
import io.mifos.core.api.context.AutoUserContext;
@@ -26,10 +27,7 @@
import org.junit.Test;

import java.security.PublicKey;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.*;

import static io.mifos.identity.api.v1.EventConstants.*;

@@ -169,7 +167,10 @@ private PermittableEndpoint buildPermittableEndpoint(final String group) {
public PublicKey getPublicKey() {
try (final AutoUserContext ignored = tenantApplicationSecurityEnvironment.createAutoSeshatContext())
{
final Signature sig = getTestSubject().getSignature();
final Anubis anubis = tenantApplicationSecurityEnvironment.getAnubis();
final List<String> signatureKeyTimestamps = anubis.getAllSignatureSets();
Assert.assertTrue(!signatureKeyTimestamps.isEmpty());
final Signature sig = anubis.getApplicationSignature(signatureKeyTimestamps.get(0));

return new RsaPublicKeyBuilder()
.setPublicKeyMod(sig.getPublicKeyMod())
@@ -95,7 +95,8 @@ public void testBoundaryInitializeCases() throws InterruptedException {
final IdentityManager testSubject = getTestSubject();


Signature firstTenantSignature = null;
final String firstTenantSignatureTimestamp;
Signature firstTenantIdentityManagerSignature = null;
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {

final String invalidSeshatToken = "notBearer";
@@ -110,7 +111,6 @@ public void testBoundaryInitializeCases() throws InterruptedException {

final String wrongSystemToken = systemTokenFromWrongKey();
try (final AutoSeshat ignored2 = new AutoSeshat(wrongSystemToken)){

testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
Assert.fail("The key was signed by the wrong source. This should've failed.");
}
@@ -130,10 +130,11 @@ public void testBoundaryInitializeCases() throws InterruptedException {
}

try (final AutoUserContext ignored2 = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
firstTenantSignature = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
firstTenantSignatureTimestamp = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));

final Signature sameSignature = testSubject.getSignature();
Assert.assertEquals(sameSignature, firstTenantSignature);
final Signature applicationSignature = tenantApplicationSecurityEnvironment.getAnubis().getApplicationSignature(firstTenantSignatureTimestamp);
firstTenantIdentityManagerSignature = tenantApplicationSecurityEnvironment.getAnubis().getSignatureSet(firstTenantSignatureTimestamp).getIdentityManagerSignature();
Assert.assertEquals(applicationSignature, firstTenantIdentityManagerSignature);


testSubject.initialize("golden_osiris");
@@ -147,12 +148,13 @@ public void testBoundaryInitializeCases() throws InterruptedException {
}


final Signature secondTenantSignature;
final String secondTenantSignatureTimestamp;
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
try (final AutoUserContext ignored2
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
secondTenantSignature = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
Assert.assertNotEquals(firstTenantSignature, secondTenantSignature);
secondTenantSignatureTimestamp = testSubject.initialize(Helpers.encodePassword(ADMIN_PASSWORD));
final Signature secondTenantIdentityManagerSignature = tenantApplicationSecurityEnvironment.getAnubis().getApplicationSignature(secondTenantSignatureTimestamp);
Assert.assertNotEquals(firstTenantIdentityManagerSignature, secondTenantIdentityManagerSignature);
}
}
catch (final Exception e)
@@ -178,6 +180,7 @@ private String systemTokenFromWrongKey()
final SystemAccessTokenSerializer.Specification tokenSpecification
= new SystemAccessTokenSerializer.Specification();

tokenSpecification.setKeyTimestamp("rando");
tokenSpecification.setPrivateKey(getWrongPrivateKey());

tokenSpecification.setRole(RoleConstants.SYSTEM_ADMIN_ROLE_IDENTIFIER);
@@ -62,6 +62,7 @@ public class AuthenticationCommandHandler {
private final Users users;
private final Roles roles;
private final PermittableGroups permittableGroups;
private final Signatures signatures;
private final Tenants tenants;
private final HashGenerator hashGenerator;
private final TenantAccessTokenSerializer tenantAccessTokenSerializer;
@@ -83,7 +84,9 @@ public class AuthenticationCommandHandler {
public AuthenticationCommandHandler(final Users users,
final Roles roles,
final PermittableGroups permittableGroups,
final Tenants tenants, final HashGenerator hashGenerator,
final Signatures signatures,
final Tenants tenants,
final HashGenerator hashGenerator,
@SuppressWarnings("SpringJavaAutowiringInspection")
final TenantAccessTokenSerializer tenantAccessTokenSerializer,
final TenantRefreshTokenSerializer tenantRefreshTokenSerializer,
@@ -94,6 +97,7 @@ public AuthenticationCommandHandler(final Users users,
this.users = users;
this.roles = roles;
this.permittableGroups = permittableGroups;
this.signatures = signatures;
this.tenants = tenants;
this.hashGenerator = hashGenerator;
this.tenantAccessTokenSerializer = tenantAccessTokenSerializer;
@@ -109,13 +113,10 @@ public AuthenticationCommandResponse process(final PasswordAuthenticationCommand
throws AmitAuthenticationException
{

final Optional<PrivateTenantInfoEntity> privateTenantInfo = tenants.getPrivateTenantInfo();
if (!privateTenantInfo.isPresent()) {
logger.error("Authentication attempted on uninitialized tenant {}.", TenantContextHolder.identifier());
throw ServiceException.internalError("Tenant is not initialized.");
}
final PrivateTenantInfoEntity privateTenantInfo = checkedGetPrivateTenantInfo();
final PrivateSignatureEntity privateSignature = checkedGetPrivateSignature();

byte[] fixedSalt = privateTenantInfo.get().getFixedSalt().array();
byte[] fixedSalt = privateTenantInfo.getFixedSalt().array();
final UserEntity user = getUser(command.getUseridentifier());

if (!this.hashGenerator.isEqual(
@@ -133,10 +134,10 @@ public AuthenticationCommandResponse process(final PasswordAuthenticationCommand

final TokenSerializationResult accessToken = getAccessToken(
user.getIdentifier(),
getTokenPermissions(user, passwordExpiration, privateTenantInfo.get().getTimeToChangePasswordAfterExpirationInDays()),
privateTenantInfo.get());
getTokenPermissions(user, passwordExpiration, privateTenantInfo.getTimeToChangePasswordAfterExpirationInDays()),
privateSignature);

final TokenSerializationResult refreshToken = getRefreshToken(user, privateTenantInfo.get());
final TokenSerializationResult refreshToken = getRefreshToken(user, privateSignature);

fireAuthenticationEvent(user.getIdentifier());

@@ -146,25 +147,42 @@ public AuthenticationCommandResponse process(final PasswordAuthenticationCommand
DateConverter.toIsoString(passwordExpiration));
}

private PrivateSignatureEntity checkedGetPrivateSignature() {
final Optional<PrivateSignatureEntity> privateSignature = signatures.getPrivateSignature();
if (!privateSignature.isPresent()) {
logger.error("Authentication attempted on tenant with no valid signature{}.", TenantContextHolder.identifier());
throw ServiceException.internalError("Tenant has no valid signature.");
}
return privateSignature.get();
}

private PrivateTenantInfoEntity checkedGetPrivateTenantInfo() {
final Optional<PrivateTenantInfoEntity> privateTenantInfo = tenants.getPrivateTenantInfo();
if (!privateTenantInfo.isPresent()) {
logger.error("Authentication attempted on uninitialized tenant {}.", TenantContextHolder.identifier());
throw ServiceException.internalError("Tenant is not initialized.");
}
return privateTenantInfo.get();
}

@CommandHandler
public AuthenticationCommandResponse process(final RefreshTokenAuthenticationCommand command)
throws AmitAuthenticationException
{
final TenantRefreshTokenSerializer.Deserialized deserializedRefreshToken =
tenantRefreshTokenSerializer.deserialize(command.getRefreshToken());

final Optional<PrivateTenantInfoEntity> privateTenantInfo = tenants.getPrivateTenantInfo();
if (!privateTenantInfo.isPresent())
throw ServiceException.internalError("Tenant is not initialized.");
final PrivateTenantInfoEntity privateTenantInfo = checkedGetPrivateTenantInfo();
final PrivateSignatureEntity privateSignature = checkedGetPrivateSignature();

final UserEntity user = getUser(deserializedRefreshToken.getUserIdentifier());

final LocalDate passwordExpiration = getExpiration(user);

final TokenSerializationResult accessToken = getAccessToken(
user.getIdentifier(),
getTokenPermissions(user, passwordExpiration, privateTenantInfo.get().getTimeToChangePasswordAfterExpirationInDays()),
privateTenantInfo.get());
getTokenPermissions(user, passwordExpiration, privateTenantInfo.getTimeToChangePasswordAfterExpirationInDays()),
privateSignature);

return new AuthenticationCommandResponse(
accessToken.getToken(), DateConverter.toIsoString(accessToken.getExpiration()),
@@ -208,15 +226,16 @@ private void fireAuthenticationEvent(final String userIdentifier) {
private TokenSerializationResult getAccessToken(
final String identifier,
final Set<TokenPermission> tokenPermissions,
final PrivateTenantInfoEntity privateTenantInfo) {
final PrivateSignatureEntity privateSignatureEntity) {

final PrivateKey privateKey = new RsaPrivateKeyBuilder()
.setPrivateKeyExp(privateTenantInfo.getPrivateKeyExp())
.setPrivateKeyMod(privateTenantInfo.getPrivateKeyMod())
.setPrivateKeyExp(privateSignatureEntity.getPrivateKeyExp())
.setPrivateKeyMod(privateSignatureEntity.getPrivateKeyMod())
.build();

final TenantAccessTokenSerializer.Specification x =
new TenantAccessTokenSerializer.Specification()
.setKeyTimestamp(privateSignatureEntity.getKeyTimestamp())
.setPrivateKey(privateKey)
.setTokenContent(new TokenContent(new ArrayList<>(tokenPermissions)))
.setSecondsToLive(accessTtl)
@@ -286,16 +305,16 @@ private TokenPermission getTokenPermission(final PermittableType permittable) {
Collections.singleton(RoleMapper.mapAllowedOperation(AllowedOperationType.fromHttpMethod(permittable.getMethod()))));
}

private TokenSerializationResult getRefreshToken(
final UserEntity user, final PrivateTenantInfoEntity privateTenantInfo) {

private TokenSerializationResult getRefreshToken(final UserEntity user,
final PrivateSignatureEntity privateSignatureEntity) {
final PrivateKey privateKey = new RsaPrivateKeyBuilder()
.setPrivateKeyExp(privateTenantInfo.getPrivateKeyExp())
.setPrivateKeyMod(privateTenantInfo.getPrivateKeyMod())
.setPrivateKeyExp(privateSignatureEntity.getPrivateKeyExp())
.setPrivateKeyMod(privateSignatureEntity.getPrivateKeyMod())
.build();

final TenantRefreshTokenSerializer.Specification x =
new TenantRefreshTokenSerializer.Specification()
.setKeyTimestamp(privateSignatureEntity.getKeyTimestamp())
.setPrivateKey(privateKey)
.setSecondsToLive(refreshTtl)
.setUser(user.getIdentifier());
@@ -16,7 +16,6 @@
package io.mifos.identity.internal.command.handler;

import com.datastax.driver.core.exceptions.InvalidQueryException;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.core.lang.ServiceException;
import io.mifos.core.lang.security.RsaKeyPairFactory;
import io.mifos.identity.api.v1.PermittableGroupIds;
@@ -40,6 +39,7 @@
*/
@Component
public class Provisioner {
private final Signatures signature;
private final Tenants tenant;
private final Users users;
private final PermittableGroups permittableGroups;
@@ -59,6 +59,7 @@ public class Provisioner {

@Autowired
Provisioner(
final Signatures signature,
final Tenants tenant,
final Users users,
final Roles roles,
@@ -67,6 +68,7 @@ public class Provisioner {
@Qualifier(IdentityConstants.LOGGER_NAME) final Logger logger,
final SaltGenerator saltGenerator)
{
this.signature = signature;
this.tenant = tenant;
this.users = users;
this.permittableGroups = permittableGroups;
@@ -76,18 +78,20 @@ public class Provisioner {
this.saltGenerator = saltGenerator;
}

public Signature provisionTenant(final String initialPasswordHash) {
public String provisionTenant(final String initialPasswordHash) {
final RsaKeyPairFactory.KeyPairHolder keys = RsaKeyPairFactory.createKeyPair();

byte[] fixedSalt = this.saltGenerator.createRandomSalt();

try {
signature.buildTable();
tenant.buildTable();
users.buildTable();
permittableGroups.buildTable();
roles.buildTable();

tenant.add(fixedSalt, keys, passwordExpiresInDays, timeToChangePasswordAfterExpirationInDays);
signature.add(keys);
tenant.add(fixedSalt, passwordExpiresInDays, timeToChangePasswordAfterExpirationInDays);

createPermittablesGroup(PermittableGroupIds.ROLE_MANAGEMENT, "/roles/*", "/permittablegroups/*");
createPermittablesGroup(PermittableGroupIds.IDENTITY_MANAGEMENT, "/users/*");
@@ -114,7 +118,7 @@ public Signature provisionTenant(final String initialPasswordHash) {
throw ServiceException.internalError("Failed to provision tenant.");
}

return new Signature(keys.getPublicKeyMod(), keys.getPublicKeyExp());
return keys.getTimestamp();
}

private PermissionType fullAccess(final String permittableGroupIdentifier) {

0 comments on commit e432884

Please sign in to comment.