Skip to content

Commit

Permalink
ass a unrestricted endpoint to get a user's profile
Browse files Browse the repository at this point in the history
Signed-off-by: David BRAQUART <[email protected]>
  • Loading branch information
dbraquart committed Apr 19, 2024
1 parent d58a3fa commit d713551
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ public ResponseEntity<Void> userIsAdmin(@PathVariable("sub") String userId) {
: ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}

@GetMapping(value = "/users/{sub}/profile")
@Operation(summary = "Get the user's profile")
@ApiResponse(responseCode = "200", description = "The user exist")
@ApiResponse(responseCode = "404", description = "The user doesn't exist")
public ResponseEntity<UserProfile> getUserProfile(@PathVariable("sub") String sub) {
return ResponseEntity.of(service.getUserProfile(sub));
}

@GetMapping(value = "/connections", produces = {MediaType.APPLICATION_JSON_VALUE})
@Operation(summary = "get the connections", description = "Access restricted to users of type: `admin`")
@ApiResponse(responseCode = "200", description = "The connections list")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ public UserProfileController(UserProfileService userService) {
}

@GetMapping(value = "", produces = {MediaType.APPLICATION_JSON_VALUE})
@Operation(summary = "get all the user profiles, or optionally for a single user")
@Operation(summary = "get all the user profiles")
@ApiResponse(responseCode = "200", description = "The profiles list")
public ResponseEntity<List<UserProfile>> getProfiles(@Parameter(description = "Only for a single user") @RequestParam(name = "sub", required = false) String sub,
public ResponseEntity<List<UserProfile>> getProfiles(@RequestHeader("userId") String userId,
@Parameter(description = "To check if parameters links are still valid") @RequestParam(name = "checkLinksValidity", required = false, defaultValue = "true") boolean checkLinksValidity) {
return ResponseEntity.ok().body(service.getProfiles(sub, checkLinksValidity));
return ResponseEntity.ok().body(service.getProfiles(userId, checkLinksValidity));
}

@GetMapping(value = "/{profileUuid}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.useradmin.server.service;

import org.gridsuite.useradmin.server.UserAdminApplicationProps;
import org.gridsuite.useradmin.server.UserAdminException;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;

import static org.gridsuite.useradmin.server.UserAdminException.Type.FORBIDDEN;

/**
* @author David Braquart <david.braquart at rte-france.com
*/
@Service
public class AdminRightService {

private final UserAdminApplicationProps applicationProps;

public AdminRightService(final UserAdminApplicationProps applicationProps) {
this.applicationProps = Objects.requireNonNull(applicationProps);
}

public List<String> getAdmins() {
return applicationProps.getAdmins();
}

public boolean isAdmin(@lombok.NonNull String sub) {
return this.getAdmins().contains(sub);
}

public void assertIsAdmin(@lombok.NonNull String sub) throws UserAdminException {
if (!this.isAdmin(sub)) {
throw new UserAdminException(FORBIDDEN);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
*/
package org.gridsuite.useradmin.server.service;

import org.gridsuite.useradmin.server.UserAdminApplicationProps;
import org.gridsuite.useradmin.server.UserAdminException;
import org.gridsuite.useradmin.server.dto.UserConnection;
import org.gridsuite.useradmin.server.dto.UserInfos;
import org.gridsuite.useradmin.server.dto.UserProfile;
import org.gridsuite.useradmin.server.entity.UserProfileEntity;
import org.gridsuite.useradmin.server.repository.UserInfosRepository;
import org.gridsuite.useradmin.server.entity.UserInfosEntity;
Expand All @@ -34,69 +34,61 @@ public class UserAdminService {
private final UserInfosRepository userInfosRepository;
private final UserProfileRepository userProfileRepository;
private final ConnectionsService connectionsService;
private final UserAdminApplicationProps applicationProps;
private final NotificationService notificationService;
private final AdminRightService adminRightService;
private final UserProfileService userProfileService;

public UserAdminService(final UserAdminApplicationProps applicationProps,
final UserInfosRepository userInfosRepository,
public UserAdminService(final UserInfosRepository userInfosRepository,
final UserProfileRepository userProfileRepository,
final ConnectionsService connectionsService,
final NotificationService notificationService) {
this.applicationProps = Objects.requireNonNull(applicationProps);
final AdminRightService adminRightService,
final NotificationService notificationService,
final UserProfileService userProfileService) {
this.userInfosRepository = Objects.requireNonNull(userInfosRepository);
this.userProfileRepository = Objects.requireNonNull(userProfileRepository);
this.connectionsService = Objects.requireNonNull(connectionsService);
this.adminRightService = Objects.requireNonNull(adminRightService);
this.notificationService = Objects.requireNonNull(notificationService);
}

private boolean isAdmin(@lombok.NonNull String sub) {
final List<String> admins = applicationProps.getAdmins();
return admins.contains(sub);
}

public void assertIsAdmin(@lombok.NonNull String sub) throws UserAdminException {
if (!this.isAdmin(sub)) {
throw new UserAdminException(FORBIDDEN);
}
this.userProfileService = Objects.requireNonNull(userProfileService);
}

private UserInfos toDtoUserInfo(final UserInfosEntity entity) {
return UserInfosEntity.toDto(entity, this::isAdmin);
return UserInfosEntity.toDto(entity, adminRightService::isAdmin);
}

@Transactional(readOnly = true)
public List<UserInfos> getUsers(String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return userInfosRepository.findAll().stream().map(this::toDtoUserInfo).toList();
}

@Transactional(readOnly = true)
public List<UserConnection> getConnections(String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return connectionsService.removeDuplicates();
}

@Transactional
public void createUser(String sub, String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
userInfosRepository.save(new UserInfosEntity(sub));
}

@Transactional
public long delete(String sub, String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return userInfosRepository.deleteBySub(sub);
}

@Transactional
public long delete(Collection<String> subs, String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return userInfosRepository.deleteAllBySubIn(subs);
}

@Transactional()
public void updateUser(String sub, String userId, UserInfos userInfos) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
UserInfosEntity user = userInfosRepository.findBySub(sub).orElseThrow(() -> new UserAdminException(NOT_FOUND));
Optional<UserProfileEntity> profile = userProfileRepository.findByName(userInfos.profileName());
user.setSub(userInfos.sub());
Expand All @@ -105,7 +97,7 @@ public void updateUser(String sub, String userId, UserInfos userInfos) {

@Transactional
public boolean subExists(String sub) {
final List<String> admins = applicationProps.getAdmins();
final List<String> admins = adminRightService.getAdmins();
final boolean isAllowed = admins.isEmpty() && userInfosRepository.count() == 0L
|| admins.contains(sub)
|| userInfosRepository.existsBySub(sub);
Expand All @@ -115,17 +107,24 @@ public boolean subExists(String sub) {

@Transactional(readOnly = true)
public Optional<UserInfos> getUser(String sub, String userId) {
assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return userInfosRepository.findBySub(sub).map(this::toDtoUserInfo);
}

@Transactional(readOnly = true)
public Optional<UserProfile> getUserProfile(String sub) {
// this method is not restricted to Admin because it is called by any user to retrieve its own profile
UserInfosEntity user = userInfosRepository.findBySub(sub).orElseThrow(() -> new UserAdminException(NOT_FOUND));
return user.getProfile() == null ? Optional.empty() : userProfileService.getProfile(user.getProfile().getId());
}

@Transactional(readOnly = true)
public boolean userIsAdmin(@NonNull String userId) {
return isAdmin(userId);
return adminRightService.isAdmin(userId);
}

public void sendMaintenanceMessage(String userId, Integer durationInSeconds, String message) {
if (!isAdmin(userId)) {
if (!adminRightService.isAdmin(userId)) {
throw new UserAdminException(FORBIDDEN);
}
if (durationInSeconds == null) {
Expand All @@ -136,7 +135,7 @@ public void sendMaintenanceMessage(String userId, Integer durationInSeconds, Str
}

public void sendCancelMaintenanceMessage(String userId) {
if (!isAdmin(userId)) {
if (!adminRightService.isAdmin(userId)) {
throw new UserAdminException(FORBIDDEN);
}
notificationService.emitCancelMaintenanceMessage();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
import com.google.common.collect.Sets;
import org.gridsuite.useradmin.server.UserAdminException;
import org.gridsuite.useradmin.server.dto.UserProfile;
import org.gridsuite.useradmin.server.entity.UserInfosEntity;
import org.gridsuite.useradmin.server.entity.UserProfileEntity;
import org.gridsuite.useradmin.server.repository.UserInfosRepository;
import org.gridsuite.useradmin.server.repository.UserProfileRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -31,30 +29,22 @@
*/
@Service
public class UserProfileService {
private final UserInfosRepository userInfosRepository;
private final UserProfileRepository userProfileRepository;
private final DirectoryService directoryService;
private final UserAdminService userAdminService;
private final AdminRightService adminRightService;

public UserProfileService(final UserInfosRepository userInfosRepository,
final UserProfileRepository userProfileRepository,
final UserAdminService userAdminService,
public UserProfileService(final UserProfileRepository userProfileRepository,
final AdminRightService adminRightService,
final DirectoryService directoryService) {
this.userInfosRepository = Objects.requireNonNull(userInfosRepository);
this.userProfileRepository = Objects.requireNonNull(userProfileRepository);
this.userAdminService = Objects.requireNonNull(userAdminService);
this.adminRightService = Objects.requireNonNull(adminRightService);
this.directoryService = Objects.requireNonNull(directoryService);
}

@Transactional(readOnly = true)
public List<UserProfile> getProfiles(String sub, boolean checkLinksValidity) {
List<UserProfileEntity> profiles;
if (sub != null) {
UserInfosEntity user = userInfosRepository.findBySub(sub).orElseThrow(() -> new UserAdminException(NOT_FOUND));
profiles = user.getProfile() == null ? List.of() : userProfileRepository.findById(user.getProfile().getId()).stream().toList();
} else {
profiles = userProfileRepository.findAll().stream().toList();
}
public List<UserProfile> getProfiles(String userId, boolean checkLinksValidity) {
adminRightService.assertIsAdmin(userId);
List<UserProfileEntity> profiles = userProfileRepository.findAll().stream().toList();
if (profiles.isEmpty()) {
return List.of();
}
Expand Down Expand Up @@ -91,30 +81,34 @@ public List<UserProfile> getProfiles(String sub, boolean checkLinksValidity) {

@Transactional(readOnly = true)
public Optional<UserProfile> getProfile(UUID profileUuid, String userId) {
userAdminService.assertIsAdmin(userId);
return userProfileRepository.findById(profileUuid).map(this::toDto);
adminRightService.assertIsAdmin(userId);
return getProfile(profileUuid);
}

@Transactional()
public void updateProfile(UUID profileUuid, String userId, UserProfile userProfile) {
userAdminService.assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
UserProfileEntity profile = userProfileRepository.findById(profileUuid).orElseThrow(() -> new UserAdminException(NOT_FOUND));
profile.setName(userProfile.name());
profile.setLoadFlowParameterId(userProfile.loadFlowParameterId());
}

@Transactional
public void createProfile(String profileName, String userId) {
userAdminService.assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
userProfileRepository.save(new UserProfileEntity(profileName));
}

@Transactional
public long deleteProfiles(List<String> names, String userId) {
userAdminService.assertIsAdmin(userId);
adminRightService.assertIsAdmin(userId);
return userProfileRepository.deleteAllByNameIn(names);
}

Optional<UserProfile> getProfile(UUID profileUuid) {
return userProfileRepository.findById(profileUuid).map(this::toDto);
}

private UserProfile toDto(final UserProfileEntity entity) {
return toDto(entity, null);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/config/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ powsybl-ws:

useradmin:
admins:
# - admin1
- bdduser
# - admin2
Original file line number Diff line number Diff line change
Expand Up @@ -408,16 +408,13 @@ private void updateUser(String updatedUserName, UserInfos userInfos, HttpStatusC

@SneakyThrows
private UserProfile getUserProfile(String userName, HttpStatusCode status) {
String response = mockMvc.perform(get("/" + UserAdminApi.API_VERSION + "/profiles?sub=" + userName)
String response = mockMvc.perform(get("/" + UserAdminApi.API_VERSION + "/users/" + userName + "/profile")
.contentType(APPLICATION_JSON))
.andExpect(status().is(status.value()))
.andReturn().getResponse().getContentAsString();
if (status == HttpStatus.OK) {
List<UserProfile> profiles = objectMapper.readValue(response,
new TypeReference<>() {
});
assertEquals(1, profiles.size());
return profiles.get(0);
return objectMapper.readValue(response, new TypeReference<>() {
});
}
return null;
}
Expand Down

0 comments on commit d713551

Please sign in to comment.