Skip to content
Permalink
Browse files
Merge pull request #10 from myrle-krantz/develop
Application specific permissions.
  • Loading branch information
myrle-krantz committed Jun 8, 2017
2 parents d11032d + 0eb8273 commit 990ea62d08a665bca509819d74eb775ea0b19cce
Showing 12 changed files with 84 additions and 10 deletions.
@@ -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";
}
@@ -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<ApplicationPermission> applicationPermissions;

AnubisAuthentication(final String token, final String userIdentifier, final String forApplicationName,
final Set<ApplicationPermission> applicationPermissions) {
AnubisAuthentication(final String token,
final String userIdentifier,
final String forApplicationName,
final String sourceApplicationName,
final Set<ApplicationPermission> 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
@@ -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;
@@ -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);
}
}
@@ -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);
@@ -78,6 +78,7 @@ AnubisAuthentication authenticate(
@SuppressWarnings("unchecked") Jwt<Header, Claims> 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) {
@@ -64,8 +64,9 @@ public UrlPermissionChecker(final Logger logger, final ApplicationName applicati
.filter(x -> x.matches(filterInvocation, applicationName, authentication.getPrincipal()))
.findAny();

//Do not put full .getRequestUrl() into log info, because in the case of identity, it includes the password.
matchedPermission.ifPresent(x -> logger.debug("Authorizing access to {} based on permission: {}"
, filterInvocation.getRequestUrl(), x));
, filterInvocation.getRequest().getServletPath(), x));

return matchedPermission.map(x -> ACCESS_GRANTED).orElse(ACCESS_DENIED);
}
@@ -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
@@ -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);
@@ -47,6 +47,7 @@ public class ApplicationPermissionTest {
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;
}
@@ -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);
@@ -109,6 +109,7 @@ private String getEverythingToken(final String userName, final List<String> 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();

0 comments on commit 990ea62

Please sign in to comment.