mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 15:17:57 +01:00
extensible authentication system
This commit is contained in:
parent
c9606f98d3
commit
99d44f2a92
@ -38,11 +38,14 @@ public class EncryptionUtil {
|
|||||||
* Generate a private key.
|
* Generate a private key.
|
||||||
*
|
*
|
||||||
* @return New random private key
|
* @return New random private key
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
*/
|
||||||
public static String generatePrivateKey() throws NoSuchAlgorithmException {
|
public static String generatePrivateKey() {
|
||||||
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
try {
|
||||||
return new BigInteger(176, random).toString(32);
|
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
return new BigInteger(176, random).toString(32);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
public static void sendRouteStepEmail(String documentId, RouteStepDto routeStepDto) {
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
Document document = documentDao.getById(documentId);
|
Document document = documentDao.getById(documentId);
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package com.sismics.util;
|
package com.sismics.util;
|
||||||
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.reflect.ClassPath;
|
import com.google.common.reflect.ClassPath;
|
||||||
import com.sismics.docs.core.util.format.PdfFormatHandler;
|
import com.sismics.docs.core.util.format.PdfFormatHandler;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes scanner.
|
* Classes scanner.
|
||||||
@ -23,11 +23,11 @@ public class ClasspathScanner<T> {
|
|||||||
*
|
*
|
||||||
* @param topClass Top class or interface
|
* @param topClass Top class or interface
|
||||||
* @param pkg In this package
|
* @param pkg In this package
|
||||||
* @return Set of classes
|
* @return List of classes
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Set<Class<T>> findClasses(Class<T> topClass, String pkg) {
|
public List<Class<T>> findClasses(Class<T> topClass, String pkg) {
|
||||||
Set<Class<T>> classes = Sets.newHashSet();
|
List<Class<T>> classes = Lists.newArrayList();
|
||||||
try {
|
try {
|
||||||
for (ClassPath.ClassInfo classInfo : ClassPath.from(topClass.getClassLoader()).getTopLevelClasses(pkg)) {
|
for (ClassPath.ClassInfo classInfo : ClassPath.from(topClass.getClassLoader()).getTopLevelClasses(pkg)) {
|
||||||
Class<?> clazz = classInfo.load();
|
Class<?> clazz = classInfo.load();
|
||||||
@ -38,7 +38,22 @@ public class ClasspathScanner<T> {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error loading format handlers", 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());
|
log.info("Found " + classes.size() + " classes for " + topClass.getSimpleName());
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classpath scanning priority.
|
||||||
|
*/
|
||||||
|
public @interface Priority {
|
||||||
|
int value() default Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.sismics.docs.core.dao.jpa;
|
|||||||
import com.sismics.docs.BaseTransactionalTest;
|
import com.sismics.docs.BaseTransactionalTest;
|
||||||
import com.sismics.docs.core.model.jpa.User;
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
import com.sismics.docs.core.util.TransactionUtil;
|
||||||
|
import com.sismics.docs.core.util.authentication.InternalAuthenticationHandler;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ public class TestJpa extends BaseTransactionalTest {
|
|||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setUsername("username");
|
user.setUsername("username");
|
||||||
|
user.setPassword("12345678");
|
||||||
user.setEmail("toto@docs.com");
|
user.setEmail("toto@docs.com");
|
||||||
user.setRoleId("admin");
|
user.setRoleId("admin");
|
||||||
user.setStorageCurrent(0l);
|
user.setStorageCurrent(0l);
|
||||||
@ -31,5 +33,8 @@ public class TestJpa extends BaseTransactionalTest {
|
|||||||
user = userDao.getById(id);
|
user = userDao.getById(id);
|
||||||
Assert.assertNotNull(user);
|
Assert.assertNotNull(user);
|
||||||
Assert.assertEquals("toto@docs.com", user.getEmail());
|
Assert.assertEquals("toto@docs.com", user.getEmail());
|
||||||
|
|
||||||
|
// Authenticate using the database
|
||||||
|
Assert.assertNotNull(new InternalAuthenticationHandler().authenticate("username", "12345678"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package com.sismics.docs.core.util;
|
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.base.Strings;
|
||||||
import com.google.common.io.ByteStreams;
|
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.
|
* Test of the encryption utilities.
|
||||||
@ -18,7 +16,7 @@ import com.google.common.io.ByteStreams;
|
|||||||
*/
|
*/
|
||||||
public class TestEncryptUtil {
|
public class TestEncryptUtil {
|
||||||
@Test
|
@Test
|
||||||
public void generatePrivateKeyTest() throws Exception {
|
public void generatePrivateKeyTest() {
|
||||||
String key = EncryptionUtil.generatePrivateKey();
|
String key = EncryptionUtil.generatePrivateKey();
|
||||||
System.out.println(key);
|
System.out.println(key);
|
||||||
Assert.assertFalse(Strings.isNullOrEmpty(key));
|
Assert.assertFalse(Strings.isNullOrEmpty(key));
|
||||||
|
@ -5,7 +5,6 @@ import com.sismics.docs.core.dao.jpa.UserDao;
|
|||||||
import com.sismics.docs.core.model.jpa.User;
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
|
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +25,7 @@ public class HeaderBasedSecurityFilter extends SecurityFilter {
|
|||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) {
|
||||||
enabled = Boolean.parseBoolean(filterConfig.getInitParameter("enabled"))
|
enabled = Boolean.parseBoolean(filterConfig.getInitParameter("enabled"))
|
||||||
|| Boolean.parseBoolean(System.getProperty("docs.header_authentication"));
|
|| Boolean.parseBoolean(System.getProperty("docs.header_authentication"));
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import com.sismics.docs.core.model.context.AppContext;
|
|||||||
import com.sismics.docs.core.model.jpa.*;
|
import com.sismics.docs.core.model.jpa.*;
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
import com.sismics.docs.core.util.EncryptionUtil;
|
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.core.util.jpa.SortCriteria;
|
||||||
import com.sismics.docs.rest.constant.BaseFunction;
|
import com.sismics.docs.rest.constant.BaseFunction;
|
||||||
import com.sismics.rest.exception.ClientException;
|
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.MediaType;
|
||||||
import javax.ws.rs.core.NewCookie;
|
import javax.ws.rs.core.NewCookie;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -101,11 +101,7 @@ public class UserResource extends BaseResource {
|
|||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
user.setStorageQuota(storageQuota);
|
user.setStorageQuota(storageQuota);
|
||||||
user.setStorageCurrent(0L);
|
user.setStorageCurrent(0L);
|
||||||
try {
|
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
|
||||||
user.setPrivateKey(EncryptionUtil.generatePrivateKey());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new ServerException("PrivateKeyError", "Error while generating a private key", e);
|
|
||||||
}
|
|
||||||
user.setCreateDate(new Date());
|
user.setCreateDate(new Date());
|
||||||
|
|
||||||
// Create the user
|
// Create the user
|
||||||
@ -339,7 +335,7 @@ public class UserResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Login as a normal user
|
// Login as a normal user
|
||||||
user = userDao.authenticate(username, password);
|
user = AuthenticationUtil.authenticate(username, password);
|
||||||
}
|
}
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
|
Loading…
Reference in New Issue
Block a user