Signup flow
This commit is contained in:
parent
f71bd9a4e0
commit
bea5f53f46
|
|
@ -47,6 +47,7 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit
|
||||||
private final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME;
|
private final String sessionAttributeName = DEFAULT_AUTHORIZED_CLIENTS_ATTR_NAME;
|
||||||
UserService userService;
|
UserService userService;
|
||||||
OrganizationService organizationService;
|
OrganizationService organizationService;
|
||||||
|
|
||||||
public ClientUserRepository(UserService userService, OrganizationService organizationService) {
|
public ClientUserRepository(UserService userService, OrganizationService organizationService) {
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.organizationService = organizationService;
|
this.organizationService = organizationService;
|
||||||
|
|
@ -92,20 +93,8 @@ public class ClientUserRepository implements ServerOAuth2AuthorizedClientReposit
|
||||||
newUser.setState(UserState.ACTIVATED);
|
newUser.setState(UserState.ACTIVATED);
|
||||||
newUser.setIsEnabled(true);
|
newUser.setIsEnabled(true);
|
||||||
|
|
||||||
/** TODO
|
return userService.findByEmail(user.getEmail())
|
||||||
* Organization here is being hardcoded. This is a stop gap measure
|
.switchIfEmpty(userService.save(newUser)); //In case the user doesnt exist, save the user.
|
||||||
* A flow needs to be added to connect a User to a Organization. Once
|
|
||||||
* that is done, during the login, the organization should be picked up
|
|
||||||
* and a user should be hence created.
|
|
||||||
*/
|
|
||||||
|
|
||||||
return organizationService.findById("5d3e90a2dfec7c00047a81ea")
|
|
||||||
.map(organization -> {
|
|
||||||
newUser.setOrganization(organization);
|
|
||||||
return newUser;
|
|
||||||
})
|
|
||||||
.then(userService.findByEmail(user.getEmail()))
|
|
||||||
.switchIfEmpty(userService.save(newUser));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,5 @@ public interface Url {
|
||||||
String SETTING_URL = BASE_URL + VERSION + "/settings";
|
String SETTING_URL = BASE_URL + VERSION + "/settings";
|
||||||
String RESOURCE_URL = BASE_URL + VERSION + "/resources";
|
String RESOURCE_URL = BASE_URL + VERSION + "/resources";
|
||||||
String ACTION_URL = BASE_URL + VERSION + "/actions";
|
String ACTION_URL = BASE_URL + VERSION + "/actions";
|
||||||
|
String USER_URL = BASE_URL + VERSION + "/users";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.appsmith.server.controllers;
|
package com.appsmith.server.controllers;
|
||||||
|
|
||||||
|
import com.appsmith.server.domains.User;
|
||||||
|
import com.appsmith.server.services.UserService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
@ -13,8 +16,16 @@ import java.security.Principal;
|
||||||
@RequestMapping("")
|
@RequestMapping("")
|
||||||
public class IndexController {
|
public class IndexController {
|
||||||
|
|
||||||
|
private final UserService service;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public IndexController(UserService service) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public Mono<String> index(Mono<Principal> principal) {
|
public Mono<String> index(Mono<Principal> principal) {
|
||||||
|
Mono<User> userMono = service.getCurrentUser();
|
||||||
return principal
|
return principal
|
||||||
.map(Principal::getName)
|
.map(Principal::getName)
|
||||||
.map(name -> String.format("Hello %s", name));
|
.map(name -> String.format("Hello %s", name));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.appsmith.server.controllers;
|
||||||
|
|
||||||
|
import com.appsmith.server.constants.Url;
|
||||||
|
import com.appsmith.server.domains.User;
|
||||||
|
import com.appsmith.server.services.UserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(Url.USER_URL)
|
||||||
|
public class UserController extends BaseController<UserService, User, String> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UserController(UserService service) {
|
||||||
|
super(service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,9 +33,9 @@ public class User extends BaseDomain implements UserDetails {
|
||||||
|
|
||||||
private UserState state;
|
private UserState state;
|
||||||
|
|
||||||
private Boolean isEnabled;
|
private Boolean isEnabled = true;
|
||||||
|
|
||||||
private Organization organization;
|
private String organizationId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ public enum AppsmithError {
|
||||||
|
|
||||||
NO_RESOURCE_FOUND(404, 1000, "Unable to find {0} with id {1}"),
|
NO_RESOURCE_FOUND(404, 1000, "Unable to find {0} with id {1}"),
|
||||||
INVALID_PARAMETER(400, 4000, "Invalid parameter {0} provided in the input"),
|
INVALID_PARAMETER(400, 4000, "Invalid parameter {0} provided in the input"),
|
||||||
PLUGIN_NOT_INSTALLED(400, 4001, "Plugin not installed for organization {0}"),
|
PLUGIN_NOT_INSTALLED(400, 4001, "Plugin {0} not installed"),
|
||||||
PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"),
|
PLUGIN_ID_NOT_GIVEN(400, 4002, "Missing plugin id. Please input correct plugin id"),
|
||||||
RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"),
|
RESOURCE_ID_NOT_GIVEN(400, 4003, "Missing resource id. Please input correct resource id"),
|
||||||
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request");
|
INTERNAL_SERVER_ERROR(500, 5000, "Internal server error while processing request");
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class GlobalExceptionHandler {
|
||||||
*
|
*
|
||||||
* @param e AppsmithException that will be caught by the function
|
* @param e AppsmithException that will be caught by the function
|
||||||
* @param exchange ServerWebExchange contract in order to extract the response and set the http status code
|
* @param exchange ServerWebExchange contract in order to extract the response and set the http status code
|
||||||
* @return Mono<ResponseDto < ErrorDTO>>
|
* @return Mono<ResponseDto < ErrorDTO>>
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
|
@ -40,7 +40,7 @@ public class GlobalExceptionHandler {
|
||||||
*
|
*
|
||||||
* @param e Exception that will be caught by the function
|
* @param e Exception that will be caught by the function
|
||||||
* @param exchange ServerWebExchange contract in order to extract the response and set the http status code
|
* @param exchange ServerWebExchange contract in order to extract the response and set the http status code
|
||||||
* @return Mono<ResponseDto < ErrorDTO>>
|
* @return Mono<ResponseDto < ErrorDTO>>
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler
|
@ExceptionHandler
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.appsmith.server.helpers;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.BeanWrapper;
|
||||||
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class BeanCopyUtils {
|
||||||
|
|
||||||
|
private static String[] getNullPropertyNames(Object source) {
|
||||||
|
final BeanWrapper src = new BeanWrapperImpl(source);
|
||||||
|
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
|
||||||
|
|
||||||
|
Set<String> emptyNames = new HashSet<String>();
|
||||||
|
for (java.beans.PropertyDescriptor pd : pds) {
|
||||||
|
Object srcValue = src.getPropertyValue(pd.getName());
|
||||||
|
if (srcValue == null) {
|
||||||
|
emptyNames.add(pd.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] result = new String[emptyNames.size()];
|
||||||
|
return emptyNames.toArray(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Use Spring BeanUtils to copy and ignore null
|
||||||
|
public static void copyNewFieldValuesIntoOldObject(Object src, Object target) {
|
||||||
|
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,7 +68,7 @@ public abstract class BaseService<R extends BaseRepository, T extends BaseDomain
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<T> getById(ID id) {
|
public Mono<T> getById(ID id) {
|
||||||
if(id == null) {
|
if (id == null) {
|
||||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
|
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,12 +98,12 @@ public abstract class BaseService<R extends BaseRepository, T extends BaseDomain
|
||||||
*/
|
*/
|
||||||
protected Mono<T> validateObject(T obj) {
|
protected Mono<T> validateObject(T obj) {
|
||||||
return Mono.just(obj)
|
return Mono.just(obj)
|
||||||
.map(o -> validator.validate(o))
|
.map(o -> validator.validate(o))
|
||||||
.flatMap(constraint -> {
|
.flatMap(constraint -> {
|
||||||
if(constraint.isEmpty()) {
|
if (constraint.isEmpty()) {
|
||||||
return Mono.just(obj);
|
return Mono.just(obj);
|
||||||
}
|
}
|
||||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, constraint.stream().findFirst().get().getPropertyPath()));
|
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, constraint.stream().findFirst().get().getPropertyPath()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.server.configurations.ClientUserRepository;
|
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.OrganizationPlugin;
|
import com.appsmith.server.domains.OrganizationPlugin;
|
||||||
import com.appsmith.server.domains.Plugin;
|
import com.appsmith.server.domains.Plugin;
|
||||||
import com.appsmith.server.domains.PluginType;
|
import com.appsmith.server.domains.PluginType;
|
||||||
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.dtos.OrganizationPluginStatus;
|
import com.appsmith.server.dtos.OrganizationPluginStatus;
|
||||||
import com.appsmith.server.dtos.PluginOrgDTO;
|
import com.appsmith.server.dtos.PluginOrgDTO;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.repositories.PluginRepository;
|
import com.appsmith.server.repositories.PluginRepository;
|
||||||
import com.appsmith.server.repositories.UserRepository;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
|
|
@ -30,13 +28,9 @@ import java.util.List;
|
||||||
public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, String> implements PluginService {
|
public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, String> implements PluginService {
|
||||||
|
|
||||||
private final PluginRepository pluginRepository;
|
private final PluginRepository pluginRepository;
|
||||||
private final UserRepository userRepository;
|
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationContext applicationContext;
|
||||||
private final ClientUserRepository clientUserRepository;
|
|
||||||
private final OrganizationService organizationService;
|
private final OrganizationService organizationService;
|
||||||
|
private final UserService userService;
|
||||||
@Value("${organization.id}")
|
|
||||||
private String organizationId;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public PluginServiceImpl(Scheduler scheduler,
|
public PluginServiceImpl(Scheduler scheduler,
|
||||||
|
|
@ -44,16 +38,13 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
|
||||||
MongoConverter mongoConverter,
|
MongoConverter mongoConverter,
|
||||||
ReactiveMongoTemplate reactiveMongoTemplate,
|
ReactiveMongoTemplate reactiveMongoTemplate,
|
||||||
PluginRepository repository,
|
PluginRepository repository,
|
||||||
UserRepository userRepository,
|
|
||||||
ApplicationContext applicationContext,
|
ApplicationContext applicationContext,
|
||||||
ClientUserRepository clientUserRepository,
|
OrganizationService organizationService, UserService userService) {
|
||||||
OrganizationService organizationService) {
|
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
||||||
this.userRepository = userRepository;
|
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
pluginRepository = repository;
|
pluginRepository = repository;
|
||||||
this.clientUserRepository = clientUserRepository;
|
|
||||||
this.organizationService = organizationService;
|
this.organizationService = organizationService;
|
||||||
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PluginExecutor getPluginExecutor(PluginType pluginType, String className) {
|
public PluginExecutor getPluginExecutor(PluginType pluginType, String className) {
|
||||||
|
|
@ -90,29 +81,17 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Organization> uninstallPlugin(PluginOrgDTO pluginDTO) {
|
public Mono<Organization> uninstallPlugin(PluginOrgDTO pluginDTO) {
|
||||||
/*TODO
|
|
||||||
* Organization & user association is being mocked here by forcefully
|
|
||||||
* only using a hardcoded organization. This needs to be replaced by
|
|
||||||
* a user-organization association flow. The Organization needs to be picked
|
|
||||||
* up from a user object. This is being used in install/uninstall
|
|
||||||
* plugin from a organization flow. Instead, the current user should be read
|
|
||||||
* using the following :
|
|
||||||
* ReactiveSecurityContextHolder.getContext()
|
|
||||||
* .map(SecurityContext::getAuthentication)
|
|
||||||
* .map(Authentication::getPrincipal);
|
|
||||||
* Once the user has been pulled using this, organization should already
|
|
||||||
* be stored as part of user and this organization should be used to store
|
|
||||||
* the installed plugin or to delete plugin during uninstallation.
|
|
||||||
*/
|
|
||||||
if (pluginDTO.getPluginId() == null) {
|
if (pluginDTO.getPluginId() == null) {
|
||||||
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
|
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find the organization using id and plugin id -> This is to find if the organization has the plugin installed
|
//Find the organization using id and plugin id -> This is to find if the organization has the plugin installed
|
||||||
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId());
|
Mono<User> userMono = userService.getCurrentUser();
|
||||||
|
Mono<Organization> organizationMono = userMono.flatMap(user ->
|
||||||
|
organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId()));
|
||||||
|
|
||||||
return organizationMono
|
return organizationMono
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId)))
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, pluginDTO.getPluginId())))
|
||||||
//In case the plugin is not found for the organization, the organizationMono would not emit and the rest of the flow would stop
|
//In case the plugin is not found for the organization, the organizationMono would not emit and the rest of the flow would stop
|
||||||
//i.e. the rest of the code flow would only happen when there is a plugin found for the organization that can
|
//i.e. the rest of the code flow would only happen when there is a plugin found for the organization that can
|
||||||
//be uninstalled.
|
//be uninstalled.
|
||||||
|
|
@ -126,40 +105,33 @@ public class PluginServiceImpl extends BaseService<PluginRepository, Plugin, Str
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Organization> storeOrganizationPlugin(PluginOrgDTO pluginDTO, OrganizationPluginStatus status) {
|
private Mono<Organization> storeOrganizationPlugin(PluginOrgDTO pluginDTO, OrganizationPluginStatus status) {
|
||||||
/*TODO
|
|
||||||
* Organization & user association is being mocked here by forcefully
|
|
||||||
* only using a hardcoded organization. This needs to be replaced by
|
|
||||||
* a user-organization association flow. The Organization needs to be picked
|
|
||||||
* up from a user object. This is being used in install/uninstall
|
|
||||||
* plugin from a organization flow. Instead, the current user should be read
|
|
||||||
* using the following :
|
|
||||||
* ReactiveSecurityContextHolder.getContext()
|
|
||||||
* .map(SecurityContext::getAuthentication)
|
|
||||||
* .map(Authentication::getPrincipal);
|
|
||||||
* Once the user has been pulled using this, organization should already
|
|
||||||
* be stored as part of user and this organization should be used to store
|
|
||||||
* the installed plugin or to delete plugin during uninstalling.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Find the organization using id and plugin id -> This is to find if the organization already has the plugin installed
|
//Find the organization using id and plugin id -> This is to find if the organization already has the plugin installed
|
||||||
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, pluginDTO.getPluginId());
|
Mono<User> userMono = userService.getCurrentUser();
|
||||||
|
Mono<Organization> organizationMono = userMono.flatMap(user ->
|
||||||
|
organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), pluginDTO.getPluginId()));
|
||||||
|
|
||||||
|
|
||||||
|
//If plugin is already present for the organization, just return the organization, else install and return organization
|
||||||
return organizationMono
|
return organizationMono
|
||||||
.switchIfEmpty(Mono.defer(() -> {
|
.switchIfEmpty(Mono.defer(() -> {
|
||||||
//If the plugin is not found in the organization, its not already installed. Install now.
|
//If the plugin is not found in the organization, its not installed already. Install now.
|
||||||
return organizationService.findById(organizationId).map(organization -> {
|
return userMono
|
||||||
List<OrganizationPlugin> organizationPluginList = organization.getPlugins();
|
.flatMap(user -> organizationService.findById(user.getOrganizationId()))
|
||||||
if (organizationPluginList == null) {
|
.map(organization -> {
|
||||||
organizationPluginList = new ArrayList<OrganizationPlugin>();
|
List<OrganizationPlugin> organizationPluginList = organization.getPlugins();
|
||||||
}
|
if (organizationPluginList == null) {
|
||||||
log.debug("Installing plugin {} for organization {}", pluginDTO.getPluginId(), organization.getName());
|
organizationPluginList = new ArrayList<OrganizationPlugin>();
|
||||||
OrganizationPlugin organizationPlugin = new OrganizationPlugin();
|
}
|
||||||
organizationPlugin.setPluginId(pluginDTO.getPluginId());
|
log.debug("Installing plugin {} for organization {}", pluginDTO.getPluginId(), organization.getName());
|
||||||
organizationPlugin.setStatus(status);
|
OrganizationPlugin organizationPlugin = new OrganizationPlugin();
|
||||||
organizationPluginList.add(organizationPlugin);
|
organizationPlugin.setPluginId(pluginDTO.getPluginId());
|
||||||
organization.setPlugins(organizationPluginList);
|
organizationPlugin.setStatus(status);
|
||||||
return organization;
|
organizationPluginList.add(organizationPlugin);
|
||||||
}).flatMap(organizationService::save);
|
organization.setPlugins(organizationPluginList);
|
||||||
|
return organization;
|
||||||
|
})
|
||||||
|
.flatMap(organizationService::save);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Resource;
|
import com.appsmith.server.domains.Resource;
|
||||||
|
import com.appsmith.server.domains.User;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.repositories.ResourceRepository;
|
import com.appsmith.server.repositories.ResourceRepository;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -21,19 +21,16 @@ import javax.validation.constraints.NotNull;
|
||||||
@Service
|
@Service
|
||||||
public class ResourceServiceImpl extends BaseService<ResourceRepository, Resource, String> implements ResourceService {
|
public class ResourceServiceImpl extends BaseService<ResourceRepository, Resource, String> implements ResourceService {
|
||||||
|
|
||||||
@Value("${organization.id}")
|
|
||||||
private String organizationId;
|
|
||||||
|
|
||||||
private final ResourceRepository repository;
|
private final ResourceRepository repository;
|
||||||
private final OrganizationService organizationService;
|
private final OrganizationService organizationService;
|
||||||
private final PluginService pluginService;
|
private final UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService) {
|
public ResourceServiceImpl(Scheduler scheduler, Validator validator, MongoConverter mongoConverter, ReactiveMongoTemplate reactiveMongoTemplate, ResourceRepository repository, OrganizationService organizationService, PluginService pluginService, UserService userService) {
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.organizationService = organizationService;
|
this.organizationService = organizationService;
|
||||||
this.pluginService = pluginService;
|
this.userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -44,17 +41,19 @@ public class ResourceServiceImpl extends BaseService<ResourceRepository, Resourc
|
||||||
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
|
return Mono.error(new AppsmithException(AppsmithError.PLUGIN_ID_NOT_GIVEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
Mono<Organization> organizationMono = organizationService.findByIdAndPluginsPluginId(organizationId, resource.getPluginId());
|
Mono<User> userMono = userService.getCurrentUser();
|
||||||
|
|
||||||
|
Mono<Organization> organizationMono = userMono.flatMap(user -> organizationService.findByIdAndPluginsPluginId(user.getOrganizationId(), resource.getPluginId()));
|
||||||
|
|
||||||
//Add organization id to the resource.
|
//Add organization id to the resource.
|
||||||
Mono<Resource> updatedResourceMono = Mono.just(resource)
|
Mono<Resource> updatedResourceMono = organizationMono
|
||||||
.map(updatedResource -> {
|
.map(organization -> {
|
||||||
updatedResource.setOrganizationId(organizationId);
|
resource.setOrganizationId(organization.getId());
|
||||||
return updatedResource;
|
return resource;
|
||||||
});
|
});
|
||||||
|
|
||||||
return organizationMono
|
return organizationMono
|
||||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, organizationId)))
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.PLUGIN_NOT_INSTALLED, resource.getPluginId())))
|
||||||
.then(updatedResourceMono)
|
.then(updatedResourceMono)
|
||||||
.flatMap(repository::save);
|
.flatMap(repository::save);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,6 @@ public interface UserService extends CrudService<User, String> {
|
||||||
Mono<User> findByEmail(String email);
|
Mono<User> findByEmail(String email);
|
||||||
|
|
||||||
Mono<User> save(User newUser);
|
Mono<User> save(User newUser);
|
||||||
|
|
||||||
|
Mono<User> getCurrentUser();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,43 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
|
import com.appsmith.server.helpers.BeanCopyUtils;
|
||||||
import com.appsmith.server.repositories.UserRepository;
|
import com.appsmith.server.repositories.UserRepository;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
|
||||||
import javax.validation.Validator;
|
import javax.validation.Validator;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class UserServiceImpl extends BaseService<UserRepository, User, String> implements UserService, UserDetailsService {
|
public class UserServiceImpl extends BaseService<UserRepository, User, String> implements UserService, UserDetailsService {
|
||||||
|
|
||||||
private UserRepository repository;
|
private UserRepository repository;
|
||||||
|
private final OrganizationService organizationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
public UserServiceImpl(Scheduler scheduler,
|
public UserServiceImpl(Scheduler scheduler,
|
||||||
Validator validator,
|
Validator validator,
|
||||||
MongoConverter mongoConverter,
|
MongoConverter mongoConverter,
|
||||||
ReactiveMongoTemplate reactiveMongoTemplate,
|
ReactiveMongoTemplate reactiveMongoTemplate,
|
||||||
UserRepository repository) {
|
UserRepository repository, OrganizationService organizationService) {
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository);
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
this.organizationService = organizationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -46,4 +59,55 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
|
||||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
return repository.findByName(username).block();
|
return repository.findByName(username).block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<User> getCurrentUser() {
|
||||||
|
return ReactiveSecurityContextHolder.getContext()
|
||||||
|
.map(SecurityContext::getAuthentication)
|
||||||
|
.map(Authentication::getPrincipal)
|
||||||
|
.flatMap(principal -> {
|
||||||
|
String email;
|
||||||
|
if (principal instanceof org.springframework.security.core.userdetails.User) {
|
||||||
|
org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) principal;
|
||||||
|
//Assumption that the user has inputted an email as username during user creation and not english passport name
|
||||||
|
email = user.getUsername();
|
||||||
|
} else {
|
||||||
|
DefaultOidcUser defaultOidcUser = (DefaultOidcUser) principal;
|
||||||
|
email = defaultOidcUser.getEmail();
|
||||||
|
}
|
||||||
|
return repository.findByEmail(email);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<User> update(String id, User userUpdate) {
|
||||||
|
Mono<User> userFromRepository = repository.findById(id);
|
||||||
|
|
||||||
|
return Mono.just(userUpdate)
|
||||||
|
.flatMap(this::validateUpdate)
|
||||||
|
//Once the new update has been validated, update the user with the new fields.
|
||||||
|
.then(userFromRepository)
|
||||||
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, id)))
|
||||||
|
.map(existingUser -> {
|
||||||
|
BeanCopyUtils.copyNewFieldValuesIntoOldObject(userUpdate, existingUser);
|
||||||
|
return existingUser;
|
||||||
|
})
|
||||||
|
.flatMap(repository::save);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validation for user update. Right now it only validates the organization id. Other checks can be added
|
||||||
|
//here in the future.
|
||||||
|
private Mono<User> validateUpdate(User updateUser) {
|
||||||
|
if (updateUser.getOrganizationId() == null) {
|
||||||
|
//No organization present implies the update to the user is not to the organization id. No checks currently
|
||||||
|
//for this scenario. Return the user successfully.
|
||||||
|
return Mono.just(updateUser);
|
||||||
|
}
|
||||||
|
return organizationService.findById(updateUser.getOrganizationId())
|
||||||
|
//If the organization is not found in the repository, throw an error
|
||||||
|
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, updateUser.getOrganizationId())))
|
||||||
|
.then(Mono.just(updateUser));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
|
import com.appsmith.server.domains.Organization;
|
||||||
|
import com.appsmith.server.domains.User;
|
||||||
|
import com.appsmith.server.domains.UserState;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest
|
||||||
|
public class UserServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
OrganizationService organizationService;
|
||||||
|
|
||||||
|
Mono<User> userMono;
|
||||||
|
Mono<Organization> organizationMono;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
//User init
|
||||||
|
User user = new User();
|
||||||
|
user.setName("user test");
|
||||||
|
user.setEmail("usertest@usertest.com");
|
||||||
|
user.setState(UserState.ACTIVATED);
|
||||||
|
//Store the user in case its not present in the database.
|
||||||
|
userMono = userService.findByEmail("usertest@usertest.com").switchIfEmpty(Mono.defer(() -> userService.save(user)));
|
||||||
|
|
||||||
|
//Organization init
|
||||||
|
Organization organization = new Organization();
|
||||||
|
organization.setName("Spring Test Organization");
|
||||||
|
organization.setDomain("appsmith-spring-test.com");
|
||||||
|
organization.setWebsite("appsmith.com");
|
||||||
|
|
||||||
|
//Store the organization in case its not present in the database.
|
||||||
|
organizationMono = organizationService.getByName("Spring Test Organization").switchIfEmpty(Mono.defer(() -> organizationService.save(organization)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Test the update organization flow.
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateInvalidUserWithAnything() {
|
||||||
|
User updateUser = new User();
|
||||||
|
updateUser.setName("Random User whose updates don't matter");
|
||||||
|
|
||||||
|
User existingUser = new User();
|
||||||
|
existingUser.setId("Random-UserId-%Not-In_The-System_For_SUre");
|
||||||
|
|
||||||
|
Mono<User> userMono1 = Mono.just(existingUser).flatMap(user -> userService.update(user.getId(), updateUser));
|
||||||
|
|
||||||
|
StepVerifier.create(userMono1)
|
||||||
|
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
|
||||||
|
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage("Random-UserId-%Not-In_The-System_For_SUre")))
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateUserWithValidOrganization() {
|
||||||
|
User updateUser = new User();
|
||||||
|
//Add valid organization id to the updateUser object.
|
||||||
|
organizationMono
|
||||||
|
.map(organization -> {
|
||||||
|
updateUser.setOrganizationId(organization.getId());
|
||||||
|
return updateUser;
|
||||||
|
}).block();
|
||||||
|
|
||||||
|
Mono<User> userMono1 = userMono.flatMap(user -> userService.update(user.getId(), updateUser));
|
||||||
|
StepVerifier.create(userMono1)
|
||||||
|
.assertNext(updatedUserInRepository -> {
|
||||||
|
assertThat(updatedUserInRepository.getOrganizationId()).isEqualTo(updateUser.getOrganizationId());
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateUserWithInvalidOrganization() {
|
||||||
|
User updateUser = new User();
|
||||||
|
updateUser.setOrganizationId("Random-OrgId-%Not-In_The-System_For_SUre");
|
||||||
|
Mono<User> userMono1 = userMono.flatMap(user -> userService.update(user.getId(), updateUser));
|
||||||
|
StepVerifier.create(userMono1)
|
||||||
|
.expectErrorMatches(throwable -> throwable instanceof AppsmithException &&
|
||||||
|
throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage("Random-OrgId-%Not-In_The-System_For_SUre")))
|
||||||
|
.verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user