extensible authentication system

This commit is contained in:
Benjamin Gamard 2018-03-26 22:07:26 +02:00
parent c9606f98d3
commit 99d44f2a92
10 changed files with 132 additions and 27 deletions

View File

@ -38,11 +38,14 @@ public class EncryptionUtil {
* Generate a private key.
*
* @return New random private key
* @throws NoSuchAlgorithmException
*/
public static String generatePrivateKey() throws NoSuchAlgorithmException {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
return new BigInteger(176, random).toString(32);
public static String generatePrivateKey() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
return new BigInteger(176, random).toString(32);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
/**

View File

@ -50,6 +50,12 @@ public class RoutingUtil {
}
}
/**
* Send an email when a route step is validated.
*
* @param documentId Document ID
* @param routeStepDto Route step DTO
*/
public static void sendRouteStepEmail(String documentId, RouteStepDto routeStepDto) {
DocumentDao documentDao = new DocumentDao();
Document document = documentDao.getById(documentId);

View File

@ -0,0 +1,19 @@
package com.sismics.docs.core.util.authentication;
import com.sismics.docs.core.model.jpa.User;
/**
* An authentication handler.
*
* @author bgamard
*/
public interface AuthenticationHandler {
/**
* Authenticate a user.
*
* @param username Username
* @param password Password
* @return Authenticated user
*/
User authenticate(String username, String password);
}

View File

@ -0,0 +1,45 @@
package com.sismics.docs.core.util.authentication;
import com.google.common.collect.Lists;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.util.ClasspathScanner;
import java.util.List;
import java.util.stream.Collectors;
/**
* User utilities.
*/
public class AuthenticationUtil {
/**
* List of authentication handlers scanned in the classpath.
*/
private static final List<AuthenticationHandler> AUTH_HANDLERS = Lists.newArrayList(
new ClasspathScanner<AuthenticationHandler>().findClasses(AuthenticationHandler.class, "com.sismics.docs.core.util.authentication")
.stream()
.map(clazz -> {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList()));
/**
* Authenticate a user.
*
* @param username Username
* @param password Password
* @return Authenticated user
*/
public static User authenticate(String username, String password) {
for (AuthenticationHandler authenticationHandler : AUTH_HANDLERS) {
User user = authenticationHandler.authenticate(username, password);
if (user != null) {
return user;
}
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package com.sismics.docs.core.util.authentication;
import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.util.ClasspathScanner;
/**
* Authenticate using the internal database.
*
* @author bgamard
*/
@ClasspathScanner.Priority(100) // We can add handlers before this one
public class InternalAuthenticationHandler implements AuthenticationHandler {
@Override
public User authenticate(String username, String password) {
UserDao userDao = new UserDao();
return userDao.authenticate(username, password);
}
}

View File

@ -1,13 +1,13 @@
package com.sismics.util;
import com.google.common.collect.Sets;
import com.google.common.collect.Lists;
import com.google.common.reflect.ClassPath;
import com.sismics.docs.core.util.format.PdfFormatHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.List;
/**
* Classes scanner.
@ -23,11 +23,11 @@ public class ClasspathScanner<T> {
*
* @param topClass Top class or interface
* @param pkg In this package
* @return Set of classes
* @return List of classes
*/
@SuppressWarnings("unchecked")
public Set<Class<T>> findClasses(Class<T> topClass, String pkg) {
Set<Class<T>> classes = Sets.newHashSet();
public List<Class<T>> findClasses(Class<T> topClass, String pkg) {
List<Class<T>> classes = Lists.newArrayList();
try {
for (ClassPath.ClassInfo classInfo : ClassPath.from(topClass.getClassLoader()).getTopLevelClasses(pkg)) {
Class<?> clazz = classInfo.load();
@ -38,7 +38,22 @@ public class ClasspathScanner<T> {
} catch (Exception e) {
log.error("Error loading format handlers", e);
}
classes.sort((o1, o2) -> {
Priority priority1 = o1.getDeclaredAnnotation(Priority.class);
Priority priority2 = o2.getDeclaredAnnotation(Priority.class);
return Integer.compare(priority1 == null ? Integer.MAX_VALUE : priority1.value(),
priority2 == null ? Integer.MAX_VALUE : priority2.value());
});
log.info("Found " + classes.size() + " classes for " + topClass.getSimpleName());
return classes;
}
/**
* Classpath scanning priority.
*/
public @interface Priority {
int value() default Integer.MAX_VALUE;
}
}

View File

@ -3,6 +3,7 @@ package com.sismics.docs.core.dao.jpa;
import com.sismics.docs.BaseTransactionalTest;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.TransactionUtil;
import com.sismics.docs.core.util.authentication.InternalAuthenticationHandler;
import org.junit.Assert;
import org.junit.Test;
@ -18,6 +19,7 @@ public class TestJpa extends BaseTransactionalTest {
UserDao userDao = new UserDao();
User user = new User();
user.setUsername("username");
user.setPassword("12345678");
user.setEmail("toto@docs.com");
user.setRoleId("admin");
user.setStorageCurrent(0l);
@ -31,5 +33,8 @@ public class TestJpa extends BaseTransactionalTest {
user = userDao.getById(id);
Assert.assertNotNull(user);
Assert.assertEquals("toto@docs.com", user.getEmail());
// Authenticate using the database
Assert.assertNotNull(new InternalAuthenticationHandler().authenticate("username", "12345678"));
}
}

View File

@ -1,15 +1,13 @@
package com.sismics.docs.core.util;
import java.io.InputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import org.junit.Assert;
import org.junit.Test;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import org.junit.Assert;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import java.io.InputStream;
/**
* Test of the encryption utilities.
@ -18,7 +16,7 @@ import com.google.common.io.ByteStreams;
*/
public class TestEncryptUtil {
@Test
public void generatePrivateKeyTest() throws Exception {
public void generatePrivateKeyTest() {
String key = EncryptionUtil.generatePrivateKey();
System.out.println(key);
Assert.assertFalse(Strings.isNullOrEmpty(key));

View File

@ -5,7 +5,6 @@ import com.sismics.docs.core.dao.jpa.UserDao;
import com.sismics.docs.core.model.jpa.User;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
/**
@ -26,7 +25,7 @@ public class HeaderBasedSecurityFilter extends SecurityFilter {
private boolean enabled;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
public void init(FilterConfig filterConfig) {
enabled = Boolean.parseBoolean(filterConfig.getInitParameter("enabled"))
|| Boolean.parseBoolean(System.getProperty("docs.header_authentication"));
}

View File

@ -16,6 +16,7 @@ import com.sismics.docs.core.model.context.AppContext;
import com.sismics.docs.core.model.jpa.*;
import com.sismics.docs.core.util.ConfigUtil;
import com.sismics.docs.core.util.EncryptionUtil;
import com.sismics.docs.core.util.authentication.AuthenticationUtil;
import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.docs.rest.constant.BaseFunction;
import com.sismics.rest.exception.ClientException;
@ -38,7 +39,6 @@ import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Set;
@ -101,11 +101,7 @@ public class UserResource extends BaseResource {
user.setEmail(email);
user.setStorageQuota(storageQuota);
user.setStorageCurrent(0L);
try {
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
} catch (NoSuchAlgorithmException e) {
throw new ServerException("PrivateKeyError", "Error while generating a private key", e);
}
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
user.setCreateDate(new Date());
// Create the user
@ -339,7 +335,7 @@ public class UserResource extends BaseResource {
}
} else {
// Login as a normal user
user = userDao.authenticate(username, password);
user = AuthenticationUtil.authenticate(username, password);
}
if (user == null) {
throw new ForbiddenClientException();