blob: 83857217c41817a44ddd153e612029c7ecc30c3e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
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.apache.fineract.cn.anubis.api.v1.TokenConstants;
import org.apache.fineract.cn.anubis.token.TenantRefreshTokenSerializer;
import org.apache.fineract.cn.anubis.token.TokenSerializationResult;
import org.apache.fineract.cn.api.context.AutoUserContext;
import org.apache.fineract.cn.api.util.FeignTargetWithCookieJar;
import org.apache.fineract.cn.api.util.InvalidTokenException;
import org.apache.fineract.cn.api.util.NotFoundException;
import org.apache.fineract.cn.test.domain.TimeStampChecker;
import org.apache.fineract.cn.test.env.TestEnvironment;
import org.apache.fineract.cn.test.fixture.TenantDataStoreTestContext;
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;
/**
* @author Myrle Krantz
*/
public class TestRefreshToken extends AbstractComponentTest {
private static final int ACCESS_TOKEN_TIME_TO_LIVE = 5;
private static final int REFRESH_TOKEN_TIME_TO_LIVE = 10;
@BeforeClass
public static void setup() throws Exception {
//Shorten access time to 5 seconds for test purposes.;
System.getProperties().setProperty("identity.token.access.ttl", String.valueOf(ACCESS_TOKEN_TIME_TO_LIVE));
System.getProperties().setProperty("identity.token.refresh.ttl", String.valueOf(REFRESH_TOKEN_TIME_TO_LIVE));
}
@Test(expected = InvalidTokenException.class)
public void adminLoginAccessTokenShouldTimeOut() throws InterruptedException {
try (final AutoUserContext ignore = loginAdmin()) {
Thread.sleep(TimeUnit.SECONDS.toMillis(ACCESS_TOKEN_TIME_TO_LIVE + 1));
getTestSubject().getUser(ADMIN_IDENTIFIER);
}
}
@Test(expected = InvalidTokenException.class)
public void adminLoginRefreshTokenShouldTimeOut() throws InterruptedException {
getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Thread.sleep(TimeUnit.SECONDS.toMillis(REFRESH_TOKEN_TIME_TO_LIVE + 1));
getTestSubject().refresh();
}
@Test
public void afterAccessTokenExpiresRefreshTokenShouldAcquireNewAccessToken() throws InterruptedException {
getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
Thread.sleep(TimeUnit.SECONDS.toMillis(ACCESS_TOKEN_TIME_TO_LIVE + 1));
final Authentication refreshAccessTokenAuthentication =
getTestSubject().refresh();
try (final AutoUserContext ignored = new AutoUserContext(ADMIN_IDENTIFIER, refreshAccessTokenAuthentication.getAccessToken())) {
getTestSubject().changeUserPassword(ADMIN_IDENTIFIER, new Password(TestEnvironment.encodePassword(ADMIN_PASSWORD)));
}
}
@Test(expected = InvalidTokenException.class)
public void refreshTokenShouldGrantAccessOnlyToOneTenant()
{
getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
try (final TenantDataStoreTestContext ignored = TenantDataStoreTestContext.forRandomTenantName(cassandraInitializer)) {
try (final AutoUserContext ignored2
= tenantApplicationSecurityEnvironment.createAutoSeshatContext()) {
getTestSubject().initialize(TestEnvironment.encodePassword(ADMIN_PASSWORD));
}
getTestSubject().refresh();
}
}
@Test
public void expirationDatesShouldBeCorrectIsoDateTimes() throws InterruptedException {
final Authentication authentication =
getTestSubject().login(ADMIN_IDENTIFIER, TestEnvironment.encodePassword(ADMIN_PASSWORD));
final TimeStampChecker preRefreshAccessTokenTimeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(ACCESS_TOKEN_TIME_TO_LIVE));
final TimeStampChecker refreshTokenTimeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(REFRESH_TOKEN_TIME_TO_LIVE));
Assert.assertNotNull(authentication);
preRefreshAccessTokenTimeStampChecker.assertCorrect(authentication.getAccessTokenExpiration());
refreshTokenTimeStampChecker.assertCorrect(authentication.getRefreshTokenExpiration());
TimeUnit.SECONDS.sleep(3);
final TimeStampChecker postRefreshAccessTokenTimeStampChecker = TimeStampChecker.inTheFuture(Duration.ofSeconds(ACCESS_TOKEN_TIME_TO_LIVE));
final Authentication refreshedAuthentication = getTestSubject().refresh();
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) { }
}
}