diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java index 4add7a97724b22b33001b95ad3f55316b156150c..3743a1bce191bab707e7403d1eea5cc14ddfeb6b 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurity.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java @@ -288,6 +288,14 @@ public interface BaseSecurity { * found */ public Authentication findAuthentication(Identity identity, String provider); + + /** + * Return the credential or null + * @param identity + * @param provider + * @return + */ + public String findCredentials(Identity identity, String provider); //fxdiff: FXOLAT-219 decrease the load for synching groups public boolean hasAuthentication(Long identityKey, String provider); @@ -604,6 +612,14 @@ public interface BaseSecurity { * @return */ public boolean isIdentityVisible(String identityName); + + /** + * Check if identity is visible. Deleted or login-denied users are not visible. + * @param identity + * @return + */ + public boolean isIdentityVisible(Identity identity); + /** * Get all SecurtityGroups an Identity is in diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 5788e528419c4c5a7f644ac7ed9e74355f47edb6..bd15608463b9ae11692f5c6c5f2a3b47ca7a07c8 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -72,7 +72,6 @@ import org.olat.user.ChangePasswordController; import org.olat.user.PersonalSettingsController; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.annotation.Transactional; /** * <h3>Description:</h3> @@ -1302,6 +1301,26 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { return results.get(0); } + @Override + public String findCredentials(Identity identity, String provider) { + if (identity==null) { + throw new IllegalArgumentException("identity must not be null"); + } + + StringBuilder sb = new StringBuilder(); + sb.append("select auth.credential from ").append(AuthenticationImpl.class.getName()) + .append(" as auth where auth.identity.key=:identityKey and auth.provider=:provider"); + + List<String> results = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), String.class) + .setParameter("identityKey", identity.getKey()) + .setParameter("provider", provider) + .getResultList(); + if (results == null || results.size() == 0) return null; + if (results.size() > 1) throw new AssertException("Found more than one Authentication for a given subject and a given provider."); + return results.get(0); + } + @Override //fxdiff: FXOLAT-219 decrease the load for synching groups public boolean hasAuthentication(Long identityKey, String provider) { @@ -1712,6 +1731,13 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { return (cntL.longValue() > 0); } + @Override + public boolean isIdentityVisible(Identity identity) { + if(identity == null) return false; + Integer status = identity.getStatus(); + return (status != null && status.intValue() < Identity.STATUS_VISIBLE_LIMIT); + } + private boolean checkAnd(StringBuilder sb, boolean needsAnd) { if (needsAnd) sb.append(" and "); return true; @@ -1758,7 +1784,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { */ @Override public Identity saveIdentityStatus(Identity identity, Integer status) { - Identity reloadedIdentity = loadForUpdate(identity.getKey()); + Identity reloadedIdentity = loadForUpdate(identity); reloadedIdentity.setStatus(status); reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); dbInstance.commit(); @@ -1767,7 +1793,7 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { @Override public Identity setIdentityLastLogin(Identity identity) { - Identity reloadedIdentity = loadForUpdate(identity.getKey()); + Identity reloadedIdentity = loadForUpdate(identity); reloadedIdentity.setLastLogin(new Date()); reloadedIdentity = dbInstance.getCurrentEntityManager().merge(reloadedIdentity); dbInstance.commit(); @@ -1779,22 +1805,22 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { * @param identityKey * @return */ - private IdentityImpl loadForUpdate(Long identityKey) { + private IdentityImpl loadForUpdate(Identity identity) { StringBuilder sb = new StringBuilder(); sb.append("select id from ").append(IdentityImpl.class.getName()).append(" as id") .append(" inner join fetch id.user user ") .append(" where id.key=:identityKey"); - List<IdentityImpl> identity = dbInstance.getCurrentEntityManager() + dbInstance.getCurrentEntityManager().detach(identity); + List<IdentityImpl> identities = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), IdentityImpl.class) - .setParameter("identityKey", identityKey) + .setParameter("identityKey", identity.getKey()) .setLockMode(LockModeType.PESSIMISTIC_WRITE) .getResultList(); - - if(identity.isEmpty()) { + if(identities.isEmpty()) { return null; } - return identity.get(0); + return identities.get(0); } @Override diff --git a/src/main/java/org/olat/commons/servlets/WebDAVManagerImpl.java b/src/main/java/org/olat/commons/servlets/WebDAVManagerImpl.java index 87559b610258f21bb705420c781d4d9a5a11227b..5ac79f82b9fb9badbc20d163786bc40dfd1df0ca 100644 --- a/src/main/java/org/olat/commons/servlets/WebDAVManagerImpl.java +++ b/src/main/java/org/olat/commons/servlets/WebDAVManagerImpl.java @@ -155,7 +155,7 @@ public class WebDAVManagerImpl extends WebDAVManager { return null; } - private UserSession handleBasicAuthentication(String credentials, HttpServletRequest request) { + protected UserSession handleBasicAuthentication(String credentials, HttpServletRequest request) { // This example uses sun.misc.* classes. // You will need to provide your own // if you are not comfortable with that. diff --git a/src/main/java/org/olat/login/OLATAuthenticationController.java b/src/main/java/org/olat/login/OLATAuthenticationController.java index ac5c5dbe778a9d5f0d92cdafb8f0f3e7d9c24515..b7fd129ec5bddca16361f59d377d0579e12e43c1 100644 --- a/src/main/java/org/olat/login/OLATAuthenticationController.java +++ b/src/main/java/org/olat/login/OLATAuthenticationController.java @@ -285,11 +285,18 @@ public class OLATAuthenticationController extends AuthenticationController imple * @deprecated should not be part of the controller */ public static Identity authenticate(String login, String pass) { - if (pass == null) return null; // do never accept empty passwords - Identity ident = BaseSecurityManager.getInstance().findIdentityByName(login); - + return authenticate(ident, login, pass); + } + + /** + * @param login + * @param pass + * @return Identity if authentication was successfull, null otherwise. + * @deprecated should not be part of the controller + */ + public static Identity authenticate(Identity ident, String login, String pass) { // check for email instead of username if ident is null if (ident == null && LoginModule.allowLoginUsingEmail()) { if (MailHelper.isValidEmailAddress(login)){ @@ -306,10 +313,8 @@ public class OLATAuthenticationController extends AuthenticationController imple } // find OLAT authentication provider - Authentication auth = BaseSecurityManager.getInstance().findAuthentication( - ident, BaseSecurityModule.getDefaultAuthProviderIdentifier()); - - if (auth != null && auth.getCredential().equals(Encoder.encrypt(pass))) return ident; + String auth = BaseSecurityManager.getInstance().findCredentials(ident, BaseSecurityModule.getDefaultAuthProviderIdentifier()); + if (auth != null && auth.equals(Encoder.encrypt(pass))) return ident; Tracing.createLoggerFor(OLATAuthenticationController.class).audit( "Error authenticating user "+login+" via provider OLAT", diff --git a/src/main/java/org/olat/login/auth/WebDAVAuthManager.java b/src/main/java/org/olat/login/auth/WebDAVAuthManager.java index a1396e10a167faa377e30a34fce47df16f931dff..7349d4f60921d01134e9fc8ad1d419666521dbcc 100644 --- a/src/main/java/org/olat/login/auth/WebDAVAuthManager.java +++ b/src/main/java/org/olat/login/auth/WebDAVAuthManager.java @@ -21,6 +21,7 @@ package org.olat.login.auth; import org.olat.basesecurity.Authentication; +import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.BaseSecurityManager; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; @@ -87,15 +88,21 @@ public class WebDAVAuthManager { * @return Identity if authentication was successful, null otherwise. */ public static Identity authenticate(String login, String pass) { - Identity ident = BaseSecurityManager.getInstance().findIdentityByName(login); - if (ident == null) return null; - boolean visible = BaseSecurityManager.getInstance().isIdentityVisible(login); - if (!visible) return null; + BaseSecurity securityManager = BaseSecurityManager.getInstance(); + Identity ident = securityManager.findIdentityByName(login); + if (ident == null) return null; + boolean visible = securityManager.isIdentityVisible(ident); + if (!visible) { + return null; + } + //find WEBDAV authentication provider - Authentication auth = BaseSecurityManager.getInstance().findAuthentication(ident, PROVIDER_WEBDAV); - if (auth != null && auth.getCredential().equals(Encoder.encrypt(pass))) return ident; + String auth = securityManager.findCredentials(ident, PROVIDER_WEBDAV); + if (auth != null && auth.equals(Encoder.encrypt(pass))) { + return ident; + } //fallback to OLAT authentication provider - return OLATAuthenticationController.authenticate(login, pass); + return OLATAuthenticationController.authenticate(ident, login, pass); } } diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java index 6d878821719751b6f1bfb2f4778cee9fc8689ab0..efa8a207a79cfde27531d9a8e57eb7b4c710b556 100644 --- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java +++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java @@ -831,4 +831,15 @@ public class BaseSecurityManagerTest extends OlatTestCase { Assert.assertTrue(permissions.contains("test.gpor-1_2")); Assert.assertFalse(permissions.contains("test.gpor-1_3")); } + + @Test + public void findCredentials() { + //create a user with the default provider + Identity id = JunitTestHelper.createAndPersistIdentityAsUser("find-cred-" + UUID.randomUUID().toString()); + + dbInstance.commitAndCloseSession(); + + String credential = securityManager.findCredentials(id, BaseSecurityModule.getDefaultAuthProviderIdentifier()); + Assert.assertNotNull(credential); + } } diff --git a/src/test/java/org/olat/commons/servlets/WebDAVManagerTest.java b/src/test/java/org/olat/commons/servlets/WebDAVManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9f3d7014d10ef4a6e39a33efb2bc52967da2dd90 --- /dev/null +++ b/src/test/java/org/olat/commons/servlets/WebDAVManagerTest.java @@ -0,0 +1,152 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.commons.servlets; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.HttpServletRequest; + +import junit.framework.Assert; + +import org.junit.Test; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.id.Identity; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.UserSession; +import org.olat.test.JunitTestHelper; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpServletRequest; + +import com.oreilly.servlet.Base64Encoder; + +/** + * + * Initial date: 21.05.2013<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class WebDAVManagerTest extends OlatTestCase { + + private static final OLog log = Tracing.createLoggerFor(WebDAVManagerTest.class); + + @Autowired + private DB dbInstance; + @Autowired + private WebDAVManagerImpl webDAVManager; + + @Test + public void handleBasicAuthentication() { + Identity id = JunitTestHelper.createAndPersistIdentityAsUser("dav-user-" + UUID.randomUUID().toString()); + + String credentialsClearText = id.getName() + ":" + JunitTestHelper.PWD; + String credentials = Base64Encoder.encode(credentialsClearText); + HttpServletRequest request = new MockHttpServletRequest(); + UserSession usess = webDAVManager.handleBasicAuthentication(credentials, request); + Assert.assertNotNull(usess); + dbInstance.commit(); + } + + @Test + public void testSetIdentityAsActiv() { + Identity id = JunitTestHelper.createAndPersistIdentityAsUser("dav-user-" + UUID.randomUUID().toString()); + String credentialsClearText = id.getName() + ":" + JunitTestHelper.PWD; + String credentials = Base64Encoder.encode(credentialsClearText); + + final int maxLoop = 50; // => 4000 x 11ms => 44sec => finished in 50sec + final int numThreads = 10; + // Let two thread call UserDeletionManager.setIdentityAsActiv + final List<Exception> exceptionHolder = Collections.synchronizedList(new ArrayList<Exception>(1)); + + CountDownLatch latch = new CountDownLatch(numThreads); + ActivThread[] threads = new ActivThread[numThreads]; + for(int i=0; i<threads.length;i++) { + threads[i] = new ActivThread(maxLoop, latch, exceptionHolder, webDAVManager, credentials); + } + + for(int i=0; i<threads.length;i++) { + threads[i].start(); + } + + try { + latch.await(120, TimeUnit.SECONDS); + } catch (InterruptedException e) { + exceptionHolder.add(e); + } + + // if not -> they are in deadlock and the db did not detect it + for (Exception exception : exceptionHolder) { + log.error("exception: ", exception); + } + assertTrue("Exceptions #" + exceptionHolder.size(), exceptionHolder.size() == 0); + } + + private static class ActivThread extends Thread { + + private final int maxLoop; + private final CountDownLatch countDown; + private final List<Exception> exceptionHolder; + private final WebDAVManagerImpl webDAVManager; + private final String credentials; + + public ActivThread(int maxLoop, CountDownLatch countDown, List<Exception> exceptionHolder, WebDAVManagerImpl webDAVManager, String credentials) { + this.maxLoop = maxLoop; + this.countDown = countDown; + this.exceptionHolder = exceptionHolder; + this.webDAVManager = webDAVManager; + this.credentials = credentials; + } + + @Override + public void run() { + try { + sleep(10); + for (int i=0; i<maxLoop; i++) { + try { + HttpServletRequest request = new MockHttpServletRequest(); + webDAVManager.handleBasicAuthentication(credentials, request); + DBFactory.getInstance().commit(); + } catch (Exception e) { + exceptionHolder.add(e); + } finally { + try { + DBFactory.getInstance().commitAndCloseSession(); + } catch (Exception e) { + // ignore + } + } + } + } catch (Exception e) { + exceptionHolder.add(e); + } finally { + countDown.countDown(); + } + } + } +} \ No newline at end of file