blob: 04e175959f7ac71981ea4806c7b14c8462f8cd62 [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.
*/
package org.apache.jackrabbit.oak.security.authentication.token;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import javax.jcr.Credentials;
import javax.jcr.GuestCredentials;
import javax.jcr.SimpleCredentials;
import javax.security.auth.login.LoginException;
import com.google.common.collect.ImmutableMap;
import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
import org.apache.jackrabbit.oak.AbstractSecurityTest;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.apache.jackrabbit.oak.spi.security.authentication.Authentication;
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConstants;
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenInfo;
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TokenAuthenticationTest extends AbstractSecurityTest {
TokenAuthentication authentication;
TokenProviderImpl tokenProvider;
String userId;
@Before
public void before() throws Exception {
super.before();
tokenProvider = new TokenProviderImpl(root,
ConfigurationParameters.EMPTY,
getUserConfiguration());
root.commit();
authentication = new TokenAuthentication(tokenProvider);
userId = getTestUser().getID();
}
@Test
public void testAuthenticateWithoutTokenProvider() throws Exception {
Authentication authentication = new TokenAuthentication(null);
assertFalse(authentication.authenticate(new TokenCredentials("token")));
}
@Test
public void testAuthenticateWithInvalidCredentials() throws Exception {
List<Credentials> invalid = new ArrayList<Credentials>();
invalid.add(new GuestCredentials());
invalid.add(new SimpleCredentials(userId, new char[0]));
for (Credentials creds : invalid) {
assertFalse(authentication.authenticate(creds));
}
}
@Test
public void testAuthenticateWithInvalidTokenCredentials() throws Exception {
try {
authentication.authenticate(new TokenCredentials(UUID.randomUUID().toString()));
fail("LoginException expected");
} catch (LoginException e) {
// success
}
}
@Test
public void testAuthenticate() throws Exception {
TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
assertTrue(authentication.authenticate(new TokenCredentials(info.getToken())));
}
@Test
public void testGetTokenInfoBeforeAuthenticate() {
try {
authentication.getTokenInfo();
fail("IllegalStateException expected");
} catch (IllegalStateException e) {
// success
}
}
@Test
public void testGetTokenInfoAfterAuthenticate() throws Exception {
TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
authentication.authenticate(new TokenCredentials(info.getToken()));
TokenInfo info2 = authentication.getTokenInfo();
assertNotNull(info2);
assertEquals(info.getUserId(), info2.getUserId());
}
@Test
public void testAuthenticateNotMatchingToken() throws Exception {
TokenInfo info = tokenProvider.createToken(userId, ImmutableMap.of(TokenConstants.TOKEN_ATTRIBUTE + "_mandatory", "val"));
try {
authentication.authenticate(new TokenCredentials(info.getToken()));
fail("LoginException expected");
} catch (LoginException e) {
// success
}
}
@Test
public void testAuthenticateExpiredToken() throws Exception {
TokenProvider tp = new TokenProviderImpl(root,
ConfigurationParameters.of(TokenProvider.PARAM_TOKEN_EXPIRATION, 1),
getUserConfiguration());
TokenInfo info = tp.createToken(userId, Collections.<String, Object>emptyMap());
waitUntilExpired(info);
try {
new TokenAuthentication(tp).authenticate(new TokenCredentials(info.getToken()));
fail("LoginException expected");
} catch (LoginException e) {
// success
}
// expired token must have been removed
assertNull(tp.getTokenInfo(info.getToken()));
}
private void waitUntilExpired(@NotNull TokenInfo info) {
long now = System.currentTimeMillis();
while (!info.isExpired(now)) {
now = waitForSystemTimeIncrement(now);
}
}
@Test(expected = IllegalStateException.class)
public void testGetUserIdBeforeLogin() {
authentication.getUserId();
}
@Test
public void testGetUserId() throws LoginException {
TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
assertTrue(authentication.authenticate(new TokenCredentials(info.getToken())));
assertEquals(userId, authentication.getUserId());
}
@Test(expected = IllegalStateException.class)
public void testGetUserPrincipalBeforeLogin() {
authentication.getUserPrincipal();
}
@Test
public void testGetUserPrincipal() throws Exception {
TokenInfo info = tokenProvider.createToken(userId, Collections.<String, Object>emptyMap());
assertTrue(authentication.authenticate(new TokenCredentials(info.getToken())));
assertEquals(getTestUser().getPrincipal(), authentication.getUserPrincipal());
}
@Test
public void testAuthenticateRefreshToken() throws Exception {
TokenCredentials tc = new TokenCredentials("token");
TokenProvider tp = Mockito.mock(TokenProvider.class);
TokenInfo ti = Mockito.mock(TokenInfo.class);
Mockito.when(tp.getTokenInfo(Mockito.anyString())).thenReturn(ti);
Mockito.when(ti.isExpired(Mockito.anyLong())).thenReturn(false);
Mockito.when(ti.matches(tc)).thenReturn(true);
TokenAuthentication auth = new TokenAuthentication(tp);
try {
assertTrue(auth.authenticate(tc));
Mockito.verify(ti).resetExpiration(Mockito.anyLong());
} catch (LoginException e) {
fail(e.getMessage());
}
}
@Test
public void testAuthenticateSkipRefreshToken() throws Exception {
TokenCredentials tc = new TokenCredentials("token");
tc.setAttribute(TokenConstants.TOKEN_SKIP_REFRESH, "");
TokenProvider tp = Mockito.mock(TokenProvider.class);
TokenInfo ti = Mockito.mock(TokenInfo.class);
Mockito.when(tp.getTokenInfo(Mockito.anyString())).thenReturn(ti);
Mockito.when(ti.isExpired(Mockito.anyLong())).thenReturn(false);
Mockito.when(ti.matches(tc)).thenReturn(true);
TokenAuthentication auth = new TokenAuthentication(tp);
try {
assertTrue(auth.authenticate(tc));
Mockito.verify(ti, Mockito.never()).resetExpiration(Mockito.anyLong());
} catch (LoginException e) {
fail(e.getMessage());
}
}
@Test
public void testAuthenticateExpiredTokenMock() throws Exception {
TokenCredentials tc = new TokenCredentials("token");
TokenProvider tp = Mockito.mock(TokenProvider.class);
TokenInfo ti = Mockito.mock(TokenInfo.class);
Mockito.when(tp.getTokenInfo(Mockito.anyString())).thenReturn(ti);
Mockito.when(ti.isExpired(Mockito.anyLong())).thenReturn(true);
TokenAuthentication auth = new TokenAuthentication(tp);
try {
auth.authenticate(tc);
fail("LoginException expected");
} catch (LoginException e) {
// success
}
Mockito.verify(ti, Mockito.never()).matches(Mockito.any());
Mockito.verify(ti, Mockito.never()).resetExpiration(Mockito.anyLong());
}
}