Skip to content
This repository has been archived by the owner on Jan 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #10 from myrle-krantz/develop
Browse files Browse the repository at this point in the history
Application specific permissions.
  • Loading branch information
myrle-krantz committed Jun 8, 2017
2 parents d11032d + 0eb8273 commit 990ea62
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand All @@ -62,7 +67,7 @@ public String getDetails() {

@Override
public AnubisPrincipal getPrincipal() {
return new AnubisPrincipal(userIdentifier, forApplicationName);
return new AnubisPrincipal(userIdentifier, forApplicationName, sourceApplicationName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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();
Expand Down

0 comments on commit 990ea62

Please sign in to comment.