Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.67.x] Ensure elytron is on the classpath before using it #2974

Merged
merged 1 commit into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,22 +214,4 @@
</dependency>

</dependencies>

<profiles>
<profile>
<id>tomcat9</id>
<activation>
<property>
<name>container.profile</name>
<value>tomcat9</value>
</property>
</activation>
<dependencies>
<dependency>
<groupId>org.wildfly.security</groupId>
<artifactId>wildfly-elytron-auth-server</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ public class ElytronIdentityProvider
extends BaseIdentityProvider {

public static boolean available() {
return SecurityDomain.getCurrent() != null;
try {
// ensure SecurityDomain is on classpath, if not directly return false
Class.forName("org.wildfly.security.auth.server.SecurityDomain");
return SecurityDomain.getCurrent() != null;
} catch (ClassNotFoundException e) {
return false;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@
import org.kie.server.services.impl.security.ElytronIdentityProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.PasswordGuessEvidence;

public class JMSSecurityAdapter implements SecurityAdapter {
private static final Logger logger = LoggerFactory.getLogger(JMSSecurityAdapter.class);
Expand All @@ -53,7 +51,17 @@ public class JMSSecurityAdapter implements SecurityAdapter {

private static ThreadLocal<UserDetails> currentUser = new ThreadLocal<UserDetails>();

private static boolean isElytronActive;
private static Class<?> passwordGuessEvidenceClass = null;

static {
try {
passwordGuessEvidenceClass = Class.forName("org.wildfly.security.evidence.PasswordGuessEvidence");
isElytronActive = true;
} catch (Exception e) {
isElytronActive = false;
}

for (SecurityAdapter adapter : securityAdapters) {
adapters.add(adapter);
}
Expand Down Expand Up @@ -96,7 +104,7 @@ public static void login(String user, String pass) {
logger.debug( "Unable to login via JAAS with message supplied user and password", e);
}
}
private static UserDetails getUserDetails(final String user, final String pass) throws LoginException, RealmUnavailableException {
private static UserDetails getUserDetails(final String user, final String pass) throws LoginException {

if (ElytronIdentityProvider.available()) {
return getElytronUserDetails(user, pass);
Expand All @@ -105,14 +113,22 @@ private static UserDetails getUserDetails(final String user, final String pass)
}
}

private static UserDetails getElytronUserDetails(final String user, final String password) throws RealmUnavailableException {
private static UserDetails getElytronUserDetails(final String user, final String password) {
final UserDetails userDetails = new UserDetails();
final List<String> roles = new ArrayList<>();

final SecurityIdentity identity = SecurityDomain.getCurrent().authenticate(user, new PasswordGuessEvidence(password.toCharArray()));
userDetails.setName(identity.getPrincipal().getName());
for (String role : identity.getRoles()) {
roles.add(role);
if (isElytronActive) {
try {
final SecurityIdentity identity = SecurityDomain.getCurrent().authenticate(user,
(Evidence) passwordGuessEvidenceClass.getDeclaredConstructor(char[].class).newInstance(password.toCharArray()));

userDetails.setName(identity.getPrincipal().getName());
for (String role : identity.getRoles()) {
roles.add(role);
}
} catch (Exception e) {
logger.error("Error getting Elytron user details", e);
}
}

userDetails.setRoles(roles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,37 @@
import java.util.stream.StreamSupport;

import org.jbpm.services.task.identity.adapter.UserGroupAdapter;
import org.kie.server.services.impl.security.ElytronIdentityProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wildfly.security.auth.server.RealmIdentity;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.authz.AuthorizationFailureException;

public class ElytronUserGroupAdapter implements UserGroupAdapter {

private static final Logger logger = LoggerFactory.getLogger(ElytronUserGroupAdapter.class);
private Class<?> authorizationFailureExceptionClass = null;

public ElytronUserGroupAdapter() {
try {
this.authorizationFailureExceptionClass = Class.forName("org.wildfly.security.authz.AuthorizationFailureException");
} catch (Exception var2) {
logger.info("Unable to find org.wildfly.security.authz.AuthorizationFailureException, disabling elytron adapter");
}
}

protected boolean isActive() {
return this.authorizationFailureExceptionClass != null;
}

@Override
public List<String> getGroupsForUser(String userId) {

String userName = getUserName();
logger.debug("Identifier Elytron as {}", userId);
if (userName == null) {

if (!isActive() || userName == null) {
return new ArrayList<>();
}

Expand All @@ -51,16 +64,18 @@ public List<String> getGroupsForUser(String userId) {
try {
if (runAsPrincipalExists(userId)) {
logger.debug("Executing run as {}", userId);
return toRunAsPrincipalRoles(userId, true);
} else {
return new ArrayList<>();
return toRunAsPrincipalRoles(userId, true);
}
} catch (Exception e) {
logger.debug("Run as {} failed", userId);
if (e.getClass().isAssignableFrom(authorizationFailureExceptionClass)) {
logger.debug("Executing run as {} without authorization", userId);
return toRunAsPrincipalRoles(userId, false);
}
} catch (AuthorizationFailureException ex) {
return toRunAsPrincipalRoles(userId, false);
} catch (RealmUnavailableException | SecurityException e) {
return new ArrayList<>();
}
}

return new ArrayList<>();
}

public List<String> toPrincipalRoles(String userId) {
Expand All @@ -87,24 +102,26 @@ public List<String> toRoles(SecurityIdentity securityIdentity) {
}

public boolean runAsPrincipalExists(String runAsPrincipal) throws RealmUnavailableException {
SecurityDomain securityDomain = SecurityDomain.getCurrent();
RealmIdentity realmIdentity = null;
try {
realmIdentity = securityDomain.getIdentity(runAsPrincipal);
return realmIdentity.exists();
} finally {
if (realmIdentity != null) {
realmIdentity.dispose();
if (isActive() && ElytronIdentityProvider.available()) {
SecurityDomain securityDomain = SecurityDomain.getCurrent();
RealmIdentity realmIdentity = null;
try {
realmIdentity = securityDomain.getIdentity(runAsPrincipal);
return realmIdentity.exists();
} finally {
if (realmIdentity != null) {
realmIdentity.dispose();
}
}
}

return false;
}

public Optional<SecurityIdentity> getCurrentSecurityIdentity() {
SecurityDomain securityDomain = SecurityDomain.getCurrent();
if (securityDomain == null) {
return Optional.empty();
} else {
return Optional.ofNullable(securityDomain.getCurrentSecurityIdentity());
if (isActive() && ElytronIdentityProvider.available()) {
return Optional.ofNullable(SecurityDomain.getCurrent().getCurrentSecurityIdentity());
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@

package org.kie.server.services.jbpm.security;

import java.util.Arrays;
import java.util.List;

import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.powermock.reflect.Whitebox;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.authz.AuthorizationFailureException;

import java.util.Arrays;
import java.util.List;

import static org.mockito.Mockito.lenient;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(MockitoJUnitRunner.class)
Expand All @@ -46,7 +49,9 @@ public class ElytronUserGroupAdapterTest {

@Test
public void testNoSecurityContext() {
when(adapter.getGroupsForUser(Mockito.anyObject())).thenCallRealMethod();
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(null);
List<String> roles = adapter.getGroupsForUser(USER_ID);

Expand All @@ -57,7 +62,9 @@ public void testNoSecurityContext() {

@Test
public void testSecurityContextNoIdentity() {
when(adapter.getGroupsForUser(Mockito.anyObject())).thenCallRealMethod();
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(USER_ID);

List<String> roles = adapter.getGroupsForUser(USER_ID);
Expand All @@ -69,7 +76,9 @@ public void testSecurityContextNoIdentity() {

@Test
public void testSecurityForWrongUser() throws RealmUnavailableException {
when(adapter.getGroupsForUser(Mockito.anyObject())).thenCallRealMethod();
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(USER_ID);
when(adapter.runAsPrincipalExists(WRONG_USER_ID)).thenReturn(true);
when(adapter.toRunAsPrincipalRoles(WRONG_USER_ID, true)).thenReturn(Arrays.asList(ROLE_1, ROLE_2));
Expand All @@ -84,9 +93,45 @@ public void testSecurityForWrongUser() throws RealmUnavailableException {

@Test
public void testSecurityForLoggedUser() {
when(adapter.getGroupsForUser(Mockito.anyObject())).thenCallRealMethod();
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(USER_ID);
when(adapter.toPrincipalRoles(Mockito.anyObject())).thenReturn(Arrays.asList(ROLE_1, ROLE_2, ROLE_3));
when(adapter.toPrincipalRoles(Mockito.anyString())).thenReturn(Arrays.asList(ROLE_1, ROLE_2, ROLE_3));

List<String> roles = adapter.getGroupsForUser(USER_ID);

Assertions.assertThat(roles)
.isNotNull()
.hasSize(3)
.contains(ROLE_1, ROLE_2, ROLE_3);
}

@Test
public void testSecurityOnRealmUnavailable() throws RealmUnavailableException {
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(WRONG_USER_ID);
when(adapter.runAsPrincipalExists(USER_ID)).thenThrow(new RealmUnavailableException());
lenient().when(adapter.toPrincipalRoles(Mockito.anyString())).thenReturn(Arrays.asList(ROLE_1, ROLE_2, ROLE_3));
lenient().when(adapter.toRunAsPrincipalRoles(Mockito.anyString(), Mockito.eq(false))).thenReturn(Arrays.asList(ROLE_1, ROLE_2, ROLE_3));

List<String> roles = adapter.getGroupsForUser(USER_ID);

Assertions.assertThat(roles)
.isNotNull()
.isEmpty();
}

@Test
public void testSecurityOnAuthorizationFailure() throws AuthorizationFailureException, RealmUnavailableException {
setAuthorizationFailureExceptionClass();
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(WRONG_USER_ID);
when(adapter.runAsPrincipalExists(USER_ID)).thenThrow(AuthorizationFailureException.class);
when(adapter.toRunAsPrincipalRoles(Mockito.anyString(), Mockito.eq(false))).thenReturn(Arrays.asList(ROLE_1, ROLE_2, ROLE_3));

List<String> roles = adapter.getGroupsForUser(USER_ID);

Expand All @@ -95,4 +140,30 @@ public void testSecurityForLoggedUser() {
.hasSize(3)
.contains(ROLE_1, ROLE_2, ROLE_3);
}

@Test
public void testSecurityElytronDisabled() throws RealmUnavailableException {
when(adapter.isActive()).thenCallRealMethod();
when(adapter.getGroupsForUser(Mockito.anyString())).thenCallRealMethod();
when(adapter.getUserName()).thenReturn(USER_ID);

List<String> roles = adapter.getGroupsForUser(USER_ID);

Assertions.assertThat(roles)
.isNotNull()
.isEmpty();
}

@Test
public void testToRolesWithEmptySecurityIdentity() {
List<String> roles = adapter.toRoles(null);
Assertions.assertThat(roles)
.isNotNull()
.isEmpty();
}

private void setAuthorizationFailureExceptionClass() {
Whitebox.setInternalState(adapter, Class.class, (Object) AuthorizationFailureException.class);
}
}