Access tokens can be acquired via refresh token passed as a parameter now (previously only cookies were allowed).
diff --git a/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java b/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
index fee00ab..e385894 100644
--- a/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
+++ b/api/src/main/java/io/mifos/identity/api/v1/client/IdentityManager.java
@@ -15,7 +15,6 @@
*/
package io.mifos.identity.api.v1.client;
-import io.mifos.anubis.api.v1.TokenConstants;
import io.mifos.anubis.api.v1.client.Anubis;
import io.mifos.anubis.api.v1.domain.ApplicationSignatureSet;
import io.mifos.anubis.api.v1.domain.Signature;
@@ -47,6 +46,11 @@
produces = {MediaType.ALL_VALUE})
Authentication refresh();
+ @RequestMapping(value = "/token?grant_type=refresh_token", method = RequestMethod.POST,
+ consumes = {MediaType.APPLICATION_JSON_VALUE},
+ produces = {MediaType.ALL_VALUE})
+ Authentication refresh(@RequestParam("refresh_token") String refreshToken);
+
@RequestMapping(value = "/token/_current", method = RequestMethod.DELETE,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
@@ -147,11 +151,6 @@
produces = {MediaType.ALL_VALUE})
void deleteApplication(@PathVariable("applicationidentifier") String applicationIdentifier);
- @RequestMapping(value = "/token?grant_type=refresh_token", method = RequestMethod.POST,
- consumes = {MediaType.APPLICATION_JSON_VALUE},
- produces = {MediaType.ALL_VALUE})
- Authentication refresh(@CookieValue(TokenConstants.REFRESH_TOKEN_COOKIE_NAME) String refreshToken);
-
@RequestMapping(value = "/applications/{applicationidentifier}/permissions", method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.ALL_VALUE})
diff --git a/component-test/src/main/java/AbstractComponentTest.java b/component-test/src/main/java/AbstractComponentTest.java
index ce69ea8..26761ed 100644
--- a/component-test/src/main/java/AbstractComponentTest.java
+++ b/component-test/src/main/java/AbstractComponentTest.java
@@ -14,12 +14,14 @@
* limitations under the License.
*/
import io.mifos.anubis.api.v1.domain.AllowedOperation;
+import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
import io.mifos.core.api.config.EnableApiFactory;
import io.mifos.core.api.context.AutoGuest;
import io.mifos.core.api.context.AutoUserContext;
import io.mifos.core.api.util.ApiFactory;
import io.mifos.core.api.util.UserContextHolder;
+import io.mifos.core.lang.security.RsaKeyPairFactory;
import io.mifos.core.test.env.TestEnvironment;
import io.mifos.core.test.fixture.TenantDataStoreContextTestRule;
import io.mifos.core.test.fixture.cassandra.CassandraInitializer;
@@ -28,8 +30,11 @@
import io.mifos.identity.api.v1.PermittableGroupIds;
import io.mifos.identity.api.v1.client.IdentityManager;
import io.mifos.identity.api.v1.domain.*;
+import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
+import io.mifos.identity.api.v1.events.ApplicationSignatureEvent;
import io.mifos.identity.api.v1.events.EventConstants;
import io.mifos.identity.config.IdentityServiceConfig;
+import org.apache.commons.lang.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
@@ -221,4 +226,49 @@
}
return new AutoUserContext(userId, authentication.getAccessToken());
}
+
+ private String createTestApplicationName()
+ {
+ return "test" + RandomStringUtils.randomNumeric(3) + "-v1";
+ }
+
+ static class ApplicationSignatureTestData {
+ private final String applicationIdentifier;
+ private final RsaKeyPairFactory.KeyPairHolder keyPair;
+
+ ApplicationSignatureTestData(final String applicationIdentifier, final RsaKeyPairFactory.KeyPairHolder keyPair) {
+ this.applicationIdentifier = applicationIdentifier;
+ this.keyPair = keyPair;
+ }
+
+ String getApplicationIdentifier() {
+ return applicationIdentifier;
+ }
+
+ RsaKeyPairFactory.KeyPairHolder getKeyPair() {
+ return keyPair;
+ }
+
+ String getKeyTimestamp() {
+ return keyPair.getTimestamp();
+ }
+ }
+
+ ApplicationSignatureTestData setApplicationSignature() throws InterruptedException {
+ final String testApplicationName = createTestApplicationName();
+ final RsaKeyPairFactory.KeyPairHolder keyPair = RsaKeyPairFactory.createKeyPair();
+ final Signature signature = new Signature(keyPair.getPublicKeyMod(), keyPair.getPublicKeyExp());
+
+ getTestSubject().setApplicationSignature(testApplicationName, keyPair.getTimestamp(), signature);
+
+ Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_PUT_APPLICATION_SIGNATURE, new ApplicationSignatureEvent(testApplicationName, keyPair.getTimestamp())));
+ return new ApplicationSignatureTestData(testApplicationName, keyPair);
+ }
+
+ void createApplicationPermission(final String applicationIdentifier, final Permission permission) throws InterruptedException {
+ getTestSubject().createApplicationPermission(applicationIdentifier, permission);
+ Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
+ new ApplicationPermissionEvent(applicationIdentifier,
+ permission.getPermittableEndpointGroupIdentifier())));
+ }
}
diff --git a/component-test/src/main/java/TestApplications.java b/component-test/src/main/java/TestApplications.java
index 94062c3..df185a6 100644
--- a/component-test/src/main/java/TestApplications.java
+++ b/component-test/src/main/java/TestApplications.java
@@ -14,22 +14,20 @@
* limitations under the License.
*/
-import io.mifos.anubis.api.v1.TokenConstants;
import io.mifos.anubis.api.v1.domain.AllowedOperation;
-import io.mifos.anubis.api.v1.domain.Signature;
import io.mifos.anubis.token.TenantRefreshTokenSerializer;
import io.mifos.anubis.token.TokenSerializationResult;
import io.mifos.core.api.context.AutoUserContext;
-import io.mifos.core.api.util.FeignTargetWithCookieJar;
import io.mifos.core.api.util.NotFoundException;
-import io.mifos.core.lang.security.RsaKeyPairFactory;
import io.mifos.identity.api.v1.PermittableGroupIds;
-import io.mifos.identity.api.v1.client.IdentityManager;
import io.mifos.identity.api.v1.domain.Authentication;
import io.mifos.identity.api.v1.domain.CallEndpointSet;
import io.mifos.identity.api.v1.domain.Permission;
import io.mifos.identity.api.v1.domain.User;
-import io.mifos.identity.api.v1.events.*;
+import io.mifos.identity.api.v1.events.ApplicationCallEndpointSetEvent;
+import io.mifos.identity.api.v1.events.ApplicationPermissionEvent;
+import io.mifos.identity.api.v1.events.ApplicationPermissionUserEvent;
+import io.mifos.identity.api.v1.events.EventConstants;
import org.apache.commons.lang.RandomStringUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -37,7 +35,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* @author Myrle Krantz
@@ -71,9 +68,7 @@
identityManagementPermission.setPermittableEndpointGroupIdentifier(PermittableGroupIds.IDENTITY_MANAGEMENT);
identityManagementPermission.setAllowedOperations(Collections.singleton(AllowedOperation.READ));
- getTestSubject().createApplicationPermission(appPlusSig.getApplicationIdentifier(), identityManagementPermission);
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
- new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(), PermittableGroupIds.IDENTITY_MANAGEMENT)));
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), identityManagementPermission);
{
final List<Permission> applicationPermissions = getTestSubject().getApplicationPermissions(appPlusSig.getApplicationIdentifier());
@@ -87,9 +82,8 @@
roleManagementPermission.setPermittableEndpointGroupIdentifier(PermittableGroupIds.ROLE_MANAGEMENT);
roleManagementPermission.setAllowedOperations(Collections.singleton(AllowedOperation.READ));
- getTestSubject().createApplicationPermission(appPlusSig.getApplicationIdentifier(), roleManagementPermission);
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
- new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(), PermittableGroupIds.ROLE_MANAGEMENT)));
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), roleManagementPermission);
+
{
final List<Permission> applicationPermissions = getTestSubject().getApplicationPermissions(appPlusSig.getApplicationIdentifier());
Assert.assertTrue(applicationPermissions.contains(identityManagementPermission));
@@ -145,10 +139,7 @@
PermittableGroupIds.ROLE_MANAGEMENT,
Collections.singleton(AllowedOperation.READ));
- getTestSubject().createApplicationPermission(appPlusSig.getApplicationIdentifier(), identityManagementPermission);
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
- new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(),
- identityManagementPermission.getPermittableEndpointGroupIdentifier())));
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), identityManagementPermission);
}
final String user1Password;
@@ -274,14 +265,9 @@
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
appPlusSig = setApplicationSignature();
- getTestSubject().createApplicationPermission(appPlusSig.getApplicationIdentifier(), rolePermission);
- getTestSubject().createApplicationPermission(appPlusSig.getApplicationIdentifier(), userPermission);
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
- new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(),
- rolePermission.getPermittableEndpointGroupIdentifier())));
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_POST_APPLICATION_PERMISSION,
- new ApplicationPermissionEvent(appPlusSig.getApplicationIdentifier(),
- userPermission.getPermittableEndpointGroupIdentifier())));
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), rolePermission);
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), userPermission);
+
getTestSubject().createApplicationCallEndpointSet(
appPlusSig.getApplicationIdentifier(),
new CallEndpointSet(CALL_ENDPOINT_SET_IDENTIFIER,
@@ -325,53 +311,11 @@
.setSourceApplication(appPlusSig.getApplicationIdentifier()));
- final FeignTargetWithCookieJar<IdentityManager> identityManagerWithCookieJar
- = apiFactory.createWithCookieJar(IdentityManager.class, testEnvironment.serverURI());
-
- identityManagerWithCookieJar.putCookie("/token", TokenConstants.REFRESH_TOKEN_COOKIE_NAME, tokenSerializationResult.getToken());
- final Authentication applicationAuthentication = identityManagerWithCookieJar.getFeignTarget().refresh();
+ final Authentication applicationAuthentication = getTestSubject().refresh(tokenSerializationResult.getToken());
try (final AutoUserContext ignored = new AutoUserContext(userid, applicationAuthentication.getAccessToken())) {
final List<User> users = getTestSubject().getUsers();
Assert.assertFalse(users.isEmpty());
}
}
-
- private String createTestApplicationName()
- {
- return "test" + RandomStringUtils.randomNumeric(3) + "-v1";
- }
-
- static class ApplicationSignatureTestData {
- private final String applicationIdentifier;
- private final RsaKeyPairFactory.KeyPairHolder keyPair;
-
- ApplicationSignatureTestData(final String applicationIdentifier, final RsaKeyPairFactory.KeyPairHolder keyPair) {
- this.applicationIdentifier = applicationIdentifier;
- this.keyPair = keyPair;
- }
-
- String getApplicationIdentifier() {
- return applicationIdentifier;
- }
-
- RsaKeyPairFactory.KeyPairHolder getKeyPair() {
- return keyPair;
- }
-
- String getKeyTimestamp() {
- return keyPair.getTimestamp();
- }
- }
-
- private ApplicationSignatureTestData setApplicationSignature() throws InterruptedException {
- final String testApplicationName = createTestApplicationName();
- final RsaKeyPairFactory.KeyPairHolder keyPair = RsaKeyPairFactory.createKeyPair();
- final Signature signature = new Signature(keyPair.getPublicKeyMod(), keyPair.getPublicKeyExp());
-
- getTestSubject().setApplicationSignature(testApplicationName, keyPair.getTimestamp(), signature);
-
- Assert.assertTrue(eventRecorder.wait(EventConstants.OPERATION_PUT_APPLICATION_SIGNATURE, new ApplicationSignatureEvent(testApplicationName, keyPair.getTimestamp())));
- return new ApplicationSignatureTestData(testApplicationName, keyPair);
- }
}
diff --git a/component-test/src/main/java/TestRefreshToken.java b/component-test/src/main/java/TestRefreshToken.java
index 1ff7a3f..b6f3894 100644
--- a/component-test/src/main/java/TestRefreshToken.java
+++ b/component-test/src/main/java/TestRefreshToken.java
@@ -14,18 +14,27 @@
* limitations under the License.
*/
+import io.mifos.anubis.api.v1.TokenConstants;
+import io.mifos.anubis.token.TenantRefreshTokenSerializer;
+import io.mifos.anubis.token.TokenSerializationResult;
import io.mifos.core.api.context.AutoUserContext;
+import io.mifos.core.api.util.FeignTargetWithCookieJar;
import io.mifos.core.api.util.InvalidTokenException;
+import io.mifos.core.api.util.NotFoundException;
import io.mifos.core.test.domain.TimeStampChecker;
import io.mifos.core.test.env.TestEnvironment;
import io.mifos.core.test.fixture.TenantDataStoreTestContext;
+import io.mifos.identity.api.v1.client.IdentityManager;
import io.mifos.identity.api.v1.domain.Authentication;
import io.mifos.identity.api.v1.domain.Password;
+import io.mifos.identity.api.v1.domain.Permission;
+import io.mifos.identity.api.v1.domain.User;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.time.Duration;
+import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -109,4 +118,65 @@
postRefreshAccessTokenTimeStampChecker.assertCorrect(refreshedAuthentication.getAccessTokenExpiration());
refreshTokenTimeStampChecker.assertCorrect(refreshedAuthentication.getRefreshTokenExpiration());
}
+
+ @Test
+ public void bothRefreshMethodsShouldProduceSamePermissions() throws InterruptedException {
+ final Permission userPermission = buildUserPermission();
+ final ApplicationSignatureTestData appPlusSig;
+ try (final AutoUserContext ignored
+ = tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
+ appPlusSig = setApplicationSignature();
+ createApplicationPermission(appPlusSig.getApplicationIdentifier(), userPermission);
+ }
+
+ try (final AutoUserContext ignored = loginAdmin()) {
+ getTestSubject().setApplicationPermissionEnabledForUser(
+ appPlusSig.getApplicationIdentifier(),
+ userPermission.getPermittableEndpointGroupIdentifier(),
+ ADMIN_IDENTIFIER,
+ true);
+ }
+
+ final TenantRefreshTokenSerializer refreshTokenSerializer = new TenantRefreshTokenSerializer();
+
+ final TokenSerializationResult tokenSerializationResult =
+ refreshTokenSerializer.build(new TenantRefreshTokenSerializer.Specification()
+ .setUser(ADMIN_IDENTIFIER)
+ .setSecondsToLive(30)
+ .setKeyTimestamp(appPlusSig.getKeyTimestamp())
+ .setPrivateKey(appPlusSig.getKeyPair().privateKey())
+ .setSourceApplication(appPlusSig.getApplicationIdentifier()));
+
+ final FeignTargetWithCookieJar<IdentityManager> identityManagerWithCookieJar
+ = apiFactory.createWithCookieJar(IdentityManager.class, testEnvironment.serverURI());
+
+ identityManagerWithCookieJar.putCookie("/token", TokenConstants.REFRESH_TOKEN_COOKIE_NAME, tokenSerializationResult.getToken());
+
+ final Authentication applicationAuthenticationViaCookie = identityManagerWithCookieJar.getFeignTarget().refresh();
+
+ final Authentication applicationAuthenticationViaParam = getTestSubject().refresh(tokenSerializationResult.getToken());
+
+ try (final AutoUserContext ignored = new AutoUserContext(ADMIN_IDENTIFIER, applicationAuthenticationViaCookie.getAccessToken()))
+ {
+ checkAccessToUsersAndOnlyUsers();
+ }
+
+ try (final AutoUserContext ignored = new AutoUserContext(ADMIN_IDENTIFIER, applicationAuthenticationViaParam.getAccessToken()))
+ {
+ checkAccessToUsersAndOnlyUsers();
+ }
+
+ }
+
+ private void checkAccessToUsersAndOnlyUsers() {
+ final List<User> users = getTestSubject().getUsers();
+ Assert.assertFalse(users.isEmpty());
+
+ try {
+ getTestSubject().getRoles();
+ Assert.fail("Shouldn't be able to get roles with token for application for which roles are not permitted.");
+ }
+ catch (final NotFoundException ignored2) { }
+ }
+
}
diff --git a/service/src/main/java/io/mifos/identity/rest/AuthorizationRestController.java b/service/src/main/java/io/mifos/identity/rest/AuthorizationRestController.java
index c214b8e..c1ff8f5 100644
--- a/service/src/main/java/io/mifos/identity/rest/AuthorizationRestController.java
+++ b/service/src/main/java/io/mifos/identity/rest/AuthorizationRestController.java
@@ -39,6 +39,7 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.WebUtils;
+import javax.annotation.Nullable;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -82,10 +83,11 @@
final HttpServletRequest request,
@RequestParam("grant_type") final String grantType,
@RequestParam(value = "username", required = false) final String username,
- @RequestParam(value = "password", required = false) final String password) throws InterruptedException {
+ @RequestParam(value = "password", required = false) final String password,
+ @RequestParam(value = "refresh_token", required = false) final String refreshTokenParam) throws InterruptedException {
switch (grantType) {
case "refresh_token": {
- final String refreshToken = getRefreshToken(request);
+ final String refreshToken = getRefreshToken(refreshTokenParam, request);
try {
final AuthenticationCommandResponse authenticationCommandResponse
@@ -134,7 +136,10 @@
return ResponseEntity.ok().build();
}
- private String getRefreshToken(final HttpServletRequest request) {
+ private String getRefreshToken(final @Nullable String refreshTokenParam, final HttpServletRequest request) {
+ if (refreshTokenParam != null)
+ return refreshTokenParam;
+
final Cookie refreshTokenCookie = WebUtils.getCookie(request, TokenConstants.REFRESH_TOKEN_COOKIE_NAME);
if (refreshTokenCookie == null)
throw ServiceException.badRequest("One (and only one) refresh token cookie must be included in the request if the grant_type is refresh_token");