Index: lams_build/build.xml =================================================================== diff -u -r5661c79b661c18221c0eb5e32fe7ec54110e5a65 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_build/build.xml (.../build.xml) (revision 5661c79b661c18221c0eb5e32fe7ec54110e5a65) +++ lams_build/build.xml (.../build.xml) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -650,7 +650,7 @@ + file="lib/micrometer/micrometer.module.xml" overwrite="true" verbose="true" /> @@ -742,8 +742,33 @@ - + + + ${ant.project.name}: Deploying custom Elytron security module + + + + + + + + + + + + + + + + + + + + ${ant.project.name}: Deploying configuration files Index: lams_build/build_base.xml =================================================================== diff -u -r1c8910051a18a9f9069876c35bd35ccad81e3955 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_build/build_base.xml (.../build_base.xml) (revision 1c8910051a18a9f9069876c35bd35ccad81e3955) +++ lams_build/build_base.xml (.../build_base.xml) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -37,7 +37,6 @@ - @@ -62,6 +61,8 @@ + + Index: lams_build/conf/standalone.xml =================================================================== diff -u -r08c6a122c4ad83bf970409c0875bd1768d205c92 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_build/conf/standalone.xml (.../standalone.xml) (revision 08c6a122c4ad83bf970409c0875bd1768d205c92) +++ lams_build/conf/standalone.xml (.../standalone.xml) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -335,9 +335,10 @@ + - + @@ -362,8 +363,8 @@ - - + + @@ -376,7 +377,12 @@ - + + + + + + @@ -608,7 +614,7 @@ - + Index: lams_build/lib/elytron/elytron.module.xml =================================================================== diff -u --- lams_build/lib/elytron/elytron.module.xml (revision 0) +++ lams_build/lib/elytron/elytron.module.xml (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + \ No newline at end of file Index: lams_central/.classpath =================================================================== diff -u -r5e9e3c6915701ed7a340d47ff6d32c12ccb9e800 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/.classpath (.../.classpath) (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) +++ lams_central/.classpath (.../.classpath) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -38,6 +38,8 @@ + + Index: lams_central/build.xml =================================================================== diff -u -r924d4186e55122ce91b5dcaba3311ab026060e94 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/build.xml (.../build.xml) (revision 924d4186e55122ce91b5dcaba3311ab026060e94) +++ lams_central/build.xml (.../build.xml) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -79,12 +79,16 @@ - + + + + + - \ No newline at end of file Index: lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java =================================================================== diff -u -rcb01d2d8c064728cb0e2ac524d2f89622a534e39 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java (.../DatabaseAuthenticator.java) (revision cb01d2d8c064728cb0e2ac524d2f89622a534e39) +++ lams_central/src/java/org/lamsfoundation/lams/security/DatabaseAuthenticator.java (.../DatabaseAuthenticator.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -33,7 +33,8 @@ import javax.sql.DataSource; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.lamsfoundation.lams.util.HashUtil; /** Index: lams_central/src/java/org/lamsfoundation/lams/security/RealmIdentity.java =================================================================== diff -u -r08c6a122c4ad83bf970409c0875bd1768d205c92 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/src/java/org/lamsfoundation/lams/security/RealmIdentity.java (.../RealmIdentity.java) (revision 08c6a122c4ad83bf970409c0875bd1768d205c92) +++ lams_central/src/java/org/lamsfoundation/lams/security/RealmIdentity.java (.../RealmIdentity.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -11,18 +11,16 @@ import java.security.Principal; import java.security.spec.AlgorithmParameterSpec; -import java.util.Arrays; import java.util.Set; +/** + * Custom Elytron realm identity. It delegates authentication and authorisation to UniversalLoginModule. + */ public class RealmIdentity implements org.wildfly.security.auth.server.RealmIdentity { private final Principal principal; - private final String password; - private final Set roles; - public RealmIdentity(Principal principal, String password, Set roles) { + public RealmIdentity(Principal principal) { this.principal = principal; - this.password = password; - this.roles = roles; } @Override @@ -32,20 +30,17 @@ @Override public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, - AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { - // do not support credential acquire + AlgorithmParameterSpec parameterSpec) { return SupportLevel.UNSUPPORTED; } @Override - public C getCredential(Class credentialType) throws RealmUnavailableException { - // do not return credentials + public C getCredential(Class credentialType) { return null; } @Override - public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) - throws RealmUnavailableException { + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) { return PasswordGuessEvidence.class.isAssignableFrom(evidenceType) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; @@ -55,7 +50,7 @@ public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { if (evidence instanceof PasswordGuessEvidence) { PasswordGuessEvidence guess = (PasswordGuessEvidence) evidence; - return Arrays.equals(password.toCharArray(), guess.getGuess()); + return UniversalLoginModule.validatePassword(principal.getName(), String.valueOf(guess.getGuess())); } return false; } @@ -67,6 +62,7 @@ @Override public Attributes getAttributes() throws RealmUnavailableException { + Set roles = UniversalLoginModule.getRoles(principal.getName()); if (roles == null || roles.isEmpty()) { return Attributes.EMPTY; } Index: lams_central/src/java/org/lamsfoundation/lams/security/SecurityRealm.java =================================================================== diff -u -r08c6a122c4ad83bf970409c0875bd1768d205c92 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/src/java/org/lamsfoundation/lams/security/SecurityRealm.java (.../SecurityRealm.java) (revision 08c6a122c4ad83bf970409c0875bd1768d205c92) +++ lams_central/src/java/org/lamsfoundation/lams/security/SecurityRealm.java (.../SecurityRealm.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -1,60 +1,44 @@ package org.lamsfoundation.lams.security; -import org.wildfly.security.credential.Credential; import org.wildfly.security.auth.SupportLevel; -import org.wildfly.security.auth.realm.CacheableSecurityRealm; +import org.wildfly.security.auth.server.RealmIdentity; import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.credential.Credential; import org.wildfly.security.evidence.Evidence; import org.wildfly.security.evidence.PasswordGuessEvidence; -import org.wildfly.security.auth.server.RealmIdentity; +import javax.naming.Context; +import javax.naming.NamingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.security.Principal; import java.security.spec.AlgorithmParameterSpec; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; -public class SecurityRealm implements CacheableSecurityRealm { +/** + * Custom Elytron security realm. + * It is deployed in a JAR as WildFly org.lamsfoundation.lams.security module. + */ +public class SecurityRealm implements org.wildfly.security.auth.server.SecurityRealm { + protected String dsJndiName; + protected String loginModuleJndiName; + protected Object loginModule; - private Map users; - private Map> roles; - - public SecurityRealm() { - // nothing + /** + * Initialise the realm with the configuration. It gets run by WildFly on startup. It is filled with configuration + * from standalone.xml. + */ + public void initialize(final Map configuration) { + dsJndiName = configuration.get("dsJndiName"); + loginModuleJndiName = configuration.get("loginModuleJndiName"); } - public SecurityRealm(Map map) { - // test - initialize(map); - } + // If we make this Security Realm implement CacheableSecurityRealm, we need to implement this method. + // @Override + // public void registerIdentityChangeListener(Consumer cnsmr) { + // } - public void initialize(Map map) { - users = new HashMap<>(); - roles = new HashMap<>(); - //Adding Guest users - String guest[] = new String[] { "Guest" }; - for (Map.Entry entry : map.entrySet()) { - // user and password - users.put(entry.getKey(), entry.getValue()); - roles.put(entry.getKey(), new HashSet(Arrays.asList(guest))); - } - // Adding other users - String array[] = new String[] { "SYSADMIN", "AUTHOR" }; - users.put("test1", "test1"); - roles.put("test1", new HashSet(Arrays.asList(array))); - System.out.println("I've loaded " + users.size() + " users "); - } - @Override - public void registerIdentityChangeListener(Consumer cnsmr) { - // nothing - } - - @Override public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { return SupportLevel.UNSUPPORTED; @@ -69,19 +53,32 @@ } @Override - public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { - // just search the user in the configured users - String password = users.get(principal.getName()); - if (password != null) { - return new org.lamsfoundation.lams.security.RealmIdentity(principal, password, - roles.get(principal.getName())); + public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException { + if (loginModule == null) { + // Obtain UniversalLoginModule as EJB3 from JNDI + // We can not use UniversalLoginModule directly + // as this class needs to be independent on LAMS code. + try { + Context context = new javax.naming.InitialContext(); + loginModule = context.lookup(loginModuleJndiName); + if (loginModule == null) { + throw new RealmUnavailableException("Could not find login module"); + } + // pass configuration to login module + Method method = loginModule.getClass().getMethod("setDsJndiName", String.class); + method.invoke(loginModule, dsJndiName); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NamingException e) { + e.printStackTrace(); + throw new RealmUnavailableException("Could not fetch login module", e); + } } - return RealmIdentity.NON_EXISTENT; - } - @Override - public String toString() { - return "SecurityRealm: " + this.users.keySet().stream().collect(Collectors.toList()); + try { + Method method = loginModule.getClass().getMethod("getRealmIdentity", Principal.class); + RealmIdentity realmIdentity = (RealmIdentity) method.invoke(loginModule, principal); + return realmIdentity == null ? RealmIdentity.NON_EXISTENT : realmIdentity; + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RealmUnavailableException("Could not authenticate", e); + } } - } \ No newline at end of file Fisheye: Tag 665bafc925d571fea2e089e161a7d164d73c96bd refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/security/SimpleGroup.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 665bafc925d571fea2e089e161a7d164d73c96bd refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/security/SimplePrincipal.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java =================================================================== diff -u -rcb01d2d8c064728cb0e2ac524d2f89622a534e39 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision cb01d2d8c064728cb0e2ac524d2f89622a534e39) +++ lams_central/src/java/org/lamsfoundation/lams/security/UniversalLoginModule.java (.../UniversalLoginModule.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -22,36 +22,9 @@ */ package org.lamsfoundation.lams.security; -import java.io.IOException; -import java.security.Principal; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.FailedLoginException; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; -import javax.sql.DataSource; - import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -import org.lamsfoundation.lams.themes.Theme; +import org.apache.logging.log4j.Logger; import org.lamsfoundation.lams.themes.dto.ThemeDTO; import org.lamsfoundation.lams.themes.service.IThemeService; import org.lamsfoundation.lams.usermanagement.AuthenticationMethodType; @@ -68,22 +41,27 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; +import org.wildfly.security.auth.server.RealmUnavailableException; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; +import java.security.Principal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + /** - * Covers all LAMS authentication. + * Covers all LAMS authentication. It is fetched by org.lamsfoundation.lams.security.SecurityRealm as EJB3 from JNDI. */ -public class UniversalLoginModule implements LoginModule { +public class UniversalLoginModule { private static Logger log = LogManager.getLogger(UniversalLoginModule.class); - private Subject subject; - private CallbackHandler callbackHandler; - /** - * Flag indicating if the login 1st phase succeeded. - */ - private boolean loginOK; - private Principal identity; - private char[] credential; - private static String dsJndiName; private static final Map internalAuthenticationTokens = new TreeMap<>(); @@ -92,187 +70,43 @@ private static IUserManagementService userManagementService; private static final long INTERNAL_AUTHENTICATION_TIMEOUT = 60 * 1000; - private static final String ROLES_QUERY = "SELECT DISTINCT r.name,'Roles' FROM lams_user u " - + "LEFT OUTER JOIN lams_user_organisation uo USING(user_id) " - + "LEFT OUTER JOIN lams_user_organisation_role urr USING(user_organisation_id) " - + "LEFT OUTER JOIN lams_role r USING (role_id) " + "WHERE u.login=?"; + private static final String ROLES_QUERY = + "SELECT DISTINCT r.name FROM lams_user u " + "LEFT OUTER JOIN lams_user_organisation uo USING(user_id) " + + "LEFT OUTER JOIN lams_user_organisation_role urr USING(user_organisation_id) " + + "LEFT OUTER JOIN lams_role r USING (role_id) WHERE u.login=?"; /** - * Method to commit the authentication process (phase 2). + * Set the JNDI name of the datasource. It is called by org.lamsfoundation.lams.security.SecurityRealm using + * reflection. */ - @Override - public boolean commit() throws LoginException { - if (!loginOK) { - return false; - } + public void setDsJndiName(String dsJndiName) { + UniversalLoginModule.dsJndiName = dsJndiName; - /* - * If the login method completed successfully as indicated by - * loginOK == true, this method adds the identity value to the subject's principals set. It also adds the - * members of - * each Group returned by getRoleSets() to the subject's principals Set. - */ - Set principals = subject.getPrincipals(); - principals.add(identity); - for (SimpleGroup group : getRoleSets()) { - String name = group.getName(); - SimpleGroup subjectGroup = createGroup(name, principals); - // Copy the group members to the Subject group - Enumeration members = group.members(); - while (members.hasMoreElements()) { - Principal role = members.nextElement(); - subjectGroup.addMember(role); - } - } - - UniversalLoginModule.log.info("User logged in: " + getUserName()); - return true; + // get service beans from the Spring context + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( + SessionManager.getServletContext()); + UniversalLoginModule.userManagementService = (IUserManagementService) ctx.getBean("userManagementService"); + UniversalLoginModule.themeService = (IThemeService) ctx.getBean("themeService"); } /** - * Method to abort the authentication process (phase 2). - * - * @return true alaways + * Get the realm identity for the given principal. It is called by org.lamsfoundation.lams.security.SecurityRealm + * using reflection. */ - @Override - public boolean abort() throws LoginException { - UniversalLoginModule.log.info("Abort log in for user: " + getUserName()); - return true; - } - - /** - * Remove the user identity and roles added to the Subject during commit. - * - * @return true always. - */ - @Override - public boolean logout() throws LoginException { - UniversalLoginModule.log.info("User logged out: " + getUserName()); - // Remove the user identity - Set principals = subject.getPrincipals(); - principals.remove(identity); - return true; - } - - /** - * Find or create a Group with the given name. Subclasses should use this method to locate the 'Roles' group or - * create additional types of groups. - * - * @return A named Group from the principals set. - */ - private SimpleGroup createGroup(String name, Set principals) { - SimpleGroup roles = null; - for (Principal principal : principals) { - if (principal instanceof SimpleGroup) { - SimpleGroup grp = (SimpleGroup) principal; - if (grp.getName().equals(name)) { - roles = grp; - break; - } - } + public org.wildfly.security.auth.server.RealmIdentity getRealmIdentity(Principal principal) { + User user = UniversalLoginModule.userManagementService.getUserByLogin(principal.getName()); + if (user == null) { + return RealmIdentity.NON_EXISTENT; } - // If we did not find a group create one - if (roles == null) { - roles = new SimpleGroup(name); - principals.add(roles); - } - return roles; + return new RealmIdentity(principal); } /** - * Perform the authentication of the username and password. + * Static method that does the real authentication. It is called directly by + * org.lamsfoundation.lams.security.RealmIdentity */ - @Override - public boolean login() throws LoginException { - loginOK = false; - String[] info = getUsernameAndPassword(); - String userName = info[0]; - String password = info[1]; - - UniversalLoginModule.log.info("Authenticate user: " + userName); - - if (identity == null) { - try { - identity = new SimplePrincipal(userName); - } catch (Exception e) { - throw new LoginException("Failed to create principal: " + e.getMessage()); - } - - if (!validatePassword(password)) { - if (UniversalLoginModule.log.isDebugEnabled()) { - UniversalLoginModule.log.info("Bad password for user: " + userName); - } - throw new FailedLoginException("Incorrect password"); - } - } - - loginOK = true; - if (UniversalLoginModule.log.isDebugEnabled()) { - UniversalLoginModule.log.debug("User authenticated: " + identity); - } - return true; - } - - /** - * Return user name from existing identity. - */ - private String getUserName() { - return identity == null ? null : identity.getName(); - } - - /** - * Called by login() to acquire the username and password strings for authentication. This method does no validation - * of either. - * - * @return String[], [0] = username, [1] = password - */ - private String[] getUsernameAndPassword() throws LoginException { - if (callbackHandler == null) { - throw new LoginException("No CallbackHandler available to collect authentication information"); - } - NameCallback nc = new NameCallback("User name: ", "guest"); - PasswordCallback pc = new PasswordCallback("Password: ", false); - Callback[] callbacks = { nc, pc }; - String username = null; - String password = null; - try { - callbackHandler.handle(callbacks); - username = nc.getName(); - char[] tmpPassword = pc.getPassword(); - if (tmpPassword != null) { - credential = new char[tmpPassword.length]; - System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length); - pc.clearPassword(); - password = new String(credential); - } - } catch (IOException ioe) { - throw new LoginException(ioe.toString()); - } catch (UnsupportedCallbackException uce) { - throw new LoginException("CallbackHandler does not support: " + uce.getCallback()); - } - return new String[] { username, password }; - } - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, - Map options) { - this.subject = subject; - this.callbackHandler = callbackHandler; - - if (UniversalLoginModule.dsJndiName == null) { - UniversalLoginModule.dsJndiName = (String) options.get("dsJndiName"); - } - } - - private boolean validatePassword(String inputPassword) { - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( - SessionManager.getServletContext()); - if (UniversalLoginModule.userManagementService == null) { - UniversalLoginModule.userManagementService = (IUserManagementService) ctx.getBean("userManagementService"); - UniversalLoginModule.themeService = (IThemeService) ctx.getBean("themeService"); - } - + public static boolean validatePassword(String userName, String inputPassword) { // there is no session if the request did not go through SsoHandler // it happens on session failover if (SessionManager.getSession() != null) { @@ -286,8 +120,6 @@ } } - String userName = getUserName(); - // empty password not allowed if (StringUtils.isBlank(inputPassword)) { if (UniversalLoginModule.log.isDebugEnabled()) { @@ -326,6 +158,8 @@ // provision a new user by checking LDAP server LdapService ldapService = null; try { + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext( + SessionManager.getServletContext()); ldapService = (LdapService) ctx.getBean("ldapService"); } catch (NoSuchBeanDefinitionException e) { // LDEV-1937 @@ -417,108 +251,55 @@ } /** - * According to Lams's security policy, all the authorization must be done locally, in other word, through Lams - * database or other "local"(logically) data resource. - * - * @return Group[] containing the sets of roles + * Static method that does the real authorisation. It is called directly by + * org.lamsfoundation.lams.security.RealmIdentity */ - private SimpleGroup[] getRoleSets() throws LoginException { - String userName = getUserName(); - Map setsMap = new HashMap<>(); - Connection conn = null; - PreparedStatement ps = null; - ResultSet rs = null; + public static Set getRoles(String userName) throws RealmUnavailableException { + Set roles = new HashSet<>(); try { - InitialContext ctx = new InitialContext(); - DataSource ds = (DataSource) ctx.lookup(UniversalLoginModule.dsJndiName); + DataSource ds = (DataSource) ctx.lookup(dsJndiName); - conn = ds.getConnection(); - // Get the user role names - ps = conn.prepareStatement(UniversalLoginModule.ROLES_QUERY); - ps.setString(1, userName); - rs = ps.executeQuery(); - if (!rs.next()) { - throw new FailedLoginException("No matching user name found in roles: " + userName); - } - - ArrayList groupMembers = new ArrayList<>(); - do { - String name = rs.getString(1); - String groupName = rs.getString(2); - if ((groupName == null) || (groupName.length() == 0)) { - groupName = "Roles"; + try (Connection conn = ds.getConnection(); + PreparedStatement ps = conn.prepareStatement(UniversalLoginModule.ROLES_QUERY)) { + // Get the user role names + ps.setString(1, userName); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + log.warn("No matching user name found in roles: " + userName); + return null; } - SimpleGroup group = setsMap.get(groupName); - if (group == null) { - group = new SimpleGroup(groupName); - setsMap.put(groupName, group); - } - try { - Principal p = null; + do { + String name = rs.getString(1); + // Assign minimal role if user has none if (name == null) { name = Role.LEARNER; UniversalLoginModule.log.info("Found no roles for user: " + userName + ", assigning: " + name); } - p = new SimplePrincipal(name); - if (!groupMembers.contains(name)) { - UniversalLoginModule.log.info("Assign user: " + userName + " to role " + p.getName()); - group.addMember(p); - groupMembers.add(name); - } + roles.add(name); + // sysadmin is always app admin - if (name.equals(Role.SYSADMIN) && !groupMembers.contains(Role.APPADMIN)) { + if (name.equals(Role.SYSADMIN) && !roles.contains(Role.APPADMIN)) { UniversalLoginModule.log.info("Assign user: " + userName + " to role " + Role.APPADMIN); - group.addMember(p); - groupMembers.add(Role.APPADMIN); + roles.add(Role.APPADMIN); } if (name.equals(Role.APPADMIN) || name.equals(Role.SYSADMIN)) { - p = new SimplePrincipal(Role.AUTHOR); UniversalLoginModule.log.info("Found role " + name); - if (!groupMembers.contains(Role.AUTHOR)) { + if (!roles.contains(Role.AUTHOR)) { UniversalLoginModule.log.info("Assign user: " + userName + " to role " + Role.AUTHOR); - group.addMember(p); - groupMembers.add(Role.AUTHOR); + roles.add(Role.AUTHOR); } } - } catch (Exception e) { - UniversalLoginModule.log.info("Failed to create principal: " + name + " for user: " + userName, e); - } - } while (rs.next()); - } catch (NamingException e) { - throw new LoginException(e.toString(true)); - } catch (SQLException e) { - throw new LoginException(e.toString()); - } finally { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - UniversalLoginModule.log.error(e); - } + + } while (rs.next()); } - if (ps != null) { - try { - ps.close(); - } catch (SQLException e) { - UniversalLoginModule.log.error(e); - } - } - if (conn != null) { - try { - conn.close(); - } catch (Exception e) { - UniversalLoginModule.log.error(e); - } - } + } catch (NamingException | SQLException e) { + throw new RealmUnavailableException("Error while fetching roles", e); } - - SimpleGroup[] roleSets = new SimpleGroup[setsMap.size()]; - setsMap.values().toArray(roleSets); - return roleSets; + return roles; } /** Index: lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java =================================================================== diff -u -r35d617e013c37361e7d89025c464a7f4a53b9b4e -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java (.../SessionListener.java) (revision 35d617e013c37361e7d89025c464a7f4a53b9b4e) +++ lams_central/src/java/org/lamsfoundation/lams/web/SessionListener.java (.../SessionListener.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -23,27 +23,19 @@ package org.lamsfoundation.lams.web; -import java.security.Principal; -import java.util.Locale; - -import javax.naming.InitialContext; -import javax.naming.NamingException; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionListener; import jakarta.servlet.jsp.jstl.core.Config; - -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -//import org.jboss.security.CacheableManager; -import org.lamsfoundation.lams.security.SimplePrincipal; -import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; import org.lamsfoundation.lams.util.LanguageUtil; import org.lamsfoundation.lams.web.filter.LocaleFilter; -import org.lamsfoundation.lams.web.session.SessionManager; -import org.lamsfoundation.lams.web.util.AttributeNames; +import java.util.Locale; + /** * Listens for creation of HTTP sessions. Sets inactive timeout and default locale. */ Index: lams_central/web/WEB-INF/ejb-jar.xml =================================================================== diff -u --- lams_central/web/WEB-INF/ejb-jar.xml (revision 0) +++ lams_central/web/WEB-INF/ejb-jar.xml (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -0,0 +1,15 @@ + + + + + lams-login-module + org.lamsfoundation.lams.security.UniversalLoginModule + Stateless + Container + + + \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java =================================================================== diff -u -r44cc1e311b449f4af21b8153341a355b2f1820a0 -r665bafc925d571fea2e089e161a7d164d73c96bd --- lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java (.../SsoHandler.java) (revision 44cc1e311b449f4af21b8153341a355b2f1820a0) +++ lams_common/src/java/org/lamsfoundation/lams/integration/security/SsoHandler.java (.../SsoHandler.java) (revision 665bafc925d571fea2e089e161a7d164d73c96bd) @@ -16,23 +16,25 @@ */ package org.lamsfoundation.lams.integration.security; -import java.io.IOException; -import java.security.AccessController; -import java.security.Principal; -import java.util.Date; -import java.util.StringTokenizer; - +import com.warrenstrange.googleauth.GoogleAuthenticator; +import io.undertow.Handlers; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.session.Session; +import io.undertow.servlet.ServletExtension; +import io.undertow.servlet.api.DeploymentInfo; +import io.undertow.servlet.handlers.ServletRequestContext; +import io.undertow.servlet.spec.HttpSessionImpl; +import io.undertow.util.Headers; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; - import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.lamsfoundation.lams.logevent.LogEvent; import org.lamsfoundation.lams.logevent.service.ILogEventService; import org.lamsfoundation.lams.usermanagement.User; @@ -48,23 +50,15 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; -import com.warrenstrange.googleauth.GoogleAuthenticator; +import java.io.IOException; +import java.security.AccessController; +import java.util.Date; +import java.util.StringTokenizer; -import io.undertow.Handlers; -import io.undertow.server.HttpServerExchange; -import io.undertow.server.session.Session; -import io.undertow.servlet.ServletExtension; -import io.undertow.servlet.api.DeploymentInfo; -import io.undertow.servlet.handlers.ServletRequestContext; -import io.undertow.servlet.spec.HttpSessionImpl; -import io.undertow.util.Headers; -import org.wildfly.security.auth.server.SecurityDomain; - /** * Allows access to LAMS WARs when an user logs in. * * @author Marcin Cieslak - * */ public class SsoHandler implements ServletExtension { private static ILogEventService logEventService = null; @@ -293,8 +287,8 @@ /** * Forward to the login page with a specific error message. Avoids a redirect. Based on the - * ServletFormAuthenticationMechanism method. The location should be relative to the current - * context and start with "/" e.g. /login.jsp + * ServletFormAuthenticationMechanism method. The location should be relative to the current context and start with + * "/" e.g. /login.jsp */ private static Integer serveLoginPage(final HttpServerExchange exchange, final HttpServletRequest request, final HttpServletResponse response, final String location) throws ServletException, IOException {