chore: refactor code split for UserUtils for super user creation (#16348)

This commit is contained in:
Trisha Anand 2022-08-27 21:39:17 +05:30 committed by GitHub
parent d00e448dda
commit 649fe2ec7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 240 additions and 175 deletions

View File

@ -131,7 +131,7 @@ public class FieldName {
public static final Object NEW_GROUP_ID = "newGroupId";
public static final String PERMISSION_GROUP_ID = "permissionGroupId";
public static final String INSTANCE_CONFIG = "instanceConfig";
public static final String INSTACE_ADMIN_ROLE = "Instance Administrator Role";
public static final String INSTANCE_ADMIN_ROLE = "Instance Administrator Role";
public static final String DEFAULT_PERMISSION_GROUP = "defaultPermissionGroup";
public static final String PUBLIC_PERMISSION_GROUP = "publicPermissionGroup";
public static final String EVENT_DATA = "eventData";

View File

@ -1,176 +1,17 @@
package com.appsmith.server.helpers;
import com.appsmith.external.models.Policy;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Config;
import com.appsmith.server.domains.PermissionGroup;
import com.appsmith.server.domains.QPermissionGroup;
import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.Permission;
import com.appsmith.server.helpers.ce.UserUtilsCE;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import com.appsmith.server.repositories.ConfigRepository;
import com.appsmith.server.repositories.PermissionGroupRepository;
import net.minidev.json.JSONObject;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.ASSIGN_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.MANAGE_INSTANCE_CONFIGURATION;
import static com.appsmith.server.acl.AclPermission.MANAGE_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.READ_INSTANCE_CONFIGURATION;
import static com.appsmith.server.constants.FieldName.DEFAULT_PERMISSION_GROUP;
import static com.appsmith.server.constants.FieldName.INSTANCE_CONFIG;
import static com.appsmith.server.repositories.BaseAppsmithRepositoryImpl.fieldName;
@Component
public class UserUtils {
private final ConfigRepository configRepository;
private final PermissionGroupRepository permissionGroupRepository;
private final CacheableRepositoryHelper cacheableRepositoryHelper;
public UserUtils(ConfigRepository configRepository, PermissionGroupRepository permissionGroupRepository,
public class UserUtils extends UserUtilsCE {
public UserUtils(ConfigRepository configRepository,
PermissionGroupRepository permissionGroupRepository,
CacheableRepositoryHelper cacheableRepositoryHelper) {
this.configRepository = configRepository;
this.permissionGroupRepository = permissionGroupRepository;
this.cacheableRepositoryHelper = cacheableRepositoryHelper;
super(configRepository, permissionGroupRepository, cacheableRepositoryHelper);
}
public Mono<Boolean> isSuperUser(User user) {
return configRepository.findByNameAsUser(INSTANCE_CONFIG, user, AclPermission.MANAGE_INSTANCE_CONFIGURATION)
.map(config -> Boolean.TRUE)
.switchIfEmpty(Mono.just(Boolean.FALSE));
}
public Mono<Boolean> isCurrentUserSuperUser() {
return configRepository.findByName(INSTANCE_CONFIG, AclPermission.MANAGE_INSTANCE_CONFIGURATION)
.map(config -> Boolean.TRUE)
.switchIfEmpty(Mono.just(Boolean.FALSE));
}
public Mono<Boolean> makeSuperUser(List<User> users) {
return configRepository.findByName(INSTANCE_CONFIG)
.switchIfEmpty(Mono.defer(() -> createInstanceConfigForSuperUser()))
.flatMap(instanceConfig -> {
JSONObject config = instanceConfig.getConfig();
String defaultPermissionGroup = (String) config.getOrDefault(DEFAULT_PERMISSION_GROUP, "");
return permissionGroupRepository.findById(defaultPermissionGroup);
})
.flatMap(permissionGroup -> {
Set<String> assignedToUserIds = new HashSet<>();
if (permissionGroup.getAssignedToUserIds() != null) {
assignedToUserIds.addAll(permissionGroup.getAssignedToUserIds());
}
assignedToUserIds.addAll(users.stream().map(User::getId).collect(Collectors.toList()));
Update updateObj = new Update();
String path = fieldName(QPermissionGroup.permissionGroup.assignedToUserIds);
updateObj.set(path, assignedToUserIds);
// Make Super User is called before the first administrator is created.
return permissionGroupRepository.updateById(permissionGroup.getId(), updateObj);
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> cacheableRepositoryHelper.evictPermissionGroupsUser(user.getEmail(), user.getTenantId()))
.then(Mono.just(Boolean.TRUE));
}
public Mono<Boolean> removeSuperUser(List<User> users) {
return configRepository.findByName(INSTANCE_CONFIG)
.switchIfEmpty(createInstanceConfigForSuperUser())
.flatMap(instanceConfig -> {
JSONObject config = instanceConfig.getConfig();
String defaultPermissionGroup = (String) config.getOrDefault(DEFAULT_PERMISSION_GROUP, "");
return permissionGroupRepository.findById(defaultPermissionGroup);
})
.flatMap(permissionGroup -> {
if (permissionGroup.getAssignedToUserIds() == null) {
permissionGroup.setAssignedToUserIds(new HashSet<>());
}
permissionGroup.getAssignedToUserIds().removeAll(users.stream().map(User::getId).collect(Collectors.toList()));
return permissionGroupRepository.updateById(permissionGroup.getId(), permissionGroup, AclPermission.ASSIGN_PERMISSION_GROUPS);
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> cacheableRepositoryHelper.evictPermissionGroupsUser(user.getEmail(), user.getTenantId()))
.then(Mono.just(Boolean.TRUE));
}
private Mono<Config> createInstanceConfigForSuperUser() {
Config instanceAdminConfiguration = new Config();
instanceAdminConfiguration.setName(FieldName.INSTANCE_CONFIG);
return configRepository.save(instanceAdminConfiguration)
.flatMap(savedInstanceConfig -> {
// Create instance management permission group
PermissionGroup instanceManagerPermissionGroup = new PermissionGroup();
instanceManagerPermissionGroup.setName(FieldName.INSTACE_ADMIN_ROLE);
instanceManagerPermissionGroup.setPermissions(
Set.of(
new Permission(savedInstanceConfig.getId(), MANAGE_INSTANCE_CONFIGURATION)
)
);
return permissionGroupRepository.save(instanceManagerPermissionGroup)
.flatMap(savedPermissionGroup -> {
// Update the instance config with the permission group id
savedInstanceConfig.setConfig(
new JSONObject(Map.of(DEFAULT_PERMISSION_GROUP, savedPermissionGroup.getId()))
);
Policy editConfigPolicy = Policy.builder().permission(MANAGE_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy readConfigPolicy = Policy.builder().permission(READ_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedInstanceConfig.setPolicies(new HashSet<>(Set.of(editConfigPolicy, readConfigPolicy)));
return configRepository.save(savedInstanceConfig).zipWith(Mono.just(savedPermissionGroup));
});
})
.flatMap(tuple -> {
Config finalInstanceConfig = tuple.getT1();
PermissionGroup savedPermissionGroup = tuple.getT2();
Set<Permission> permissions = new HashSet<>(savedPermissionGroup.getPermissions());
permissions.addAll(
Set.of(
new Permission(savedPermissionGroup.getId(), MANAGE_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS)
)
);
savedPermissionGroup.setPermissions(permissions);
// Also give the permission group permission to update & assign to itself
Policy updatePermissionGroupPolicy = Policy.builder().permission(MANAGE_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy assignPermissionGroupPolicy = Policy.builder().permission(ASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedPermissionGroup.setPolicies(new HashSet<>(Set.of(updatePermissionGroupPolicy, assignPermissionGroupPolicy)));
return permissionGroupRepository.save(savedPermissionGroup).thenReturn(finalInstanceConfig);
});
}
}

View File

@ -0,0 +1,195 @@
package com.appsmith.server.helpers.ce;
import com.appsmith.external.models.Policy;
import com.appsmith.server.acl.AclPermission;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Config;
import com.appsmith.server.domains.PermissionGroup;
import com.appsmith.server.domains.QPermissionGroup;
import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.Permission;
import com.appsmith.server.repositories.CacheableRepositoryHelper;
import com.appsmith.server.repositories.ConfigRepository;
import com.appsmith.server.repositories.PermissionGroupRepository;
import net.minidev.json.JSONObject;
import org.springframework.data.mongodb.core.query.Update;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.appsmith.server.acl.AclPermission.ASSIGN_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.MANAGE_INSTANCE_CONFIGURATION;
import static com.appsmith.server.acl.AclPermission.READ_INSTANCE_CONFIGURATION;
import static com.appsmith.server.acl.AclPermission.READ_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.UNASSIGN_PERMISSION_GROUPS;
import static com.appsmith.server.constants.FieldName.DEFAULT_PERMISSION_GROUP;
import static com.appsmith.server.constants.FieldName.INSTANCE_CONFIG;
import static com.appsmith.server.repositories.BaseAppsmithRepositoryImpl.fieldName;
public class UserUtilsCE {
private final ConfigRepository configRepository;
private final PermissionGroupRepository permissionGroupRepository;
private final CacheableRepositoryHelper cacheableRepositoryHelper;
public UserUtilsCE(ConfigRepository configRepository, PermissionGroupRepository permissionGroupRepository,
CacheableRepositoryHelper cacheableRepositoryHelper) {
this.configRepository = configRepository;
this.permissionGroupRepository = permissionGroupRepository;
this.cacheableRepositoryHelper = cacheableRepositoryHelper;
}
public Mono<Boolean> isSuperUser(User user) {
return configRepository.findByNameAsUser(INSTANCE_CONFIG, user, AclPermission.MANAGE_INSTANCE_CONFIGURATION)
.map(config -> Boolean.TRUE)
.switchIfEmpty(Mono.just(Boolean.FALSE));
}
public Mono<Boolean> isCurrentUserSuperUser() {
return configRepository.findByName(INSTANCE_CONFIG, AclPermission.MANAGE_INSTANCE_CONFIGURATION)
.map(config -> Boolean.TRUE)
.switchIfEmpty(Mono.just(Boolean.FALSE));
}
public Mono<Boolean> makeSuperUser(List<User> users) {
return configRepository.findByName(INSTANCE_CONFIG)
.switchIfEmpty(Mono.defer(() -> createInstanceConfigForSuperUser()))
.flatMap(instanceConfig -> {
JSONObject config = instanceConfig.getConfig();
String defaultPermissionGroup = (String) config.getOrDefault(DEFAULT_PERMISSION_GROUP, "");
return permissionGroupRepository.findById(defaultPermissionGroup);
})
.flatMap(permissionGroup -> {
Set<String> assignedToUserIds = new HashSet<>();
if (permissionGroup.getAssignedToUserIds() != null) {
assignedToUserIds.addAll(permissionGroup.getAssignedToUserIds());
}
assignedToUserIds.addAll(users.stream().map(User::getId).collect(Collectors.toList()));
Update updateObj = new Update();
String path = fieldName(QPermissionGroup.permissionGroup.assignedToUserIds);
updateObj.set(path, assignedToUserIds);
// Make Super User is called before the first administrator is created.
return permissionGroupRepository.updateById(permissionGroup.getId(), updateObj);
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> cacheableRepositoryHelper.evictPermissionGroupsUser(user.getEmail(), user.getTenantId()))
.then(Mono.just(Boolean.TRUE));
}
public Mono<Boolean> removeSuperUser(List<User> users) {
return configRepository.findByName(INSTANCE_CONFIG)
.switchIfEmpty(Mono.defer(() -> createInstanceConfigForSuperUser()))
.flatMap(instanceConfig -> {
JSONObject config = instanceConfig.getConfig();
String defaultPermissionGroup = (String) config.getOrDefault(DEFAULT_PERMISSION_GROUP, "");
return permissionGroupRepository.findById(defaultPermissionGroup);
})
.flatMap(permissionGroup -> {
if (permissionGroup.getAssignedToUserIds() == null) {
permissionGroup.setAssignedToUserIds(new HashSet<>());
}
permissionGroup.getAssignedToUserIds().removeAll(users.stream().map(User::getId).collect(Collectors.toList()));
return permissionGroupRepository.updateById(permissionGroup.getId(), permissionGroup, AclPermission.ASSIGN_PERMISSION_GROUPS);
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> cacheableRepositoryHelper.evictPermissionGroupsUser(user.getEmail(), user.getTenantId()))
.then(Mono.just(Boolean.TRUE));
}
protected Mono<Config> createInstanceConfigForSuperUser() {
Mono<Tuple2<PermissionGroup, Config>> savedConfigAndPermissionGroupMono = createConfigAndPermissionGroupForSuperAdmin();
// return the saved instance config
return savedConfigAndPermissionGroupMono
.map(Tuple2::getT2);
}
protected Mono<Tuple2<PermissionGroup, Config>> createConfigAndPermissionGroupForSuperAdmin() {
return Mono.zip(createInstanceAdminConfigObject(), createInstanceAdminPermissionGroupWithoutPermissions())
.flatMap(tuple -> {
Config savedInstanceConfig = tuple.getT1();
PermissionGroup savedPermissionGroup = tuple.getT2();
// Update the instance config with the permission group id
savedInstanceConfig.setConfig(
new JSONObject(Map.of(DEFAULT_PERMISSION_GROUP, savedPermissionGroup.getId()))
);
Policy editConfigPolicy = Policy.builder().permission(MANAGE_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy readConfigPolicy = Policy.builder().permission(READ_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedInstanceConfig.setPolicies(Set.of(editConfigPolicy, readConfigPolicy));
// Add config permissions to permission group
Set<Permission> configPermissions = Set.of(
new Permission(savedInstanceConfig.getId(), MANAGE_INSTANCE_CONFIGURATION)
);
return Mono.zip(addPermissionsToPermissionGroup(savedPermissionGroup, configPermissions),
configRepository.save(savedInstanceConfig));
});
}
private Mono<Config> createInstanceAdminConfigObject() {
Config instanceAdminConfiguration = new Config();
instanceAdminConfiguration.setName(FieldName.INSTANCE_CONFIG);
return configRepository.save(instanceAdminConfiguration);
}
private Mono<PermissionGroup> createInstanceAdminPermissionGroupWithoutPermissions() {
PermissionGroup instanceAdminPermissionGroup = new PermissionGroup();
instanceAdminPermissionGroup.setName(FieldName.INSTANCE_ADMIN_ROLE);
return permissionGroupRepository.save(instanceAdminPermissionGroup)
.flatMap(savedPermissionGroup -> {
Set<Permission> permissions = Set.of(
new Permission(savedPermissionGroup.getId(), READ_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), UNASSIGN_PERMISSION_GROUPS)
);
savedPermissionGroup.setPermissions(permissions);
Policy readPermissionGroupPolicy = Policy.builder().permission(READ_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy assignPermissionGroupPolicy = Policy.builder().permission(ASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy unassignPermissionGroupPolicy = Policy.builder().permission(UNASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedPermissionGroup.setPolicies(Set.of(readPermissionGroupPolicy, assignPermissionGroupPolicy, unassignPermissionGroupPolicy));
return permissionGroupRepository.save(savedPermissionGroup);
});
}
protected Mono<PermissionGroup> addPermissionsToPermissionGroup(PermissionGroup permissionGroup, Set<Permission> permissions) {
Set<Permission> existingPermissions = new HashSet<>(permissionGroup.getPermissions());
existingPermissions.addAll(permissions);
permissionGroup.setPermissions(existingPermissions);
return permissionGroupRepository.save(permissionGroup);
}
}

View File

@ -100,8 +100,8 @@ import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullP
import static com.appsmith.server.acl.AclPermission.ASSIGN_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.MANAGE_INSTANCE_CONFIGURATION;
import static com.appsmith.server.acl.AclPermission.MANAGE_INSTANCE_ENV;
import static com.appsmith.server.acl.AclPermission.MANAGE_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.READ_INSTANCE_CONFIGURATION;
import static com.appsmith.server.acl.AclPermission.READ_PERMISSION_GROUPS;
import static com.appsmith.server.acl.AclPermission.READ_THEMES;
import static com.appsmith.server.constants.FieldName.DEFAULT_PERMISSION_GROUP;
import static com.appsmith.server.constants.FieldName.PERMISSION_GROUP_ID;
@ -2063,7 +2063,7 @@ public class DatabaseChangelog2 {
// Create instance management permission group
PermissionGroup instanceManagerPermissionGroup = new PermissionGroup();
instanceManagerPermissionGroup.setName(FieldName.INSTACE_ADMIN_ROLE);
instanceManagerPermissionGroup.setName(FieldName.INSTANCE_ADMIN_ROLE);
instanceManagerPermissionGroup.setPermissions(
Set.of(
new Permission(savedInstanceConfig.getId(), MANAGE_INSTANCE_CONFIGURATION)
@ -2095,7 +2095,7 @@ public class DatabaseChangelog2 {
mongockTemplate.save(savedInstanceConfig);
// Also give the permission group permission to update & assign to itself
// Also give the permission group permission to unassign & assign & read to itself
Policy updatePermissionGroupPolicy = Policy.builder().permission(AclPermission.UNASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
@ -2104,13 +2104,18 @@ public class DatabaseChangelog2 {
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy readPermissionGroupPolicy = Policy.builder().permission(READ_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedPermissionGroup.setPolicies(new HashSet<>(Set.of(updatePermissionGroupPolicy, assignPermissionGroupPolicy)));
Set<Permission> permissions = new HashSet<>(savedPermissionGroup.getPermissions());
permissions.addAll(
Set.of(
new Permission(savedPermissionGroup.getId(), AclPermission.UNASSIGN_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS)
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), READ_PERMISSION_GROUPS)
)
);
savedPermissionGroup.setPermissions(permissions);

View File

@ -3,10 +3,10 @@ package com.appsmith.server.solutions;
import com.appsmith.server.authentication.handlers.AuthenticationSuccessHandler;
import com.appsmith.server.configurations.CommonConfig;
import com.appsmith.server.domains.User;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.PolicyUtils;
import com.appsmith.server.helpers.UserUtils;
import com.appsmith.server.helpers.ValidationUtils;
import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.ApplicationPageService;
import com.appsmith.server.services.CaptchaService;
@ -21,6 +21,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static com.appsmith.server.helpers.ValidationUtils.LOGIN_PASSWORD_MAX_LENGTH;
import static com.appsmith.server.helpers.ValidationUtils.LOGIN_PASSWORD_MIN_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
public class UserSignupTest {
@MockBean
@ -73,18 +78,37 @@ public class UserSignupTest {
@Test
public void signupAndLogin_WhenPasswordTooShort_RaisesException() {
User user = new User();
user.setPassword(createRandomString(ValidationUtils.LOGIN_PASSWORD_MIN_LENGTH - 1));
user.setEmail("testemail@test123.com");
user.setPassword(createRandomString(LOGIN_PASSWORD_MIN_LENGTH - 1));
Mono<User> userMono = userSignup.signupAndLogin(user, null);
StepVerifier.create(userMono).expectError(AppsmithException.class).verify();
StepVerifier.create(userMono)
.expectErrorSatisfies(error -> {
assertTrue(error instanceof AppsmithException);
String expectedErrorMessage = AppsmithError.INVALID_PASSWORD_LENGTH
.getMessage(LOGIN_PASSWORD_MIN_LENGTH, LOGIN_PASSWORD_MAX_LENGTH);
assertEquals(expectedErrorMessage, error.getMessage());
})
.verify();
}
@Test
public void signupAndLogin_WhenPasswordTooLong_RaisesException() {
User user = new User();
user.setPassword(createRandomString(ValidationUtils.LOGIN_PASSWORD_MAX_LENGTH + 1));
user.setEmail("testemail@test123.com");
user.setPassword(createRandomString(LOGIN_PASSWORD_MAX_LENGTH + 1));
Mono<User> userMono = userSignup.signupAndLogin(user, null);
StepVerifier.create(userMono).expectError(AppsmithException.class).verify();
StepVerifier.create(userMono)
.expectErrorSatisfies(error -> {
assertTrue(error instanceof AppsmithException);
String expectedErrorMessage = AppsmithError.INVALID_PASSWORD_LENGTH
.getMessage(LOGIN_PASSWORD_MIN_LENGTH, LOGIN_PASSWORD_MAX_LENGTH);
assertEquals(expectedErrorMessage, error.getMessage());
})
.verify();
}
}