Merge branch 'develop' of https://github.com/mifosio/anubis into develop
diff --git a/api/src/main/java/io/mifos/anubis/api/v1/TokenConstants.java b/api/src/main/java/io/mifos/anubis/api/v1/TokenConstants.java
index dfd45ac..e2b2eba 100644
--- a/api/src/main/java/io/mifos/anubis/api/v1/TokenConstants.java
+++ b/api/src/main/java/io/mifos/anubis/api/v1/TokenConstants.java
@@ -24,6 +24,7 @@
String PREFIX = "Bearer ";
String JWT_SIGNATURE_TIMESTAMP_CLAIM = "/mifos.io/signatureTimestamp";
+ String JWT_ENDPOINT_SET_CLAIM = "/mifos.io/endpointSet";
String JWT_CONTENT_CLAIM = "/mifos.io/tokenContent";
String REFRESH_TOKEN_COOKIE_NAME = "org.apache.fineract.refreshToken";
diff --git a/component-test/src/main/java/TestAnubisInitialize.java b/component-test/src/main/java/TestAnubisInitialize.java
index 9d6343d..69ad24f 100644
--- a/component-test/src/main/java/TestAnubisInitialize.java
+++ b/component-test/src/main/java/TestAnubisInitialize.java
@@ -16,6 +16,7 @@
import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.client.AnubisApiFactory;
+import io.mifos.anubis.api.v1.domain.AllowedOperation;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.example.simple.Example;
import io.mifos.anubis.example.simple.ExampleConfiguration;
@@ -158,6 +159,20 @@
}
}
+ @Test(expected = InvalidTokenException.class)
+ public void testAuthenticateWithoutInitialize() {
+ try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
+
+ final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
+ = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);
+ final String permissionToken = tenantApplicationSecurityEnvironment.getPermissionToken("bubba", "foo", AllowedOperation.READ);
+ try (final AutoUserContext ignored2 = new AutoUserContext("bubba", permissionToken)) {
+ Assert.assertFalse(example.foo());
+ Assert.fail("Not found exception should be thrown when authentication is attempted ");
+ }
+ }
+ }
+
private void initialize() {
final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
= new TenantApplicationSecurityEnvironmentTestRule(testEnvironment);
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java
index a88dffa..e9b4e0c 100644
--- a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/ExampleRestController.java
@@ -53,7 +53,7 @@
final ApplicationSignatureSet applicationSignatureSet = new ApplicationSignatureSet(identityManagerKeyPair.getTimestamp(), applicationSignature, identityManagerSignature);
- this.specialTenantSignatureRepository.addSignatureSet(applicationSignatureSet);
+ this.specialTenantSignatureRepository.addSignatureSet(applicationSignatureSet, applicationKeyPair);
initialized = true;
return new ResponseEntity<>(HttpStatus.OK);
}
diff --git a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java
index 679d4e3..ba6794a 100644
--- a/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java
+++ b/component-test/src/main/java/io/mifos/anubis/example/nokeystorage/SpecialTenantSignatureRepository.java
@@ -18,6 +18,7 @@
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.config.TenantSignatureRepository;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@@ -31,15 +32,38 @@
*/
@Component
public class SpecialTenantSignatureRepository implements TenantSignatureRepository {
- private final Map<String, ApplicationSignatureSet> applicationSignatureSetMap = new HashMap<>();
+ private static class AllTheKeyInfos {
+ final ApplicationSignatureSet applicationSignatureSet;
+ final RsaKeyPairFactory.KeyPairHolder applicationKeyPair;
- void addSignatureSet(final ApplicationSignatureSet applicationSignatureSet) {
- applicationSignatureSetMap.put(applicationSignatureSet.getTimestamp(), applicationSignatureSet);
+ private AllTheKeyInfos(
+ final ApplicationSignatureSet applicationSignatureSet,
+ final RsaKeyPairFactory.KeyPairHolder applicationKeyPair) {
+ this.applicationSignatureSet = applicationSignatureSet;
+ this.applicationKeyPair = applicationKeyPair;
+ }
+
+ ApplicationSignatureSet getApplicationSignatureSet() {
+ return applicationSignatureSet;
+ }
+
+ RsaKeyPairFactory.KeyPairHolder getApplicationKeyPair() {
+ return applicationKeyPair;
+ }
+ }
+ private final Map<String, AllTheKeyInfos> applicationSignatureSetMap = new HashMap<>();
+
+ void addSignatureSet(final ApplicationSignatureSet applicationSignatureSet,
+ final RsaKeyPairFactory.KeyPairHolder applicationKeyPair) {
+ applicationSignatureSetMap.put(applicationSignatureSet.getTimestamp(),
+ new AllTheKeyInfos(applicationSignatureSet, applicationKeyPair));
}
@Override
public Optional<Signature> getIdentityManagerSignature(final String timestamp) throws IllegalArgumentException {
- final Optional<ApplicationSignatureSet> sigset = Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ final Optional<ApplicationSignatureSet> sigset =
+ Optional.ofNullable(applicationSignatureSetMap.get(timestamp))
+ .map(AllTheKeyInfos::getApplicationSignatureSet);
return sigset.map(ApplicationSignatureSet::getIdentityManagerSignature);
}
@@ -50,7 +74,8 @@
@Override
public Optional<ApplicationSignatureSet> getSignatureSet(final String timestamp) {
- return Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ return Optional.ofNullable(applicationSignatureSetMap.get(timestamp))
+ .map(AllTheKeyInfos::getApplicationSignatureSet);
}
@Override
@@ -60,24 +85,34 @@
@Override
public Optional<Signature> getApplicationSignature(final String timestamp) {
- final Optional<ApplicationSignatureSet> sigset = Optional.ofNullable(applicationSignatureSetMap.get(timestamp));
+ final Optional<ApplicationSignatureSet> sigset
+ = Optional.ofNullable(applicationSignatureSetMap.get(timestamp))
+ .map(AllTheKeyInfos::getApplicationSignatureSet);
return sigset.map(ApplicationSignatureSet::getApplicationSignature);
}
@Override
public Optional<ApplicationSignatureSet> getLatestSignatureSet() {
- Optional<String> timestamp = getMostRecentTimestamp();
+ final Optional<String> timestamp = getMostRecentTimestamp();
return timestamp.flatMap(this::getSignatureSet);
}
@Override
public Optional<Signature> getLatestApplicationSignature() {
- Optional<String> timestamp = getMostRecentTimestamp();
+ final Optional<String> timestamp = getMostRecentTimestamp();
return timestamp.flatMap(this::getApplicationSignature);
}
+ @Override
+ public Optional<RsaKeyPairFactory.KeyPairHolder> getLatestApplicationSigningKeyPair() {
+ final Optional<String> timestamp = getMostRecentTimestamp();
+ return timestamp
+ .flatMap(x -> Optional.ofNullable(applicationSignatureSetMap.get(x)))
+ .map(AllTheKeyInfos::getApplicationKeyPair);
+ }
+
private Optional<String> getMostRecentTimestamp() {
return getAllSignatureSetKeyTimestamps().stream()
.max(String::compareTo);
}
-}
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/io/mifos/anubis/example/simple/Example.java b/component-test/src/main/java/io/mifos/anubis/example/simple/Example.java
index 928a41f..4a3f29c 100644
--- a/component-test/src/main/java/io/mifos/anubis/example/simple/Example.java
+++ b/component-test/src/main/java/io/mifos/anubis/example/simple/Example.java
@@ -30,4 +30,7 @@
@RequestMapping(value = "initialize", method = RequestMethod.DELETE)
void uninitialize();
+
+ @RequestMapping(value = "foo", method = RequestMethod.GET)
+ boolean foo();
}
diff --git a/component-test/src/main/java/io/mifos/anubis/example/simple/ExampleRestController.java b/component-test/src/main/java/io/mifos/anubis/example/simple/ExampleRestController.java
index acc4229..d988f27 100644
--- a/component-test/src/main/java/io/mifos/anubis/example/simple/ExampleRestController.java
+++ b/component-test/src/main/java/io/mifos/anubis/example/simple/ExampleRestController.java
@@ -54,4 +54,10 @@
initialized = false;
return new ResponseEntity<>(HttpStatus.OK);
}
+
+ @RequestMapping(value = "/foo", method = RequestMethod.GET)
+ @Permittable(AcceptedTokenType.TENANT)
+ public ResponseEntity<Boolean> foo() {
+ return ResponseEntity.ok(false);
+ }
}
diff --git a/library/src/main/java/io/mifos/anubis/config/AnubisSecurityConfigurerAdapter.java b/library/src/main/java/io/mifos/anubis/config/AnubisSecurityConfigurerAdapter.java
index 923f66a..6ca7835 100644
--- a/library/src/main/java/io/mifos/anubis/config/AnubisSecurityConfigurerAdapter.java
+++ b/library/src/main/java/io/mifos/anubis/config/AnubisSecurityConfigurerAdapter.java
@@ -21,6 +21,7 @@
import io.mifos.anubis.security.IsisAuthenticatedAuthenticationProvider;
import io.mifos.anubis.security.UrlPermissionChecker;
import org.apache.http.HttpStatus;
+import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
@@ -42,6 +43,8 @@
import java.util.ArrayList;
import java.util.List;
+import static io.mifos.anubis.config.AnubisConstants.LOGGER_NAME;
+
/**
* @author Myrle Krantz
*/
@@ -49,6 +52,11 @@
@Configuration
@EnableWebSecurity
public class AnubisSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
+ final private Logger logger;
+
+ public AnubisSecurityConfigurerAdapter(final @Qualifier(LOGGER_NAME) Logger logger) {
+ this.logger = logger;
+ }
@PostConstruct
public void configureSecurityContext()
@@ -83,7 +91,7 @@
private AccessDecisionManager defaultAccessDecisionManager() {
final List<AccessDecisionVoter<?>> voters = new ArrayList<>();
- voters.add(new UrlPermissionChecker());
+ voters.add(new UrlPermissionChecker(logger));
return new UnanimousBased(voters);
}
diff --git a/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java b/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
index 5ab3d40..07d4b4e 100644
--- a/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
+++ b/library/src/main/java/io/mifos/anubis/config/TenantSignatureRepository.java
@@ -18,7 +18,9 @@
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
+import java.security.interfaces.RSAPrivateKey;
import java.util.List;
import java.util.Optional;
@@ -42,4 +44,6 @@
Optional<Signature> getApplicationSignature(String timestamp);
Optional<Signature> getLatestApplicationSignature();
+
+ Optional<RsaKeyPairFactory.KeyPairHolder> getLatestApplicationSigningKeyPair();
}
diff --git a/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java b/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
index d07aed7..5d64422 100644
--- a/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
+++ b/library/src/main/java/io/mifos/anubis/repository/TenantAuthorizationDataRepository.java
@@ -16,6 +16,7 @@
package io.mifos.anubis.repository;
import com.datastax.driver.core.*;
+import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
@@ -27,6 +28,8 @@
import io.mifos.core.cassandra.core.CassandraSessionProvider;
import io.mifos.core.lang.ApplicationName;
import io.mifos.core.lang.security.RsaKeyPairFactory;
+import io.mifos.core.lang.security.RsaPrivateKeyBuilder;
+import io.mifos.core.lang.security.RsaPublicKeyBuilder;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -35,6 +38,10 @@
import javax.annotation.Nonnull;
import java.math.BigInteger;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -94,6 +101,9 @@
Assert.notNull(identityManagerSignature);
//TODO: add validation to make sure this timestamp is more recent than any already stored.
+ logger.info("Creating application signature set for timestamp '" + timestamp +
+ "'. Identity manager signature is: " + identityManagerSignature);
+
final RsaKeyPairFactory.KeyPairHolder applicationSignature = RsaKeyPairFactory.createKeyPair();
final Session session = cassandraSessionProvider.getTenantSession();
@@ -115,6 +125,7 @@
Assert.notNull(timestamp);
//Don't actually delete, just invalidate, so that if someone starts coming at me with an older keyset, I'll
//know what's happening.
+ logger.info("Invalidationg signature set for timestamp '" + timestamp + "'.");
final Session session = cassandraSessionProvider.getTenantSession();
invalidateEntry(session, timestamp);
}
@@ -250,13 +261,18 @@
final Session tenantSession = cassandraSessionProvider.getTenantSession();
final Select.Where query = timestampToSignatureQueryMap.computeIfAbsent(timestamp, timestampKey ->
QueryBuilder.select().from(tableName).where(QueryBuilder.eq(TIMESTAMP_COLUMN, timestampKey)));
- final Row row = tenantSession.execute(query).one();
- final Optional<Row> ret = Optional.ofNullable(row);
- ret.map(TenantAuthorizationDataRepository::mapRowToValid).ifPresent(valid -> {
- if (!valid)
- logger.warn("Invalidated keyset for timestamp '" + timestamp + "' requested. Pretending no keyset exists.");
- });
- return ret.filter(TenantAuthorizationDataRepository::mapRowToValid);
+ try {
+ final Row row = tenantSession.execute(query).one();
+ final Optional<Row> ret = Optional.ofNullable(row);
+ ret.map(TenantAuthorizationDataRepository::mapRowToValid).ifPresent(valid -> {
+ if (!valid)
+ logger.warn("Invalidated keyset for timestamp '" + timestamp + "' requested. Pretending no keyset exists.");
+ });
+ return ret.filter(TenantAuthorizationDataRepository::mapRowToValid);
+ }
+ catch (final InvalidQueryException authorizationDataTableProbablyIsntConfiguredYet) {
+ throw new IllegalArgumentException("Tenant not found.");
+ }
}
private static Boolean mapRowToValid(final @Nonnull Row row) {
@@ -283,6 +299,24 @@
return getSignature(row, APPLICATION_PUBLIC_KEY_MOD_COLUMN, APPLICATION_PUBLIC_KEY_EXP_COLUMN);
}
+ private static RsaKeyPairFactory.KeyPairHolder mapRowToKeyPairHolder(final @Nonnull Row row) {
+ final BigInteger publicKeyModulus = row.get(APPLICATION_PUBLIC_KEY_MOD_COLUMN, BigInteger.class);
+ final BigInteger publicKeyExponent = row.get(APPLICATION_PUBLIC_KEY_EXP_COLUMN, BigInteger.class);
+ final BigInteger privateKeyModulus = row.get(APPLICATION_PRIVATE_KEY_MOD_COLUMN, BigInteger.class);
+ final BigInteger privateKeyExponent = row.get(APPLICATION_PRIVATE_KEY_EXP_COLUMN, BigInteger.class);
+
+ final PublicKey publicKey = new RsaPublicKeyBuilder()
+ .setPublicKeyMod(publicKeyModulus)
+ .setPublicKeyExp(publicKeyExponent)
+ .build();
+ final PrivateKey privateKey = new RsaPrivateKeyBuilder()
+ .setPrivateKeyMod(privateKeyModulus)
+ .setPrivateKeyExp(privateKeyExponent)
+ .build();
+ final String timestamp = row.get(TIMESTAMP_COLUMN, String.class);
+ return new RsaKeyPairFactory.KeyPairHolder(timestamp, (RSAPublicKey)publicKey, (RSAPrivateKey)privateKey);
+ }
+
private static ApplicationSignatureSet mapRowToSignatureSet(final @Nonnull Row row) {
final String timestamp = row.get(TIMESTAMP_COLUMN, String.class);
final Signature identityManagerSignature = mapRowToIdentityManagerSignature(row);
@@ -316,6 +350,12 @@
return timestamp.flatMap(this::getApplicationSignature);
}
+ @Override
+ public Optional<RsaKeyPairFactory.KeyPairHolder> getLatestApplicationSigningKeyPair() {
+ Optional<String> timestamp = getMostRecentTimestamp();
+ return timestamp.flatMap(this::getRow).map(TenantAuthorizationDataRepository::mapRowToKeyPairHolder);
+ }
+
private Optional<String> getMostRecentTimestamp() {
return getAllSignatureSetKeyTimestamps().stream()
.max(String::compareTo);
diff --git a/library/src/main/java/io/mifos/anubis/security/ApplicationPermission.java b/library/src/main/java/io/mifos/anubis/security/ApplicationPermission.java
index 2396214..a34e94e 100644
--- a/library/src/main/java/io/mifos/anubis/security/ApplicationPermission.java
+++ b/library/src/main/java/io/mifos/anubis/security/ApplicationPermission.java
@@ -25,8 +25,10 @@
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.BiPredicate;
-import java.util.stream.IntStream;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* @author Myrle Krantz
@@ -71,21 +73,24 @@
final boolean opMatches = allowedOperation.containsHttpMethod(method);
final String[] requestPathSegments = servletPath.split("/");
- if (servletPathSegmentMatchers.size() == requestPathSegments.length + 1)
- return lastSegmentIsStarSegment(servletPathSegmentMatchers);
-
- if (servletPathSegmentMatchers.size() > requestPathSegments.length)
+ if (servletPathSegmentMatchers.size() > requestPathSegments.length + 1)
return false;
+ if (servletPathSegmentMatchers.size() == requestPathSegments.length + 1)
+ if (!lastSegmentIsStarSegment(servletPathSegmentMatchers))
+ return false;
+
if (servletPathSegmentMatchers.size() < requestPathSegments.length)
- return lastSegmentIsStarSegment(servletPathSegmentMatchers);
+ if (!lastSegmentIsStarSegment(servletPathSegmentMatchers))
+ return false;
- final boolean aNonMappableSegmentExistsInServletPath =
- IntStream.range(0, servletPathSegmentMatchers.size())
- .filter(i -> !segmentMatcher.test(servletPathSegmentMatchers.get(i), requestPathSegments[i]))
- .findFirst().isPresent();
+ final Optional<Integer> indexOfFirstNonMappableSegment =
+ Stream.iterate(0, n -> n + 1)
+ .limit(Math.min(servletPathSegmentMatchers.size(), requestPathSegments.length))
+ .filter(i -> !segmentMatcher.test(servletPathSegmentMatchers.get(i), requestPathSegments[i]))
+ .findFirst();
- return opMatches && !aNonMappableSegmentExistsInServletPath;
+ return opMatches && !indexOfFirstNonMappableSegment.isPresent();
}
private static boolean lastSegmentIsStarSegment(
@@ -106,4 +111,12 @@
@Override public int hashCode() {
return Objects.hash(servletPathSegmentMatchers, allowedOperation);
}
+
+ @Override
+ public String toString() {
+ return "ApplicationPermission{" +
+ "servletPathSegmentMatchers='" + servletPathSegmentMatchers.stream().map(PermissionSegmentMatcher::getPermissionSegment).collect(Collectors.joining("/")) +
+ "', allowedOperation=" + allowedOperation +
+ '}';
+ }
}
diff --git a/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java b/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java
index 533868c..6c9270a 100644
--- a/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java
+++ b/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java
@@ -18,27 +18,36 @@
import io.mifos.anubis.annotation.AcceptedTokenType;
import io.mifos.anubis.api.v1.RoleConstants;
import io.mifos.anubis.service.PermittableService;
+import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.Set;
+import static io.mifos.anubis.config.AnubisConstants.LOGGER_NAME;
+
/**
* @author Myrle Krantz
*/
@Component
public class GuestAuthenticator {
private Set<ApplicationPermission> permissions;
+ private final Logger logger;
@Autowired
- public GuestAuthenticator(final PermittableService permittableService) {
+ public GuestAuthenticator(final PermittableService permittableService,
+ final @Qualifier(LOGGER_NAME) Logger logger) {
this.permissions = permittableService.getPermittableEndpointsAsPermissions(AcceptedTokenType.GUEST);
+ this.logger = logger;
}
AnubisAuthentication authenticate(final String user) {
if (!user.equals(RoleConstants.GUEST_USER_IDENTIFIER))
throw AmitAuthenticationException.invalidHeader();
+ logger.info("Guest access \"authenticated\" successfully.", user);
+
return new AnubisAuthentication(null, RoleConstants.GUEST_USER_IDENTIFIER, permissions);
}
}
diff --git a/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java b/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java
index 49fd679..3031dfd 100644
--- a/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java
+++ b/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java
@@ -62,28 +62,31 @@
public AnubisAuthentication authenticate(
final String user,
final String token,
- final String timestamp) {
+ final String keyTimestamp) {
if (!user.equals(ApiConstants.SYSTEM_SU))
throw AmitAuthenticationException.invalidHeader();
try {
final JwtParser jwtParser = Jwts.parser()
- .setSigningKey(systemRsaKeyProvider.getPublicKey(timestamp))
+ .setSigningKey(systemRsaKeyProvider.getPublicKey(keyTimestamp))
.requireAudience(applicationName.toString())
.requireIssuer(TokenType.SYSTEM.getIssuer())
- .require(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, timestamp);
+ .require(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, keyTimestamp);
TenantContextHolder.identifier().ifPresent(jwtParser::requireSubject);
jwtParser.parse(token);
+ logger.info("System token for user {}, with key timestamp {} authenticated successfully.", user, keyTimestamp);
return new AnubisAuthentication(token, user, permissions);
}
catch (final JwtException e) {
logger.debug("token = {}", token);
+ logger.info("System token for user {}, with key timestamp {} failed to authenticate. Exception was {}", user, keyTimestamp, e);
throw AmitAuthenticationException.invalidToken();
} catch (final InvalidKeyTimestampException e) {
- throw AmitAuthenticationException.invalidTokenKeyTimestamp("system", timestamp);
+ logger.info("System token for user {}, with key timestamp {} failed to authenticate. Exception was {}", user, keyTimestamp, e);
+ throw AmitAuthenticationException.invalidTokenKeyTimestamp("system", keyTimestamp);
}
}
}
diff --git a/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java b/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java
index d604ba3..9cf42a0 100644
--- a/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java
+++ b/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java
@@ -26,6 +26,7 @@
import io.mifos.anubis.service.PermittableService;
import io.mifos.anubis.token.TokenType;
import io.mifos.core.lang.ApplicationName;
+import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@@ -36,6 +37,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static io.mifos.anubis.config.AnubisConstants.LOGGER_NAME;
+
/**
* @author Myrle Krantz
*/
@@ -45,18 +48,21 @@
private final String applicationNameWithVersion;
private final Gson gson;
private final Set<ApplicationPermission> guestPermissions;
+ private final Logger logger;
@Autowired
public TenantAuthenticator(
final TenantRsaKeyProvider tenantRsaKeyProvider,
final ApplicationName applicationName,
final PermittableService permittableService,
- final @Qualifier("anubisGson") Gson gson) {
+ final @Qualifier("anubisGson") Gson gson,
+ final @Qualifier(LOGGER_NAME) Logger logger) {
this.tenantRsaKeyProvider = tenantRsaKeyProvider;
this.applicationNameWithVersion = applicationName.toString();
this.gson = gson;
this.guestPermissions
= permittableService.getPermittableEndpointsAsPermissions(AcceptedTokenType.GUEST);
+ this.logger = logger;
}
AnubisAuthentication authenticate(
@@ -79,13 +85,17 @@
final Set<ApplicationPermission> permissions = translatePermissions(tokenContent.getTokenPermissions());
permissions.addAll(guestPermissions);
+ logger.info("Tenant token for user {}, with key timestamp {} authenticated successfully.", user, keyTimestamp);
+
return new AnubisAuthentication(token,
jwt.getBody().getSubject(), permissions
);
}
catch (final JwtException e) {
+ logger.info("Tenant token for user {}, with key timestamp {} failed to authenticate. Exception was {}", user, keyTimestamp, e);
throw AmitAuthenticationException.invalidToken();
} catch (final InvalidKeyTimestampException e) {
+ logger.info("Tenant token for user {}, with key timestamp {} failed to authenticate. Exception was {}", user, keyTimestamp, e);
throw AmitAuthenticationException.invalidTokenKeyTimestamp("tenant", keyTimestamp);
}
}
diff --git a/library/src/main/java/io/mifos/anubis/security/UrlPermissionChecker.java b/library/src/main/java/io/mifos/anubis/security/UrlPermissionChecker.java
index 957279e..22c185d 100644
--- a/library/src/main/java/io/mifos/anubis/security/UrlPermissionChecker.java
+++ b/library/src/main/java/io/mifos/anubis/security/UrlPermissionChecker.java
@@ -15,6 +15,7 @@
*/
package io.mifos.anubis.security;
+import org.slf4j.Logger;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
@@ -22,11 +23,18 @@
import org.springframework.security.web.FilterInvocation;
import java.util.Collection;
+import java.util.Optional;
/**
* @author Myrle Krantz
*/
public class UrlPermissionChecker implements AccessDecisionVoter<FilterInvocation> {
+ private final Logger logger;
+
+ public UrlPermissionChecker(final Logger logger) {
+ this.logger = logger;
+ }
+
@Override public boolean supports(final ConfigAttribute attribute) {
return attribute.getAttribute().equals(ApplicationPermission.URL_AUTHORITY);
}
@@ -48,10 +56,14 @@
final AnubisAuthentication authentication = (AnubisAuthentication) unAuthentication;
final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
- return authorities.stream()
- .map(x -> (ApplicationPermission)x)
- .filter(x -> x.matches(filterInvocation, authentication.getPrincipal()))
- .findAny()
- .map(x -> ACCESS_GRANTED).orElse(ACCESS_DENIED);
+ final Optional<ApplicationPermission> matchedPermission = authorities.stream()
+ .map(x -> (ApplicationPermission) x)
+ .filter(x -> x.matches(filterInvocation, authentication.getPrincipal()))
+ .findAny();
+
+ matchedPermission.ifPresent(x -> logger.debug("Authorizing access to {} based on permission: {}"
+ , filterInvocation.getRequestUrl(), x));
+
+ return matchedPermission.map(x -> ACCESS_GRANTED).orElse(ACCESS_DENIED);
}
}
diff --git a/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java b/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java
index 80eea7a..a997558 100644
--- a/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java
+++ b/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java
@@ -42,7 +42,7 @@
return permissionSegment.startsWith("{") && permissionSegment.endsWith("}");
}
- String getPermissionSegment() { return permissionSegment; }
+ public String getPermissionSegment() { return permissionSegment; }
public boolean matches(final String requestSegment, final String principal, boolean isSu) {
if (isStarSegment())
diff --git a/library/src/main/java/io/mifos/anubis/token/TenantApplicationRsaKeyProvider.java b/library/src/main/java/io/mifos/anubis/token/TenantApplicationRsaKeyProvider.java
new file mode 100644
index 0000000..9c4e905
--- /dev/null
+++ b/library/src/main/java/io/mifos/anubis/token/TenantApplicationRsaKeyProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.mifos.anubis.token;
+
+import io.mifos.anubis.provider.InvalidKeyTimestampException;
+
+import java.security.PublicKey;
+
+/**
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("WeakerAccess")
+public interface TenantApplicationRsaKeyProvider {
+ PublicKey getApplicationPublicKey(String issuingApplication, String timestamp) throws InvalidKeyTimestampException;
+}
diff --git a/library/src/main/java/io/mifos/anubis/token/TenantRefreshTokenSerializer.java b/library/src/main/java/io/mifos/anubis/token/TenantRefreshTokenSerializer.java
index 97a597e..600bdeb 100644
--- a/library/src/main/java/io/mifos/anubis/token/TenantRefreshTokenSerializer.java
+++ b/library/src/main/java/io/mifos/anubis/token/TenantRefreshTokenSerializer.java
@@ -18,9 +18,7 @@
import io.jsonwebtoken.*;
import io.mifos.anubis.api.v1.TokenConstants;
import io.mifos.anubis.provider.InvalidKeyTimestampException;
-import io.mifos.anubis.provider.TenantRsaKeyProvider;
import io.mifos.anubis.security.AmitAuthenticationException;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
@@ -36,15 +34,6 @@
@SuppressWarnings("WeakerAccess")
@Component
public class TenantRefreshTokenSerializer {
-
- final private TenantRsaKeyProvider tenantRsaKeyProvider;
-
- @SuppressWarnings("SpringJavaAutowiringInspection")
- @Autowired
- TenantRefreshTokenSerializer(final TenantRsaKeyProvider tenantRsaKeyProvider) {
- this.tenantRsaKeyProvider = tenantRsaKeyProvider;
- }
-
@SuppressWarnings("WeakerAccess")
public static class Specification {
private String keyTimestamp;
@@ -52,6 +41,7 @@
private String user;
private long secondsToLive;
private String sourceApplication;
+ private String endpointSet = null; //Optional
public Specification setKeyTimestamp(final String keyTimestamp) {
this.keyTimestamp = keyTimestamp;
@@ -77,6 +67,11 @@
this.sourceApplication = sourceApplication;
return this;
}
+
+ public Specification setEndpointSet(String endpointSet) {
+ this.endpointSet = endpointSet;
+ return this;
+ }
}
public TokenSerializationResult build(final Specification specification)
@@ -92,6 +87,11 @@
if (specification.sourceApplication == null) {
throw new IllegalArgumentException("token source application must not be null.");
}
+ if (specification.secondsToLive <= 0) {
+ throw new IllegalArgumentException("token secondsToLive must be positive.");
+ }
+
+ final Date expiration = new Date(issued + TimeUnit.SECONDS.toMillis(specification.secondsToLive));
final JwtBuilder jwtBuilder =
Jwts.builder()
@@ -99,18 +99,15 @@
.setSubject(specification.user)
.claim(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, specification.keyTimestamp)
.setIssuedAt(new Date(issued))
- .signWith(SignatureAlgorithm.RS512, specification.privateKey);
- if (specification.secondsToLive <= 0) {
- throw new IllegalArgumentException("token secondsToLive must be positive.");
- }
-
- final Date expiration = new Date(issued + TimeUnit.SECONDS.toMillis(specification.secondsToLive));
- jwtBuilder.setExpiration(expiration);
+ .signWith(SignatureAlgorithm.RS512, specification.privateKey)
+ .setExpiration(expiration);
+ if (specification.endpointSet != null)
+ jwtBuilder.claim(TokenConstants.JWT_ENDPOINT_SET_CLAIM, specification.endpointSet);
return new TokenSerializationResult(TokenConstants.PREFIX + jwtBuilder.compact(), expiration);
}
- public TokenDeserializationResult deserialize(final String refreshToken)
+ public TokenDeserializationResult deserialize(final TenantApplicationRsaKeyProvider tenantRsaKeyProvider, final String refreshToken)
{
final Optional<String> tokenString = getJwtTokenString(refreshToken);
@@ -121,9 +118,10 @@
final JwtParser parser = Jwts.parser().setSigningKeyResolver(new SigningKeyResolver() {
@Override public Key resolveSigningKey(final JwsHeader header, final Claims claims) {
final String keyTimestamp = getKeyTimestampFromClaims(claims);
+ final String issuingApplication = getIssuingApplicationFromClaims(claims);
try {
- return tenantRsaKeyProvider.getPublicKey(keyTimestamp);
+ return tenantRsaKeyProvider.getApplicationPublicKey(issuingApplication, keyTimestamp);
}
catch (final IllegalArgumentException e)
{
@@ -142,7 +140,11 @@
@SuppressWarnings("unchecked") Jwt<Header, Claims> jwt = parser.parse(token);
- return new TokenDeserializationResult(jwt.getBody().getSubject(), jwt.getBody().getExpiration(), jwt.getBody().getIssuer());
+ return new TokenDeserializationResult(
+ jwt.getBody().getSubject(),
+ jwt.getBody().getExpiration(),
+ jwt.getBody().getIssuer(),
+ jwt.getBody().get(TokenConstants.JWT_ENDPOINT_SET_CLAIM, String.class));
}
catch (final JwtException e) {
throw AmitAuthenticationException.invalidToken();
@@ -165,4 +167,9 @@
String getKeyTimestampFromClaims(final Claims claims) {
return claims.get(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, String.class);
}
+
+ private @Nonnull
+ String getIssuingApplicationFromClaims(final Claims claims) {
+ return claims.getIssuer();
+ }
}
diff --git a/library/src/main/java/io/mifos/anubis/token/TokenDeserializationResult.java b/library/src/main/java/io/mifos/anubis/token/TokenDeserializationResult.java
index 012d155..df659c8 100644
--- a/library/src/main/java/io/mifos/anubis/token/TokenDeserializationResult.java
+++ b/library/src/main/java/io/mifos/anubis/token/TokenDeserializationResult.java
@@ -25,11 +25,17 @@
final private String userIdentifier;
final private Date expiration;
final private String sourceApplication;
+ final private String endpointSet;
- TokenDeserializationResult(final String userIdentifier, final Date expiration, final String sourceApplication) {
+ TokenDeserializationResult(
+ final String userIdentifier,
+ final Date expiration,
+ final String sourceApplication,
+ final String endpointSet) {
this.userIdentifier = userIdentifier;
this.expiration = expiration;
this.sourceApplication = sourceApplication;
+ this.endpointSet = endpointSet;
}
public String getUserIdentifier() {
@@ -43,4 +49,8 @@
public String getSourceApplication() {
return sourceApplication;
}
+
+ public String getEndpointSet() {
+ return endpointSet;
+ }
}
diff --git a/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java b/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java
index 0872592..f7fa1e1 100644
--- a/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java
+++ b/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java
@@ -149,8 +149,14 @@
.permittedPath("/{parameter}/").requestedPath("/value")
.expectedResult(true));
ret.add(new TestCase("{parameter} without su")
- .permittedPath("/{parameter}/").requestedPath("/value")
- .expectedResult(false));
+ .permittedPath("/{parameter}/").requestedPath("/value")
+ .expectedResult(false));
+ ret.add(new TestCase("* at end with request containing more segments")
+ .permittedPath("/roles/*").requestedPath("/users/antony/password")
+ .expectedResult(false));
+ ret.add(new TestCase("* at end with request containing same # segments")
+ .permittedPath("/x/y/z/*").requestedPath("/m/n/o/")
+ .expectedResult(false));
return ret;
}
diff --git a/library/src/test/java/io/mifos/anubis/token/TenantRefreshTokenSerializerTest.java b/library/src/test/java/io/mifos/anubis/token/TenantRefreshTokenSerializerTest.java
index 7713ff2..8cfd586 100644
--- a/library/src/test/java/io/mifos/anubis/token/TenantRefreshTokenSerializerTest.java
+++ b/library/src/test/java/io/mifos/anubis/token/TenantRefreshTokenSerializerTest.java
@@ -21,7 +21,6 @@
import io.jsonwebtoken.Jwts;
import io.mifos.anubis.api.v1.TokenConstants;
import io.mifos.anubis.provider.InvalidKeyTimestampException;
-import io.mifos.anubis.provider.TenantRsaKeyProvider;
import io.mifos.anubis.security.AmitAuthenticationException;
import io.mifos.anubis.token.TenantRefreshTokenSerializer.Specification;
import io.mifos.core.lang.security.RsaKeyPairFactory;
@@ -33,6 +32,7 @@
import java.time.Duration;
import java.time.LocalDateTime;
+import java.time.ZoneOffset;
/**
* @author Myrle Krantz
@@ -42,6 +42,7 @@
private static final String APPLICATION_NAME = "mifosio-core";
private static final int SECONDS_TO_LIVE = 15;
private static final String USER = "who";
+ private static final String ENDPOINT_SET = "what";
private static RsaKeyPairFactory.KeyPairHolder keyPairHolder;
@BeforeClass
@@ -53,7 +54,7 @@
@Test
public void shouldCreateValidRefreshToken() throws Exception {
final Specification specification = getValidSpecification();
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
final TimeStampChecker timeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(SECONDS_TO_LIVE));
@@ -79,74 +80,117 @@
Assert.assertTrue(expires > issued);
final String signatureTimestamp = parsedToken.getBody().get(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, String.class);
Assert.assertEquals(keyPairHolder.getTimestamp(), signatureTimestamp);
+ final String endpointSetClaim = parsedToken.getBody().get(TokenConstants.JWT_ENDPOINT_SET_CLAIM, String.class);
+ Assert.assertEquals(null, endpointSetClaim);
- final TokenDeserializationResult tokenDeserializationResult = testSubject.deserialize(tokenSerializationResult.getToken());
+ final TokenDeserializationResult tokenDeserializationResult = testSubject.deserialize(getTenantApplicationRsaKeyProvider(), tokenSerializationResult.getToken());
Assert.assertNotNull(tokenDeserializationResult);
Assert.assertEquals(APPLICATION_NAME, tokenDeserializationResult.getSourceApplication());
Assert.assertEquals(USER, tokenDeserializationResult.getUserIdentifier());
- Assert.assertEquals(tokenDeserializationResult.getExpiration(), tokenDeserializationResult.getExpiration());
+ timeStampChecker.assertCorrect(LocalDateTime.ofInstant(tokenDeserializationResult.getExpiration().toInstant(), ZoneOffset.UTC));
+ Assert.assertEquals(null, tokenDeserializationResult.getEndpointSet());
+ }
+
+ @Test
+ public void shouldCreateValidRefreshTokenWithEndpointSet() throws Exception {
+ final Specification specification = getValidSpecification();
+ specification.setEndpointSet(ENDPOINT_SET);
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+
+ final TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
+
+ @SuppressWarnings("unchecked") final Jwt<Header, Claims> parsedToken = Jwts
+ .parser()
+ .setSigningKey(keyPairHolder.publicKey())
+ .parse(tokenSerializationResult.getToken().substring("Bearer ".length()).trim());
+
+
+ final String endpointSetClaim = parsedToken.getBody().get(TokenConstants.JWT_ENDPOINT_SET_CLAIM, String.class);
+ Assert.assertEquals(ENDPOINT_SET, endpointSetClaim);
+
+ final TokenDeserializationResult tokenDeserializationResult = testSubject.deserialize(getTenantApplicationRsaKeyProvider(), tokenSerializationResult.getToken());
+ Assert.assertEquals(ENDPOINT_SET, tokenDeserializationResult.getEndpointSet());
}
@Test(expected = IllegalArgumentException.class)
public void invalidSecondsToLiveCausesException() throws Exception {
final Specification specification = getValidSpecification().setSecondsToLive(-1);
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
testSubject.build(specification);
}
@Test(expected = IllegalArgumentException.class)
public void missingKeyTimestampCausesException() throws Exception {
final Specification specification = getValidSpecification().setKeyTimestamp(null);
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
testSubject.build(specification);
}
@Test(expected = IllegalArgumentException.class)
public void missingApplicationCausesException() throws Exception {
final Specification specification = getValidSpecification().setSourceApplication(null);
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
testSubject.build(specification);
}
@Test(expected = IllegalArgumentException.class)
public void missingPrivateKeyCausesException() throws Exception {
final Specification specification = getValidSpecification().setPrivateKey(null);
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
testSubject.build(specification);
}
@Test(expected = AmitAuthenticationException.class)
public void deserializeNullCausesAmitException() throws Exception {
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
- testSubject.deserialize(null);
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+ testSubject.deserialize(getTenantApplicationRsaKeyProvider(), null);
}
@Test(expected = AmitAuthenticationException.class)
public void deserializeUnprefixedTokenCausesAmitException() throws Exception {
- final TenantRefreshTokenSerializer testSubject = getTestSubject();
- testSubject.deserialize("randostring");
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+ testSubject.deserialize(getTenantApplicationRsaKeyProvider(), "randostring");
}
@Test(expected = AmitAuthenticationException.class)
public void tenantHasNotProvidedAPublicKeyDuringDeserializationCausesAmitException() throws Exception {
final Specification specification = getValidSpecification();
- final TenantRsaKeyProvider tenantRsaKeyProvider = Mockito.mock(TenantRsaKeyProvider.class);
- Mockito.when(tenantRsaKeyProvider.getPublicKey(keyPairHolder.getTimestamp())).thenThrow(new IllegalArgumentException());
+ final TenantApplicationRsaKeyProvider tenantApplicationRsaKeyProvider
+ = Mockito.mock(TenantApplicationRsaKeyProvider.class);
+ Mockito.when(tenantApplicationRsaKeyProvider.getApplicationPublicKey(APPLICATION_NAME, keyPairHolder.getTimestamp()))
+ .thenThrow(new IllegalArgumentException());
- final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer(tenantRsaKeyProvider);
- TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
- testSubject.deserialize(tokenSerializationResult.getToken());
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+ final TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
+ testSubject.deserialize(tenantApplicationRsaKeyProvider, tokenSerializationResult.getToken());
}
@Test(expected = AmitAuthenticationException.class)
public void tenantHasNotProvidedAPublicKeyForKeyTimestampDuringDeserializationCausesAmitException() throws Exception {
final Specification specification = getValidSpecification();
- final TenantRsaKeyProvider tenantRsaKeyProvider = Mockito.mock(TenantRsaKeyProvider.class);
- Mockito.when(tenantRsaKeyProvider.getPublicKey(keyPairHolder.getTimestamp())).thenThrow(new InvalidKeyTimestampException(""));
+ final TenantApplicationRsaKeyProvider tenantApplicationRsaKeyProvider
+ = Mockito.mock(TenantApplicationRsaKeyProvider.class);
+ Mockito.when(tenantApplicationRsaKeyProvider.getApplicationPublicKey(APPLICATION_NAME, keyPairHolder.getTimestamp()))
+ .thenThrow(new InvalidKeyTimestampException(""));
- final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer(tenantRsaKeyProvider);
- TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
- testSubject.deserialize(tokenSerializationResult.getToken());
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+ final TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
+ testSubject.deserialize(tenantApplicationRsaKeyProvider, tokenSerializationResult.getToken());
+ }
+
+ @Test(expected = AmitAuthenticationException.class)
+ public void tokenIsSignedWithAWrongKeyCausesAmitException() throws Exception {
+ final RsaKeyPairFactory.KeyPairHolder otherKeyPairHolder = RsaKeyPairFactory.createKeyPair();
+
+ final Specification specification = getValidSpecification();
+ final TenantApplicationRsaKeyProvider tenantApplicationRsaKeyProvider
+ = Mockito.mock(TenantApplicationRsaKeyProvider.class);
+ Mockito.when(tenantApplicationRsaKeyProvider.getApplicationPublicKey(APPLICATION_NAME, keyPairHolder.getTimestamp()))
+ .thenReturn(otherKeyPairHolder.publicKey());
+
+ final TenantRefreshTokenSerializer testSubject = new TenantRefreshTokenSerializer();
+ final TokenSerializationResult tokenSerializationResult = testSubject.build(specification);
+ testSubject.deserialize(tenantApplicationRsaKeyProvider, tokenSerializationResult.getToken());
}
private Specification getValidSpecification() {
@@ -158,10 +202,10 @@
.setSecondsToLive(SECONDS_TO_LIVE);
}
- private TenantRefreshTokenSerializer getTestSubject() throws InvalidKeyTimestampException {
- final TenantRsaKeyProvider tenantRsaKeyProvider = Mockito.mock(TenantRsaKeyProvider.class);
- Mockito.when(tenantRsaKeyProvider.getPublicKey(keyPairHolder.getTimestamp())).thenReturn(keyPairHolder.publicKey());
+ private TenantApplicationRsaKeyProvider getTenantApplicationRsaKeyProvider() throws InvalidKeyTimestampException {
+ final TenantApplicationRsaKeyProvider tenantRsaKeyProvider = Mockito.mock(TenantApplicationRsaKeyProvider.class);
+ Mockito.when(tenantRsaKeyProvider.getApplicationPublicKey(APPLICATION_NAME, keyPairHolder.getTimestamp())).thenReturn(keyPairHolder.publicKey());
- return new TenantRefreshTokenSerializer(tenantRsaKeyProvider);
+ return tenantRsaKeyProvider;
}
}