From 77fd22ef330a9a29622bc10114c4495aa89de457 Mon Sep 17 00:00:00 2001 From: myrle-krantz Date: Fri, 2 Jun 2017 15:08:40 +0200 Subject: [PATCH] Begun work to validate application identifier designator for tenant token as well. --- .../mifos/anubis/api/v1/TokenConstants.java | 1 + .../anubis/security/AnubisAuthentication.java | 11 ++++-- .../anubis/security/AnubisPrincipal.java | 8 ++++- .../anubis/security/GuestAuthenticator.java | 7 +++- .../anubis/security/SystemAuthenticator.java | 7 +++- .../anubis/security/TenantAuthenticator.java | 3 +- .../service/PermissionSegmentMatcher.java | 4 ++- .../token/TenantAccessTokenSerializer.java | 10 ++++++ .../security/ApplicationPermissionTest.java | 36 ++++++++++++++++++- .../TenantAccessTokenSerializerTest.java | 2 ++ .../test/v1/SystemSecurityEnvironment.java | 2 ++ 11 files changed, 82 insertions(+), 9 deletions(-) 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 e2b2eba..d8da9fe 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 @@ -26,6 +26,7 @@ public interface TokenConstants { String JWT_SIGNATURE_TIMESTAMP_CLAIM = "/mifos.io/signatureTimestamp"; String JWT_ENDPOINT_SET_CLAIM = "/mifos.io/endpointSet"; String JWT_CONTENT_CLAIM = "/mifos.io/tokenContent"; + String JWT_SOURCE_APPLICATION_CLAIM = "/mifos.io/sourceApplication"; String REFRESH_TOKEN_COOKIE_NAME = "org.apache.fineract.refreshToken"; } diff --git a/library/src/main/java/io/mifos/anubis/security/AnubisAuthentication.java b/library/src/main/java/io/mifos/anubis/security/AnubisAuthentication.java index ab7385f..65b3a7c 100644 --- a/library/src/main/java/io/mifos/anubis/security/AnubisAuthentication.java +++ b/library/src/main/java/io/mifos/anubis/security/AnubisAuthentication.java @@ -33,15 +33,20 @@ class AnubisAuthentication implements Authentication { private final String token; private final String userIdentifier; private final String forApplicationName; + private final String sourceApplicationName; private final Set applicationPermissions; - AnubisAuthentication(final String token, final String userIdentifier, final String forApplicationName, - final Set applicationPermissions) { + AnubisAuthentication(final String token, + final String userIdentifier, + final String forApplicationName, + final String sourceApplicationName, + final Set applicationPermissions) { authenticated = true; this.token = token; this.userIdentifier = userIdentifier; this.forApplicationName = forApplicationName; + this.sourceApplicationName = sourceApplicationName; this.applicationPermissions = Collections.unmodifiableSet(new HashSet<>(applicationPermissions)); } @@ -62,7 +67,7 @@ public String getDetails() { @Override public AnubisPrincipal getPrincipal() { - return new AnubisPrincipal(userIdentifier, forApplicationName); + return new AnubisPrincipal(userIdentifier, forApplicationName, sourceApplicationName); } @Override diff --git a/library/src/main/java/io/mifos/anubis/security/AnubisPrincipal.java b/library/src/main/java/io/mifos/anubis/security/AnubisPrincipal.java index 5b2a9bd..f0c0da2 100644 --- a/library/src/main/java/io/mifos/anubis/security/AnubisPrincipal.java +++ b/library/src/main/java/io/mifos/anubis/security/AnubisPrincipal.java @@ -24,10 +24,12 @@ public class AnubisPrincipal { private final String user; private final String forApplicationName; + private final String sourceApplicationName; - AnubisPrincipal(String user, String forApplicationName) { + AnubisPrincipal(String user, String forApplicationName, String sourceApplicationName) { this.user = user; this.forApplicationName = forApplicationName; + this.sourceApplicationName = sourceApplicationName; } public String getUser() { @@ -38,6 +40,10 @@ public String getForApplicationName() { return forApplicationName; } + public String getSourceApplicationName() { + return sourceApplicationName; + } + @Override public boolean equals(Object o) { if (this == o) return true; 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 12b2ce4..3a2e00e 100644 --- a/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java +++ b/library/src/main/java/io/mifos/anubis/security/GuestAuthenticator.java @@ -52,6 +52,11 @@ AnubisAuthentication authenticate(final String user) { logger.info("Guest access \"authenticated\" successfully.", user); - return new AnubisAuthentication(null, RoleConstants.GUEST_USER_IDENTIFIER, applicationName.toString(), permissions); + return new AnubisAuthentication( + null, + RoleConstants.GUEST_USER_IDENTIFIER, + applicationName.toString(), + applicationName.toString(), + 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 46c01e3..70ec3e7 100644 --- a/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java +++ b/library/src/main/java/io/mifos/anubis/security/SystemAuthenticator.java @@ -78,7 +78,12 @@ public AnubisAuthentication authenticate( logger.info("System token for user {}, with key timestamp {} authenticated successfully.", user, keyTimestamp); - return new AnubisAuthentication(TokenConstants.PREFIX + token, user, result.getBody().getAudience(), permissions); + return new AnubisAuthentication( + TokenConstants.PREFIX + token, + user, + result.getBody().getAudience(), + TokenType.SYSTEM.getIssuer(), + permissions); } catch (final JwtException e) { logger.debug("token = {}", token); 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 bd53f33..b575de0 100644 --- a/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java +++ b/library/src/main/java/io/mifos/anubis/security/TenantAuthenticator.java @@ -78,6 +78,7 @@ AnubisAuthentication authenticate( @SuppressWarnings("unchecked") Jwt jwt = parser.parse(token); final String serializedTokenContent = jwt.getBody().get(TokenConstants.JWT_CONTENT_CLAIM, String.class); + final String sourceApplication = jwt.getBody().get(TokenConstants.JWT_SOURCE_APPLICATION_CLAIM, String.class); final TokenContent tokenContent = gson.fromJson(serializedTokenContent, TokenContent.class); if (tokenContent == null) throw AmitAuthenticationException.missingTokenContent(); @@ -88,7 +89,7 @@ AnubisAuthentication authenticate( logger.info("Tenant token for user {}, with key timestamp {} authenticated successfully.", user, keyTimestamp); return new AnubisAuthentication(TokenConstants.PREFIX + token, - jwt.getBody().getSubject(), applicationNameWithVersion, permissions + jwt.getBody().getSubject(), applicationNameWithVersion, sourceApplication, permissions ); } catch (final JwtException e) { 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 1d856e4..84dbacb 100644 --- a/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java +++ b/library/src/main/java/io/mifos/anubis/service/PermissionSegmentMatcher.java @@ -59,8 +59,10 @@ public boolean matches( return true; else if (isUserIdentifierSegment()) return requestSegment.equals(principal.getUser()); - else if (isApplicationIdentifierSegment() && acceptTokenIntendedForForeignApplication) + else if (acceptTokenIntendedForForeignApplication && isApplicationIdentifierSegment()) return requestSegment.equals(principal.getForApplicationName()); + else if (!acceptTokenIntendedForForeignApplication && isApplicationIdentifierSegment()) + return requestSegment.equals(principal.getSourceApplicationName()); else if (isParameterSegment()) return isSu; else diff --git a/library/src/main/java/io/mifos/anubis/token/TenantAccessTokenSerializer.java b/library/src/main/java/io/mifos/anubis/token/TenantAccessTokenSerializer.java index 29805f9..afdcc18 100644 --- a/library/src/main/java/io/mifos/anubis/token/TenantAccessTokenSerializer.java +++ b/library/src/main/java/io/mifos/anubis/token/TenantAccessTokenSerializer.java @@ -50,6 +50,7 @@ public static class Specification { private String user; private TokenContent tokenContent; private long secondsToLive; + private String sourceApplication; public Specification setKeyTimestamp(final String keyTimestamp) { this.keyTimestamp = keyTimestamp; @@ -66,6 +67,11 @@ public Specification setUser(final String user) { return this; } + public Specification setSourceApplication(final String applicationIdentifier) { + this.sourceApplication = applicationIdentifier; + return this; + } + public Specification setTokenContent(final TokenContent tokenContent) { this.tokenContent = tokenContent; return this; @@ -89,12 +95,16 @@ public TokenSerializationResult build(final Specification specification) if (specification.privateKey == null) { throw new IllegalArgumentException("token signature privateKey must not be null."); } + if (specification.sourceApplication == null) { + throw new IllegalArgumentException("token signature source application must not be null."); + } final JwtBuilder jwtBuilder = Jwts.builder() .setSubject(specification.user) .claim(TokenConstants.JWT_SIGNATURE_TIMESTAMP_CLAIM, specification.keyTimestamp) .claim(TokenConstants.JWT_CONTENT_CLAIM, serializedTokenContent) + .claim(TokenConstants.JWT_SOURCE_APPLICATION_CLAIM, specification.sourceApplication) .setIssuer(TokenType.TENANT.getIssuer()) .setIssuedAt(new Date(issued)) .signWith(SignatureAlgorithm.RS512, specification.privateKey); 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 8780f92..0061b7b 100644 --- a/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java +++ b/library/src/test/java/io/mifos/anubis/security/ApplicationPermissionTest.java @@ -47,6 +47,7 @@ private static class TestCase private String requestedOperation = "GET"; private String user = "Nebamun"; private String forApplication = "graincounter-v1"; + private String sourceApplication = "identity-v1"; private boolean expectedResult = true; private TestCase(final String caseName) { @@ -110,7 +111,7 @@ TestCase requestedOperation(String requestedOperation) { } AnubisPrincipal getPrincipal() { - return new AnubisPrincipal(user, forApplication); + return new AnubisPrincipal(user, forApplication, sourceApplication); } TestCase user(final String newVal) @@ -125,6 +126,12 @@ TestCase forApplication(final String newVal) return this; } + TestCase sourceApplication(final String newVal) + { + this.sourceApplication = newVal; + return this; + } + boolean getExpectedResult() { return expectedResult; } @@ -222,6 +229,33 @@ public static Collection testCases() { .allowedOperation(AllowedOperation.CHANGE) .requestedOperation("PUT") .expectedResult(true)); + ret.add(new TestCase("access token acquired from application refresh token accessing its own resource.") + .permittedPath("/applications/{applicationidentifier}/permissions") + .requestedPath("/applications/bop-v1/permissions") + .acceptTokenIntendedForForeignApplication(false) + .calledApplication("identity-v1").forApplication("identity-v1") + .sourceApplication("bop-v1") + .allowedOperation(AllowedOperation.CHANGE) + .requestedOperation("POST") + .expectedResult(true)); + ret.add(new TestCase("access token acquired from application refresh token accessing another apps resource.") + .permittedPath("/applications/{applicationidentifier}/permissions") + .requestedPath("/applications/bop-v1/permissions") + .acceptTokenIntendedForForeignApplication(false) + .calledApplication("identity-v1").forApplication("identity-v1") + .sourceApplication("bee-v1") + .allowedOperation(AllowedOperation.CHANGE) + .requestedOperation("POST") + .expectedResult(false)); + ret.add(new TestCase("access token acquired from application refresh token accessing sub resource not allowed.") + .permittedPath("/applications/{applicationidentifier}/permissions") + .requestedPath("/applications/bop-v1/permissions/identity__v1__roles/users/Nebamun/enabled") + .acceptTokenIntendedForForeignApplication(false) + .calledApplication("identity-v1").forApplication("identity-v1") + .sourceApplication("bop-v1") + .allowedOperation(AllowedOperation.CHANGE) + .requestedOperation("PUT") + .expectedResult(false)); return ret; } diff --git a/library/src/test/java/io/mifos/anubis/token/TenantAccessTokenSerializerTest.java b/library/src/test/java/io/mifos/anubis/token/TenantAccessTokenSerializerTest.java index cd396a7..ef050ef 100644 --- a/library/src/test/java/io/mifos/anubis/token/TenantAccessTokenSerializerTest.java +++ b/library/src/test/java/io/mifos/anubis/token/TenantAccessTokenSerializerTest.java @@ -57,6 +57,7 @@ public void shouldCreateValidAccessToken() throws Exception = new TenantAccessTokenSerializer.Specification() .setKeyTimestamp("1234567") .setUser(USER) + .setSourceApplication("doo-v1") .setTokenContent(EXAMPLE_TOKEN_CONTENT) .setPrivateKey(keyPairHolder.privateKey()) .setSecondsToLive(SECONDS_TO_LIVE); @@ -95,6 +96,7 @@ public void invalidSecondsToLiveCausesException() final TenantAccessTokenSerializer.Specification specification = new TenantAccessTokenSerializer.Specification() .setUser(USER) + .setSourceApplication("doo-v1") .setTokenContent(EXAMPLE_TOKEN_CONTENT) .setPrivateKey(keyPairHolder.privateKey()) .setSecondsToLive(0); diff --git a/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java b/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java index faed373..4e00dd9 100644 --- a/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java +++ b/test/src/main/java/io/mifos/anubis/test/v1/SystemSecurityEnvironment.java @@ -109,6 +109,7 @@ private String getEverythingToken(final String userName, final List appl { return tenantAccessTokenSerializer.build(new TenantAccessTokenSerializer.Specification() .setUser(userName) + .setSourceApplication("test-v1") .setTokenContent(getTokenContentForStarEndpoint(applicationNames)) .setSecondsToLive(TimeUnit.HOURS.toSeconds(10)) .setKeyTimestamp(tenantKeyTimestamp()) @@ -124,6 +125,7 @@ public String getPermissionToken( new TenantAccessTokenSerializer.Specification().setPrivateKey(tenantPrivateKey()) .setKeyTimestamp(tenantKeyTimestamp()) .setSecondsToLive(100) + .setSourceApplication("test-v1") .setUser(userName) .setTokenContent(generateOnePermissionTokenContent(applicationName, uri, allowedOperation)) ).getToken();