Skip to content
Snippets Groups Projects
Commit 0821169b authored by Florian Spreckelsen's avatar Florian Spreckelsen
Browse files

Merge branch 'f-dot-in-username' into 'dev'

Make password and user name requirements configurable

Closes caosdb/customers/leibniz-zmt/management#95 and caosdb/customers/leibniz-zmt/management#94

See merge request !63
parents bdb2d0a3 a1f3c334
Branches
Tags
2 merge requests!66REL: prepare release 0.8.0,!63Make password and user name requirements configurable
Pipeline #25354 passed
...@@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
* Configurable requirements for user names and passwords. The default is the old hard-coded configuration.
### Changed ### Changed
* Minimal changes to the error messages for invalid user names and passwords.
### Deprecated ### Deprecated
### Removed ### Removed
...@@ -37,6 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -37,6 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### Fixed
* [caosdb-server#142](https://gitlab.com/caosdb/caosdb-server/-/issues/142)
Can't create users with dots in their user names
* `ldap_authentication.sh <username>` failed on every attempt when used in * `ldap_authentication.sh <username>` failed on every attempt when used in
combination with OpenLDAP with default configuration. combination with OpenLDAP with default configuration.
* `ldap_authentication.sh` allowed empty and even wrong passwords when used in * `ldap_authentication.sh` allowed empty and even wrong passwords when used in
......
...@@ -199,6 +199,18 @@ CHECK_ENTITY_ACL_ROLES_MODE=MUST ...@@ -199,6 +199,18 @@ CHECK_ENTITY_ACL_ROLES_MODE=MUST
# part of any Entity ACL. # part of any Entity ACL.
GLOBAL_ENTITY_PERMISSIONS_FILE=./conf/core/global_entity_permissions.xml GLOBAL_ENTITY_PERMISSIONS_FILE=./conf/core/global_entity_permissions.xml
# --------------------------------------------------
# User Account Settings
# --------------------------------------------------
# Requirements for user names. The default is POSIX compliant, see
# https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap03.html#tag_03_426
USER_NAME_VALID_REGEX=^[\\w\\.][\\w\\.-]*{1,32}$
USER_NAME_INVALID_MESSAGE=User names must have a length from 1 to 32 characters. They must contain only latin letters a-z (upper case or lower case), number 0-9, dots (.), underscores (_), or hyphens (-). They must no start with a hyphen.
PASSWORD_VALID_REGEX=^((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\p{Punct}]).{8,128})$
PASSWORD_INVALID_MESSAGE=Passwords must have a length from 8 to 128 characters. THe must contain at least one [A-Z], one [a-z], one [0-9] and a special character e.g. [!§$%&/()=?.;,:#+*+~].
# -------------------------------------------------- # --------------------------------------------------
# Extensions # Extensions
# -------------------------------------------------- # --------------------------------------------------
......
...@@ -108,8 +108,6 @@ public class ServerProperties extends Properties { ...@@ -108,8 +108,6 @@ public class ServerProperties extends Properties {
public static final String KEY_NEW_USER_DEFAULT_ACTIVITY = "NEW_USER_DEFAULT_ACTIVITY"; public static final String KEY_NEW_USER_DEFAULT_ACTIVITY = "NEW_USER_DEFAULT_ACTIVITY";
public static final String KEY_PASSWORD_STRENGTH_REGEX = "PASSWORD_STRENGTH_REGEX";
public static final String KEY_QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS = public static final String KEY_QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS =
"QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS"; "QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS";
...@@ -141,6 +139,12 @@ public class ServerProperties extends Properties { ...@@ -141,6 +139,12 @@ public class ServerProperties extends Properties {
public static final String KEY_PROJECT_REVISTION = "project.revision"; public static final String KEY_PROJECT_REVISTION = "project.revision";
public static final String KEY_BUILD_TIMESTAMP = "build.timestamp"; public static final String KEY_BUILD_TIMESTAMP = "build.timestamp";
public static final String KEY_USER_NAME_VALID_REGEX = "USER_NAME_VALID_REGEX";
public static final String KEY_USER_NAME_INVALID_MESSAGE = "USER_NAME_INVALID_MESSAGE";
public static final String KEY_PASSWORD_STRENGTH_REGEX = "PASSWORD_VALID_REGEX";
public static final String KEY_PASSWORD_WEAK_MESSAGE = "PASSWORD_INVALID_MESSAGE";
/** /**
* Read the config files and initialize the server properties. * Read the config files and initialize the server properties.
* *
......
...@@ -128,10 +128,9 @@ public class UserResource extends AbstractCaosDBServerResource { ...@@ -128,10 +128,9 @@ public class UserResource extends AbstractCaosDBServerResource {
return error(m, Status.CLIENT_ERROR_NOT_FOUND); return error(m, Status.CLIENT_ERROR_NOT_FOUND);
} else if (m == ServerMessages.ENTITY_DOES_NOT_EXIST) { } else if (m == ServerMessages.ENTITY_DOES_NOT_EXIST) {
return error(m, Status.CLIENT_ERROR_CONFLICT); return error(m, Status.CLIENT_ERROR_CONFLICT);
} else if (m == ServerMessages.PASSWORD_TOO_WEAK) { } else {
return error(m, Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY); return error(m, Status.CLIENT_ERROR_BAD_REQUEST);
} }
throw m;
} catch (final NumberFormatException e) { } catch (final NumberFormatException e) {
return error(ServerMessages.CANNOT_PARSE_INT_VALUE, Status.CLIENT_ERROR_BAD_REQUEST); return error(ServerMessages.CANNOT_PARSE_INT_VALUE, Status.CLIENT_ERROR_BAD_REQUEST);
} }
...@@ -163,10 +162,9 @@ public class UserResource extends AbstractCaosDBServerResource { ...@@ -163,10 +162,9 @@ public class UserResource extends AbstractCaosDBServerResource {
} catch (final Message m) { } catch (final Message m) {
if (m == ServerMessages.ACCOUNT_NAME_NOT_UNIQUE) { if (m == ServerMessages.ACCOUNT_NAME_NOT_UNIQUE) {
return error(m, Status.CLIENT_ERROR_CONFLICT); return error(m, Status.CLIENT_ERROR_CONFLICT);
} else if (m == ServerMessages.PASSWORD_TOO_WEAK) { } else {
return error(m, Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY); return error(m, Status.CLIENT_ERROR_BAD_REQUEST);
} }
throw m;
} }
final Document doc = new Document(); final Document doc = new Document();
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
package org.caosdb.server.transaction; package org.caosdb.server.transaction;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.caosdb.server.CaosDBServer;
import org.caosdb.server.ServerProperties;
import org.caosdb.server.accessControl.ACMPermissions; import org.caosdb.server.accessControl.ACMPermissions;
import org.caosdb.server.accessControl.Principal; import org.caosdb.server.accessControl.Principal;
import org.caosdb.server.accessControl.UserSources; import org.caosdb.server.accessControl.UserSources;
...@@ -86,18 +88,13 @@ public class InsertUserTransaction extends AccessControlTransaction { ...@@ -86,18 +88,13 @@ public class InsertUserTransaction extends AccessControlTransaction {
} }
/* /*
* Names should have at least a length of 1, a maximum length of 32 and match * Check requirements for user names (length, character set). Default config is POSIX compliant.
* ^[a-zA-Z_][a-zA-Z0-9_-]*$.
*/ */
private void checkUserName(String name) throws Message { private void checkUserName(String name) throws Message {
// Make this configurable? String regex = CaosDBServer.getServerProperty(ServerProperties.KEY_USER_NAME_VALID_REGEX);
final boolean length = name.length() >= 1 && name.length() <= 32; if (!name.matches(regex)) {
final boolean match =
name.matches("^[\\p{Lower}\\p{Upper}_][\\p{Lower}\\p{Upper}\\p{Digit}_-]*$");
if (!(length && match)) {
throw ServerMessages.INVALID_USER_NAME( throw ServerMessages.INVALID_USER_NAME(
"User names must have a length from 1 to 32 characters, begin with a latin letter a-z (upper case or lower case) or an underscore (_), and all other characters must be latin letters, arabic numbers, hyphens (-) or undescores (_)."); CaosDBServer.getServerProperty(ServerProperties.KEY_USER_NAME_INVALID_MESSAGE));
} }
if (UserSources.isUserExisting(new Principal(this.user.realm, this.user.name))) { if (UserSources.isUserExisting(new Principal(this.user.realm, this.user.name))) {
......
...@@ -337,7 +337,7 @@ public class ServerMessages { ...@@ -337,7 +337,7 @@ public class ServerMessages {
new Message( new Message(
MessageType.Error, MessageType.Error,
MessageCode.MESSAGE_CODE_UNKNOWN, MessageCode.MESSAGE_CODE_UNKNOWN,
"This user name is yet in use. Please choose another one."); "This user name is already in use. Please choose another one.");
public static final Message ACCOUNT_HAS_BEEN_DELETED = public static final Message ACCOUNT_HAS_BEEN_DELETED =
new Message( new Message(
...@@ -399,11 +399,12 @@ public class ServerMessages { ...@@ -399,11 +399,12 @@ public class ServerMessages {
MessageCode.MESSAGE_CODE_UNKNOWN, MessageCode.MESSAGE_CODE_UNKNOWN,
"This email address is not RFC822 compliant."); "This email address is not RFC822 compliant.");
public static final Message PASSWORD_TOO_WEAK = public static final Message PASSWORD_TOO_WEAK(String policy) {
new Message( return new Message(
MessageType.Error, MessageType.Error,
MessageCode.MESSAGE_CODE_UNKNOWN, MessageCode.MESSAGE_CODE_UNKNOWN,
"This password is too weak. It should be longer than 8 characters and sufficiently random."); "The password does not comply with the current policies for passwords: " + policy);
}
public static final Message AFFILIATION_ERROR = public static final Message AFFILIATION_ERROR =
new Message( new Message(
...@@ -609,7 +610,7 @@ public class ServerMessages { ...@@ -609,7 +610,7 @@ public class ServerMessages {
return new Message( return new Message(
MessageType.Error, MessageType.Error,
MessageCode.MESSAGE_CODE_UNKNOWN, MessageCode.MESSAGE_CODE_UNKNOWN,
"The user name does not comply with the policies for user names: " + policy); "The user name does not comply with the current policies for user names: " + policy);
} }
public static final Message CANNOT_DELETE_YOURSELF() { public static final Message CANNOT_DELETE_YOURSELF() {
......
...@@ -35,6 +35,8 @@ import java.text.DecimalFormat; ...@@ -35,6 +35,8 @@ import java.text.DecimalFormat;
import java.util.Scanner; import java.util.Scanner;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base32; import org.apache.commons.codec.binary.Base32;
import org.caosdb.server.CaosDBServer;
import org.caosdb.server.ServerProperties;
import org.caosdb.server.entity.Message; import org.caosdb.server.entity.Message;
import org.jdom2.Document; import org.jdom2.Document;
import org.jdom2.Element; import org.jdom2.Element;
...@@ -304,18 +306,15 @@ public class Utils { ...@@ -304,18 +306,15 @@ public class Utils {
* @param password The password to be checked. * @param password The password to be checked.
*/ */
public static void checkPasswordStrength(final String password) throws Message { public static void checkPasswordStrength(final String password) throws Message {
final boolean length = password.length() >= 8; String regex = CaosDBServer.getServerProperty(ServerProperties.KEY_PASSWORD_STRENGTH_REGEX);
final boolean uppercase = password.matches(".*\\p{Upper}.*");
final boolean lowercase = password.matches(".*\\p{Lower}.*");
final boolean number = password.matches(".*\\p{Digit}.*");
final boolean punct = password.matches(".*\\p{Punct}.*");
if (!(length && uppercase && lowercase && number && punct)) { if (password.equals("correcthorsebatterystaple")) {
throw ServerMessages.PASSWORD_TOO_WEAK; throw ServerMessages.PASSWORD_TOO_WEAK("PWNED!");
} }
if (password.equals("correcthorsebatterystaple")) { if (!password.matches(regex)) {
throw ServerMessages.PASSWORD_TOO_WEAK; throw ServerMessages.PASSWORD_TOO_WEAK(
CaosDBServer.getServerProperty(ServerProperties.KEY_PASSWORD_WEAK_MESSAGE));
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment