Skip to content

Commit

Permalink
Ensure elytron is on the classpath before using it (#2967)
Browse files Browse the repository at this point in the history
* Ensure elytron is on the classpath before using it

* Safe wildfly-elytron usage on ElytronUserGroupAdapter

* Improved smoke tests covering more branches on ElytronUserGroupAdapterTest

* Fixing sonar code smells
  • Loading branch information
lampajr authored and web-flow committed Jun 5, 2023
1 parent f51c9b7 commit 3a32a10
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 58 deletions.
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);
}
}

0 comments on commit 3a32a10

Please sign in to comment.