Merge branch 'master' into bug5230-old

# Conflicts:
#	java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PermissionChecker.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementServiceAsync.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/AbstractRoleDefinitionDialog.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionEditDialog.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionsPanel.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/SecurityDTOFactory.java
#	java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
This commit is contained in:
Dennis Aulenbacher
2021-02-18 14:27:27 +01:00
2897 changed files with 416248 additions and 61440 deletions
@@ -6,9 +6,7 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sse.security.userstore.mongodb.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sse,
com.sap.sse.util,
org.json.simple,
Import-Package: org.json.simple,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.5.1"
Bundle-ActivationPolicy: lazy
@@ -21,7 +19,8 @@ Require-Bundle: org.apache.shiro.core;bundle-version="1.2.2",
org.mongodb.mongo-java-driver;bundle-version="3.6.4",
com.sap.sse.replication,
com.sap.sse,
com.sap.sse.security.interface
com.sap.sse.security.interface,
com.sap.sse.security
Export-Package: com.sap.sse.security.userstore.mongodb,
com.sap.sse.security.userstore.mongodb.impl;x-friends:="com.sap.sse.security.storemerging,com.sap.sse.security.test"
Automatic-Module-Name: com.sap.sse.security.userstore.mongodb
@@ -2,12 +2,16 @@ package com.sap.sse.security.userstore.mongodb;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.sap.sse.common.Util;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.LockUtil.RunnableWithResult;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.concurrent.RunnableWithResult;
import com.sap.sse.security.interfaces.AccessControlStore;
import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.shared.AccessControlListAnnotation;
@@ -30,6 +34,20 @@ public class AccessControlStoreImpl implements AccessControlStore {
* maps from object ID string representations to the access control lists for the respective key object
*/
private final ConcurrentHashMap<QualifiedObjectIdentifier, AccessControlListAnnotation> accessControlLists;
/**
* For quick lookup of denying ACLs during meta-permission checks (permission to grant a permission) this map
* contains a subset of the {@link #accessControlList} map, keyed by the
* {@link QualifiedObjectIdentifier#getTypeIdentifier() type identifier} (which is a {@link String}) of the objects
* to which those ACLs pertain, and as a nested map, by the {@link UserGroup} that is the key in the ACL that denies
* permission to an action (could be the {@code null} group, in that case meaning the anonymous users). Access to
* this map, like access to {@link #accessControlLists}, has to be synchronized under the
* {@link #lockForManagementMappings}.<p>
*
* As the inner maps are implemented as non-concurrent maps, {@code null} keys are permissible, so the anonymous
* group is actually represented as a {@code null} key.
*/
private final ConcurrentHashMap<String, Map<UserGroup, Set<QualifiedObjectIdentifier>>> accessControlListsWithDenials;
/**
* maps from object ID string representations to the ownership information for the respective key object
@@ -42,7 +60,17 @@ public class AccessControlStoreImpl implements AccessControlStore {
* (prevented by the lock)
*/
private final ConcurrentHashMap<User, Set<OwnershipAnnotation>> userToOwnership;
/**
* The anonymous {@code null} group is represented by {@link #NULL_GROUP} instead as a {@link ConcurrentHashMap}
* cannot handle {@code null} keys/values.
*/
private final ConcurrentHashMap<UserGroup, Set<OwnershipAnnotation>> userGroupToOwnership;
/**
* The anonymous {@code null} group is represented by {@link #NULL_GROUP} instead as a {@link ConcurrentHashMap}
* cannot handle {@code null} keys/values.
*/
private final ConcurrentHashMap<UserGroup, Set<AccessControlListAnnotation>> userGroupToAccessControlListAnnotation;
private static final UserGroupImpl NULL_GROUP = new UserGroupImpl(null, "<null group>");
@@ -75,12 +103,12 @@ public class AccessControlStoreImpl implements AccessControlStore {
public AccessControlStoreImpl(final DomainObjectFactory domainObjectFactory,
final MongoObjectFactory mongoObjectFactory, final UserStore userStore) {
accessControlLists = new ConcurrentHashMap<>();
accessControlListsWithDenials = new ConcurrentHashMap<>();
ownerships = new ConcurrentHashMap<>();
userToOwnership = new ConcurrentHashMap<>();
userGroupToOwnership = new ConcurrentHashMap<>();
userGroupToAccessControlListAnnotation = new ConcurrentHashMap<>();
lockForManagementMappings = new NamedReentrantReadWriteLock("ownershipLock", true);
this.mongoObjectFactory = mongoObjectFactory;
this.domainObjectFactory = domainObjectFactory;
this.userStore = userStore;
@@ -104,6 +132,7 @@ public class AccessControlStoreImpl implements AccessControlStore {
}
private void internalAddACL(AccessControlListAnnotation acl) {
assert lockForManagementMappings.isWriteLockedByCurrentThread();
accessControlLists.put(acl.getIdOfAnnotatedObject(), acl);
for (UserGroup owner : acl.getAnnotation().getActionsByUserGroup().keySet()) {
internalMapUserGroupToACL(owner, acl);
@@ -139,9 +168,9 @@ public class AccessControlStoreImpl implements AccessControlStore {
final String displayNameOfAccessControlledObject) {
return LockUtil.executeWithWriteLockAndResult(lockForManagementMappings,
new RunnableWithResult<AccessControlListAnnotation>() {
@Override
public AccessControlListAnnotation run() {
removeAccessControlList(idOfAccessControlledObject);
AccessControlListAnnotation acl = new AccessControlListAnnotation(new AccessControlList(),
idOfAccessControlledObject, displayNameOfAccessControlledObject);
accessControlLists.put(idOfAccessControlledObject, acl);
@@ -166,6 +195,7 @@ public class AccessControlStoreImpl implements AccessControlStore {
}
private AccessControlListAnnotation getOrCreateAcl(QualifiedObjectIdentifier idOfAccessControlledObject) {
assert lockForManagementMappings.isWriteLockedByCurrentThread();
return accessControlLists.computeIfAbsent(idOfAccessControlledObject,
id->new AccessControlListAnnotation(new AccessControlList(), id, /* display name */ null));
}
@@ -199,34 +229,56 @@ public class AccessControlStoreImpl implements AccessControlStore {
});
}
private void internalMapUserGroupToACL(final UserGroup userGroup2, final AccessControlListAnnotation acl) {
private void internalMapUserGroupToACL(final UserGroup userGroup, final AccessControlListAnnotation acl) {
if (!lockForManagementMappings.isWriteLockedByCurrentThread()) {
throw new IllegalStateException("Current thread has no write lock!");
}
final UserGroup userGroup = userGroup2 == null ? NULL_GROUP : userGroup2;
final UserGroup effectiveUserGroup = userGroup == null ? NULL_GROUP : userGroup;
Set<AccessControlListAnnotation> currentACLsContainingGroup = userGroupToAccessControlListAnnotation
.get(userGroup);
.get(effectiveUserGroup);
if (currentACLsContainingGroup == null) {
currentACLsContainingGroup = Collections
.newSetFromMap(new ConcurrentHashMap<AccessControlListAnnotation, Boolean>());
userGroupToAccessControlListAnnotation.put(userGroup, currentACLsContainingGroup);
userGroupToAccessControlListAnnotation.put(effectiveUserGroup, currentACLsContainingGroup);
}
currentACLsContainingGroup.add(acl);
final String type = acl.getIdOfAnnotatedObject().getTypeIdentifier();
// FIXME bug5239: add if an action is denied; remove if no action denied for group
Map<UserGroup, Set<QualifiedObjectIdentifier>> aclsByGroupForType = accessControlListsWithDenials.get(type);
final Set<String> deniedActions = acl.getAnnotation().getDeniedActions(userGroup);
if (deniedActions == null || deniedActions.isEmpty()) {
if (aclsByGroupForType != null) {
Util.removeFromValueSet(aclsByGroupForType, userGroup, acl.getIdOfAnnotatedObject());
}
} else {
if (aclsByGroupForType == null) {
aclsByGroupForType = new HashMap<>();
accessControlListsWithDenials.put(type, aclsByGroupForType);
Util.addToValueSet(aclsByGroupForType, userGroup, acl.getIdOfAnnotatedObject());
}
}
}
private void internalRemoveUserGroupToACLMapping(final UserGroup userGroup2,
private void internalRemoveUserGroupToACLMapping(final UserGroup userGroup,
final AccessControlListAnnotation acl) {
if (!lockForManagementMappings.isWriteLockedByCurrentThread()) {
throw new IllegalStateException("Current thread has no write lock!");
}
final UserGroup userGroup = userGroup2 == null ? NULL_GROUP : userGroup2;
final UserGroup effectiveUserGroup = userGroup == null ? NULL_GROUP : userGroup;
Set<AccessControlListAnnotation> currentACLsContainingGroup = userGroupToAccessControlListAnnotation
.get(userGroup);
.get(effectiveUserGroup);
if (currentACLsContainingGroup != null) {
currentACLsContainingGroup.remove(acl);
if (currentACLsContainingGroup.isEmpty()) {
userGroupToAccessControlListAnnotation.remove(userGroup);
userGroupToAccessControlListAnnotation.remove(effectiveUserGroup);
}
}
final String typeIdentifier = acl.getIdOfAnnotatedObject().getTypeIdentifier();
final Map<UserGroup, Set<QualifiedObjectIdentifier>> aclsByGroupForEvent = accessControlListsWithDenials.get(typeIdentifier);
if (aclsByGroupForEvent != null) {
aclsByGroupForEvent.remove(userGroup);
if (aclsByGroupForEvent.isEmpty()) {
accessControlListsWithDenials.remove(typeIdentifier);
}
}
}
@@ -334,6 +386,12 @@ public class AccessControlStoreImpl implements AccessControlStore {
}
}
}
@Override
public Iterable<OwnershipAnnotation> getOwnerhipsWithGroupOwner(UserGroup owningUserGroup) {
final Set<OwnershipAnnotation> ownerships = userGroupToOwnership.get(owningUserGroup);
return ownerships==null ? Collections.emptySet() : Collections.unmodifiableCollection(ownerships);
}
@Override
public OwnershipAnnotation getOwnership(final QualifiedObjectIdentifier idOfOwnedObjectAsString) {
@@ -371,6 +429,7 @@ public class AccessControlStoreImpl implements AccessControlStore {
private void removeAll() {
accessControlLists.clear();
accessControlListsWithDenials.clear();
ownerships.clear();
userGroupToAccessControlListAnnotation.clear();
userGroupToOwnership.clear();
@@ -392,14 +451,26 @@ public class AccessControlStoreImpl implements AccessControlStore {
}
});
}
@Override
public Set<AccessControlListAnnotation> getAccessControlListsForGroup(UserGroup group) {
final Set<AccessControlListAnnotation> aclsForGroup = userGroupToAccessControlListAnnotation.get(group);
return aclsForGroup == null ? null : Collections.unmodifiableSet(aclsForGroup);
}
@Override
public void removeAllOwnershipsFor(final UserGroup userGroup2) {
final UserGroup userGroup = userGroup2 == null ? NULL_GROUP : userGroup2;
public Map<UserGroup, Set<QualifiedObjectIdentifier>> getAccessControlListsWithDenials(String typeIdentifier) {
final Map<UserGroup, Set<QualifiedObjectIdentifier>> aclsForType = accessControlListsWithDenials.get(typeIdentifier);
return aclsForType == null ? null : Collections.unmodifiableMap(aclsForType);
}
@Override
public void removeAllOwnershipsFor(final UserGroup userGroup) {
final UserGroup effectiveUserGroup = userGroup == null ? NULL_GROUP : userGroup;
LockUtil.executeWithWriteLock(lockForManagementMappings, new Runnable() {
@Override
public void run() {
Set<OwnershipAnnotation> knownOwnerships = userGroupToOwnership.get(userGroup);
Set<OwnershipAnnotation> knownOwnerships = userGroupToOwnership.get(effectiveUserGroup);
if (knownOwnerships != null) {
// do not use setOwnership, we know the user will not change, and we can use the more effective
// remove
@@ -411,17 +482,20 @@ public class AccessControlStoreImpl implements AccessControlStore {
ownerships.put(ownership.getIdOfAnnotatedObject(), groupLessOwnership);
mongoObjectFactory.storeOwnership(groupLessOwnership);
}
userGroupToOwnership.remove(userGroup);
userGroupToOwnership.remove(effectiveUserGroup);
}
Set<AccessControlListAnnotation> knownACLEntries = userGroupToAccessControlListAnnotation
.get(userGroup);
.get(effectiveUserGroup);
if (knownACLEntries != null) {
for (AccessControlListAnnotation acl : knownACLEntries) {
internalRemoveUserGroupToACLMapping(userGroup, acl);
acl.getAnnotation().setPermissions(effectiveUserGroup, Collections.emptySet());
internalRemoveUserGroupToACLMapping(effectiveUserGroup, acl);
mongoObjectFactory.storeAccessControlList(acl);
}
userGroupToAccessControlListAnnotation.remove(userGroup);
userGroupToAccessControlListAnnotation.remove(effectiveUserGroup);
for (final Entry<String, Map<UserGroup, Set<QualifiedObjectIdentifier>>> e : accessControlListsWithDenials.entrySet()) {
e.getValue().remove(effectiveUserGroup);
}
}
}
});
@@ -4,19 +4,26 @@ import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sse.ServerInfo;
import com.sap.sse.common.Util;
import com.sap.sse.mongodb.MongoDBService;
import com.sap.sse.security.interfaces.AccessControlStore;
import com.sap.sse.security.interfaces.PreferenceConverterRegistrationManager;
import com.sap.sse.security.interfaces.UserStore;
import com.sap.sse.security.subscription.SubscriptionApiService;
import com.sap.sse.security.subscription.SubscriptionDataHandler;
import com.sap.sse.security.userstore.mongodb.AccessControlStoreImpl;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
import com.sap.sse.util.ServiceTrackerFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
private static BundleContext context;
private static ServiceTracker<SubscriptionApiService, SubscriptionApiService> subscriptionApiServiceTracker;
private ServiceRegistration<?> accessControlStoreRegistration;
private ServiceRegistration<?> userStoreRegistration;
private PreferenceConverterRegistrationManager preferenceConverterRegistrationManager;
@@ -24,7 +31,7 @@ public class Activator implements BundleActivator {
static BundleContext getContext() {
return context;
}
/*
* (non-Javadoc)
*
@@ -32,15 +39,13 @@ public class Activator implements BundleActivator {
*/
public void start(BundleContext bundleContext) throws Exception {
Activator.context = bundleContext;
final String defaultServerGroupName = System.getProperty(UserStore.DEFAULT_SERVER_GROUP_NAME_PROPERTY_NAME, ServerInfo.getName() + "-server");
logger.info("Creating user store");
final String defaultServerGroupName = System.getProperty(UserStore.DEFAULT_SERVER_GROUP_NAME_PROPERTY_NAME,
ServerInfo.getName() + "-server");
subscriptionApiServiceTracker = ServiceTrackerFactory.createAndOpen(context, SubscriptionApiService.class);
final UserStoreImpl userStore = new UserStoreImpl(defaultServerGroupName);
AccessControlStoreImpl accessControlStore = new AccessControlStoreImpl(userStore);
accessControlStoreRegistration = context.registerService(AccessControlStore.class.getName(),
accessControlStore, null);
userStoreRegistration = context.registerService(UserStore.class.getName(),
userStore, null);
accessControlStoreRegistration = context.registerService(AccessControlStore.class.getName(), accessControlStore, null);
userStoreRegistration = context.registerService(UserStore.class.getName(), userStore, null);
preferenceConverterRegistrationManager = new PreferenceConverterRegistrationManager(bundleContext, userStore);
logger.info("User store registered.");
for (CollectionNames name : CollectionNames.values()) {
@@ -60,4 +65,15 @@ public class Activator implements BundleActivator {
Activator.context = null;
}
public static SubscriptionDataHandler getSubscriptionDataHandler(String providerName) {
if (subscriptionApiServiceTracker != null) {
ServiceReference<SubscriptionApiService>[] serviceReferences = subscriptionApiServiceTracker.getServiceReferences();
for (final ServiceReference<SubscriptionApiService> serviceReference : serviceReferences) {
if (Util.equalsWithNull(serviceReference.getProperty(SubscriptionApiService.PROVIDER_NAME_OSGI_REGISTRY_KEY), providerName)) {
return context.getService(serviceReference).getDataHandler();
}
}
}
return null;
}
}
@@ -41,6 +41,9 @@ 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;
import com.sap.sse.security.shared.subscription.Subscription;
import com.sap.sse.security.subscription.SubscriptionData;
import com.sap.sse.security.subscription.SubscriptionDataHandler;
import com.sap.sse.security.userstore.mongodb.DomainObjectFactory;
import com.sap.sse.security.userstore.mongodb.impl.FieldNames.Tenant;
@@ -52,11 +55,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
public DomainObjectFactoryImpl(MongoDatabase db) {
this.db = db;
}
@Override
public Iterable<AccessControlListAnnotation> loadAllAccessControlLists(UserStore userStore) {
ArrayList<AccessControlListAnnotation> result = new ArrayList<>();
MongoCollection<org.bson.Document> aclCollection = db.getCollection(CollectionNames.ACCESS_CONTROL_LISTS.name());
MongoCollection<org.bson.Document> aclCollection = db
.getCollection(CollectionNames.ACCESS_CONTROL_LISTS.name());
try {
for (Document o : aclCollection.find()) {
result.add(loadAccessControlList(o, userStore));
@@ -67,20 +71,23 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return result;
}
@SuppressWarnings("unchecked")
private AccessControlListAnnotation loadAccessControlList(Document aclDBObject, UserStore userStore) {
final QualifiedObjectIdentifier id = QualifiedObjectIdentifierImpl
.fromDBWithoutEscaping((String) aclDBObject.get(FieldNames.AccessControlList.OBJECT_ID.name()));
final String displayName = (String) aclDBObject.get(FieldNames.AccessControlList.OBJECT_DISPLAY_NAME.name());
List<Object> dbPermissionMap = ((List<Object>) aclDBObject.get(FieldNames.AccessControlList.PERMISSION_MAP.name()));
List<Object> dbPermissionMap = ((List<Object>) aclDBObject
.get(FieldNames.AccessControlList.PERMISSION_MAP.name()));
Map<UserGroup, Set<String>> permissionMap = new HashMap<>();
for (Object dbPermissionMapEntryO : dbPermissionMap) {
Document dbPermissionMapEntry = (Document) dbPermissionMapEntryO;
final UUID userGroupKey = (UUID) dbPermissionMapEntry.get(FieldNames.AccessControlList.PERMISSION_MAP_USER_GROUP_ID.name());
final UUID userGroupKey = (UUID) dbPermissionMapEntry
.get(FieldNames.AccessControlList.PERMISSION_MAP_USER_GROUP_ID.name());
final UserGroup userGroup = userStore.getUserGroup(userGroupKey);
Set<String> actions = new HashSet<>();
for (Object o : (List<Object>) dbPermissionMapEntry.get(FieldNames.AccessControlList.PERMISSION_MAP_ACTIONS.name())) {
for (Object o : (List<Object>) dbPermissionMapEntry
.get(FieldNames.AccessControlList.PERMISSION_MAP_ACTIONS.name())) {
actions.add(o.toString());
}
permissionMap.put(userGroup, actions);
@@ -89,7 +96,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
displayName);
return result;
}
@Override
public Iterable<OwnershipAnnotation> loadAllOwnerships(UserStore userStore) {
ArrayList<OwnershipAnnotation> result = new ArrayList<>();
@@ -104,18 +111,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return result;
}
private OwnershipAnnotation loadOwnership(Document ownershipDBObject, UserStore userStore) {
String escapedId = (String) ownershipDBObject.get(FieldNames.Ownership.OBJECT_ID.name());
final QualifiedObjectIdentifier idOfOwnedObject = QualifiedObjectIdentifierImpl.fromDBWithoutEscaping(escapedId);
final String displayNameOfOwnedObject = (String) ownershipDBObject.get(FieldNames.Ownership.OBJECT_DISPLAY_NAME.name());
final QualifiedObjectIdentifier idOfOwnedObject = QualifiedObjectIdentifierImpl
.fromDBWithoutEscaping(escapedId);
final String displayNameOfOwnedObject = (String) ownershipDBObject
.get(FieldNames.Ownership.OBJECT_DISPLAY_NAME.name());
final String userOwnerName = (String) ownershipDBObject.get(FieldNames.Ownership.OWNER_USERNAME.name());
final UUID tenantOwnerId = (UUID) ownershipDBObject.get(FieldNames.Ownership.TENANT_OWNER_ID.name());
final User userOwner = userStore.getUserByName(userOwnerName);
final UserGroup tenantOwner = userStore.getUserGroup(tenantOwnerId);
return new OwnershipAnnotation(new Ownership(userOwner, tenantOwner), idOfOwnedObject, displayNameOfOwnedObject);
return new OwnershipAnnotation(new Ownership(userOwner, tenantOwner), idOfOwnedObject,
displayNameOfOwnedObject);
}
@Override
public Iterable<RoleDefinition> loadAllRoleDefinitions() {
ArrayList<RoleDefinition> result = new ArrayList<>();
@@ -160,8 +170,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return userGroups;
}
private UserGroup loadUserGroupWithProxyUsers(Document groupDBObject, Map<UUID, RoleDefinition> roleDefinitionsById) {
private UserGroup loadUserGroupWithProxyUsers(Document groupDBObject,
Map<UUID, RoleDefinition> roleDefinitionsById) {
final UUID id = (UUID) groupDBObject.get(FieldNames.UserGroup.ID.name());
final String name = (String) groupDBObject.get(FieldNames.UserGroup.NAME.name());
Set<User> users = new HashSet<>();
@@ -201,15 +212,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
* {@link Role#getQualifiedForTenant() tenant qualification} as defined by this parameter; if this
* parameter is {@code null}, role migration will throw an exception.
* @param userGroups
* the user groups to resolve tenant IDs against for users' default tenants as well as role tenant qualifiers
* the user groups to resolve tenant IDs against for users' default tenants as well as role tenant
* qualifiers
* @return the user objects returned have a fully resolved default tenant as well as fully-resolved role tenant/user
* qualifiers; the {@link Tenant} objects passed in the {@code tenants} map may still have an empty user
* group that is filled later.
*/
@Override
public Iterable<User> loadAllUsers(
Map<UUID, RoleDefinition> roleDefinitionsById, RoleMigrationConverter roleMigrationConverter,
Map<UUID, UserGroup> userGroups, UserGroupProvider userGroupProvider) throws UserManagementException {
public Iterable<User> loadAllUsers(Map<UUID, RoleDefinition> roleDefinitionsById,
RoleMigrationConverter roleMigrationConverter, Map<UUID, UserGroup> userGroups,
UserGroupProvider userGroupProvider) throws UserManagementException {
Map<String, User> result = new HashMap<>();
MongoCollection<org.bson.Document> userCollection = db.getCollection(CollectionNames.USERS.name());
try {
@@ -225,7 +237,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
resolveRoleUserQualifiers(result);
return result.values();
}
private void resolveRoleUserQualifiers(Map<String, User> users) throws UserManagementException {
for (final User user : users.values()) {
final Set<Role> userRoles = new HashSet<>();
@@ -234,14 +246,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final User userQualifierProxy = roleWithUserQualifierProxy.getQualifiedForUser();
if (userQualifierProxy != null) {
final User resolvedUserQualifier = users.get(userQualifierProxy.getName());
if (resolvedUserQualifier == null) {
throw new UserManagementException("Unable to resolve user named "+userQualifierProxy.getName()+
" which serves as a role qualifier for role "+roleWithUserQualifierProxy.getName()+
" for user "+user.getName());
}
user.removeRole(roleWithUserQualifierProxy);
user.addRole(new Role(roleWithUserQualifierProxy.getRoleDefinition(),
roleWithUserQualifierProxy.getQualifiedForTenant(), resolvedUserQualifier));
if (resolvedUserQualifier == null) {
logger.severe("Unable to resolve user named "+userQualifierProxy.getName()+
" which serves as a role qualifier for role "+roleWithUserQualifierProxy.getName()+
" for user "+user.getName()+". Removing role.");
} else {
user.addRole(new Role(roleWithUserQualifierProxy.getRoleDefinition(),
roleWithUserQualifierProxy.getQualifiedForTenant(), resolvedUserQualifier));
}
}
}
}
@@ -266,8 +279,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final String email = (String) userDBObject.get(FieldNames.User.EMAIL.name());
final String fullName = (String) userDBObject.get(FieldNames.User.FULLNAME.name());
final String company = (String) userDBObject.get(FieldNames.User.COMPANY.name());
final String localeRaw = (String) userDBObject.get(FieldNames.User.LOCALE.name());
final Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null;
final String localeRaw = (String) userDBObject.get(FieldNames.User.LOCALE.name());
final Locale locale = localeRaw != null ? Locale.forLanguageTag(localeRaw) : null;
Boolean emailValidated = (Boolean) userDBObject.get(FieldNames.User.EMAIL_VALIDATED.name());
String passwordResetSecret = (String) userDBObject.get(FieldNames.User.PASSWORD_RESET_SECRET.name());
String validationSecret = (String) userDBObject.get(FieldNames.User.VALIDATION_SECRET.name());
@@ -281,7 +294,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (role != null) {
roles.add(role);
} else {
logger.warning("Role with ID "+o+" that used to be assigned to user "+username+" not found");
logger.warning(
"Role with ID " + o + " that used to be assigned to user " + username + " not found");
}
}
} else {
@@ -289,20 +303,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
// try to find an equal-named role in the set of role definitions and create a role
// that is qualified by the default tenant; for this a default tenant must exist because
// otherwise a user would obtain global rights by means of migration which must not happen.
logger.info("Migrating roles of user "+username);
logger.info("Migrating roles of user " + username);
List<?> roleNames = (List<?>) userDBObject.get("ROLES");
if (roleNames != null) {
logger.info("Found old roles "+roleNames+" for user "+username);
logger.info("Found old roles " + roleNames + " for user " + username);
for (Object o : roleNames) {
final Role convertedRole = roleMigrationConverter.convert(o.toString(), username);
if (convertedRole != null) {
logger.info("Found role "+convertedRole.getRoleDefinition()+" for old role "+o.toString()+" for user "+username);
logger.info("Found role " + convertedRole.getRoleDefinition() + " for old role " + o.toString()
+ " for user " + username);
// we do not do role associations, to stay similar as before, meaning that all admins can
// edit the roles. Without this we would need to determine which admin (if
// multiple present) should own this association.
roles.add(convertedRole);
rolesMigrated = true;
}else {
} else {
logger.warning("Role " + o.toString() + " for user " + username
+ " not found during migration. User will no longer be in this role.");
}
@@ -315,7 +330,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
permissions.add((String) o);
}
}
final Map<String, UserGroup> defaultTenant = new ConcurrentHashMap<>();
final List<?> defaultTenantIds = (List<?>) userDBObject.get(FieldNames.User.DEFAULT_TENANT_IDS.name());
if (defaultTenantIds != null) {
@@ -350,6 +364,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
// is also only interested in the object's ID
new MongoObjectFactoryImpl(db).storeUser(result);
}
final List<?> subscriptionDocs = (List<?>) userDBObject.get(FieldNames.User.SUBSCRIPTIONS.name());
result.setSubscriptions(loadSubscriptions(subscriptionDocs));
return result;
}
@@ -371,7 +387,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private Map<AccountType, Account> createAccountMapFromdDBObject(Document accountsMap) {
Map<AccountType, Account> accounts = new HashMap<>();
for (Entry<?, ?> e : accountsMap.entrySet()){
for (Entry<?, ?> e : accountsMap.entrySet()) {
AccountType type = AccountType.valueOf((String) e.getKey());
Account account = createAccountFromDBObject((Document) e.getValue(), type);
accounts.put(type, account);
@@ -386,10 +402,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
String saltedPassword = (String) dbAccount.get(FieldNames.UsernamePassword.SALTED_PW.name());
Binary salt = (Binary) dbAccount.get(FieldNames.UsernamePassword.SALT.name());
return new UsernamePasswordAccount(name, saltedPassword, salt.getData());
//TODO [D056866] add other Account-types
// TODO [D056866] add other Account-types
case SOCIAL_USER:
SocialUserAccount socialUserAccount = new SocialUserAccount();
for (Social s : Social.values()){
for (Social s : Social.values()) {
socialUserAccount.setProperty(s.name(), (String) dbAccount.get(s.name()));
}
return socialUserAccount;
@@ -402,22 +418,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
public Map<String, Object> loadSettings() {
Map<String, Object> result = new HashMap<>();
MongoCollection<org.bson.Document> settingsCollection = db.getCollection(CollectionNames.SETTINGS.name());
try {
Document query = new Document();
query.put(FieldNames.Settings.NAME.name(), FieldNames.Settings.VALUES.name());
Document settingDBObject = settingsCollection.find(query).first();
if (settingDBObject != null) {
result = loadSettingMap(settingDBObject);
}
else {
} else {
logger.info("No stored settings found!");
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load settings.");
logger.log(Level.SEVERE, "loadSettings", e);
}
return result;
}
@@ -428,7 +441,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
try {
for (Object o : settingsCollection.find()) {
Document usernameAndPreferencesMap = (Document) o;
Map<String, String> userMap = loadPreferencesMap((Iterable<?>) usernameAndPreferencesMap.get(FieldNames.Preferences.KEYS_AND_VALUES.name()));
Map<String, String> userMap = loadPreferencesMap(
(Iterable<?>) usernameAndPreferencesMap.get(FieldNames.Preferences.KEYS_AND_VALUES.name()));
String username = (String) usernameAndPreferencesMap.get(FieldNames.Preferences.USERNAME.name());
result.put(username, userMap);
}
@@ -453,7 +467,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private Map<String, Object> loadSettingMap(Document settingDBObject) {
Map<String, Object> result = new HashMap<>();
Map<?, ?> map = ((Document) settingDBObject.get(FieldNames.Settings.MAP.name()));
for (Entry<?, ?> e : map.entrySet()){
for (Entry<?, ?> e : map.entrySet()) {
String key = (String) e.getKey();
Object value = e.getValue();
result.put(key, value);
@@ -465,29 +479,26 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
public Map<String, Class<?>> loadSettingTypes() {
Map<String, Class<?>> result = new HashMap<String, Class<?>>();
MongoCollection<Document> settingsCollection = db.getCollection(CollectionNames.SETTINGS.name());
try {
Document query = new Document();
query.put(FieldNames.Settings.NAME.name(), FieldNames.Settings.TYPES.name());
Document settingTypesDBObject = settingsCollection.find(query).first();
if (settingTypesDBObject != null) {
result = loadSettingTypesMap(settingTypesDBObject);
}
else {
} else {
logger.info("No stored setting types found!");
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load setting types.");
logger.log(Level.SEVERE, "loadSettingTypes", e);
}
return result;
}
private Map<String, Class<?>> loadSettingTypesMap(Document settingTypesDBObject) {
Map<String, Class<?>> result = new HashMap<>();
Map<?, ?> map = (Document) settingTypesDBObject.get(FieldNames.Settings.MAP.name());
for (Entry<?, ?> e : map.entrySet()){
for (Entry<?, ?> e : map.entrySet()) {
String key = (String) e.getKey();
Class<?> value = null;
try {
@@ -499,4 +510,25 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
return result;
}
private Subscription[] loadSubscriptions(List<?> subscriptionsDoc) {
final Subscription[] subscriptions;
if (subscriptionsDoc != null) {
subscriptions = new Subscription[subscriptionsDoc.size()];
int i = 0;
for (Object o : subscriptionsDoc) {
final Document doc = (Document) o;
Map<String, Object> data = new HashMap<String, Object>();
for (Entry<String, Object> entry : doc.entrySet()) {
data.put(entry.getKey(), entry.getValue());
}
SubscriptionData subscriptionData = new SubscriptionData(data);
final SubscriptionDataHandler subscriptionDataHandler = Activator.getSubscriptionDataHandler(subscriptionData.getProviderName());
subscriptions[i++] = subscriptionDataHandler.toSubscription(subscriptionData);
}
} else {
subscriptions = null;
}
return subscriptions;
}
}
@@ -39,8 +39,8 @@ public class FieldNames {
NAME,
USERNAMES,
ROLE_DEFINITION_MAP, // a list of objects with two components each:
ROLE_DEFINITION_MAP_ROLE_ID,
ROLE_DEFINITION_MAP_FOR_ALL
ROLE_DEFINITION_MAP_ROLE_ID,
ROLE_DEFINITION_MAP_FOR_ALL
}
public static enum User {
@@ -56,7 +56,8 @@ public class FieldNames {
PASSWORD_RESET_SECRET,
VALIDATION_SECRET,
DEFAULT_TENANT_SERVER,
DEFAULT_TENANT_GROUP;
DEFAULT_TENANT_GROUP,
SUBSCRIPTIONS;
}
public static enum Settings {
@@ -82,6 +83,4 @@ public class FieldNames {
SALTED_PW,
SALT;
}
}
@@ -31,6 +31,8 @@ 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.subscription.Subscription;
import com.sap.sse.security.subscription.SubscriptionDataHandler;
import com.sap.sse.security.userstore.mongodb.MongoObjectFactory;
public class MongoObjectFactoryImpl implements MongoObjectFactory {
@@ -38,7 +40,6 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
private final MongoDatabase db;
final MongoCollection<org.bson.Document> settingCollection;
public MongoObjectFactoryImpl(MongoDatabase db) {
this.db = db;
settingCollection = db.getCollection(CollectionNames.PREFERENCES.name());
@@ -230,6 +231,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
defaultTennants.add(tenant);
}
dbUser.put(FieldNames.User.DEFAULT_TENANT_IDS.name(), defaultTennants);
dbUser.put(FieldNames.User.SUBSCRIPTIONS.name(), createSubscriptions(user.getSubscriptions()));
usersCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).replaceOne(query, dbUser, new UpdateOptions().upsert(true));
}
@@ -333,4 +335,19 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
return dbSettingTypes;
}
private BasicDBList createSubscriptions(Iterable<Subscription> subscriptions) {
final BasicDBList result;
if (subscriptions != null) {
result = new BasicDBList();
for (final Subscription subscription : subscriptions) {
final Document doc = new Document();
final SubscriptionDataHandler subscriptionDataHandler = Activator.getSubscriptionDataHandler(subscription.getProviderName());
doc.putAll(subscriptionDataHandler.toMap(subscription));
result.add(doc);
}
} else {
result = null;
}
return result;
}
}
@@ -14,6 +14,7 @@ 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;
import com.sap.sse.security.shared.subscription.Subscription;
public class UserProxy implements User {
private static final long serialVersionUID = 1L;
@@ -207,4 +208,29 @@ public class UserProxy implements User {
public String createRandomSecret() {
throw new UnsupportedOperationException();
}
@Override
public Iterable<Subscription> getSubscriptions() {
throw new UnsupportedOperationException();
}
@Override
public void setSubscriptions(Subscription[] subscriptions) {
throw new UnsupportedOperationException();
}
@Override
public Subscription getSubscriptionByPlan(String planId) {
throw new UnsupportedOperationException();
}
@Override
public Subscription getSubscriptionById(String subscriptionId) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasActiveSubscription() {
throw new UnsupportedOperationException();
}
}