mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-19 20:25:31 +00:00
refactored User-related methods on SecurityService to avoid sending User objects through replication operations;
this way, no patching of the userGroupProvider property after de-serialization is necessary, and no replacement of a User object in the UserStore should ever happen. Change-Id: I23a8ae53b6a53ca89a2a0b8a11dc779f4034db57
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ public interface OperationWithResult<S, R> extends Operation<S>, Serializable {
|
||||
* Tells whether this operation requires explicit transitive replication to other replicas when received by a
|
||||
* replica. This is the case for all operations whose {@link #internalApplyTo(Object)} method will not trigger
|
||||
* replication. An example for an operation that does <em>not</em> require explicit transitive replication is the
|
||||
* insertion of a GPS fix into a competitor's track because the insertion of the track, as implemented by
|
||||
* insertion of a GPS fix into a competitor's track because the insertion of the fix, as implemented by
|
||||
* {@link #internalApplyTo} will trigger the replication.
|
||||
* <p>
|
||||
*
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
package com.sap.sse.security.shared;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.security.shared.impl.Ownership;
|
||||
import com.sap.sse.security.shared.impl.Role;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.shared.impl.UserGroup;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
|
||||
/**
|
||||
* Keeps track of all {@link User}, {@link UserGroupImpl} and {@link Role}
|
||||
* objects persistently; furthermore, aspects such as user access tokens, preferences and
|
||||
* settings are stored durably.<p>
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface BasicUserStore extends UserGroupProvider, Named {
|
||||
Iterable<UserGroup> getUserGroups();
|
||||
|
||||
UserGroup getUserGroup(UUID groupId);
|
||||
|
||||
UserGroup getUserGroupByName(String name);
|
||||
|
||||
UserGroup createUserGroup(UUID groupId, String name) throws UserGroupManagementException;
|
||||
|
||||
void updateUserGroup(UserGroup userGroup);
|
||||
|
||||
void deleteUserGroup(UserGroup userGroup) throws UserGroupManagementException;
|
||||
|
||||
Iterable<User> getUsers();
|
||||
|
||||
boolean hasUsers();
|
||||
|
||||
/**
|
||||
* The user with that {@link UserImpl#getName() name} or {@code null} if no such user exists
|
||||
*/
|
||||
User getUserByName(String username);
|
||||
|
||||
/**
|
||||
* The user with that {@link UserImpl#getEmail() email} or {@code null} if no such user exists
|
||||
*/
|
||||
User getUserByEmail(String email);
|
||||
|
||||
User getUserByAccessToken(String accessToken);
|
||||
|
||||
User createUser(String name, String email, Account... accounts)
|
||||
throws UserManagementException;
|
||||
|
||||
void updateUser(User user);
|
||||
|
||||
Iterable<Role> getRolesFromUser(String username) throws UserManagementException;
|
||||
|
||||
void addRoleForUser(String username, Role role) throws UserManagementException;
|
||||
|
||||
void removeRoleFromUser(String username, Role role) throws UserManagementException;
|
||||
|
||||
Iterable<WildcardPermission> getPermissionsFromUser(String username) throws UserManagementException;
|
||||
|
||||
void removePermissionFromUser(String username, WildcardPermission permission) throws UserManagementException;
|
||||
|
||||
void addPermissionForUser(String username, WildcardPermission permission) throws UserManagementException;
|
||||
|
||||
void deleteUser(String username) throws UserManagementException;
|
||||
|
||||
Iterable<RoleDefinition> getRoleDefinitions();
|
||||
RoleDefinition getRoleDefinition(UUID roleDefinitionId);
|
||||
RoleDefinition createRoleDefinition(UUID roleDefinitionId, String displayName, Iterable<WildcardPermission> permissions);
|
||||
void setRoleDefinitionPermissions(UUID roleDefinitionId, Set<WildcardPermission> permissions);
|
||||
void addRoleDefinitionPermission(UUID roleDefinitionId, WildcardPermission permission);
|
||||
void removeRoleDefinitionPermission(UUID roleDefinitionId, WildcardPermission permission);
|
||||
void setRoleDefinitionDisplayName(UUID roleDefinitionId, String displayName);
|
||||
void removeRoleDefinition(RoleDefinition roleDefinition);
|
||||
|
||||
/**
|
||||
* Registers a settings key together with its type. Calling this method is necessary for {@link #setSetting(String, Object)}
|
||||
* to have an effect for <code>key</code>. Calls to {@link #setSetting(String, Object)} will only accept values whose type
|
||||
* is compatible with <code>type</code>. Note that the store implementation may impose constraints on the types supported.
|
||||
* All store implementations are required to support at least {@link String} and {@link UUID} as types.
|
||||
*/
|
||||
void addSetting(String key, Class<?> type);
|
||||
|
||||
void setPreference(String username, String key, String value);
|
||||
|
||||
/**
|
||||
* Always returns a valid map which may be empty.
|
||||
*/
|
||||
Map<String, String> getAllPreferences(String username);
|
||||
|
||||
void unsetPreference(String username, String key);
|
||||
|
||||
String getPreference(String username, String key);
|
||||
|
||||
/**
|
||||
* Sets a value for a key if that key was previously added to this store using {@link #addSetting(String, Class)}.
|
||||
* For user store implementations that maintain their data persistently and make it available after a server
|
||||
* restart, it is sufficient to register the settings key once because these registrations will be stored
|
||||
* persistently, too.
|
||||
* <p>
|
||||
*
|
||||
* If the <code>key</code> was not registered before by a call to {@link #addSetting(String, Class)}, or if the
|
||||
* <code>setting</code> object does not conform with the type passed to {@link #addSetting(String, Class)}, a call
|
||||
* to this method will have no effect and return <code>false</code>.
|
||||
*
|
||||
* @Return whether applying the setting was successful; <code>false</code> means that no update was performed to the
|
||||
* setting because either the key was not registered before by {@link #addSetting(String, Class)} or the type of the
|
||||
* <code>setting</code> object does not conform to the type used in {@link #addSetting(String, Class)}
|
||||
*/
|
||||
boolean setSetting(String key, Object setting);
|
||||
|
||||
<T> T getSetting(String key, Class<T> clazz);
|
||||
|
||||
Map<String, Object> getAllSettings();
|
||||
|
||||
Map<String, Class<?>> getAllSettingTypes();
|
||||
|
||||
/**
|
||||
* Removes all users and all their preferences and all settings from this store's in-memory representation.
|
||||
* For safety reasons and because a replica's DB state is undefined anyhow, leaves persistent content in place.
|
||||
* Registered listeners will not be removed automatically.
|
||||
* Use with due care.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Stores an access token that can be used to authenticate the user identified by <code>username</code>.
|
||||
* If there is no user by that name, calling this method has no effect and it will return <code>false</code>.
|
||||
*
|
||||
* @return whether a user could be identified by <code>username</code>
|
||||
*/
|
||||
boolean setAccessToken(String username, String accessToken);
|
||||
|
||||
void removeAccessToken(String username);
|
||||
|
||||
/**
|
||||
* The owner and any subject having the {@link DefaultRoles#ADMIN} role can retrieve an existing
|
||||
* authentication token for the user. {@code null} may result in case for the user identified by
|
||||
* {@code username} no access token has previously been {@link #setAccessToken(String, String) set}.
|
||||
*/
|
||||
String getAccessToken(String username);
|
||||
|
||||
/**
|
||||
* If a valid default tenant name was passed to the constructor, this field will contain a valid
|
||||
* {@link UserGroupImpl} object whose name equals that of the default tenant name. It will have been used
|
||||
* during role migration where string-based roles are mapped to a corresponding {@link RoleDefinition}
|
||||
* and the users with the original role will obtain a corresponding {@link Role} with this default
|
||||
* tenant as the {@link Role#getQualifiedForTenant() tenant qualifier}.
|
||||
*/
|
||||
UserGroup getDefaultTenant();
|
||||
|
||||
/**
|
||||
* Ensures that the predefined role definitions, particularly the "admin" and the "user" role, exist.
|
||||
*/
|
||||
void ensureDefaultRolesExist();
|
||||
|
||||
/**
|
||||
* @return a pair with: <br/>
|
||||
* If A is true, at least one user has an unqualified version of the {@link #roleToCheck} (without tenant or
|
||||
* user qualification). In this case, B is null.<br/>
|
||||
* If A is false, B contains all the ownerships of {@link #roleToCheck}
|
||||
*/
|
||||
Pair<Boolean, Set<Ownership>> getExistingQualificationsForRoleDefinition(RoleDefinition roleToCheck);
|
||||
|
||||
Set<Pair<User, Role>> getRolesQualifiedByUserGroup(UserGroup groupQualification);
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.NamedWithID;
|
||||
import com.sap.sse.common.Renamable;
|
||||
import com.sap.sse.security.shared.impl.Ownership;
|
||||
import com.sap.sse.security.shared.impl.Role;
|
||||
|
||||
/**
|
||||
* A role definition provides an ID, a (changeable) name and a set of permissions. As such, it represents a group of
|
||||
|
||||
@@ -51,7 +51,12 @@ public interface User extends SecurityUser<RoleDefinition, Role, UserGroup> {
|
||||
|
||||
Map<AccountType, Account> getAllAccounts();
|
||||
|
||||
String setEmail(String email);
|
||||
/**
|
||||
* Sets an e-mail address for this user. The address is considered not yet validated, therefore the
|
||||
* caller shall also ensure that {@link #startEmailValidation} is invoked hereafter, with a secret
|
||||
* produced by {@link #createRandomSecret()}.
|
||||
*/
|
||||
void setEmail(String email);
|
||||
|
||||
/**
|
||||
* When someone has requested a password reset, only the owner of the validated e-mail address is
|
||||
@@ -61,9 +66,14 @@ public interface User extends SecurityUser<RoleDefinition, Role, UserGroup> {
|
||||
*/
|
||||
String getPasswordResetSecret();
|
||||
|
||||
String startPasswordReset();
|
||||
void startPasswordReset(String randomSecret);
|
||||
|
||||
String startEmailValidation();
|
||||
/**
|
||||
* Resets the {@link #isEmailValidated()} property and stores the new {@code randomSecret} as the
|
||||
* e-mail validation secret. The {@link #isEmailValidated()} method will return {@code true} only
|
||||
* after {@link #validate(String)} has been called with the {@code randomSecret} passed here.
|
||||
*/
|
||||
void startEmailValidation(String randomSecret);
|
||||
|
||||
boolean validate(String validationSecret);
|
||||
|
||||
@@ -99,4 +109,6 @@ public interface User extends SecurityUser<RoleDefinition, Role, UserGroup> {
|
||||
void setUserGroupProvider(UserGroupProvider userGroupProvider);
|
||||
|
||||
UserGroupProvider getUserGroupProvider();
|
||||
|
||||
String createRandomSecret();
|
||||
}
|
||||
|
||||
+11
-17
@@ -225,15 +225,9 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an e-mail address for this user. The address is considered not yet validated, therefore the
|
||||
* {@link #emailValidated} flag is reset, and a new {@link #validationSecret} is generated and returned which
|
||||
* can be used in a call to {@link #validate(String)} to validate the e-mail address.
|
||||
*/
|
||||
@Override
|
||||
public String setEmail(String email) {
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
return startEmailValidation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,10 +236,9 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
|
||||
* can be used in a call to {@link #validate(String)} to validate the e-mail address.
|
||||
*/
|
||||
@Override
|
||||
public String startEmailValidation() {
|
||||
validationSecret = createRandomSecret();
|
||||
public void startEmailValidation(String randomSecret) {
|
||||
validationSecret = randomSecret;
|
||||
emailValidated = false;
|
||||
return validationSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,9 +247,8 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
|
||||
* a user's password in case the service can provide the correct password reset secret.
|
||||
*/
|
||||
@Override
|
||||
public String startPasswordReset() {
|
||||
passwordResetSecret = createRandomSecret();
|
||||
return passwordResetSecret;
|
||||
public void startPasswordReset(String randomSecret) {
|
||||
passwordResetSecret = randomSecret;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -264,7 +256,8 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
|
||||
return passwordResetSecret;
|
||||
}
|
||||
|
||||
private String createRandomSecret() {
|
||||
@Override
|
||||
public String createRandomSecret() {
|
||||
final byte[] bytes1 = new byte[64];
|
||||
new SecureRandom().nextBytes(bytes1);
|
||||
final byte[] bytes2 = new byte[64];
|
||||
@@ -273,9 +266,10 @@ public class UserImpl extends SecurityUserImpl<RoleDefinition, Role, UserGroup,
|
||||
}
|
||||
|
||||
/**
|
||||
* If the <code>validationSecret</code> passed matches {@link #validationSecret}, the e-mail is
|
||||
* {@link #emailValidated marked as validated}, and <code>true</code> is returned. Otherwise, the validation secret
|
||||
* on this user remains in place, and the e-mail address is not marked as validated.
|
||||
* If the user's e-mail has already been {@link #isEmailValidated() validated}, or the <code>validationSecret</code>
|
||||
* passed matches {@link #validationSecret}, the e-mail is {@link #emailValidated marked as validated}, and
|
||||
* <code>true</code> is returned. Otherwise, the validation secret on this user remains in place, and the e-mail
|
||||
* address is not marked as validated.
|
||||
*/
|
||||
@Override
|
||||
public boolean validate(final String validationSecret) {
|
||||
|
||||
+2
-161
@@ -1,21 +1,10 @@
|
||||
package com.sap.sse.security.interfaces;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.Named;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.security.shared.Account;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.BasicUserStore;
|
||||
import com.sap.sse.security.shared.UserGroupManagementException;
|
||||
import com.sap.sse.security.shared.UserGroupProvider;
|
||||
import com.sap.sse.security.shared.UserManagementException;
|
||||
import com.sap.sse.security.shared.WildcardPermission;
|
||||
import com.sap.sse.security.shared.impl.Ownership;
|
||||
import com.sap.sse.security.shared.impl.Role;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.shared.impl.UserGroup;
|
||||
import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
|
||||
/**
|
||||
@@ -26,7 +15,7 @@ import com.sap.sse.security.shared.impl.UserGroupImpl;
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface UserStore extends UserGroupProvider, Named {
|
||||
public interface UserStore extends BasicUserStore {
|
||||
/**
|
||||
* An instance of the bundle hosting this service may have a default tenant. If so, the default tenant's name is
|
||||
* read from a system property whose name is provided by this constant.
|
||||
@@ -34,82 +23,6 @@ public interface UserStore extends UserGroupProvider, Named {
|
||||
String DEFAULT_TENANT_NAME_PROPERTY_NAME = "security.defaultTenantName";
|
||||
String ADMIN_USERNAME = "admin";
|
||||
|
||||
|
||||
Iterable<UserGroup> getUserGroups();
|
||||
|
||||
UserGroup getUserGroup(UUID groupId);
|
||||
|
||||
UserGroup getUserGroupByName(String name);
|
||||
|
||||
UserGroup createUserGroup(UUID groupId, String name) throws UserGroupManagementException;
|
||||
|
||||
void updateUserGroup(UserGroup userGroup);
|
||||
|
||||
void deleteUserGroup(UserGroup userGroup) throws UserGroupManagementException;
|
||||
|
||||
Iterable<User> getUsers();
|
||||
|
||||
boolean hasUsers();
|
||||
|
||||
/**
|
||||
* The user with that {@link UserImpl#getName() name} or {@code null} if no such user exists
|
||||
*/
|
||||
User getUserByName(String username);
|
||||
|
||||
/**
|
||||
* The user with that {@link UserImpl#getEmail() email} or {@code null} if no such user exists
|
||||
*/
|
||||
User getUserByEmail(String email);
|
||||
|
||||
User getUserByAccessToken(String accessToken);
|
||||
|
||||
User createUser(String name, String email, Account... accounts)
|
||||
throws UserManagementException;
|
||||
|
||||
void updateUser(User user);
|
||||
|
||||
Iterable<Role> getRolesFromUser(String username) throws UserManagementException;
|
||||
|
||||
void addRoleForUser(String username, Role role) throws UserManagementException;
|
||||
|
||||
void removeRoleFromUser(String username, Role role) throws UserManagementException;
|
||||
|
||||
Iterable<WildcardPermission> getPermissionsFromUser(String username) throws UserManagementException;
|
||||
|
||||
void removePermissionFromUser(String username, WildcardPermission permission) throws UserManagementException;
|
||||
|
||||
void addPermissionForUser(String username, WildcardPermission permission) throws UserManagementException;
|
||||
|
||||
void deleteUser(String username) throws UserManagementException;
|
||||
|
||||
Iterable<RoleDefinition> getRoleDefinitions();
|
||||
RoleDefinition getRoleDefinition(UUID roleDefinitionId);
|
||||
RoleDefinition createRoleDefinition(UUID roleDefinitionId, String displayName, Iterable<WildcardPermission> permissions);
|
||||
void setRoleDefinitionPermissions(UUID roleDefinitionId, Set<WildcardPermission> permissions);
|
||||
void addRoleDefinitionPermission(UUID roleDefinitionId, WildcardPermission permission);
|
||||
void removeRoleDefinitionPermission(UUID roleDefinitionId, WildcardPermission permission);
|
||||
void setRoleDefinitionDisplayName(UUID roleDefinitionId, String displayName);
|
||||
void removeRoleDefinition(RoleDefinition roleDefinition);
|
||||
|
||||
/**
|
||||
* Registers a settings key together with its type. Calling this method is necessary for {@link #setSetting(String, Object)}
|
||||
* to have an effect for <code>key</code>. Calls to {@link #setSetting(String, Object)} will only accept values whose type
|
||||
* is compatible with <code>type</code>. Note that the store implementation may impose constraints on the types supported.
|
||||
* All store implementations are required to support at least {@link String} and {@link UUID} as types.
|
||||
*/
|
||||
void addSetting(String key, Class<?> type);
|
||||
|
||||
void setPreference(String username, String key, String value);
|
||||
|
||||
/**
|
||||
* Always returns a valid map which may be empty.
|
||||
*/
|
||||
Map<String, String> getAllPreferences(String username);
|
||||
|
||||
void unsetPreference(String username, String key);
|
||||
|
||||
String getPreference(String username, String key);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* In an OSGi environment, this shouldn't be called manually, but instead automatically managed by setting a
|
||||
@@ -164,88 +77,16 @@ public interface UserStore extends UserGroupProvider, Named {
|
||||
*/
|
||||
String setPreferenceObject(String username, String key, Object preferenceObject) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Sets a value for a key if that key was previously added to this store using {@link #addSetting(String, Class)}.
|
||||
* For user store implementations that maintain their data persistently and make it available after a server
|
||||
* restart, it is sufficient to register the settings key once because these registrations will be stored
|
||||
* persistently, too.
|
||||
* <p>
|
||||
*
|
||||
* If the <code>key</code> was not registered before by a call to {@link #addSetting(String, Class)}, or if the
|
||||
* <code>setting</code> object does not conform with the type passed to {@link #addSetting(String, Class)}, a call
|
||||
* to this method will have no effect and return <code>false</code>.
|
||||
*
|
||||
* @Return whether applying the setting was successful; <code>false</code> means that no update was performed to the
|
||||
* setting because either the key was not registered before by {@link #addSetting(String, Class)} or the type of the
|
||||
* <code>setting</code> object does not conform to the type used in {@link #addSetting(String, Class)}
|
||||
*/
|
||||
boolean setSetting(String key, Object setting);
|
||||
|
||||
<T> T getSetting(String key, Class<T> clazz);
|
||||
|
||||
Map<String, Object> getAllSettings();
|
||||
|
||||
Map<String, Class<?>> getAllSettingTypes();
|
||||
|
||||
/**
|
||||
* Removes all users and all their preferences and all settings from this store's in-memory representation.
|
||||
* For safety reasons and because a replica's DB state is undefined anyhow, leaves persistent content in place.
|
||||
* Registered listeners will not be removed automatically.
|
||||
* Use with due care.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Replaces all existing contents by those provided by the <code>newUserStore</code>. This has no impact on the persistent
|
||||
* representation of this store and is meant for use on a replica only; the replica's database state is undefined.
|
||||
*/
|
||||
void replaceContentsFrom(UserStore newUserStore);
|
||||
|
||||
/**
|
||||
* Stores an access token that can be used to authenticate the user identified by <code>username</code>.
|
||||
* If there is no user by that name, calling this method has no effect and it will return <code>false</code>.
|
||||
*
|
||||
* @return whether a user could be identified by <code>username</code>
|
||||
*/
|
||||
boolean setAccessToken(String username, String accessToken);
|
||||
|
||||
void removeAccessToken(String username);
|
||||
|
||||
/**
|
||||
* The owner and any subject having the {@link DefaultRoles#ADMIN} role can retrieve an existing
|
||||
* authentication token for the user. {@code null} may result in case for the user identified by
|
||||
* {@code username} no access token has previously been {@link #setAccessToken(String, String) set}.
|
||||
*/
|
||||
String getAccessToken(String username);
|
||||
|
||||
void addPreferenceObjectListener(String key, PreferenceObjectListener<?> listener, boolean fireForAlreadyExistingPreferences);
|
||||
|
||||
void removePreferenceObjectListener(PreferenceObjectListener<?> listener);
|
||||
|
||||
/**
|
||||
* If a valid default tenant name was passed to the constructor, this field will contain a valid
|
||||
* {@link UserGroupImpl} object whose name equals that of the default tenant name. It will have been used
|
||||
* during role migration where string-based roles are mapped to a corresponding {@link RoleDefinition}
|
||||
* and the users with the original role will obtain a corresponding {@link Role} with this default
|
||||
* tenant as the {@link Role#getQualifiedForTenant() tenant qualifier}.
|
||||
*/
|
||||
UserGroup getDefaultTenant();
|
||||
|
||||
/**
|
||||
* Ensures that the predefined role definitions, particularly the "admin" and the "user" role, exist.
|
||||
*/
|
||||
void ensureDefaultRolesExist();
|
||||
|
||||
/**
|
||||
* @return a pair with: <br/>
|
||||
* If A is true, at least one user has an unqualified version of the {@link #roleToCheck} (without tenant or
|
||||
* user qualification). In this case, B is null.<br/>
|
||||
* If A is false, B contains all the ownerships of {@link #roleToCheck}
|
||||
*/
|
||||
Pair<Boolean, Set<Ownership>> getExistingQualificationsForRoleDefinition(RoleDefinition roleToCheck);
|
||||
|
||||
Set<Pair<User, Role>> getRolesQualifiedByUserGroup(UserGroup groupQualification);
|
||||
|
||||
/**
|
||||
* Do not call this before the RolePrototypes are created/loaded, as else a migration cannot succeed. But do call
|
||||
* this before the SecurityService is created, as else new defaults (eg admin user) will be created
|
||||
|
||||
-4
@@ -694,10 +694,6 @@ public class UserStoreImpl implements UserStore {
|
||||
@Override
|
||||
public void updateUser(User user) {
|
||||
logger.info("Updating user " + user + " in DB");
|
||||
if (user.getUserGroupProvider() != this) {
|
||||
logger.info("Adjusting user group provider for user "+user+"; probably after de-serialization.");
|
||||
user.setUserGroupProvider(this);
|
||||
}
|
||||
users.put(user.getName(), user);
|
||||
removeFromUsersByEmail(user);
|
||||
addToUsersByEmail(user);
|
||||
|
||||
-1
@@ -207,7 +207,6 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
dbPermissions.add(permission.toString());
|
||||
}
|
||||
dbUser.put(FieldNames.User.PERMISSIONS.name(), dbPermissions);
|
||||
|
||||
List<Object> defaultTennants = new BasicDBList();
|
||||
for (Entry<String, UserGroup> entries : user.getDefaultTenantMap().entrySet()) {
|
||||
Document tenant = new Document();
|
||||
|
||||
+8
-3
@@ -124,7 +124,7 @@ public class UserProxy implements User {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setEmail(String email) {
|
||||
public void setEmail(String email) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -134,12 +134,12 @@ public class UserProxy implements User {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String startPasswordReset() {
|
||||
public void startPasswordReset(String randomSecret) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String startEmailValidation() {
|
||||
public void startEmailValidation(String randomSecret) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -202,4 +202,9 @@ public class UserProxy implements User {
|
||||
public UserGroupProvider getUserGroupProvider() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createRandomSecret() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ public class Activator implements BundleActivator {
|
||||
if (cm instanceof ReplicatingCacheManager) {
|
||||
((ReplicatingCacheManager) cm).clear();
|
||||
}
|
||||
|
||||
UserStore userStore = userStoreTracker.waitForService(0);
|
||||
AccessControlStore accessControlStore = accessControlStoreTracker.waitForService(0);
|
||||
userStore.clear();
|
||||
|
||||
+17
-1
@@ -1,11 +1,13 @@
|
||||
package com.sap.sse.security.impl;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.shiro.session.Session;
|
||||
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.shared.Account;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.RoleDefinition;
|
||||
import com.sap.sse.security.shared.UserGroupManagementException;
|
||||
@@ -51,7 +53,19 @@ public interface ReplicableSecurityService extends SecurityService {
|
||||
Void internalRemoveRoleDefinitionFromUserGroup(UUID groupId, UUID roleDefinitionId)
|
||||
throws UserGroupManagementException;
|
||||
|
||||
Void internalStoreUser(User user);
|
||||
/**
|
||||
* Creates and stores a <em>new</em> user in the system. <em>Don't</em> use this to update an existing user.
|
||||
* Use other {@code internal...} methods to update individual user properties instead.
|
||||
*/
|
||||
User internalCreateUser(String username, String email, Account... accounts) throws UserManagementException;
|
||||
|
||||
Void internalUpdateSimpleUserEmail(String username, String newEmail, String validationSecret);
|
||||
|
||||
Void internalUpdateSimpleUserPassword(String username, byte[] salt, String hashedPasswordBase64);
|
||||
|
||||
Void internalUpdateUserProperties(User user, String fullName, String company, Locale locale);
|
||||
|
||||
Boolean internalValidateEmail(String username, String validationSecret);
|
||||
|
||||
Void internalSetPreference(String username, String key, String value);
|
||||
|
||||
@@ -94,4 +108,6 @@ public interface ReplicableSecurityService extends SecurityService {
|
||||
|
||||
Void internalSetDefaultTenantForServerForUser(String username, UUID defaultTenantId, String serverName);
|
||||
|
||||
Void internalResetPassword(String username, String passwordResetSecret);
|
||||
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
if (store.getUserByName(SecurityService.ALL_USERNAME) == null) {
|
||||
isInitialOrMigration = true;
|
||||
logger.info(SecurityService.ALL_USERNAME + " not found -> creating it now");
|
||||
User allUser = createUserInternal(SecurityService.ALL_USERNAME, null);
|
||||
User allUser = apply(s->s.internalCreateUser(SecurityService.ALL_USERNAME, null));
|
||||
|
||||
// <all> user is explicitly not owned by itself because this would enable anybody to modify this user
|
||||
setOwnership(allUser.getIdentifier(), null, getDefaultTenant());
|
||||
@@ -369,8 +369,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
if (!user.isEmailValidated()) {
|
||||
throw new UserManagementException(UserManagementException.CANNOT_RESET_PASSWORD_WITHOUT_VALIDATED_EMAIL);
|
||||
}
|
||||
final String passwordResetSecret = user.startPasswordReset();
|
||||
apply(s->s.internalStoreUser(user)); // durably storing the password reset secret
|
||||
final String passwordResetSecret = user.createRandomSecret();
|
||||
apply(s->s.internalResetPassword(username, passwordResetSecret));
|
||||
Map<String, String> urlParameters = new HashMap<>();
|
||||
try {
|
||||
urlParameters.put("u", URLEncoder.encode(user.getName(), "UTF-8"));
|
||||
@@ -397,6 +397,12 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalResetPassword(String username, String passwordResetSecret) {
|
||||
getUserByName(username).startPasswordReset(passwordResetSecret);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachingSecurityManager getSecurityManager() {
|
||||
return this.securityManager;
|
||||
@@ -784,7 +790,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
|
||||
@Override
|
||||
public User createSimpleUser(final String username, final String email, String password, String fullName,
|
||||
String company, Locale locale, final String validationBaseURL, UserGroup userOwner)
|
||||
String company, Locale locale, final String validationBaseURL, UserGroup groupOwningUser)
|
||||
throws UserManagementException, MailException, UserGroupManagementException {
|
||||
logger.info("Creating user "+username);
|
||||
if (store.getUserByName(username) != null) {
|
||||
@@ -801,35 +807,15 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
byte[] salt = rng.nextBytes().getBytes();
|
||||
String hashedPasswordBase64 = hashPassword(password, salt);
|
||||
UsernamePasswordAccount upa = new UsernamePasswordAccount(username, hashedPasswordBase64, salt);
|
||||
final User result = createUserInternal(username, email, upa);
|
||||
final User result = apply(s->s.internalCreateUser(username, email, upa)); // This also replicated the user creation
|
||||
addUserRoleToUser(result);
|
||||
|
||||
final UserGroup tenant = getOrCreateTenantForUser(result);
|
||||
setDefaultTenantForCurrentServerForUser(username, tenant.getId());
|
||||
|
||||
// the new user becomes its owner to ensure the user role is correctly working
|
||||
// the default tenant is the owning tenant to allow users having admin role for a specific server tenant to also be able to delete users
|
||||
accessControlStore.setOwnership(result.getIdentifier(), result, userOwner, username);
|
||||
|
||||
result.setFullName(fullName);
|
||||
result.setCompany(company);
|
||||
result.setLocale(locale);
|
||||
final String emailValidationSecret = result.startEmailValidation();
|
||||
// don't replicate exception handling; replicate only the effect on the user store
|
||||
apply(s->s.internalStoreUser(result));
|
||||
if (validationBaseURL != null && email != null && !email.trim().isEmpty()) {
|
||||
new Thread("e-mail validation for user " + username + " with e-mail address " + email) {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
startEmailValidation(result, emailValidationSecret, validationBaseURL);
|
||||
} catch (MailException e) {
|
||||
logger.log(Level.SEVERE, "Error sending mail for new account validation of user " + username
|
||||
+ " to address " + email, e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
apply(s->s.internalSetOwnership(result.getIdentifier(), username, groupOwningUser.getId(), username));
|
||||
updateUserProperties(username, fullName, company, locale);
|
||||
updateSimpleUserEmail(username, email, validationBaseURL);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -858,13 +844,9 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
return tenant;
|
||||
}
|
||||
|
||||
private User createUserInternal(String username, String email, Account... accounts)
|
||||
throws UserManagementException {
|
||||
final User result = store.createUser(username, email, accounts); // TODO: get the principal
|
||||
// as owner
|
||||
// now the user creation needs to be replicated so that when replicating role addition and group assignment
|
||||
// the replica will be able to resolve the user correctly
|
||||
apply(s -> s.internalStoreUser(result));
|
||||
@Override
|
||||
public User internalCreateUser(String username, String email, Account... accounts) throws UserManagementException {
|
||||
final User result = store.createUser(username, email, accounts); // TODO: get the principal as owner
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -872,12 +854,6 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
return username + "-tenant";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalStoreUser(User user) {
|
||||
store.updateUser(user);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSimpleUserPassword(String username, String newPassword) throws UserManagementException {
|
||||
final User user = store.getUserByName(username);
|
||||
@@ -892,14 +868,20 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
throw new UserManagementException(UserManagementException.PASSWORD_DOES_NOT_MEET_REQUIREMENTS);
|
||||
}
|
||||
// for non-admins, check that the old password is correct
|
||||
final UsernamePasswordAccount account = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD);
|
||||
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
|
||||
byte[] salt = rng.nextBytes().getBytes();
|
||||
String hashedPasswordBase64 = hashPassword(newPassword, salt);
|
||||
apply(s->s.internalUpdateSimpleUserPassword(user.getName(), salt, hashedPasswordBase64));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalUpdateSimpleUserPassword(String username, byte[] salt, String hashedPasswordBase64) {
|
||||
final User user = getUserByName(username);
|
||||
final UsernamePasswordAccount account = (UsernamePasswordAccount) user.getAccount(AccountType.USERNAME_PASSWORD);
|
||||
account.setSalt(salt);
|
||||
account.setSaltedPassword(hashedPasswordBase64);
|
||||
user.passwordWasReset();
|
||||
apply(s->s.internalStoreUser(user));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -908,14 +890,16 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
if (user == null) {
|
||||
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
|
||||
}
|
||||
updateUserProperties(user, fullName, company, locale);
|
||||
apply(s->s.internalUpdateUserProperties(user, fullName, company, locale));
|
||||
}
|
||||
|
||||
private void updateUserProperties(User user, String fullName, String company, Locale locale) {
|
||||
@Override
|
||||
public Void internalUpdateUserProperties(User user, String fullName, String company, Locale locale) {
|
||||
user.setFullName(fullName);
|
||||
user.setCompany(company);
|
||||
user.setLocale(locale);
|
||||
apply(s->s.internalStoreUser(user));
|
||||
store.updateUser(user);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -945,7 +929,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
|
||||
}
|
||||
logger.info("Changing e-mail address of user "+username+" to "+newEmail);
|
||||
final String validationSecret = user.setEmail(newEmail);
|
||||
final String validationSecret = user.createRandomSecret();
|
||||
apply(s->s.internalUpdateSimpleUserEmail(username, newEmail, validationSecret));
|
||||
new Thread("e-mail validation after changing e-mail of user " + username + " to " + newEmail) {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -957,7 +942,14 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
apply(s->s.internalStoreUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalUpdateSimpleUserEmail(final String username, final String newEmail, final String validationSecret) {
|
||||
final User user = getUserByName(username);
|
||||
user.setEmail(newEmail);
|
||||
user.startEmailValidation(validationSecret);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -966,17 +958,25 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
if (user == null) {
|
||||
throw new UserManagementException(UserManagementException.USER_DOES_NOT_EXIST);
|
||||
}
|
||||
return apply(s->s.internalValidateEmail(username, validationSecret));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean internalValidateEmail(String username, String validationSecret) {
|
||||
final User user = store.getUserByName(username);
|
||||
final boolean result = user.validate(validationSecret);
|
||||
apply(s->s.internalStoreUser(user));
|
||||
if (result) {
|
||||
store.updateUser(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link UserImpl#startEmailValidation() Triggers} e-mail validation for the <code>user</code> object and sends out a
|
||||
* {@link UserImpl#startEmailValidation(String) Triggers} e-mail validation for the <code>user</code> object and sends out a
|
||||
* URL to the user's e-mail that has the validation secret ready for validation by clicking.
|
||||
*
|
||||
* @param validationSecret
|
||||
* the result of either {@link UserImpl#startEmailValidation()} or {@link UserImpl#setEmail(String)}.
|
||||
* the result of either {@link UserImpl#startEmailValidation(String)} or {@link UserImpl#setEmail(String)}.
|
||||
* @param baseURL
|
||||
* the URL under which the user can reach the e-mail validation service; this URL is required to assemble
|
||||
* a validation URL that is sent by e-mail to the user, to make the user return the validation secret to
|
||||
|
||||
Reference in New Issue
Block a user