implement ui client session id

This changeset introduces a client side session id, which is generated
once by the client (ui: apiClient) and is send with each request to server.
The server makes the session id available by the PrincipalCollection of the
subject.
This commit is contained in:
Sebastian Sdorra
2019-11-13 14:03:48 +01:00
parent f0f134daeb
commit 42ab81cf50
16 changed files with 367 additions and 166 deletions

View File

@@ -839,7 +839,7 @@
<jaxb.version>2.3.0</jaxb.version> <jaxb.version>2.3.0</jaxb.version>
<!-- event bus --> <!-- event bus -->
<legman.version>1.5.1</legman.version> <legman.version>1.6.0</legman.version>
<!-- webserver --> <!-- webserver -->
<jetty.version>9.4.22.v20191022</jetty.version> <jetty.version>9.4.22.v20191022</jetty.version>

View File

@@ -37,6 +37,8 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
import javax.annotation.Nullable;
/** /**
* Token used for authentication with bearer tokens. * Token used for authentication with bearer tokens.
* *
@@ -45,14 +47,17 @@ import org.apache.shiro.authc.AuthenticationToken;
*/ */
public final class BearerToken implements AuthenticationToken { public final class BearerToken implements AuthenticationToken {
private final SessionId sessionId;
private final String raw; private final String raw;
/** /**
* Constructs a new instance. * Constructs a new instance.
* *
* @param sessionId session id of the client
* @param raw raw bearer token * @param raw raw bearer token
*/ */
private BearerToken(String raw) { private BearerToken(SessionId sessionId, String raw) {
this.sessionId = sessionId;
this.raw = raw; this.raw = raw;
} }
@@ -67,13 +72,13 @@ public final class BearerToken implements AuthenticationToken {
} }
/** /**
* Returns always {@code null}. * Returns the session id or {@code null}.
* *
* @return {@code null} * @return session id or {@code null}
*/ */
@Override @Override
public Object getPrincipal() { public SessionId getPrincipal() {
return null; return sessionId;
} }
/** /**
@@ -85,6 +90,23 @@ public final class BearerToken implements AuthenticationToken {
*/ */
public static BearerToken valueOf(String raw){ public static BearerToken valueOf(String raw){
Preconditions.checkArgument(!Strings.isNullOrEmpty(raw), "raw token is required"); Preconditions.checkArgument(!Strings.isNullOrEmpty(raw), "raw token is required");
return new BearerToken(raw); return new BearerToken(null, raw);
}
/**
* Creates a new {@link BearerToken} from raw string representation for the given ui session id.
*
* @param sessionId session id of the client
* @param rawToken bearer token string representation
*
* @return new bearer token
*/
public static BearerToken create(@Nullable String sessionId, String rawToken) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(rawToken), "raw token is required");
SessionId session = null;
if (!Strings.isNullOrEmpty(sessionId)) {
session = SessionId.valueOf(sessionId);
}
return new BearerToken(session, rawToken);
} }
} }

View File

@@ -115,7 +115,7 @@ public final class DAORealmHelper {
UsernamePasswordToken upt = (UsernamePasswordToken) token; UsernamePasswordToken upt = (UsernamePasswordToken) token;
String principal = upt.getUsername(); String principal = upt.getUsername();
return getAuthenticationInfo(principal, null, null); return getAuthenticationInfo(principal, null, null, null);
} }
/** /**
@@ -129,8 +129,9 @@ public final class DAORealmHelper {
return new AuthenticationInfoBuilder(principal); return new AuthenticationInfoBuilder(principal);
} }
private SimpleAuthenticationInfo getAuthenticationInfo(
private AuthenticationInfo getAuthenticationInfo(String principal, String credentials, Scope scope) { String principal, String credentials, Scope scope, SessionId sessionId
) {
checkArgument(!Strings.isNullOrEmpty(principal), "username is required"); checkArgument(!Strings.isNullOrEmpty(principal), "username is required");
LOG.debug("try to authenticate {}", principal); LOG.debug("try to authenticate {}", principal);
@@ -150,6 +151,10 @@ public final class DAORealmHelper {
collection.add(user, realm); collection.add(user, realm);
collection.add(MoreObjects.firstNonNull(scope, Scope.empty()), realm); collection.add(MoreObjects.firstNonNull(scope, Scope.empty()), realm);
if (sessionId != null) {
collection.add(sessionId, realm);
}
String creds = credentials; String creds = credentials;
if (credentials == null) { if (credentials == null) {
@@ -170,7 +175,7 @@ public final class DAORealmHelper {
private String credentials; private String credentials;
private Scope scope; private Scope scope;
private Iterable<String> groups = Collections.emptySet(); private SessionId sessionId;
private AuthenticationInfoBuilder(String principal) { private AuthenticationInfoBuilder(String principal) {
this.principal = principal; this.principal = principal;
@@ -201,17 +206,17 @@ public final class DAORealmHelper {
return this; return this;
} }
// /** /**
// * With groups adds extra groups, besides those which come from the {@link GroupDAO}, to the authentication info. * With the session id.
// * *
// * @param groups extra groups * @param sessionId session id
// * *
// * @return {@code this} * @return {@code this}
// */ */
// public AuthenticationInfoBuilder withGroups(Iterable<String> groups) { public AuthenticationInfoBuilder withSessionId(SessionId sessionId) {
// this.groups = groups; this.sessionId = sessionId;
// return this; return this;
// } }
/** /**
* Build creates the authentication info from the given information. * Build creates the authentication info from the given information.
@@ -219,7 +224,7 @@ public final class DAORealmHelper {
* @return authentication info * @return authentication info
*/ */
public AuthenticationInfo build() { public AuthenticationInfo build() {
return getAuthenticationInfo(principal, credentials, scope); return getAuthenticationInfo(principal, credentials, scope, sessionId);
} }
} }

View File

@@ -0,0 +1,41 @@
package sonia.scm.security;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Objects;
/**
* Client side session id.
*/
public final class SessionId {
private final String value;
private SessionId(String value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SessionId sessionID = (SessionId) o;
return Objects.equals(value, sessionID.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public String toString() {
return value;
}
public static SessionId valueOf(String value) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(value), "session id could not be empty or null");
return new SessionId(value);
}
}

View File

@@ -116,6 +116,12 @@ public final class HttpUtil
*/ */
public static final String HEADER_SCM_CLIENT = "X-SCM-Client"; public static final String HEADER_SCM_CLIENT = "X-SCM-Client";
/**
* header for identifying the scm-manager client session
* @since 2.0.0
*/
public static final String HEADER_SCM_SESSION = "X-SCM-Session-ID";
/** Field description */ /** Field description */
public static final String HEADER_USERAGENT = "User-Agent"; public static final String HEADER_USERAGENT = "User-Agent";
@@ -698,8 +704,10 @@ public final class HttpUtil
String defaultValue) String defaultValue)
{ {
String value = request.getHeader(header); String value = request.getHeader(header);
if (value == null) {
return MoreObjects.firstNonNull(value, defaultValue); value = defaultValue;
}
return value;
} }
/** /**

View File

@@ -27,9 +27,6 @@ class DAORealmHelperTest {
@Mock @Mock
private UserDAO userDAO; private UserDAO userDAO;
@Mock
private GroupDAO groupDAO;
private DAORealmHelper helper; private DAORealmHelper helper;
@BeforeEach @BeforeEach
@@ -87,6 +84,21 @@ class DAORealmHelperTest {
assertThat(principals.oneByType(Scope.class)).isSameAs(scope); assertThat(principals.oneByType(Scope.class)).isSameAs(scope);
} }
@Test
void shouldReturnAuthenticationInfoWithSessionId() {
User user = new User("trillian");
when(userDAO.get("trillian")).thenReturn(user);
SessionId session = SessionId.valueOf("abc123");
AuthenticationInfo authenticationInfo = helper.authenticationInfoBuilder("trillian")
.withSessionId(session)
.build();
PrincipalCollection principals = authenticationInfo.getPrincipals();
assertThat(principals.oneByType(SessionId.class)).isSameAs(session);
}
@Test @Test
void shouldReturnAuthenticationInfoWithCredentials() { void shouldReturnAuthenticationInfoWithCredentials() {
User user = new User("trillian"); User user = new User("trillian");

View File

@@ -54,6 +54,31 @@ import javax.servlet.http.HttpServletRequest;
public class HttpUtilTest public class HttpUtilTest
{ {
@Test
public void testGetHeader() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getHeader("Test")).thenReturn("Value-One");
String value = HttpUtil.getHeader(request, "Test", "Fallback");
assertEquals("Value-One", value);
}
@Test
public void testGetHeaderWithDefaultValue() {
HttpServletRequest request = mock(HttpServletRequest.class);
String value = HttpUtil.getHeader(request, "Test", "Fallback");
assertEquals("Fallback", value);
}
@Test
public void testGetHeaderWithNullAsDefaultValue() {
HttpServletRequest request = mock(HttpServletRequest.class);
String value = HttpUtil.getHeader(request, "Test", null);
assertNull(value);
}
@Test @Test
public void concatenateTest() { public void concatenateTest() {
assertEquals( assertEquals(

View File

@@ -2,12 +2,20 @@ import { contextPath } from "./urls";
import { createBackendError, ForbiddenError, isBackendError, UnauthorizedError } from "./errors"; import { createBackendError, ForbiddenError, isBackendError, UnauthorizedError } from "./errors";
import { BackendErrorContent } from "./errors"; import { BackendErrorContent } from "./errors";
const sessionId = (
Date.now().toString(36) +
Math.random()
.toString(36)
.substr(2, 5)
).toUpperCase();
const applyFetchOptions: (p: RequestInit) => RequestInit = o => { const applyFetchOptions: (p: RequestInit) => RequestInit = o => {
o.credentials = "same-origin"; o.credentials = "same-origin";
o.headers = { o.headers = {
Cache: "no-cache", Cache: "no-cache",
// identify the request as ajax request // identify the request as ajax request
"X-Requested-With": "XMLHttpRequest" "X-Requested-With": "XMLHttpRequest",
"X-SCM-Session-ID": sessionId
}; };
return o; return o;
}; };

View File

@@ -104,6 +104,7 @@ public class BearerRealm extends AuthenticatingRealm
return helper.authenticationInfoBuilder(accessToken.getSubject()) return helper.authenticationInfoBuilder(accessToken.getSubject())
.withCredentials(bt.getCredentials()) .withCredentials(bt.getCredentials())
.withScope(Scopes.fromClaims(accessToken.getClaims())) .withScope(Scopes.fromClaims(accessToken.getClaims()))
.withSessionId(bt.getPrincipal())
.build(); .build();
} }

View File

@@ -70,7 +70,8 @@ public class BearerWebTokenGenerator extends SchemeBasedWebTokenGenerator
if (HttpUtil.AUTHORIZATION_SCHEME_BEARER.equalsIgnoreCase(scheme)) if (HttpUtil.AUTHORIZATION_SCHEME_BEARER.equalsIgnoreCase(scheme))
{ {
token = BearerToken.valueOf(authorization); String sessionId = request.getHeader(HttpUtil.HEADER_SCM_SESSION);
token = BearerToken.create(sessionId, authorization);
} }
return token; return token;

View File

@@ -43,7 +43,7 @@ import javax.servlet.http.HttpServletRequest;
import sonia.scm.util.HttpUtil; import sonia.scm.util.HttpUtil;
/** /**
* Creates an {@link BearerToken} from the {@link #COOKIE_NAME} * Creates an {@link BearerToken} from the {@link HttpUtil#COOKIE_BEARER_AUTHENTICATION}
* cookie. * cookie.
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
@@ -54,7 +54,7 @@ public class CookieBearerWebTokenGenerator implements WebTokenGenerator
{ {
/** /**
* Creates an {@link BearerToken} from the {@link #COOKIE_NAME} * Creates an {@link BearerToken} from the {@link HttpUtil#COOKIE_BEARER_AUTHENTICATION}
* cookie. * cookie.
* *
* @param request http servlet request * @param request http servlet request
@@ -73,7 +73,8 @@ public class CookieBearerWebTokenGenerator implements WebTokenGenerator
{ {
if (HttpUtil.COOKIE_BEARER_AUTHENTICATION.equals(cookie.getName())) if (HttpUtil.COOKIE_BEARER_AUTHENTICATION.equals(cookie.getName()))
{ {
token = BearerToken.valueOf(cookie.getValue()); String sessionId = HttpUtil.getHeader(request, HttpUtil.HEADER_SCM_SESSION, null);
token = BearerToken.create(sessionId, cookie.getValue());
break; break;
} }

View File

@@ -177,45 +177,26 @@ public class DefaultAdministrationContext implements AdministrationContext
//J+ //J+
} }
/** private void doRunAsInNonWebSessionContext(PrivilegedAction action) {
* Method description logger.trace("bind shiro security manager to current thread");
*
*
* @param action
*/
private void doRunAsInNonWebSessionContext(PrivilegedAction action)
{
if (logger.isTraceEnabled())
{
logger.trace("bind shiro security manager to current thread");
}
try try {
{
SecurityUtils.setSecurityManager(securityManager); SecurityUtils.setSecurityManager(securityManager);
Subject subject = createAdminSubject(); Subject subject = createAdminSubject();
ThreadState state = new SubjectThreadState(subject); ThreadState state = new SubjectThreadState(subject);
state.bind(); state.bind();
try try
{ {
if (logger.isInfoEnabled()) logger.info("execute action {} in administration context", action.getClass().getName());
{
logger.info("execute action {} in administration context",
action.getClass().getName());
}
action.run(); action.run();
} finally {
logger.trace("restore current thread state");
state.restore();
} }
finally } finally {
{
state.clear();
}
}
finally
{
SecurityUtils.setSecurityManager(null); SecurityUtils.setSecurityManager(null);
} }
} }

View File

@@ -84,11 +84,9 @@ class BearerRealmTest {
@Test @Test
void shouldDoGetAuthentication() { void shouldDoGetAuthentication() {
BearerToken bearerToken = BearerToken.valueOf("__bearer__"); BearerToken bearerToken = BearerToken.create("__session__", "__bearer__");
AccessToken accessToken = mock(AccessToken.class); AccessToken accessToken = mock(AccessToken.class);
Set<String> groups = ImmutableSet.of("HeartOfGold", "Puzzle42");
when(accessToken.getSubject()).thenReturn("trillian"); when(accessToken.getSubject()).thenReturn("trillian");
when(accessToken.getClaims()).thenReturn(new HashMap<>()); when(accessToken.getClaims()).thenReturn(new HashMap<>());
when(accessTokenResolver.resolve(bearerToken)).thenReturn(accessToken); when(accessTokenResolver.resolve(bearerToken)).thenReturn(accessToken);
@@ -96,6 +94,7 @@ class BearerRealmTest {
when(realmHelper.authenticationInfoBuilder("trillian")).thenReturn(builder); when(realmHelper.authenticationInfoBuilder("trillian")).thenReturn(builder);
when(builder.withCredentials("__bearer__")).thenReturn(builder); when(builder.withCredentials("__bearer__")).thenReturn(builder);
when(builder.withScope(any(Scope.class))).thenReturn(builder); when(builder.withScope(any(Scope.class))).thenReturn(builder);
when(builder.withSessionId(any(SessionId.class))).thenReturn(builder);
when(builder.build()).thenReturn(authenticationInfo); when(builder.build()).thenReturn(authenticationInfo);
AuthenticationInfo result = realm.doGetAuthenticationInfo(bearerToken); AuthenticationInfo result = realm.doGetAuthenticationInfo(bearerToken);

View File

@@ -33,47 +33,73 @@ package sonia.scm.web;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*; import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.security.BearerToken; import sonia.scm.security.BearerToken;
import sonia.scm.security.SessionId;
import sonia.scm.util.HttpUtil;
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
@RunWith(MockitoJUnitRunner.class) @ExtendWith(MockitoExtension.class)
public class BearerWebTokenGeneratorTest { class BearerWebTokenGeneratorTest {
private final BearerWebTokenGenerator tokenGenerator = new BearerWebTokenGenerator();
@Mock @Mock
private HttpServletRequest request; private HttpServletRequest request;
private final BearerWebTokenGenerator tokenGenerator = new BearerWebTokenGenerator();
@Test @Test
public void testCreateTokenWithWrongScheme() void shouldNotCreateTokenWithWrongScheme() {
{
when(request.getHeader("Authorization")).thenReturn("BASIC ASD"); when(request.getHeader("Authorization")).thenReturn("BASIC ASD");
assertNull(tokenGenerator.createToken(request));
}
@Test
public void testCreateTokenWithoutAuthorizationHeader(){
assertNull(tokenGenerator.createToken(request));
}
@Test
public void testCreateToken(){
when(request.getHeader("Authorization")).thenReturn("Bearer asd");
AuthenticationToken token = tokenGenerator.createToken(request); AuthenticationToken token = tokenGenerator.createToken(request);
assertNotNull(token);
assertThat(token, instanceOf(BearerToken.class)); assertThat(token).isNull();
}
@Test
void shouldNotCreateTokenWithoutAuthorizationHeader(){
AuthenticationToken token = tokenGenerator.createToken(request);
assertThat(token).isNull();
}
@Test
void shouldCreateToken(){
when(request.getHeader("Authorization")).thenReturn("Bearer asd");
AuthenticationToken token = tokenGenerator.createToken(request);
assertThat(token)
.isNotNull()
.isInstanceOf(BearerToken.class);
BearerToken bt = (BearerToken) token; BearerToken bt = (BearerToken) token;
assertThat(bt.getCredentials(), equalTo("asd")); assertThat(bt.getCredentials()).isEqualTo("asd");
}
@Test
void shouldCreateTokenWithSessionId(){
doReturn("Bearer asd").when(request).getHeader("Authorization");
doReturn("bcd123").when(request).getHeader(HttpUtil.HEADER_SCM_SESSION);
AuthenticationToken token = tokenGenerator.createToken(request);
assertThat(token)
.isNotNull()
.isInstanceOf(BearerToken.class);
BearerToken bt = (BearerToken) token;
assertThat(bt.getPrincipal()).isEqualTo(SessionId.valueOf("bcd123"));
assertThat(bt.getCredentials()).isEqualTo("asd");
} }
} }

View File

@@ -35,82 +35,81 @@ package sonia.scm.web;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.security.BearerToken; import sonia.scm.security.BearerToken;
import sonia.scm.security.SessionId;
import static org.junit.Assert.*; import sonia.scm.util.HttpUtil;
import static org.mockito.Mockito.*;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import sonia.scm.util.HttpUtil;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
//~--- JDK imports ------------------------------------------------------------
/** /**
* *
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
@RunWith(MockitoJUnitRunner.class) @ExtendWith(MockitoExtension.class)
public class CookieBearerWebTokenGeneratorTest class CookieBearerWebTokenGeneratorTest {
{
private final CookieBearerWebTokenGenerator tokenGenerator = new CookieBearerWebTokenGenerator();
@Mock
private HttpServletRequest request;
/**
* Method description
*
*/
@Test @Test
public void testCreateToken() void shouldCreateToken() {
{ assignBearerCookie("value");
Cookie c = mock(Cookie.class);
when(c.getName()).thenReturn(HttpUtil.COOKIE_BEARER_AUTHENTICATION);
when(c.getValue()).thenReturn("value");
when(request.getCookies()).thenReturn(new Cookie[] { c });
BearerToken token = tokenGenerator.createToken(request); BearerToken token = tokenGenerator.createToken(request);
assertNotNull(token); assertThat(token).isNotNull();
assertEquals("value", token.getCredentials()); assertThat(token.getPrincipal()).isNull();
assertThat(token.getCredentials()).isEqualTo("value");
} }
/**
* Method description
*
*/
@Test @Test
public void testCreateTokenWithWrongCookie() void shouldCreateTokenWithSessionId() {
{ when(request.getHeader(HttpUtil.HEADER_SCM_SESSION)).thenReturn("abc123");
assignBearerCookie("authc");
BearerToken token = tokenGenerator.createToken(request);
assertThat(token).isNotNull();
assertThat(token.getPrincipal()).isEqualTo(SessionId.valueOf("abc123"));
assertThat(token.getCredentials()).isEqualTo("authc");
}
private void assignBearerCookie(String value) {
assignCookie(HttpUtil.COOKIE_BEARER_AUTHENTICATION, value);
}
private void assignCookie(String name, String value) {
Cookie c = mock(Cookie.class); Cookie c = mock(Cookie.class);
when(c.getName()).thenReturn("other-cookie"); when(c.getName()).thenReturn(name);
when(request.getCookies()).thenReturn(new Cookie[] { c }); lenient().when(c.getValue()).thenReturn(value);
assertNull(tokenGenerator.createToken(request)); when(request.getCookies()).thenReturn(new Cookie[]{c});
} }
/**
* Method description
*
*/
@Test @Test
public void testCreateTokenWithoutCookies() void shouldNotCreateTokenForWrongCookie() {
{ assignCookie("other-cookie", "with-some-value");
assertNull(tokenGenerator.createToken(request));
BearerToken token = tokenGenerator.createToken(request);
assertThat(token).isNull();
} }
//~--- fields --------------------------------------------------------------- @Test
void shouldNotCreateTokenWithoutCookies() {
/** Field description */ BearerToken token = tokenGenerator.createToken(request);
private final CookieBearerWebTokenGenerator tokenGenerator = assertThat(token).isNull();
new CookieBearerWebTokenGenerator(); }
/** Field description */
@Mock
private HttpServletRequest request;
} }

View File

@@ -0,0 +1,72 @@
package sonia.scm.web.security;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
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 static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class DefaultAdministrationContextTest {
private DefaultAdministrationContext context;
@Mock
private Subject subject;
@BeforeEach
void create() {
Injector injector = Guice.createInjector();
SecurityManager securityManager = new DefaultSecurityManager();
context = new DefaultAdministrationContext(injector, securityManager);
}
@Test
void shouldBindSubject() {
context.runAsAdmin(() -> {
Subject adminSubject = SecurityUtils.getSubject();
assertThat(adminSubject.getPrincipal()).isEqualTo("scmsystem");
});
}
@Test
void shouldBindSubjectEvenIfAlreadyBound() {
ThreadContext.bind(subject);
try {
context.runAsAdmin(() -> {
Subject adminSubject = SecurityUtils.getSubject();
assertThat(adminSubject.getPrincipal()).isEqualTo("scmsystem");
});
} finally {
ThreadContext.unbindSubject();
}
}
@Test
void shouldRestoreCurrentSubject() {
when(subject.getPrincipal()).thenReturn("tricia");
ThreadContext.bind(subject);
try {
context.runAsAdmin(() -> {});
Subject currentSubject = SecurityUtils.getSubject();
assertThat(currentSubject.getPrincipal()).isEqualTo("tricia");
} finally {
ThreadContext.unbindSubject();
}
}
}