From e0c193f86b421ed38cdaff6671c7d2de24ee12fa Mon Sep 17 00:00:00 2001 From: spyroukon Date: Fri, 9 Jul 2021 19:01:49 +0000 Subject: [PATCH] 1. refactoring and changes of rest api --- ...ndLinkURIAuthenticationSuccessHandler.java | 2 +- .../config/OpenAIREAuthoritiesMapper.java | 32 ++- .../controllers/AaiUserRoleController.java | 196 ------------------ .../manager/controllers/PiWikController.java | 6 +- .../controllers/RepositoryController.java | 62 +++++- .../controllers/UserRoleController.java | 131 ++++++++++++ .../dnetlib/repo/manager/domain/dto/User.java | 61 ++++++ .../exception/ResourceNotFoundException.java | 4 + .../repo/manager/service/EmailUtilsImpl.java | 15 +- .../service/RepositoryServiceImpl.java | 27 ++- .../aai/registry/AaiRegistryService.java | 4 + .../service/aai/registry/RegistryCalls.java | 15 ++ ...ceImpl.java => AaiRoleMappingService.java} | 38 ++-- .../service/security/AuthoritiesMapper.java | 64 ++++-- .../service/security/AuthoritiesUpdater.java | 70 ++++--- .../security/AuthorizationService.java | 43 +++- .../security/AuthorizationServiceImpl.java | 98 ++++++++- ...leService.java => RoleMappingService.java} | 27 ++- .../dnetlib/repo/manager/utils/HttpUtils.java | 36 ++-- 19 files changed, 609 insertions(+), 322 deletions(-) delete mode 100644 src/main/java/eu/dnetlib/repo/manager/controllers/AaiUserRoleController.java create mode 100644 src/main/java/eu/dnetlib/repo/manager/controllers/UserRoleController.java create mode 100644 src/main/java/eu/dnetlib/repo/manager/domain/dto/User.java rename src/main/java/eu/dnetlib/repo/manager/service/security/{AaiUserRoleServiceImpl.java => AaiRoleMappingService.java} (67%) rename src/main/java/eu/dnetlib/repo/manager/service/security/{AaiUserRoleService.java => RoleMappingService.java} (67%) diff --git a/src/main/java/eu/dnetlib/repo/manager/config/FrontEndLinkURIAuthenticationSuccessHandler.java b/src/main/java/eu/dnetlib/repo/manager/config/FrontEndLinkURIAuthenticationSuccessHandler.java index 4adb3f5..1143531 100644 --- a/src/main/java/eu/dnetlib/repo/manager/config/FrontEndLinkURIAuthenticationSuccessHandler.java +++ b/src/main/java/eu/dnetlib/repo/manager/config/FrontEndLinkURIAuthenticationSuccessHandler.java @@ -84,7 +84,7 @@ public class FrontEndLinkURIAuthenticationSuccessHandler implements Authenticati accessToken.setPath("/"); - Cookie openAIREUser = new Cookie("openAIREUser", new Gson().toJson(userInfo)); + Cookie openAIREUser = new Cookie("openAIREUser", URLEncoder.encode(new Gson().toJson(userInfo), "UTF-8")); openAIREUser.setMaxAge(accessToken.getMaxAge()); openAIREUser.setPath("/"); if (aai_mode.equalsIgnoreCase("production") || aai_mode.equalsIgnoreCase("beta")) diff --git a/src/main/java/eu/dnetlib/repo/manager/config/OpenAIREAuthoritiesMapper.java b/src/main/java/eu/dnetlib/repo/manager/config/OpenAIREAuthoritiesMapper.java index 58a2170..afb6579 100644 --- a/src/main/java/eu/dnetlib/repo/manager/config/OpenAIREAuthoritiesMapper.java +++ b/src/main/java/eu/dnetlib/repo/manager/config/OpenAIREAuthoritiesMapper.java @@ -6,22 +6,48 @@ import eu.dnetlib.repo.manager.service.security.AuthoritiesMapper; import org.apache.log4j.Logger; import org.mitre.openid.connect.client.OIDCAuthoritiesMapper; import org.mitre.openid.connect.model.UserInfo; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.ComponentScan; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; @ComponentScan @Component public class OpenAIREAuthoritiesMapper implements OIDCAuthoritiesMapper { private static final Logger logger = Logger.getLogger(OpenAIREAuthoritiesMapper.class); + + @Value("${services.repo-manager.adminEmail}") + String adminEmail; + @Override public Collection mapAuthorities(JWT jwtToken, UserInfo userInfo) { - logger.info("entitlements" + userInfo.getSource().getAsJsonArray("edu_person_entitlements").size()); - JsonArray entitlements = userInfo.getSource().getAsJsonArray("edu_person_entitlements"); - return AuthoritiesMapper.map(entitlements); + JsonArray entitlements = null; + List authorities = new ArrayList<>(); + if (userInfo != null && userInfo.getSource() != null) { + if (userInfo.getSource().getAsJsonArray("edu_person_entitlements") != null) { + entitlements = userInfo.getSource().getAsJsonArray("edu_person_entitlements"); + } else if (userInfo.getSource().getAsJsonArray("eduperson_entitlement") != null) { + entitlements = userInfo.getSource().getAsJsonArray("eduperson_entitlement"); + } else { + authorities.add(new SimpleGrantedAuthority("ROLE_USER")); + logger.error("Could not read user 'edu_person_entitlements' && 'eduperson_entitlement'\nAdding default role 'ROLE_USER' to user: " + userInfo.toString()); + } + logger.debug("user info: " + userInfo + "\nentitlements: " + entitlements); + + // FIXME: delete this if statement + if (userInfo.getEmail() != null && userInfo.getEmail().equals(adminEmail)) { + authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); + } + + authorities.addAll(AuthoritiesMapper.map(entitlements)); + } + return authorities; } } diff --git a/src/main/java/eu/dnetlib/repo/manager/controllers/AaiUserRoleController.java b/src/main/java/eu/dnetlib/repo/manager/controllers/AaiUserRoleController.java deleted file mode 100644 index 2bc8341..0000000 --- a/src/main/java/eu/dnetlib/repo/manager/controllers/AaiUserRoleController.java +++ /dev/null @@ -1,196 +0,0 @@ -package eu.dnetlib.repo.manager.controllers; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import eu.dnetlib.repo.manager.domain.dto.Role; -import eu.dnetlib.repo.manager.service.aai.registry.AaiRegistryService; -import eu.dnetlib.repo.manager.service.security.AaiUserRoleService; -import eu.dnetlib.repo.manager.service.security.AuthoritiesUpdater; -import eu.dnetlib.repo.manager.utils.JsonUtils; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.mitre.openid.connect.model.OIDCAuthenticationToken; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.util.ArrayList; -import java.util.List; - -@RestController -@RequestMapping(value = "/aai-user-management") -@Api(description = "AAI User Management", value = "aai-user-management") -public class AaiUserRoleController { - - private final AaiRegistryService aaiRegistryService; - private final AuthoritiesUpdater authoritiesUpdater; - private final AaiUserRoleService aaiUserRoleService; - - @Autowired - AaiUserRoleController(AaiRegistryService aaiRegistryService, - AuthoritiesUpdater authoritiesUpdater, - AaiUserRoleService aaiUserRoleService) { - this.aaiRegistryService = aaiRegistryService; - this.authoritiesUpdater = authoritiesUpdater; - this.aaiUserRoleService = aaiUserRoleService; - } - - private String sendEmail() { - OIDCAuthenticationToken authenticationToken = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); - return authenticationToken.getUserInfo().getEmail(); - } - - /** - * Get the role with the given name and description. - **/ - @RequestMapping(method = RequestMethod.GET, path = "/role/id/get") -// @PreAuthorize("hasAnyAuthority('ROLE_USER', 'ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") - public Response getRole(@RequestParam(value = "type", defaultValue = "datasource") String type, @RequestParam("id") String id) { - int roleId = aaiRegistryService.getCouId(type, id); - return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role id is: " + roleId).toString()).type(MediaType.APPLICATION_JSON).build(); - } - - /** - * Create a new role with the given name and description. - **/ - @RequestMapping(method = RequestMethod.POST, path = "/createRole") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") - public Response createRole(@RequestBody Role role) { - aaiRegistryService.createRole(role); - return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been created").toString()).type(MediaType.APPLICATION_JSON).build(); - } - - /** - * Subscribe to a type(Community, etc.) with id(ee, egi, etc.) - */ - @ApiOperation(value = "subscribe") - @RequestMapping(method = RequestMethod.POST, path = "/subscribe/{type}/{id}") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") - public Response subscribe(@PathVariable("type") String type, @PathVariable("id") String id) { - Integer coPersonId = aaiRegistryService.getCoPersonIdByIdentifier(); - if (coPersonId == null) { - coPersonId = aaiRegistryService.getCoPersonIdByEmail(); - } - Integer couId = aaiRegistryService.getCouId(type, id); - if (couId != null) { - Integer role = aaiRegistryService.getRoleId(coPersonId, couId); - aaiRegistryService.assignMemberRole(coPersonId, couId, role); - - // Add role to current user authorities - authoritiesUpdater.addRole(aaiUserRoleService.convertRepoIdToAuthority(id)); - - return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been assigned").toString()).type(MediaType.APPLICATION_JSON).build(); - } else { - return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("Role has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); - } - } - - - /** - * Remove the member role from user with email for a type(Community, etc.) with id(ee, egi, etc.) - */ - @ApiOperation(value = "Remove role from member") - @RequestMapping(method = RequestMethod.DELETE, path = "/{type}/{id}/member/{email}") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") // FIXME: ?? - public Response removeMemberRole(@PathVariable("type") String type, @PathVariable("id") String - id, @PathVariable("email") String email) { - Integer coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); - if (coPersonId != null) { - Integer couId = aaiRegistryService.getCouId(type, id); - Integer role = null; - if (couId != null) { - role = aaiRegistryService.getRoleId(coPersonId, couId); - } - if (couId != null && role != null) { - aaiRegistryService.removeMemberRole(coPersonId, couId, role); - - // Remove role from current user authorities - authoritiesUpdater.removeRole(aaiUserRoleService.convertRepoIdToAuthority(id)); - - return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been removed").toString()).type(MediaType.APPLICATION_JSON).build(); - } else { - return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("Role has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); - } - } else { - return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("User has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); - } - } - - - /** - * Subscribe to role-repo by his email - */ - @RequestMapping(method = RequestMethod.POST, path = "/subscribe/repo-role/{id}") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#id)") - public Response subscribeRoleByEmail(@PathVariable("id") String id, @RequestParam("email") String email) { - Integer coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); - if (coPersonId != null) { - Integer couId = aaiRegistryService.getCouId("datasource", id); - if (couId != null) { - Integer role = aaiRegistryService.getRoleId(coPersonId, couId); - aaiRegistryService.assignMemberRole(coPersonId, couId, role); - - // Add role to current user authorities - authoritiesUpdater.addRole(aaiUserRoleService.convertRepoIdToAuthority(id)); - - return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been assigned").toString()).type(MediaType.APPLICATION_JSON).build(); - } else { - return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("Role has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); - } - } else { - return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("User with this email has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); - } - - - } - - - /** - * Get all the users that have the role that is associated with repoId - */ - @RequestMapping(method = RequestMethod.GET, path = "/repo/{id}/all-users") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") // FIXME: ?? - public ResponseEntity> getAllUsersOfARepo(@PathVariable("id") String id) { - - List userList = new ArrayList<>(); - - // find couId by role name - Integer couId = aaiRegistryService.getCouId("datasource", id); - if (couId != null) { - JsonArray users = aaiRegistryService.getUsersByCouId(couId); - for (JsonElement jsonElement : users) { - userList.add(jsonElement.toString()); - } - return ResponseEntity.ok(userList); - } - - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - ///////////////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////////////////////// - - @RequestMapping(method = RequestMethod.GET, path = "/users/couid/{id}") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") - public ResponseEntity getUsersByCouId(@PathVariable("id") Integer id) { -// calls.getUserByCoId() - return ResponseEntity.ok(aaiRegistryService.getUsersByCouId(id).toString()); - } - - - @RequestMapping(method = RequestMethod.GET, path = "/user/roles") - @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or hasRole('ROLE_USER') and authentication.userInfo.email==#email") - public ResponseEntity> getRolesByEmail(@RequestParam("email") String email) { - int coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); - List list = new ArrayList<>(); - for (JsonElement element : aaiRegistryService.getRoles(coPersonId)) { - list.add(element.toString()); - } - return ResponseEntity.ok(list); - } - -} \ No newline at end of file diff --git a/src/main/java/eu/dnetlib/repo/manager/controllers/PiWikController.java b/src/main/java/eu/dnetlib/repo/manager/controllers/PiWikController.java index a328399..a2c81e8 100644 --- a/src/main/java/eu/dnetlib/repo/manager/controllers/PiWikController.java +++ b/src/main/java/eu/dnetlib/repo/manager/controllers/PiWikController.java @@ -42,13 +42,13 @@ public class PiWikController { @RequestMapping(value = "/getPiwikSiteForRepo/{repositoryId}" , method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#repositoryId) or (@repositoryService.getRepositoryById(#repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") + @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#repositoryId) or (@repositoryService.getRepositoryById(#repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") public PiwikInfo getPiwikSiteForRepo(@PathVariable("repositoryId") String repositoryId) { return piWikService.getPiwikSiteForRepo(repositoryId); } @RequestMapping(value = "/savePiwikInfo" , method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE) - @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#piwikInfo.repositoryId) or (@repositoryService.getRepositoryById(#piwikInfo.repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") + @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#piwikInfo.repositoryId) or (@repositoryService.getRepositoryById(#piwikInfo.repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") public PiwikInfo savePiwikInfo(@RequestBody PiwikInfo piwikInfo) { return piWikService.savePiwikInfo(piwikInfo); } @@ -157,7 +157,7 @@ public class PiWikController { @RequestMapping(value = "/getOpenaireId/{repositoryId}" , method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#repositoryId) or (@repositoryService.getRepositoryById(#repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") + @PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#repositoryId) or (@repositoryService.getRepositoryById(#repositoryId).registeredBy=='null' and hasRole('ROLE_USER'))") public String getOpenaireId(@PathVariable("repositoryId") String repositoryId){ return piWikService.getOpenaireId(repositoryId); } diff --git a/src/main/java/eu/dnetlib/repo/manager/controllers/RepositoryController.java b/src/main/java/eu/dnetlib/repo/manager/controllers/RepositoryController.java index d106f74..6d5c72c 100644 --- a/src/main/java/eu/dnetlib/repo/manager/controllers/RepositoryController.java +++ b/src/main/java/eu/dnetlib/repo/manager/controllers/RepositoryController.java @@ -3,20 +3,26 @@ package eu.dnetlib.repo.manager.controllers; import eu.dnetlib.domain.data.Repository; import eu.dnetlib.domain.data.RepositoryInterface; import eu.dnetlib.repo.manager.domain.*; +import eu.dnetlib.repo.manager.domain.dto.User; import eu.dnetlib.repo.manager.exception.ResourceNotFoundException; import eu.dnetlib.repo.manager.service.RepositoryServiceImpl; +import eu.dnetlib.repo.manager.service.security.AuthorizationService; +import eu.dnetlib.repo.manager.utils.JsonUtils; import io.swagger.annotations.Api; import org.apache.log4j.Logger; import org.json.JSONException; import org.mitre.openid.connect.model.OIDCAuthenticationToken; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; +import javax.ws.rs.core.Response; import java.io.IOException; import java.util.List; import java.util.Map; @@ -28,8 +34,15 @@ public class RepositoryController { private static final Logger logger = Logger.getLogger(RepositoryController.class); + private final RepositoryServiceImpl repositoryService; //TODO: why not RepositoryService?? + private final AuthorizationService authorizationService; + @Autowired - private RepositoryServiceImpl repositoryService; + RepositoryController(RepositoryServiceImpl repositoryService, + AuthorizationService authorizationService) { + this.repositoryService = repositoryService; + this.authorizationService = authorizationService; + } @RequestMapping(value = "/getCountries", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @@ -87,7 +100,7 @@ public class RepositoryController { @RequestMapping(value = "/getRepositoryById/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PostAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#id) or (returnObject.registeredBy=='null' and hasRole('ROLE_USER'))") + @PostAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#id) or (returnObject.registeredBy=='null' and hasRole('ROLE_USER'))") public Repository getRepositoryById(@PathVariable("id") String id) throws JSONException, ResourceNotFoundException { Repository repo = repositoryService.getRepositoryById(id); @@ -124,7 +137,7 @@ public class RepositoryController { @RequestMapping(value = "/getRepositoryInterface/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PostAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#id) or (@repositoryService.getRepositoryById(#id).registeredBy=='null' and hasRole('ROLE_USER'))") + @PostAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#id) or (@repositoryService.getRepositoryById(#id).registeredBy=='null' and hasRole('ROLE_USER'))") public List getRepositoryInterface(@PathVariable("id") String id) throws JSONException { return repositoryService.getRepositoryInterface(id); } @@ -132,7 +145,7 @@ public class RepositoryController { @RequestMapping(value = "/addRepository", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody -// @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or (hasRole(@aaiUserRoleService.convertRepoIdToRoleId(#repository.id)) or hasRole(@aaiUserRoleService.convertRepoIdToRoleId(returnObject.id)))") +// @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or (hasRole(@authorizationService.convertRepoIdToRoleId(#repository.id)) or hasRole(@authorizationService.convertRepoIdToRoleId(returnObject.id)))") @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or hasRole('ROLE_USER')") public Repository addRepository(@RequestParam("datatype") String datatype, @RequestBody Repository repository) throws Exception { @@ -164,13 +177,13 @@ public class RepositoryController { @RequestMapping(value = "/updateRepository", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#repository.id)") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#repository.id)") public Repository updateRepository(@RequestBody Repository repository, Authentication authentication) throws Exception { return repositoryService.updateRepository(repository, authentication); } @RequestMapping(value = "/deleteInterface/", method = RequestMethod.DELETE) - @PreAuthorize("@aaiUserRoleService.isMemberOf(#id)") + @PreAuthorize("@authorizationService.isMemberOf(#id)") public void deleteRepositoryInterface(@RequestParam("id") String id, @RequestParam("registeredBy") String registeredBy) { repositoryService.deleteRepositoryInterface(id, registeredBy); @@ -179,7 +192,7 @@ public class RepositoryController { @RequestMapping(value = "/addInterface", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#repoId)") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#repoId)") public RepositoryInterface addRepositoryInterface(@RequestParam("datatype") String datatype, @RequestParam("repoId") String repoId, @RequestParam("registeredBy") String registeredBy, @@ -235,11 +248,44 @@ public class RepositoryController { @RequestMapping(value = "/updateRepositoryInterface", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) @ResponseBody - @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @aaiUserRoleService.isMemberOf(#repoId)") + @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#repoId)") public RepositoryInterface updateRepositoryInterface(@RequestParam("repoId") String repoId, @RequestParam("registeredBy") String registeredBy, @RequestParam(value = "comment", required = false) String comment, @RequestBody RepositoryInterface repositoryInterface) throws Exception { return repositoryService.updateRepositoryInterface(repoId, registeredBy, comment, repositoryInterface); } + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Get all the admins of the repository + */ + @RequestMapping(method = RequestMethod.GET, path = "{repoId}/admins") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#id)") + public ResponseEntity> getAdminsOfARepo(@PathVariable("repoId") String id) { + return new ResponseEntity<>(authorizationService.getAdminsOfRepo(id), HttpStatus.OK); + } + + /** + * Subscribe to repo by email + */ + @RequestMapping(method = RequestMethod.POST, path = "{repoId}/admins") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#id)") + public Response subscribeByEmail(@PathVariable("repoId") String id, @RequestBody String email) throws ResourceNotFoundException { + authorizationService.addAdmin(id, email); + return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been assigned").toString()).type(javax.ws.rs.core.MediaType.APPLICATION_JSON).build(); + } + + /** + * Unsubscribe from repo by email + */ + @RequestMapping(method = RequestMethod.DELETE, path = "{repoId}/admins/{email}") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or @authorizationService.isMemberOf(#id)") + public Response unsubscribeByEmail(@PathVariable("id") String id, @RequestBody String email) throws ResourceNotFoundException { + authorizationService.removeAdmin(id, email); + return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been removed").toString()).type(javax.ws.rs.core.MediaType.APPLICATION_JSON).build(); + } } diff --git a/src/main/java/eu/dnetlib/repo/manager/controllers/UserRoleController.java b/src/main/java/eu/dnetlib/repo/manager/controllers/UserRoleController.java new file mode 100644 index 0000000..0665910 --- /dev/null +++ b/src/main/java/eu/dnetlib/repo/manager/controllers/UserRoleController.java @@ -0,0 +1,131 @@ +package eu.dnetlib.repo.manager.controllers; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import eu.dnetlib.repo.manager.domain.dto.Role; +import eu.dnetlib.repo.manager.service.aai.registry.AaiRegistryService; +import eu.dnetlib.repo.manager.service.security.RoleMappingService; +import eu.dnetlib.repo.manager.service.security.AuthoritiesMapper; +import eu.dnetlib.repo.manager.service.security.AuthoritiesUpdater; +import eu.dnetlib.repo.manager.utils.JsonUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.mitre.openid.connect.model.OIDCAuthenticationToken; +import org.mitre.openid.connect.model.UserInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@RestController +@RequestMapping(value = "/role-management") +@Api(description = "Role Management", value = "role-management") +public class UserRoleController { + + private final AaiRegistryService aaiRegistryService; + private final AuthoritiesUpdater authoritiesUpdater; + private final RoleMappingService roleMappingService; + + @Autowired + UserRoleController(AaiRegistryService aaiRegistryService, + AuthoritiesUpdater authoritiesUpdater, + RoleMappingService roleMappingService) { + this.aaiRegistryService = aaiRegistryService; + this.authoritiesUpdater = authoritiesUpdater; + this.roleMappingService = roleMappingService; + } + + /** + * Get the role with the given id. + **/ + @RequestMapping(method = RequestMethod.GET, path = "/role/{id}") +// @PreAuthorize("hasAnyAuthority('ROLE_USER', 'ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") + public Response getRole(@RequestParam(value = "type", defaultValue = "datasource") String type, @PathVariable("id") String id) { + int roleId = aaiRegistryService.getCouId(type, id); + return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role id is: " + roleId).toString()).type(MediaType.APPLICATION_JSON).build(); + } + + /** + * Create a new role with the given name and description. + **/ + @RequestMapping(method = RequestMethod.POST, path = "/role") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") + public Response createRole(@RequestBody Role role) { + aaiRegistryService.createRole(role); + return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been created").toString()).type(MediaType.APPLICATION_JSON).build(); + } + + /** + * Subscribe to a type(Community, etc.) with id(ee, egi, etc.) + */ + @ApiOperation(value = "subscribe") + @RequestMapping(method = RequestMethod.POST, path = "/subscribe/{type}/{id}") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") + public Response subscribe(@PathVariable("type") String type, @PathVariable("id") String id) { + Integer coPersonId = aaiRegistryService.getCoPersonIdByIdentifier(); + if (coPersonId == null) { + coPersonId = aaiRegistryService.getCoPersonIdByEmail(); + } + Integer couId = aaiRegistryService.getCouId(type, id); + if (couId != null) { + Integer role = aaiRegistryService.getRoleId(coPersonId, couId); + aaiRegistryService.assignMemberRole(coPersonId, couId, role); + + // Add role to current authorities + authoritiesUpdater.addRole(roleMappingService.convertRepoIdToAuthority(id)); + + return Response.status(HttpStatus.OK.value()).entity(JsonUtils.createResponse("Role has been assigned").toString()).type(MediaType.APPLICATION_JSON).build(); + } else { + return Response.status(HttpStatus.NOT_FOUND.value()).entity(JsonUtils.createResponse("Role has not been found").toString()).type(MediaType.APPLICATION_JSON).build(); + } + } + ///////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////// + + @RequestMapping(method = RequestMethod.GET, path = "/users/couid/{id}") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN')") + public ResponseEntity getUsersByCouId(@PathVariable("id") Integer id) { +// calls.getUserByCoId() + return ResponseEntity.ok(aaiRegistryService.getUsersByCouId(id).toString()); + } + + + @RequestMapping(method = RequestMethod.GET, path = "/users/{email}/roles") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_PROVIDE_ADMIN') or hasRole('ROLE_USER') and authentication.userInfo.email==#email") + public ResponseEntity> getRolesByEmail(@PathVariable("email") String email) { + int coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); + List list = new ArrayList<>(); + // FIXME: getRoles returns all roles of user, requested and active + for (JsonElement element : aaiRegistryService.getRoles(coPersonId)) { + list.add(element.getAsJsonObject().get("CouId").getAsInt()); + } + return ResponseEntity.ok(aaiRegistryService.getCouNames(list).values()); + } + + + @RequestMapping(method = RequestMethod.GET, path = "/user/roles/my") + @PreAuthorize("hasRole('ROLE_USER')") + public ResponseEntity> getRoleNames() { + List roles; + JsonArray entitlements = null; + UserInfo userInfo = ((OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication()).getUserInfo(); + if (userInfo.getSource().getAsJsonArray("edu_person_entitlements") != null) { + entitlements = userInfo.getSource().getAsJsonArray("edu_person_entitlements"); + } else if (userInfo.getSource().getAsJsonArray("eduperson_entitlement") != null) { + entitlements = userInfo.getSource().getAsJsonArray("eduperson_entitlement"); + } else { + return ResponseEntity.ok(null); + } + roles = AuthoritiesMapper.entitlementRoles(entitlements); + return ResponseEntity.ok(roles); + } + +} \ No newline at end of file diff --git a/src/main/java/eu/dnetlib/repo/manager/domain/dto/User.java b/src/main/java/eu/dnetlib/repo/manager/domain/dto/User.java new file mode 100644 index 0000000..c14c0bb --- /dev/null +++ b/src/main/java/eu/dnetlib/repo/manager/domain/dto/User.java @@ -0,0 +1,61 @@ +package eu.dnetlib.repo.manager.domain.dto; + +import org.mitre.openid.connect.model.UserInfo; + +public class User { + + private String sub; + private String firstName; + private String lastName; + private String email; + + public User() {} + + public User(String sub, String firstName, String lastName, String email) { + this.sub = sub; + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + + public static User from(UserInfo userInfo) { + User user = new User(); + user.setSub(user.getSub()); + user.setFirstName(userInfo.getGivenName()); + user.setLastName(userInfo.getFamilyName()); + user.setEmail(userInfo.getEmail()); + return user; + } + + public String getSub() { + return sub; + } + + public void setSub(String sub) { + this.sub = sub; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/main/java/eu/dnetlib/repo/manager/exception/ResourceNotFoundException.java b/src/main/java/eu/dnetlib/repo/manager/exception/ResourceNotFoundException.java index 68042cf..eb7fd3c 100644 --- a/src/main/java/eu/dnetlib/repo/manager/exception/ResourceNotFoundException.java +++ b/src/main/java/eu/dnetlib/repo/manager/exception/ResourceNotFoundException.java @@ -9,6 +9,10 @@ public class ResourceNotFoundException extends Exception { super("Resource Not Found"); } + public ResourceNotFoundException(String message) { + super(message); + } + public ResourceNotFoundException(String id, String resourceType) { super(resourceType + " with id " + id + " was not found"); } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/EmailUtilsImpl.java b/src/main/java/eu/dnetlib/repo/manager/service/EmailUtilsImpl.java index 975e62e..164c0d6 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/EmailUtilsImpl.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/EmailUtilsImpl.java @@ -33,11 +33,9 @@ public class EmailUtilsImpl implements EmailUtils { private boolean override = false, logonly = false; private String overrideEmail = null, from = null; - @Autowired - private MailLibrary mailLibrary; - - @Autowired - private CascadingPropertyLoader pLoader; + private final MailLibrary mailLibrary; + private final CascadingPropertyLoader pLoader; + private final RepositoryService repositoryService; @Value("${services.repo-manager.baseUrl}") private String baseUrl; @@ -55,7 +53,12 @@ public class EmailUtilsImpl implements EmailUtils { private String valBaseUrl; @Autowired - private RepositoryService repositoryService; + EmailUtilsImpl(MailLibrary mailLibrary, CascadingPropertyLoader pLoader, + RepositoryService repositoryService) { + this.mailLibrary = mailLibrary; + this.pLoader = pLoader; + this.repositoryService = repositoryService; + } @PostConstruct diff --git a/src/main/java/eu/dnetlib/repo/manager/service/RepositoryServiceImpl.java b/src/main/java/eu/dnetlib/repo/manager/service/RepositoryServiceImpl.java index 02b4780..b4e4a55 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/RepositoryServiceImpl.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/RepositoryServiceImpl.java @@ -13,7 +13,7 @@ import eu.dnetlib.repo.manager.domain.*; import eu.dnetlib.repo.manager.domain.dto.Role; import eu.dnetlib.repo.manager.exception.ResourceNotFoundException; import eu.dnetlib.repo.manager.service.aai.registry.AaiRegistryService; -import eu.dnetlib.repo.manager.service.security.AaiUserRoleService; +import eu.dnetlib.repo.manager.service.security.RoleMappingService; import eu.dnetlib.repo.manager.service.security.AuthoritiesUpdater; import eu.dnetlib.repo.manager.utils.Converter; import gr.uoa.di.driver.enabling.vocabulary.VocabularyLoader; @@ -30,7 +30,6 @@ import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.*; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; @@ -51,7 +50,7 @@ public class RepositoryServiceImpl implements RepositoryService { private static final Logger LOGGER = Logger.getLogger(RepositoryServiceImpl.class); - private final AaiUserRoleService aaiUserRoleService; + private final RoleMappingService roleMappingService; private final AaiRegistryService registryCalls; private final AuthoritiesUpdater authoritiesUpdater; private final RestTemplate restTemplate; @@ -85,14 +84,15 @@ public class RepositoryServiceImpl implements RepositoryService { private HttpHeaders httpHeaders; @Autowired - public RepositoryServiceImpl(AaiUserRoleService aaiUserRoleService, + public RepositoryServiceImpl(RoleMappingService roleMappingService, AaiRegistryService registryCalls, AuthoritiesUpdater authoritiesUpdater, - VocabularyLoader vocabularyLoader, EmailUtils emailUtils, + VocabularyLoader vocabularyLoader, RestTemplate restTemplate, + @Lazy EmailUtils emailUtils, @Lazy ValidatorService validatorService, @Lazy PiWikService piWikService) { - this.aaiUserRoleService = aaiUserRoleService; + this.roleMappingService = roleMappingService; this.registryCalls = registryCalls; this.authoritiesUpdater = authoritiesUpdater; this.vocabularyLoader = vocabularyLoader; @@ -514,7 +514,7 @@ public class RepositoryServiceImpl implements RepositoryService { // TODO: move the following code elsewhere (creation and assignment of role to user) ?? // Create new role - String newRoleName = aaiUserRoleService.getRoleIdByRepoId(repository.getId()); + String newRoleName = roleMappingService.getRoleIdByRepoId(repository.getId()); Role newRole = new Role(newRoleName, repository.getOfficialName()); Integer couId = null; try { @@ -537,7 +537,7 @@ public class RepositoryServiceImpl implements RepositoryService { registryCalls.assignMemberRole(coPersonId, couId, role); // Add role to current user authorities - authoritiesUpdater.addRole(aaiUserRoleService.convertRepoIdToAuthority(repository.getId())); + authoritiesUpdater.addRole(roleMappingService.convertRepoIdToAuthority(repository.getId())); } catch (Exception e) { LOGGER.debug("Exception on assign role to user during add repository", e); throw e; @@ -686,9 +686,14 @@ public class RepositoryServiceImpl implements RepositoryService { this.updateCompliance(repoId, repositoryInterface.getId(), repositoryInterface.getCompliance()); this.updateValidationSet(repoId, repositoryInterface.getId(), repositoryInterface.getAccessSet()); - Repository e = this.getRepositoryById(repoId); - emailUtils.sendAdminUpdateInterfaceEmail(e, comment, repositoryInterface, SecurityContextHolder.getContext().getAuthentication()); - emailUtils.sendUserUpdateInterfaceEmail(e, comment, repositoryInterface, SecurityContextHolder.getContext().getAuthentication()); + Repository repository = this.getRepositoryById(repoId); + try { + emailUtils.sendAdminUpdateInterfaceEmail(repository, comment, repositoryInterface, SecurityContextHolder.getContext().getAuthentication()); + emailUtils.sendUserUpdateInterfaceEmail(repository, comment, repositoryInterface, SecurityContextHolder.getContext().getAuthentication()); + } catch (Exception e) { + LOGGER.warn("Could not send emails", e); + } + submitInterfaceValidation(getRepositoryById(repoId), registeredBy, repositoryInterface, true); return repositoryInterface; diff --git a/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/AaiRegistryService.java b/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/AaiRegistryService.java index 3dbe14b..839f62a 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/AaiRegistryService.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/AaiRegistryService.java @@ -3,6 +3,7 @@ package eu.dnetlib.repo.manager.service.aai.registry; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import eu.dnetlib.repo.manager.domain.dto.Role; +import eu.dnetlib.repo.manager.domain.dto.User; import java.util.List; import java.util.Map; @@ -247,4 +248,7 @@ public interface AaiRegistryService { * @return */ Map getCouNames(List couIds); + + // TODO: add description + List getUsers(Integer couId); } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/RegistryCalls.java b/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/RegistryCalls.java index 10d3a67..c177ce0 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/RegistryCalls.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/aai/registry/RegistryCalls.java @@ -4,6 +4,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import eu.dnetlib.repo.manager.domain.dto.Role; +import eu.dnetlib.repo.manager.domain.dto.User; import eu.dnetlib.repo.manager.service.aai.registry.utils.RegistryUtils; import eu.dnetlib.repo.manager.utils.HttpUtils; import org.apache.log4j.Logger; @@ -270,6 +271,20 @@ public class RegistryCalls implements AaiRegistryService { return infos; } + @Override + public List getUsers(Integer couId) { + List users = new ArrayList<>(); + JsonArray infos = getUserEmailByCouId(couId, false); + + infos.forEach(info -> { + User user = new User(); + user.setEmail(info.getAsJsonObject().get("email").getAsString()); + + users.add(user); + }); + return users; + } + @Override public JsonArray getUserNamesByCouId(Integer couId, boolean admin) { Map params = new HashMap<>(); diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleServiceImpl.java b/src/main/java/eu/dnetlib/repo/manager/service/security/AaiRoleMappingService.java similarity index 67% rename from src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleServiceImpl.java rename to src/main/java/eu/dnetlib/repo/manager/service/security/AaiRoleMappingService.java index dede120..73d1c20 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleServiceImpl.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/AaiRoleMappingService.java @@ -2,16 +2,16 @@ package eu.dnetlib.repo.manager.service.security; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import java.net.URLEncoder; -@Service("aaiUserRoleService") -public class AaiUserRoleServiceImpl implements AaiUserRoleService { +@Service("roleMappingService") +public class AaiRoleMappingService implements RoleMappingService { - private static final Logger logger = Logger.getLogger(AaiUserRoleServiceImpl.class); + private static final Logger logger = Logger.getLogger(AaiRoleMappingService.class); @Value("${registry.production:true}") private boolean production; @@ -29,6 +29,11 @@ public class AaiUserRoleServiceImpl implements AaiUserRoleService { return null; } + @Override + public String getRepoIdByRoleId(String roleId) { + return roleId.replaceFirst(".*datasource\\.", "").replace("$", ":"); + } + @Override public String getRoleIdByRepoId(String repoId) { String roleId = ""; @@ -42,6 +47,23 @@ public class AaiUserRoleServiceImpl implements AaiUserRoleService { } + @Override + public String convertAuthorityIdToRepoId(String authorityId) { + String repo = ""; + if (authorityId != null) { + repo = authorityId + .replaceFirst(".*datasource\\.", "") + .replace("$", ":") + .toLowerCase(); + } + return repo; + } + + @Override + public String convertAuthorityToRepoId(GrantedAuthority authority) { + return convertAuthorityIdToRepoId(authority.toString()); + } + @Override public String convertRepoIdToAuthorityId(String repoId) { StringBuilder roleBuilder = new StringBuilder(); @@ -65,12 +87,4 @@ public class AaiUserRoleServiceImpl implements AaiUserRoleService { String role = convertRepoIdToEncodedAuthorityId(repoId); return new SimpleGrantedAuthority(role); } - - - @Override - public boolean isMemberOf(String repoId) { - String repoRole = convertRepoIdToEncodedAuthorityId(repoId); - return SecurityContextHolder.getContext().getAuthentication().getAuthorities() - .parallelStream().anyMatch(authority -> authority.toString().equals(repoRole)); - } } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesMapper.java b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesMapper.java index f435d98..379f21e 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesMapper.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesMapper.java @@ -6,6 +6,7 @@ import org.apache.log4j.Logger; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import java.net.URLDecoder; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -13,42 +14,65 @@ import java.util.regex.Pattern; public class AuthoritiesMapper { private static final Logger logger = Logger.getLogger(AuthoritiesMapper.class); + private static final String ENTITLEMENT_REGEX = "urn:geant:openaire[.]eu:group:([^:]*):?(.*)?:role=member#aai[.]openaire[.]eu"; private AuthoritiesMapper() { } - public static Collection map(JsonArray entitlements) { - HashSet authorities = new HashSet<>(); - //entitlements.add("urn:geant:openaire.eu:group:datasource.opendoar____$$3469:role=member#aai.openaire.eu"); + public static Collection map(JsonArray entitlements) { + HashSet authorities = new HashSet<>(); provideRoles(entitlements, authorities); entityRoles(entitlements, authorities); return authorities; } - private static void entityRoles(JsonArray entitlements, Set authorities) { - String regex = "urn:geant:openaire[.]eu:group:([^:]*):?(.*)?:role=member#aai[.]openaire[.]eu"; - for (JsonElement obj : entitlements) { - Matcher matcher = Pattern.compile(regex).matcher(obj.getAsString()); - if (matcher.find()) { - StringBuilder sb = new StringBuilder(); - if (matcher.group(1) != null && matcher.group(1).length() > 0) { - sb.append(matcher.group(1).replace("+-+", "_").replaceAll("[+.]", "_").toUpperCase()); - } - if (matcher.group(2).length() > 0) { - sb.append("_"); - if (matcher.group(2).equals("admins")) { - sb.append("MANAGER"); - } else { - sb.append(matcher.group(2).toUpperCase()); + public static List entitlementRoles(JsonArray entitlements) { + List roles = new ArrayList<>(); + if (entitlements != null) { + for (JsonElement obj : entitlements) { + Matcher matcher = Pattern.compile(ENTITLEMENT_REGEX).matcher(obj.getAsString()); + if (matcher.find()) { + StringBuilder sb = new StringBuilder(); + if (matcher.group(1) != null && matcher.group(1).length() > 0) { + sb.append(matcher.group(1)); } + if (matcher.group(2).length() > 0) { + sb.append(":"); + sb.append(matcher.group(2)); + } + String role = sb.toString().replace("+", " "); + roles.add(URLDecoder.decode(role)); + } + } + } + return roles; + } + + private static void entityRoles(JsonArray entitlements, Set authorities) { + if (entitlements != null) { + for (JsonElement obj : entitlements) { + Matcher matcher = Pattern.compile(ENTITLEMENT_REGEX).matcher(obj.getAsString()); + if (matcher.find()) { + StringBuilder sb = new StringBuilder(); + if (matcher.group(1) != null && matcher.group(1).length() > 0) { + sb.append(matcher.group(1).replace("+-+", "_").replaceAll("[+.]", "_").toUpperCase()); + } + if (matcher.group(2).length() > 0) { + sb.append("_"); + if (matcher.group(2).equals("admins")) { + sb.append("MANAGER"); + } else { + sb.append(matcher.group(2).toUpperCase()); + } + } + authorities.add(new SimpleGrantedAuthority(sb.toString())); } - authorities.add(new SimpleGrantedAuthority(sb.toString())); } } } // TODO: remove when ROLE_ADMIN and ROLE_PROVIDE_ADMIN are removed from project - private static void provideRoles(JsonArray entitlements, Set authorities) { + private static void provideRoles(JsonArray entitlements, Set authorities) { Map userRoles = new HashMap() {{ put("urn:geant:openaire.eu:group:Super+Administrator:role=member#aai.openaire.eu", "ROLE_ADMIN"); put("urn:geant:openaire.eu:group:Content+Provider+Dashboard+Administrator:role=member#aai.openaire.eu", "ROLE_PROVIDE_ADMIN"); diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesUpdater.java b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesUpdater.java index b064532..94c58ca 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesUpdater.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthoritiesUpdater.java @@ -26,61 +26,69 @@ public class AuthoritiesUpdater extends HttpSessionSecurityContextRepository { @Autowired FindByIndexNameSessionRepository sessions; - public void update(String id, Collection authorities) { + public void update(String email, Collection authorities) { if (sessions != null) { Map map = sessions. - findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, id); - logger.debug(map.values().toArray().length); - for (ExpiringSession session : map.values()) { - logger.debug(session.getId()); - if (!session.isExpired()) { - SecurityContext securityContext = session.getAttribute(SPRING_SECURITY_CONTEXT_KEY); - Authentication authentication = securityContext.getAuthentication(); - if (authentication instanceof OIDCAuthenticationToken) { - OIDCAuthenticationToken authOIDC = (OIDCAuthenticationToken) authentication; - logger.debug(authorities); - securityContext.setAuthentication(new OIDCAuthenticationToken(authOIDC.getSub(), authOIDC.getIssuer(), - authOIDC.getUserInfo(), authorities, authOIDC.getIdToken(), - authOIDC.getAccessTokenValue(), authOIDC.getRefreshTokenValue())); - logger.debug("Update authorities"); - session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext); - sessions.save(session); + findByIndexNameAndIndexValue(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, email); + if (map != null) { + logger.debug(map.values().toArray().length); + for (ExpiringSession session : map.values()) { + logger.debug(session.getId()); + if (!session.isExpired()) { + SecurityContext securityContext = session.getAttribute(SPRING_SECURITY_CONTEXT_KEY); + Authentication authentication = securityContext.getAuthentication(); + if (authentication instanceof OIDCAuthenticationToken) { + OIDCAuthenticationToken authOIDC = (OIDCAuthenticationToken) authentication; + logger.debug(authorities); + securityContext.setAuthentication(new OIDCAuthenticationToken(authOIDC.getSub(), authOIDC.getIssuer(), + authOIDC.getUserInfo(), authorities, authOIDC.getIdToken(), + authOIDC.getAccessTokenValue(), authOIDC.getRefreshTokenValue())); + logger.debug("Update authorities"); + session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext); + sessions.save(session); + } } } } } } - public void update(String id, Update update) { + public void update(String email, Update update) { Collection authorities = update.authorities(SecurityContextHolder.getContext().getAuthentication().getAuthorities()); - this.update(id, authorities); + this.update(email, authorities); + } + + public void addRole(String email, GrantedAuthority role) { + this.update(email, old -> { + HashSet authorities = new HashSet<>(old); + authorities.add(role); + return authorities; + }); } public void addRole(GrantedAuthority role) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth instanceof OIDCAuthenticationToken) { OIDCAuthenticationToken oidcAuth = (OIDCAuthenticationToken) auth; - this.update(oidcAuth.getUserInfo().getEmail(), old -> { - HashSet authorities = new HashSet<>(old); - authorities.add(role); - return authorities; - }); + this.addRole(oidcAuth.getUserInfo().getEmail(), role); } else { throw new UnauthorizedClientException("User auth is not instance of OIDCAuthenticationToken"); } } + public void removeRole(String email, GrantedAuthority role) { + this.update(email, old -> { + HashSet authorities = new HashSet<>(old); + authorities.remove(role); + return authorities; + }); + } + public void removeRole(GrantedAuthority role) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth instanceof OIDCAuthenticationToken) { OIDCAuthenticationToken oidcAuth = (OIDCAuthenticationToken) auth; - this.update(oidcAuth.getUserInfo().getEmail(), old -> { - HashSet authorities = new HashSet<>(old); - authorities.remove(role); - return authorities; - }); - } else { - throw new UnauthorizedClientException("User auth is not instance of OIDCAuthenticationToken"); + this.removeRole(oidcAuth.getUserInfo().getEmail(), role); } } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationService.java b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationService.java index f41df4b..d06fab3 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationService.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationService.java @@ -1,12 +1,51 @@ package eu.dnetlib.repo.manager.service.security; +import eu.dnetlib.repo.manager.domain.dto.User; +import eu.dnetlib.repo.manager.exception.ResourceNotFoundException; + +import java.util.List; + public interface AuthorizationService { /** - * * @param type * @param id * @return */ - String member(String type, String id); + String member(String type, String id); //TODO: use or delete + + /** + * @param id Resource Id to check. + * @return Checks if a user is a member of a resource. + */ + boolean isMemberOf(String id); + + + /** + * Returns a list of admins of the resource. + * + * @param repoId + * @return + */ + List getAdminsOfRepo(String repoId); + + /** + * Add a user as admin to a resource. + * + * @param id Resource id + * @param email User email + * @return + * @throws ResourceNotFoundException + */ + boolean addAdmin(String id, String email) throws ResourceNotFoundException; + + /** + * Remove user from resource admins. + * + * @param id Resource id + * @param email User email + * @return + * @throws ResourceNotFoundException + */ + boolean removeAdmin(String id, String email) throws ResourceNotFoundException; } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationServiceImpl.java b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationServiceImpl.java index e43b17a..c5fdd11 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationServiceImpl.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/AuthorizationServiceImpl.java @@ -1,14 +1,36 @@ package eu.dnetlib.repo.manager.service.security; -import org.springframework.stereotype.Component; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import eu.dnetlib.repo.manager.domain.dto.User; +import eu.dnetlib.repo.manager.exception.ResourceNotFoundException; +import eu.dnetlib.repo.manager.service.aai.registry.AaiRegistryService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; -@Component("AuthorizationService") +import java.util.ArrayList; +import java.util.List; + +@Service("authorizationService") public class AuthorizationServiceImpl implements AuthorizationService { public final String ROLE_ADMIN = "ROLE_ADMIN"; public final String ROLE_PROVIDE_ADMIN = "ROLE_PROVIDE_ADMIN"; public final String ROLE_USER = "ROLE_USER"; + private final RoleMappingService roleMappingService; + private final AaiRegistryService aaiRegistryService; + private final AuthoritiesUpdater authoritiesUpdater; + + @Autowired + AuthorizationServiceImpl(RoleMappingService roleMappingService, AaiRegistryService aaiRegistryService, + AuthoritiesUpdater authoritiesUpdater) { + this.roleMappingService = roleMappingService; + this.aaiRegistryService = aaiRegistryService; + this.authoritiesUpdater = authoritiesUpdater; + } + private String mapType(String type) { if (type.equals("datasource")) { type = "datasource"; @@ -19,8 +41,80 @@ public class AuthorizationServiceImpl implements AuthorizationService { /** * Type = DATASOURCE */ + @Override public String member(String type, String id) { return mapType(type).toUpperCase() + "_" + id.toUpperCase(); } + @Override + public boolean isMemberOf(String repoId) { + String repoRole = roleMappingService.convertRepoIdToEncodedAuthorityId(repoId); + return SecurityContextHolder.getContext().getAuthentication().getAuthorities() + .parallelStream().anyMatch(authority -> authority.toString().equals(repoRole)); + } + + @Override + public List getAdminsOfRepo(String repoId) { + List userList = new ArrayList<>(); + + // find couId by role name + String role = roleMappingService.getRoleIdByRepoId(repoId); + Integer couId = aaiRegistryService.getCouId(role); + if (couId != null) { + JsonArray users = aaiRegistryService.getUsersByCouId(couId); + for (JsonElement jsonElement : users) { + userList.add(jsonElement.toString()); + } + } + return aaiRegistryService.getUsers(couId); + } + + + @Override + public boolean addAdmin(String id, String email) throws ResourceNotFoundException { + Integer coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); + if (coPersonId != null) { + String role = roleMappingService.getRoleIdByRepoId(id); + Integer couId = aaiRegistryService.getCouId(role); + if (couId != null) { + Integer roleId = aaiRegistryService.getRoleId(coPersonId, couId); + aaiRegistryService.assignMemberRole(coPersonId, couId, roleId); + + // Add role to user current authorities + authoritiesUpdater.addRole(email, roleMappingService.convertRepoIdToAuthority(id)); + + return true; + } else { + throw new ResourceNotFoundException("Cannot find CouId for role: " + role); + } + } else { + throw new ResourceNotFoundException("Cannot find coPersonId for user with email: " + email); + } + } + + @Override + public boolean removeAdmin(String id, String email) throws ResourceNotFoundException { + Integer coPersonId = aaiRegistryService.getCoPersonIdByEmail(email); + if (coPersonId != null) { + String role = roleMappingService.getRoleIdByRepoId(id); + Integer couId = aaiRegistryService.getCouId(role); + Integer roleId = null; + if (couId != null) { + roleId = aaiRegistryService.getRoleId(coPersonId, couId); + } + if (couId != null && roleId != null) { + aaiRegistryService.removeMemberRole(coPersonId, couId, roleId); + + // Remove role from user current authorities + authoritiesUpdater.removeRole(email, roleMappingService.convertRepoIdToAuthority(id)); + + return true; + } else { + throw new ResourceNotFoundException("Cannot find CouId for role: " + role); + } + } else { + throw new ResourceNotFoundException("Cannot find coPersonId for user with email: " + email); + } + } + } diff --git a/src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleService.java b/src/main/java/eu/dnetlib/repo/manager/service/security/RoleMappingService.java similarity index 67% rename from src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleService.java rename to src/main/java/eu/dnetlib/repo/manager/service/security/RoleMappingService.java index aa77d61..9138b4d 100644 --- a/src/main/java/eu/dnetlib/repo/manager/service/security/AaiUserRoleService.java +++ b/src/main/java/eu/dnetlib/repo/manager/service/security/RoleMappingService.java @@ -1,8 +1,9 @@ package eu.dnetlib.repo.manager.service.security; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -public interface AaiUserRoleService { +public interface RoleMappingService { /** * @param fullName @@ -11,12 +12,30 @@ public interface AaiUserRoleService { */ String getRepoNameWithoutType(String fullName, String prefix); + /** + * @param roleId Role Id + * @return Converts {@param roleId} to a repo Id. + */ + String getRepoIdByRoleId(String roleId); + /** * @param repoId Repository Id * @return Converts {@param repoId} to a role Id. */ String getRoleIdByRepoId(String repoId); + /** + * @param authorityId Authority Id + * @return Converts {@param authorityId} to repo Id. + */ + String convertAuthorityIdToRepoId(String authorityId); + + /** + * @param authority Granted authority + * @return Converts {@param authority} to repo Id. + */ + String convertAuthorityToRepoId(GrantedAuthority authority); + /** * @param repoId Repository Id * @return @@ -37,10 +56,4 @@ public interface AaiUserRoleService { */ SimpleGrantedAuthority convertRepoIdToAuthority(String repoId); - /** - * @param repoId Repository Id to check. - * @return Checks if a user is a member of a repository or not. - */ - boolean isMemberOf(String repoId); - } diff --git a/src/main/java/eu/dnetlib/repo/manager/utils/HttpUtils.java b/src/main/java/eu/dnetlib/repo/manager/utils/HttpUtils.java index ddbfb96..6819cf1 100644 --- a/src/main/java/eu/dnetlib/repo/manager/utils/HttpUtils.java +++ b/src/main/java/eu/dnetlib/repo/manager/utils/HttpUtils.java @@ -36,11 +36,7 @@ public class HttpUtils { headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity<>(body.toString(), headers); ResponseEntity responseEntity = restTemplate.exchange(issuer + path, HttpMethod.POST, request, String.class); - if (responseEntity.getBody() != null) { - return new JsonParser().parse(responseEntity.getBody()); - } else { - return null; - } + return getResponseEntityAsJsonElement(responseEntity); } public JsonElement put(String path, JsonObject body) { @@ -49,11 +45,7 @@ public class HttpUtils { headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity request = new HttpEntity<>(body.toString(), headers); ResponseEntity responseEntity = restTemplate.exchange(issuer + path, HttpMethod.PUT, request, String.class); - if (responseEntity.getBody() != null) { - return new JsonParser().parse(responseEntity.getBody()); - } else { - return null; - } + return getResponseEntityAsJsonElement(responseEntity); } public JsonElement get(String path, Map params) { @@ -61,11 +53,7 @@ public class HttpUtils { String url = issuer + path + ((params != null) ? createParams(params) : null); ResponseEntity responseEntity = restTemplate.exchange (url, HttpMethod.GET, new HttpEntity<>(createHeaders(user, password)), String.class); - if (responseEntity.getBody() != null) { - return new JsonParser().parse(responseEntity.getBody()); - } else { - return null; - } + return getResponseEntityAsJsonElement(responseEntity); } public JsonElement delete(String path) { @@ -73,11 +61,7 @@ public class HttpUtils { String url = issuer + path; ResponseEntity responseEntity = restTemplate.exchange (url, HttpMethod.DELETE, new HttpEntity<>(createHeaders(user, password)), String.class); - if (responseEntity.getBody() != null) { - return new JsonParser().parse(responseEntity.getBody()); - } else { - return null; - } + return getResponseEntityAsJsonElement(responseEntity); } @@ -103,4 +87,16 @@ public class HttpUtils { set("Authorization", authHeader); }}; } + + private JsonElement getResponseEntityAsJsonElement(ResponseEntity responseEntity) { + if (responseEntity != null && responseEntity.getBody() != null) { + logger.info(responseEntity.getBody()); + try { + return new JsonParser().parse(responseEntity.getBody()); + } catch (Exception e) { + logger.warn("Could not parse response body", e); + } + } + return null; + } }