mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 15:05:44 +01:00
added option to define extra groups for AccessToken
This commit is contained in:
@@ -33,6 +33,7 @@ package sonia.scm.security;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An access token can be used to access scm-manager without providing username and password. An {@link AccessToken} can
|
||||
@@ -103,6 +104,13 @@ public interface AccessToken {
|
||||
*/
|
||||
Scope getScope();
|
||||
|
||||
/**
|
||||
* Returns name of groups, in which the user should be a member.
|
||||
*
|
||||
* @return name of groups
|
||||
*/
|
||||
Set<String> getGroups();
|
||||
|
||||
/**
|
||||
* Returns an optional value of a custom token field.
|
||||
*
|
||||
|
||||
@@ -99,6 +99,15 @@ public interface AccessTokenBuilder {
|
||||
*/
|
||||
AccessTokenBuilder scope(Scope scope);
|
||||
|
||||
/**
|
||||
* Define the logged in user as member of the given groups.
|
||||
*
|
||||
* @param groups group names
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
AccessTokenBuilder groups(String... groups);
|
||||
|
||||
/**
|
||||
* Creates a new {@link AccessToken} with the provided settings.
|
||||
*
|
||||
|
||||
@@ -37,7 +37,6 @@ import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
@@ -54,6 +53,8 @@ import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserDAO;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
@@ -63,8 +64,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
* @author Sebastian Sdorra
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class DAORealmHelper
|
||||
{
|
||||
public final class DAORealmHelper {
|
||||
|
||||
/**
|
||||
* the logger for DAORealmHelper
|
||||
@@ -111,35 +111,35 @@ public final class DAORealmHelper
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Creates {@link AuthenticationInfo} from a {@link UsernamePasswordToken}. The method accepts
|
||||
* {@link AuthenticationInfo} as argument, so that the caller does not need to cast.
|
||||
*
|
||||
* @param token authentication token, it must be {@link UsernamePasswordToken}
|
||||
*
|
||||
* @param token
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @return authentication info
|
||||
*/
|
||||
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
|
||||
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) {
|
||||
checkArgument(token instanceof UsernamePasswordToken, "%s is required", UsernamePasswordToken.class);
|
||||
|
||||
UsernamePasswordToken upt = (UsernamePasswordToken) token;
|
||||
String principal = upt.getUsername();
|
||||
|
||||
return getAuthenticationInfo(principal, null, null);
|
||||
return getAuthenticationInfo(principal, null, null, Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
* Returns a builder for {@link AuthenticationInfo}.
|
||||
*
|
||||
* @param principal name of principal (username)
|
||||
*
|
||||
* @param principal
|
||||
* @param credentials
|
||||
* @param scope
|
||||
*
|
||||
* @return
|
||||
* @return authentication info builder
|
||||
*/
|
||||
public AuthenticationInfo getAuthenticationInfo(String principal, String credentials, Scope scope) {
|
||||
public AuthenticationInfoBuilder authenticationInfoBuilder(String principal) {
|
||||
return new AuthenticationInfoBuilder(principal);
|
||||
}
|
||||
|
||||
|
||||
private AuthenticationInfo getAuthenticationInfo(String principal, String credentials, Scope scope, Iterable<String> groups) {
|
||||
checkArgument(!Strings.isNullOrEmpty(principal), "username is required");
|
||||
|
||||
LOG.debug("try to authenticate {}", principal);
|
||||
@@ -157,7 +157,7 @@ public final class DAORealmHelper
|
||||
|
||||
collection.add(principal, realm);
|
||||
collection.add(user, realm);
|
||||
collection.add(collectGroups(principal), realm);
|
||||
collection.add(collectGroups(principal, groups), realm);
|
||||
collection.add(MoreObjects.firstNonNull(scope, Scope.empty()), realm);
|
||||
|
||||
String creds = credentials;
|
||||
@@ -171,11 +171,15 @@ public final class DAORealmHelper
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
private GroupNames collectGroups(String principal) {
|
||||
private GroupNames collectGroups(String principal, Iterable<String> groupNames) {
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
builder.add(GroupNames.AUTHENTICATED);
|
||||
|
||||
for (String group : groupNames) {
|
||||
builder.add(group);
|
||||
}
|
||||
|
||||
for (Group group : groupDAO.getAll()) {
|
||||
if (group.isMember(principal)) {
|
||||
builder.add(group.getName());
|
||||
@@ -187,6 +191,69 @@ public final class DAORealmHelper
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link AuthenticationInfo}.
|
||||
*/
|
||||
public class AuthenticationInfoBuilder {
|
||||
|
||||
private final String principal;
|
||||
|
||||
private String credentials;
|
||||
private Scope scope;
|
||||
private Iterable<String> groups = Collections.emptySet();
|
||||
|
||||
private AuthenticationInfoBuilder(String principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* With credentials uses the given credentials for the {@link AuthenticationInfo}, this is particularly important
|
||||
* for caching purposes.
|
||||
*
|
||||
* @param credentials credentials such as password
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public AuthenticationInfoBuilder withCredentials(String credentials) {
|
||||
this.credentials = credentials;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* With the scope object it is possible to limit the access permissions to scm-manager.
|
||||
*
|
||||
* @param scope scope object
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public AuthenticationInfoBuilder withScope(Scope scope) {
|
||||
this.scope = scope;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* With groups adds extra groups, besides those which come from the {@link GroupDAO}, to the authentication info.
|
||||
*
|
||||
* @param groups extra groups
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public AuthenticationInfoBuilder withGroups(Iterable<String> groups) {
|
||||
this.groups = groups;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build creates the authentication info from the given information.
|
||||
*
|
||||
* @return authentication info
|
||||
*/
|
||||
public AuthenticationInfo build() {
|
||||
return getAuthenticationInfo(principal, credentials, scope, groups);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class RetryLimitPasswordMatcher implements CredentialsMatcher {
|
||||
|
||||
private final LoginAttemptHandler loginAttemptHandler;
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package sonia.scm.security;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
import org.apache.shiro.authc.UnknownAccountException;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupDAO;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserDAO;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DAORealmHelperTest {
|
||||
|
||||
@Mock
|
||||
private LoginAttemptHandler loginAttemptHandler;
|
||||
|
||||
@Mock
|
||||
private UserDAO userDAO;
|
||||
|
||||
@Mock
|
||||
private GroupDAO groupDAO;
|
||||
|
||||
private DAORealmHelper helper;
|
||||
|
||||
@BeforeEach
|
||||
void setUpObjectUnderTest() {
|
||||
helper = new DAORealmHelper(loginAttemptHandler, userDAO, groupDAO, "hitchhiker");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWithoutUsername() {
|
||||
assertThrows(IllegalArgumentException.class, () -> helper.authenticationInfoBuilder(null).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWithEmptyUsername() {
|
||||
assertThrows(IllegalArgumentException.class, () -> helper.authenticationInfoBuilder("").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWithUnknownUser() {
|
||||
assertThrows(UnknownAccountException.class, () -> helper.authenticationInfoBuilder("trillian").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionOnDisabledAccount() {
|
||||
User user = new User("trillian");
|
||||
user.setActive(false);
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
assertThrows(DisabledAccountException.class, () -> helper.authenticationInfoBuilder("trillian").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAuthenticationInfo() {
|
||||
User user = new User("trillian");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian").build();
|
||||
PrincipalCollection principals = authenticationInfo.getPrincipals();
|
||||
assertThat(principals.oneByType(User.class)).isSameAs(user);
|
||||
assertThat(principals.oneByType(GroupNames.class)).containsOnly("_authenticated");
|
||||
assertThat(principals.oneByType(Scope.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAuthenticationInfoWithGroups() {
|
||||
User user = new User("trillian");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
Group one = new Group("xml", "one", "trillian");
|
||||
Group two = new Group("xml", "two", "trillian");
|
||||
Group six = new Group("xml", "six", "dent");
|
||||
when(groupDAO.getAll()).thenReturn(ImmutableList.of(one, two, six));
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian")
|
||||
.withGroups(ImmutableList.of("three"))
|
||||
.build();
|
||||
|
||||
PrincipalCollection principals = authenticationInfo.getPrincipals();
|
||||
assertThat(principals.oneByType(GroupNames.class)).containsOnly("_authenticated", "one", "two", "three");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAuthenticationInfoWithScope() {
|
||||
User user = new User("trillian");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
Scope scope = Scope.valueOf("user:*", "group:*");
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian")
|
||||
.withScope(scope)
|
||||
.build();
|
||||
|
||||
PrincipalCollection principals = authenticationInfo.getPrincipals();
|
||||
assertThat(principals.oneByType(Scope.class)).isSameAs(scope);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAuthenticationInfoWithCredentials() {
|
||||
User user = new User("trillian");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian")
|
||||
.withCredentials("secret")
|
||||
.build();
|
||||
|
||||
assertThat(authenticationInfo.getCredentials()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAuthenticationInfoWithCredentialsFromUser() {
|
||||
User user = new User("trillian");
|
||||
user.setPassword("secret");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian").build();
|
||||
|
||||
assertThat(authenticationInfo.getCredentials()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWithWrongTypeOfToken() {
|
||||
assertThrows(IllegalArgumentException.class, () -> helper.getAuthenticationInfo(BearerToken.valueOf("__bearer__")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetAuthenticationInfo() {
|
||||
User user = new User("trillian");
|
||||
when(userDAO.get("trillian")).thenReturn(user);
|
||||
|
||||
AuthenticationInfo authenticationInfo = helper.getAuthenticationInfo(new UsernamePasswordToken("trillian", "secret"));
|
||||
|
||||
PrincipalCollection principals = authenticationInfo.getPrincipals();
|
||||
assertThat(principals.oneByType(User.class)).isSameAs(user);
|
||||
assertThat(principals.oneByType(GroupNames.class)).containsOnly("_authenticated");
|
||||
assertThat(principals.oneByType(Scope.class)).isEmpty();
|
||||
|
||||
assertThat(authenticationInfo.getCredentials()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -101,11 +101,11 @@ public class BearerRealm extends AuthenticatingRealm
|
||||
BearerToken bt = (BearerToken) token;
|
||||
AccessToken accessToken = tokenResolver.resolve(bt);
|
||||
|
||||
return helper.getAuthenticationInfo(
|
||||
accessToken.getSubject(),
|
||||
bt.getCredentials(),
|
||||
Scopes.fromClaims(accessToken.getClaims())
|
||||
);
|
||||
return helper.authenticationInfoBuilder(accessToken.getSubject())
|
||||
.withCredentials(bt.getCredentials())
|
||||
.withScope(Scopes.fromClaims(accessToken.getClaims()))
|
||||
.withGroups(accessToken.getGroups())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,12 +30,15 @@
|
||||
*/
|
||||
package sonia.scm.security;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.jsonwebtoken.Claims;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
@@ -49,6 +52,8 @@ public final class JwtAccessToken implements AccessToken {
|
||||
|
||||
public static final String REFRESHABLE_UNTIL_CLAIM_KEY = "scm-manager.refreshExpiration";
|
||||
public static final String PARENT_TOKEN_ID_CLAIM_KEY = "scm-manager.parentTokenId";
|
||||
public static final String GROUPS_CLAIM_KEY = "scm-manager.groups";
|
||||
|
||||
private final Claims claims;
|
||||
private final String compact;
|
||||
|
||||
@@ -103,6 +108,16 @@ public final class JwtAccessToken implements AccessToken {
|
||||
return Optional.ofNullable(claims.get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<String> getGroups() {
|
||||
Iterable<String> groups = claims.get(GROUPS_CLAIM_KEY, Iterable.class);
|
||||
if (groups != null) {
|
||||
return ImmutableSet.copyOf(groups);
|
||||
}
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String compact() {
|
||||
return compact;
|
||||
|
||||
@@ -39,9 +39,12 @@ import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
@@ -74,6 +77,7 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
|
||||
private Instant refreshExpiration;
|
||||
private String parentKeyId;
|
||||
private Scope scope = Scope.empty();
|
||||
private Set<String> groups = new HashSet<>();
|
||||
|
||||
private final Map<String,Object> custom = Maps.newHashMap();
|
||||
|
||||
@@ -134,6 +138,12 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtAccessTokenBuilder groups(String... groups) {
|
||||
Collections.addAll(this.groups, groups);
|
||||
return this;
|
||||
}
|
||||
|
||||
JwtAccessTokenBuilder refreshExpiration(Instant refreshExpiration) {
|
||||
this.refreshExpiration = refreshExpiration;
|
||||
this.refreshableFor = 0;
|
||||
@@ -195,6 +205,10 @@ public final class JwtAccessTokenBuilder implements AccessTokenBuilder {
|
||||
claims.setIssuer(issuer);
|
||||
}
|
||||
|
||||
if (!groups.isEmpty()) {
|
||||
claims.put(JwtAccessToken.GROUPS_CLAIM_KEY, groups);
|
||||
}
|
||||
|
||||
// sign token and create compact version
|
||||
String compact = Jwts.builder()
|
||||
.setClaims(claims)
|
||||
|
||||
@@ -31,11 +31,15 @@
|
||||
|
||||
package sonia.scm.security;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
@@ -43,6 +47,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -65,6 +70,9 @@ class BearerRealmTest {
|
||||
@Mock
|
||||
private DAORealmHelper realmHelper;
|
||||
|
||||
@Mock
|
||||
private DAORealmHelper.AuthenticationInfoBuilder builder;
|
||||
|
||||
@Mock
|
||||
private AccessTokenResolver accessTokenResolver;
|
||||
|
||||
@@ -84,15 +92,19 @@ class BearerRealmTest {
|
||||
void shouldDoGetAuthentication() {
|
||||
BearerToken bearerToken = BearerToken.valueOf("__bearer__");
|
||||
AccessToken accessToken = mock(AccessToken.class);
|
||||
when(accessToken.getSubject()).thenReturn("trillian");
|
||||
when(accessToken.getClaims()).thenReturn(new HashMap<>());
|
||||
|
||||
Set<String> groups = ImmutableSet.of("HeartOfGold", "Puzzle42");
|
||||
|
||||
when(accessToken.getSubject()).thenReturn("trillian");
|
||||
when(accessToken.getGroups()).thenReturn(groups);
|
||||
when(accessToken.getClaims()).thenReturn(new HashMap<>());
|
||||
when(accessTokenResolver.resolve(bearerToken)).thenReturn(accessToken);
|
||||
|
||||
// we have to use answer, because we could not mock the result of Scopes
|
||||
when(realmHelper.getAuthenticationInfo(
|
||||
anyString(), anyString(), any(Scope.class)
|
||||
)).thenAnswer(createAnswer("trillian", "__bearer__", true));
|
||||
when(realmHelper.authenticationInfoBuilder("trillian")).thenReturn(builder);
|
||||
when(builder.withGroups(groups)).thenReturn(builder);
|
||||
when(builder.withCredentials("__bearer__")).thenReturn(builder);
|
||||
when(builder.withScope(any(Scope.class))).thenReturn(builder);
|
||||
when(builder.build()).thenReturn(authenticationInfo);
|
||||
|
||||
AuthenticationInfo result = realm.doGetAuthenticationInfo(bearerToken);
|
||||
assertThat(result).isSameAs(authenticationInfo);
|
||||
@@ -102,25 +114,4 @@ class BearerRealmTest {
|
||||
void shouldThrowIllegalArgumentExceptionForWrongTypeOfToken() {
|
||||
assertThrows(IllegalArgumentException.class, () -> realm.doGetAuthenticationInfo(new UsernamePasswordToken()));
|
||||
}
|
||||
|
||||
private Answer<AuthenticationInfo> createAnswer(String expectedSubject, String expectedCredentials, boolean scopeEmpty) {
|
||||
return (iom) -> {
|
||||
String subject = iom.getArgument(0);
|
||||
assertThat(subject).isEqualTo(expectedSubject);
|
||||
String credentials = iom.getArgument(1);
|
||||
assertThat(credentials).isEqualTo(expectedCredentials);
|
||||
Scope scope = iom.getArgument(2);
|
||||
assertThat(scope.isEmpty()).isEqualTo(scopeEmpty);
|
||||
|
||||
return authenticationInfo;
|
||||
};
|
||||
}
|
||||
|
||||
private class MyAnswer implements Answer<AuthenticationInfo> {
|
||||
|
||||
@Override
|
||||
public AuthenticationInfo answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.isEmptyOrNullString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -135,6 +134,7 @@ public class JwtAccessTokenBuilderTest {
|
||||
.issuer("https://www.scm-manager.org")
|
||||
.expiresIn(5, TimeUnit.SECONDS)
|
||||
.custom("a", "b")
|
||||
.groups("one", "two", "three")
|
||||
.scope(Scope.valueOf("repo:*"))
|
||||
.build();
|
||||
|
||||
@@ -161,5 +161,6 @@ public class JwtAccessTokenBuilderTest {
|
||||
assertEquals(token.getIssuer().get(), "https://www.scm-manager.org");
|
||||
assertEquals("b", token.getCustom("a").get());
|
||||
assertEquals("[\"repo:*\"]", token.getScope().toString());
|
||||
assertThat(token.getGroups(), containsInAnyOrder("one", "two", "three"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user