You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/08/01 14:34:49 UTC
[isis] branch v2 updated: ISIS-2156 adds smoketest for Secman using
delegated LDAP authentication
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/v2 by this push:
new d89166c ISIS-2156 adds smoketest for Secman using delegated LDAP authentication
d89166c is described below
commit d89166c1e5e304ca555a389f1f859b33fa715468
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Aug 1 16:34:40 2019 +0200
ISIS-2156 adds smoketest for Secman using delegated LDAP authentication
- testing for proper user auto-creation (initial state disabled)
- testing proper login/logout for existing user
---
.../isis/testdomain/ldap/LdapEmbeddedServer.java | 4 +-
.../testdomain/ldap/LdapEmbeddedServerTest.java | 2 +-
.../isis/testdomain/shiro/ShiroLdapTest.java | 6 +-
...ShiroLdapTest.java => ShiroSecmanLdapTest.java} | 70 +++++++++++++++++-----
.../smoketest/src/test/resources/ldap-users.ldif | 26 +++++---
.../smoketest/src/test/resources/shiro-ldap.ini | 9 +--
.../{shiro-ldap.ini => shiro-secman-ldap.ini} | 29 +++------
...oduleSecurityRegularUserRoleAndPermissions.java | 2 +-
...ticationStrategyForIsisModuleSecurityRealm.java | 29 +++++----
...inglePrincipalForApplicationUserInAnyRealm.java | 3 +-
10 files changed, 112 insertions(+), 68 deletions(-)
diff --git a/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServer.java b/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServer.java
index 9d1739b..4c8ef42 100644
--- a/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServer.java
+++ b/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServer.java
@@ -38,7 +38,7 @@ import lombok.val;
//@RunWith(FrameworkRunner.class) //when picked up as a regular JUnit Test just act as a no-op.
@CreateDS(name = "myDS",
partitions = {
- @CreatePartition(name = "test", suffix = "dc=myorg,dc=com")
+ @CreatePartition(name = "mojo", suffix = "o=mojo")
})
@CreateLdapServer(transports = {
@CreateTransport(protocol = "LDAP", address = "localhost", port = LdapEmbeddedServer.PORT)})
@@ -47,6 +47,8 @@ public class LdapEmbeddedServer extends AbstractLdapTestUnit {
/** IP port for the LDAP server to listen on */
public static final int PORT = 10389;
+ public static final String SVEN_PRINCIPAL = "cn=Sven Mojo,o=mojo";
+ public static final String OLAF_PRINCIPAL = "cn=Olaf Mojo,o=mojo";
@Test
public void authenticateAgainstLdap() {
diff --git a/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServerTest.java b/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServerTest.java
index 2d658d8..aa4c98a 100644
--- a/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServerTest.java
+++ b/examples/smoketest/src/test/java/org/apache/isis/testdomain/ldap/LdapEmbeddedServerTest.java
@@ -51,7 +51,7 @@ class LdapEmbeddedServerTest {
env.put(Context.PROVIDER_URL, "ldap://localhost:" + LdapEmbeddedServer.PORT);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put(Context.SECURITY_PRINCIPAL, "cn=Sven Tester,ou=Users,dc=myorg,dc=com");
+ env.put(Context.SECURITY_PRINCIPAL, LdapEmbeddedServer.SVEN_PRINCIPAL);
env.put(Context.SECURITY_CREDENTIALS, "pass");
try {
diff --git a/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java b/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java
index 9622069..673b469 100644
--- a/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java
+++ b/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import javax.inject.Inject;
import org.apache.isis.testdomain.jdo.JdoTestDomainModule_withShiro;
+import org.apache.isis.testdomain.ldap.LdapEmbeddedServer;
import org.apache.isis.testdomain.ldap.LdapServerService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
@@ -80,11 +81,10 @@ class ShiroLdapTest extends AbstractShiroTest {
val subject = SecurityUtils.getSubject();
assertNotNull(subject);
-
assertFalse(subject.isAuthenticated());
val token = (AuthenticationToken) new UsernamePasswordToken(
- "cn=Sven Tester,ou=Users,dc=myorg,dc=com",
+ LdapEmbeddedServer.SVEN_PRINCIPAL,
"pass");
subject.login(token);
@@ -106,7 +106,7 @@ class ShiroLdapTest extends AbstractShiroTest {
assertFalse(subject.isAuthenticated());
val token = (AuthenticationToken) new UsernamePasswordToken(
- "cn=Sven Tester,ou=Users,dc=myorg,dc=com",
+ LdapEmbeddedServer.SVEN_PRINCIPAL,
"invalid-pass");
assertThrows(Exception.class, ()->{
diff --git a/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java b/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdapTest.java
similarity index 59%
copy from examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java
copy to examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdapTest.java
index 9622069..22d27c2 100644
--- a/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroLdapTest.java
+++ b/examples/smoketest/src/test/java/org/apache/isis/testdomain/shiro/ShiroSecmanLdapTest.java
@@ -25,10 +25,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import javax.inject.Inject;
+import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
+import org.apache.isis.extensions.secman.api.role.ApplicationRoleRepository;
+import org.apache.isis.extensions.secman.api.user.ApplicationUserRepository;
+import org.apache.isis.extensions.secman.encryption.jbcrypt.IsisBootSecmanEncryptionJbcrypt;
+import org.apache.isis.extensions.secman.jdo.IsisBootSecmanPersistenceJdo;
+import org.apache.isis.extensions.secman.model.IsisBootSecmanModel;
+import org.apache.isis.extensions.secman.shiro.IsisBootSecmanRealmShiro;
import org.apache.isis.testdomain.jdo.JdoTestDomainModule_withShiro;
+import org.apache.isis.testdomain.ldap.LdapEmbeddedServer;
import org.apache.isis.testdomain.ldap.LdapServerService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.junit.jupiter.api.AfterAll;
@@ -38,7 +47,6 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import lombok.val;
-import lombok.extern.log4j.Log4j2;
@SpringBootTest(
classes = {
@@ -49,19 +57,28 @@ import lombok.extern.log4j.Log4j2;
"smoketest.withShiro=true", // enable shiro specific config to be picked up by Spring
})
@Import({
+
LdapServerService.class,
-})
-@Log4j2
-class ShiroLdapTest extends AbstractShiroTest {
- @Inject LdapServerService ldapServerService;
+ // Security Manager Extension (secman)
+ IsisBootSecmanModel.class,
+ IsisBootSecmanRealmShiro.class,
+ IsisBootSecmanPersistenceJdo.class,
+ IsisBootSecmanEncryptionJbcrypt.class,
+})
+class ShiroSecmanLdapTest extends AbstractShiroTest {
+ @Inject LdapServerService ldapServerService;
+ @Inject ApplicationUserRepository applicationUserRepository;
+ @Inject ApplicationRoleRepository applicationRoleRepository;
+ @Inject SecurityModuleConfig securityModuleConfig;
+
@BeforeAll
static void beforeClass() {
- // Build and set the SecurityManager used to build Subject instances used in your tests
- // This typically only needs to be done once per class if your shiro.ini doesn't change,
- // otherwise, you'll need to do this logic in each test that is different
- val factory = new IniSecurityManagerFactory("classpath:shiro-ldap.ini");
+ // Build and set the SecurityManager used to build Subject instances used in your tests
+ // This typically only needs to be done once per class if your shiro.ini doesn't change,
+ // otherwise, you'll need to do this logic in each test that is different
+ val factory = new IniSecurityManagerFactory("classpath:shiro-secman-ldap.ini");
setSecurityManager(factory.getInstance());
}
@@ -73,18 +90,22 @@ class ShiroLdapTest extends AbstractShiroTest {
@Test
void loginLogoutRoundtrip() {
- log.info("starting login/logout roundtrip");
-
+ // setup sven account in DB
+ val regularUserRoleName = securityModuleConfig.getRegularUserRoleName();
+ val regularUserRole = applicationRoleRepository.findByName(regularUserRoleName);
+ val enabled = true;
+ applicationUserRepository.newDelegateUser(LdapEmbeddedServer.SVEN_PRINCIPAL, regularUserRole, enabled);
+ //
+
val secMan = SecurityUtils.getSecurityManager();
assertNotNull(secMan);
val subject = SecurityUtils.getSubject();
assertNotNull(subject);
-
assertFalse(subject.isAuthenticated());
val token = (AuthenticationToken) new UsernamePasswordToken(
- "cn=Sven Tester,ou=Users,dc=myorg,dc=com",
+ LdapEmbeddedServer.SVEN_PRINCIPAL,
"pass");
subject.login(token);
@@ -94,6 +115,27 @@ class ShiroLdapTest extends AbstractShiroTest {
assertFalse(subject.isAuthenticated());
}
+
+ @Test
+ void login_withAccount_thatOnlyExistsWithLdap() {
+
+ val secMan = getSecurityManager();
+ assertNotNull(secMan);
+
+ val subject = SecurityUtils.getSubject();
+ assertNotNull(subject);
+ assertFalse(subject.isAuthenticated());
+
+ val token = (AuthenticationToken) new UsernamePasswordToken(
+ LdapEmbeddedServer.OLAF_PRINCIPAL,
+ "pass");
+
+ // default behavior is to create the account within the DB but leave it disabled
+ assertThrows(DisabledAccountException.class, ()->{
+ subject.login(token);
+ });
+
+ }
@Test
void login_withInvalidPassword() {
@@ -106,7 +148,7 @@ class ShiroLdapTest extends AbstractShiroTest {
assertFalse(subject.isAuthenticated());
val token = (AuthenticationToken) new UsernamePasswordToken(
- "cn=Sven Tester,ou=Users,dc=myorg,dc=com",
+ LdapEmbeddedServer.SVEN_PRINCIPAL,
"invalid-pass");
assertThrows(Exception.class, ()->{
diff --git a/examples/smoketest/src/test/resources/ldap-users.ldif b/examples/smoketest/src/test/resources/ldap-users.ldif
index 61e3a9b..cb36e94 100644
--- a/examples/smoketest/src/test/resources/ldap-users.ldif
+++ b/examples/smoketest/src/test/resources/ldap-users.ldif
@@ -18,27 +18,37 @@
#
version: 1
-dn: dc=myorg,dc=com
+dn: o=mojo
objectClass: domain
objectClass: top
dc: myorg
-dn: ou=Users,dc=myorg,dc=com
+dn: ou=users,o=mojo
objectClass: organizationalUnit
objectClass: top
-ou: Users
+ou: users
-dn: ou=Groups,dc=myorg,dc=com
+dn: ou=groups,o=mojo
objectClass: organizationalUnit
objectClass: top
-ou: Groups
+ou: groups
-dn: cn=Sven Tester,ou=Users,dc=myorg,dc=com
+dn: cn=Sven Mojo,o=mojo
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
-cn: Sven Tester
-sn: Ldap
+cn: Sven Mojo
+sn: Mojo
uid: sven
+userPassword: pass
+
+dn: cn=Olaf Mojo,o=mojo
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Olaf Mojo
+sn: Mojo
+uid: olaf
userPassword: pass
\ No newline at end of file
diff --git a/examples/smoketest/src/test/resources/shiro-ldap.ini b/examples/smoketest/src/test/resources/shiro-ldap.ini
index 2340e4b..fef9ace 100644
--- a/examples/smoketest/src/test/resources/shiro-ldap.ini
+++ b/examples/smoketest/src/test/resources/shiro-ldap.ini
@@ -50,14 +50,11 @@ ldapRealm.permissionsByRole=\
securityManager.realms = $ldapRealm
-#authenticationStrategy=org.apache.isis.extensions.secman.shiro.AuthenticationStrategyForIsisModuleSecurityRealm
-#isisModuleSecurityRealm=org.apache.isis.extensions.secman.shiro.IsisModuleSecurityRealm
-
-#securityManager.authenticator.authenticationStrategy = $authenticationStrategy
-#securityManager.realms = $isisModuleSecurityRealm
-
[users]
+# unused
+
[roles]
+# unused
diff --git a/examples/smoketest/src/test/resources/shiro-ldap.ini b/examples/smoketest/src/test/resources/shiro-secman-ldap.ini
similarity index 64%
copy from examples/smoketest/src/test/resources/shiro-ldap.ini
copy to examples/smoketest/src/test/resources/shiro-secman-ldap.ini
index 2340e4b..1429f38 100644
--- a/examples/smoketest/src/test/resources/shiro-ldap.ini
+++ b/examples/smoketest/src/test/resources/shiro-secman-ldap.ini
@@ -28,36 +28,23 @@ contextFactory.systemPassword = secret
ldapRealm = org.apache.isis.security.shiro.IsisLdapRealm
ldapRealm.contextFactory = $contextFactory
-
ldapRealm.searchBase = ou=groups,o=mojo
ldapRealm.groupObjectClass = groupOfUniqueNames
ldapRealm.uniqueMemberAttribute = uniqueMember
ldapRealm.uniqueMemberAttributeValueTemplate = uid={0}
-# optional mapping from physical groups to logical application roles
-ldapRealm.rolesByGroup = \
- LDN_USERS: user_role,\
- NYK_USERS: user_role,\
- HKG_USERS: user_role,\
- GLOBAL_ADMIN: admin_role,\
- DEMOS: self-install_role
-
-ldapRealm.permissionsByRole=\
- user_role = *:InventoryManager:*:*,\
- *:Product:*:*; \
- self-install_role = *:FixtureScriptsDefault:*:* ; \
- admin_role = *
-
-securityManager.realms = $ldapRealm
+authenticationStrategy=org.apache.isis.extensions.secman.shiro.AuthenticationStrategyForIsisModuleSecurityRealm
+isisModuleSecurityRealm=org.apache.isis.extensions.secman.shiro.IsisModuleSecurityRealm
+isisModuleSecurityRealm.delegateAuthenticationRealm=$ldapRealm
-#authenticationStrategy=org.apache.isis.extensions.secman.shiro.AuthenticationStrategyForIsisModuleSecurityRealm
-#isisModuleSecurityRealm=org.apache.isis.extensions.secman.shiro.IsisModuleSecurityRealm
-
-#securityManager.authenticator.authenticationStrategy = $authenticationStrategy
-#securityManager.realms = $isisModuleSecurityRealm
+securityManager.authenticator.authenticationStrategy = $authenticationStrategy
+securityManager.realms = $isisModuleSecurityRealm
[users]
+# unused
+
[roles]
+# unused
diff --git a/extensions/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/IsisModuleSecurityRegularUserRoleAndPermissions.java b/extensions/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/IsisModuleSecurityRegularUserRoleAndPermissions.java
index 01eaa2b..0ec2fd6 100644
--- a/extensions/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/IsisModuleSecurityRegularUserRoleAndPermissions.java
+++ b/extensions/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/seed/scripts/IsisModuleSecurityRegularUserRoleAndPermissions.java
@@ -31,7 +31,7 @@ import org.apache.isis.extensions.secman.model.app.user.MeService;
*/
public class IsisModuleSecurityRegularUserRoleAndPermissions extends AbstractRoleAndPermissionsFixtureScript {
- //public static final String ROLE_NAME = "isis-module-security-regular-user";
+ //public static final String ROLE_NAME = "isis-module-security-regular-user"; .. moved to SecurityModuleConfig
public IsisModuleSecurityRegularUserRoleAndPermissions(SecurityModuleConfig configBean) {
super(configBean.getRegularUserRoleName(), "Regular user of the security module");
diff --git a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthenticationStrategyForIsisModuleSecurityRealm.java b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthenticationStrategyForIsisModuleSecurityRealm.java
index dab0357..3f702d6 100644
--- a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthenticationStrategyForIsisModuleSecurityRealm.java
+++ b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/AuthenticationStrategyForIsisModuleSecurityRealm.java
@@ -27,26 +27,31 @@ import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.realm.Realm;
+import lombok.val;
+
public class AuthenticationStrategyForIsisModuleSecurityRealm extends AllSuccessfulStrategy {
/**
- * Reconfigures the SimpleAuthenticationInfo to use a implementation for storing its PrincipalCollections.
+ * Reconfigures the SimpleAuthenticationInfo to use an implementation for storing its PrincipalCollections.
*
* <p>
- * The default implementation uses a {@link org.apache.shiro.subject.SimplePrincipalCollection}, however this
- * doesn't play well with the Isis Addons' security module which ends up chaining together multiple instances of
- * {@link org.apache.isis.extensions.secman.shiro.PrincipalForApplicationUser} for each login. This is probably
- * because of it doing double duty with holding authorization information. There may be a better design here,
- * but for now the solution I've chosen is to use a different implementation of
- * {@link org.apache.shiro.subject.PrincipalCollection} that will only ever store one instance of
- * {@link org.apache.isis.extensions.secman.shiro.PrincipalForApplicationUser} as a principal.
+ * The default implementation uses a {@link org.apache.shiro.subject.SimplePrincipalCollection},
+ * however this doesn't play well with the Isis security module which ends up chaining
+ * together multiple instances of {@link PrincipalForApplicationUser} for each login.
+ * This is probably because of it doing double duty with holding authorization information.
+ * There may be a better design here, but for now the solution I've chosen is to use a different
+ * implementation of {@link org.apache.shiro.subject.PrincipalCollection} that will only ever store
+ * one instance of {@link PrincipalForApplicationUser} as a principal.
* </p>
*/
@Override
- public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
- final SimpleAuthenticationInfo authenticationInfo = (SimpleAuthenticationInfo) super.beforeAllAttempts(realms, token);
-
- authenticationInfo.setPrincipals(new PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm());
+ public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token)
+ throws AuthenticationException {
+
+ val authenticationInfo = (SimpleAuthenticationInfo) super.beforeAllAttempts(realms, token);
+ val principalCollection = new PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm();
+
+ authenticationInfo.setPrincipals(principalCollection);
return authenticationInfo;
}
diff --git a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm.java b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm.java
index 3576c22..9550069 100644
--- a/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm.java
+++ b/extensions/secman/realm-shiro/src/main/java/org/apache/isis/extensions/secman/shiro/PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm.java
@@ -23,7 +23,8 @@ import java.util.Collection;
import org.apache.shiro.subject.SimplePrincipalCollection;
@SuppressWarnings("rawtypes")
-public class PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm extends SimplePrincipalCollection {
+public class PrincipalCollectionWithSinglePrincipalForApplicationUserInAnyRealm
+extends SimplePrincipalCollection {
private static final long serialVersionUID = 1L;