Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions openaev-api/src/main/java/io/openaev/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ public long globalCount() {

@Transactional(rollbackFor = Exception.class)
public User createUser(UserInput input) {
return createUser(input, input.tenantIds(), true);
}

@Transactional(rollbackFor = Exception.class)
public User createTenantUser(UserInput input) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: seems weird to have createTenantUser inside userservice to call just after createUser. Why not call direclty createUser ?

return createUser(input, List.of(), false);
}

private User createUser(UserInput input, List<String> tenantIds, boolean includePlatformScope) {
if (!StringUtils.hasLength(input.plainPassword())) {
throw new IllegalArgumentException("Password is required when creating a user");
}
Expand All @@ -143,13 +152,13 @@ public User createUser(UserInput input) {
user.setOrganization(referenceResolver.resolve(input.organizationId(), Organization.class));
user.setTenants(
new ArrayList<>(
referenceResolver.resolve(
input.tenantIds(), Tenant.class, tenantRepository::countByIdIn)));
referenceResolver.resolve(tenantIds, Tenant.class, tenantRepository::countByIdIn)));
// The user's id is generated on save (UUID generator), not before: evict only after
// persisting, using the saved user's id, or evictForUser is called with a null key.
User createdUser = createUser(user, input.plainPassword(), UUID.randomUUID().toString());
if (!CollectionUtils.isEmpty(input.tenantIds())) {
tenantMembershipCacheManager.evictForUser(createdUser.getId(), input.tenantIds());
User createdUser =
createUser(user, input.plainPassword(), UUID.randomUUID().toString(), includePlatformScope);
if (!CollectionUtils.isEmpty(tenantIds)) {
tenantMembershipCacheManager.evictForUser(createdUser.getId(), tenantIds);
}
return createdUser;
}
Expand All @@ -166,15 +175,17 @@ public User createInternalUser(
user.setFirstname(firstname);
user.setLastname(lastname);
user.setAdmin(isAdmin);
return createUser(user, null, token);
return createUser(user, null, token, true);
}

private User createUser(User user, String password, String token) {
private User createUser(User user, String password, String token, boolean includePlatformScope) {
if (StringUtils.hasLength(password)) {
user.setPassword(this.encodeUserPassword(password));
}
// Creation enters every scope at once: platform, plus each tenant attached in the input.
assignAutoAssignGroups(user, user.getTenants().stream().map(Tenant::getId).toList(), true);
// Creation enters every scope at once: the platform when created from the platform screen,
// plus each tenant attached in the input.
assignAutoAssignGroups(
user, user.getTenants().stream().map(Tenant::getId).toList(), includePlatformScope);
User savedUser = userRepository.save(user);
this.createUserToken(savedUser, token);
return savedUser;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public UserOutput createOrAttach(UserInput input) {
User reloaded = userRepository.findById(userId).orElseThrow();
return UserMapper.toOutput(reloaded);
}
User user = userService.createUser(input);
User user = userService.createTenantUser(input);
attachToTenant(user.getId(), tenantId);
userService.assignAutoAssignGroups(user.getId(), List.of(tenantId));
// Reload user after @Modifying queries cleared the persistence context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
import io.openaev.database.raw.RawUser;
import io.openaev.database.repository.GroupRepository;
import io.openaev.database.repository.TenantRepository;
import io.openaev.database.repository.UserRepository;
import io.openaev.rest.exception.ElementNotFoundException;
import io.openaev.utils.fixtures.PaginationFixture;
import io.openaev.utils.fixtures.TenantGroupFixture;
import io.openaev.utils.fixtures.composers.TenantGroupComposer;
import io.openaev.utils.fixtures.composers.UserComposer;
import io.openaev.utils.fixtures.platform.PlatformGroupComposer;
import io.openaev.utils.fixtures.platform.PlatformGroupFixture;
import io.openaev.utils.fixtures.tenants.TenantComposer;
import io.openaev.utils.mockUser.WithMockUser;
import io.openaev.utils.pagination.SearchPaginationInput;
Expand All @@ -45,7 +48,9 @@ class TenantUserServiceTest extends IntegrationTest {
@Autowired private UserComposer userComposer;
@Autowired private TenantComposer tenantComposer;
@Autowired private TenantGroupComposer tenantGroupComposer;
@Autowired private PlatformGroupComposer platformGroupComposer;
@Autowired private GroupRepository groupRepository;
@Autowired private UserRepository userRepository;
@Autowired private EntityManager entityManager;

private Tenant tenant;
Expand Down Expand Up @@ -348,5 +353,65 @@ void given_existingUser_should_autoAssignOnAttach() {
Group reloaded = groupRepository.findById(autoGroup.getId()).orElseThrow();
assertThat(reloaded.getUsers()).extracting(User::getId).contains(existingUser.getId());
}

@Test
@DisplayName("Given a platform auto-assign group, should not assign a user created in a tenant")
void given_platformAutoAssignGroup_should_notAssignTenantCreatedUser() {
// -- ARRANGE --
Group platformAutoGroup = PlatformGroupFixture.getPlatformGroup("AutoAssignPlatform");
platformAutoGroup.setDefaultUserAssignation(true);
platformGroupComposer.forPlatformGroup(platformAutoGroup).persist();
Group tenantAutoGroup = TenantGroupFixture.getGroup("AutoAssignTenantScoped");
tenantAutoGroup.setDefaultUserAssignation(true);
tenantGroupComposer.forGroup(tenantAutoGroup).persist();
entityManager.flush();

UserInput input = getUserInput("tenant-scoped@test.invalid", "Tenant", "Scoped");

// -- ACT --
UserOutput result = tenantUserService.createOrAttach(input);

// -- ASSERT --
entityManager.flush();
entityManager.clear();
Group reloadedPlatform = groupRepository.findById(platformAutoGroup.getId()).orElseThrow();
assertThat(reloadedPlatform.getUsers()).extracting(User::getId).doesNotContain(result.id());
Group reloadedTenant = groupRepository.findById(tenantAutoGroup.getId()).orElseThrow();
assertThat(reloadedTenant.getUsers()).extracting(User::getId).contains(result.id());
}

@Test
@DisplayName("Given tenants in the input, should ignore them when creating from a tenant")
void given_tenantIdsInInput_should_ignoreThem() {
// -- ARRANGE --
Tenant otherTenant = tenantComposer.forTenant(getTenant("Other tenant")).persist().get();
entityManager.flush();
UserInput base = getUserInput("cross-tenant@test.invalid", "Cross", "Tenant");
UserInput input =
new UserInput(
base.email(),
base.firstname(),
base.lastname(),
base.plainPassword(),
base.pgpKey(),
base.phone(),
base.phone2(),
base.organizationId(),
base.tagIds(),
base.admin(),
List.of(otherTenant.getId()));

// -- ACT --
UserOutput result = tenantUserService.createOrAttach(input);

// -- ASSERT --
entityManager.flush();
entityManager.clear();
User reloaded = userRepository.findById(result.id()).orElseThrow();
assertThat(reloaded.getTenants())
.extracting(Tenant::getId)
.containsExactly(tenant.getId())
.doesNotContain(otherTenant.getId());
}
}
}
Loading