diff --git a/.gitignore b/.gitignore index 410aba1b0d99a0e9e9997fe1f279461e8d09eb4f..793bc232224a8f08354552b90a87b14396b51fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -54,12 +54,10 @@ nbactions.xml ### VS Code ### .vscode/ -/gms-ui/target/ /gms/nbactions-release-profile.xml -/gms-client/gms-client-lib/target/ -/gms-client/gms-cli/target/ /gms/node/ +**/target/* nb-configuration.xml dependency-reduced-pom.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..eb81edda0645763bb29f8524ef811a54f0b4db3d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,42 @@ +stages: + - build + - test + - deploy + +build_gms_client: + stage: build + tags: + - docker + script: + - cd gms-client/gms-client + - mvn clean package -DskipTests -DfinalName=gms-client + artifacts: + paths: + - gms-client/gms-client/target/gms-client.jar + - gms-client/gms-client/pom.xml + expire_in: 7 days + only: + - master + +test_gms_client: + stage: test + tags: + - docker + script: + - cd gms-client/gms-client + - mvn clean test + only: + - master + +deploy_gms_client: + stage: deploy + tags: + - docker + script: + - mvn deploy:deploy-file + -Dfile=gms-client/gms-client/target/gms-client.jar + -DrepositoryId=ia2.snapshots + -DpomFile=gms-client/gms-client/pom.xml + -Durl=${IA2_MVN_REPO_SNAPSHOTS} + only: + - master diff --git a/gms-client/gms-cli/gms.properties b/gms-client/gms-cli/gms.properties index facdd476b33bf19050e1f17165c4de0bd35f01d6..b7f4928fcf75092b831c7005d049ef5a19aac333 100644 --- a/gms-client/gms-cli/gms.properties +++ b/gms-client/gms-cli/gms.properties @@ -1,2 +1,4 @@ -base_url=http://localhost:8082/gms/ws/jwt -token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjM0ZmU4MDcwMDVhNTcxMTYifQ.eyJpc3MiOiJzc28uaWEyLmluYWYuaXQiLCJzdWIiOiIyMzg2IiwiaWF0IjoxNTg3NjU5NzYxLCJleHAiOjE1ODc3NDYxNjEsImF1ZCI6ImdtcyJ9.KcXRAciG3ApqlE8MFM8VYW9WAX3hEZb7Vk8jB9uJtWsOMU48ha_Ybb4k_f0nrD2jhOxwaNn2QMxWZuflwCf1N-KiCj5Ff9f8xKOrrXZrl-w1H3_dwtMlIS8t2b0-w0WwRJ7UIhrwVBzmCcWinD3qJhFPzyO2pi-A4aXV57RpJ68VXfALQXeHK0sslrf-RgAU3xWYOgjGTUoGB5BQYC9huA_bZ0eV1HFcancs9pDdoTusqZs8OkPFCJbo7-L5eibsuykqnLHztYdCcP2Vtvtwb0pww-ofWZblIHzoMI8i-ipnfLJETG8Dpc7FrhjCYLw3AEGZg4U1wYTeqG3HRbPXSQ +gms_url=http://localhost:8082/gms/ws/jwt +client_id=gms_cli +client_secret=gms +rap_url=http://localhost/rap-ia2 diff --git a/gms-client/gms-cli/pom.xml b/gms-client/gms-cli/pom.xml index cfaad419eeaad50ae0942a437f3bffb821ce11ee..26e78d841a057a4814d77f1cde517c0a0c15f284 100644 --- a/gms-client/gms-cli/pom.xml +++ b/gms-client/gms-cli/pom.xml @@ -17,7 +17,7 @@ <dependencies> <dependency> <groupId>${project.groupId}</groupId> - <artifactId>gms-client-lib</artifactId> + <artifactId>gms-client</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> @@ -46,4 +46,10 @@ </plugin> </plugins> </build> + <repositories> + <repository> + <id>ia2.snapshot</id> + <url>http://repo.ia2.inaf.it/maven/repository/snapshots</url> + </repository> + </repositories> </project> diff --git a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java index 4d4ff78503c87ba822095512ea700b1e59fb01ba..71b413bee175693c0df3ccc602667e6106a26f5b 100644 --- a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java +++ b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java @@ -1,8 +1,10 @@ package it.inaf.ia2.gms.cli; +import it.inaf.ia2.client.ClientException; import it.inaf.ia2.gms.client.GmsClient; -import it.inaf.ia2.gms.client.GmsClientBuilder; import it.inaf.ia2.gms.client.model.Permission; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.AccessTokenResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -61,7 +63,11 @@ public class CLI { default: verifyConfigLoaded(); createClient(); - parseCommand(); + try { + parseCommand(); + } catch (ClientException ex) { + System.err.println(ex.getMessage()); + } commandParsed = true; break; } @@ -96,17 +102,17 @@ public class CLI { } private void createClient() { - GmsClientBuilder clientBuilder = new GmsClientBuilder() - .setGmsBaseUrl(gmsBaseUrl); + + client = new GmsClient(gmsBaseUrl); if (token != null) { - client = clientBuilder.build(); client.setAccessToken(token); } else { - client = clientBuilder.setClientId(clientId) - .setClientSecret(clientSecret) - .setRapBaseUrl(rapBaseUrl) - .build(); + RapClient rapClient = new RapClient(rapBaseUrl) + .setClientId(clientId) + .setClientSecret(clientSecret); + AccessTokenResponse accessTokenResponse = rapClient.getAccessTokenFromClientCredentials(); + client.setAccessToken(accessTokenResponse.getAccessToken()); } } @@ -118,7 +124,7 @@ public class CLI { } Properties properties = new Properties(); - try (InputStream in = new FileInputStream(config)) { + try ( InputStream in = new FileInputStream(config)) { properties.load(in); } catch (IOException ex) { throw new UncheckedIOException(ex); @@ -141,7 +147,7 @@ public class CLI { System.exit(1); } - try (InputStream in = new FileInputStream(tokenFile)) { + try ( InputStream in = new FileInputStream(tokenFile)) { java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\A"); token = s.next().trim(); } catch (IOException ex) { diff --git a/gms-client/gms-cli/src/main/resources/application.properties b/gms-client/gms-cli/src/main/resources/application.properties deleted file mode 100644 index 2a867fa5f89b8104fe8d51524d699a0d5c9b064f..0000000000000000000000000000000000000000 --- a/gms-client/gms-cli/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -spring.main.banner-mode=off -logging.level.root=OFF \ No newline at end of file diff --git a/gms-client/gms-client-lib/pom.xml b/gms-client/gms-client-lib/pom.xml deleted file mode 100644 index 571ed7f18cf704af8107649566019753a04996f9..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>it.inaf.ia2</groupId> - <artifactId>gms-client-lib</artifactId> - <version>1.0-SNAPSHOT</version> - <packaging>jar</packaging> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <maven.compiler.source>12</maven.compiler.source> - <maven.compiler.target>12</maven.compiler.target> - </properties> - <dependencies> - <dependency> - <groupId>org.apache.logging.log4j</groupId> - <artifactId>log4j-to-slf4j</artifactId> - <version>2.12.1</version> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>4.12</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <version>2.23.4</version> - <scope>test</scope> - </dependency> - </dependencies> -</project> \ No newline at end of file diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java deleted file mode 100644 index 8dcf0419ed854c95e77acb73fc553302bf2b24a6..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java +++ /dev/null @@ -1,91 +0,0 @@ -package it.inaf.ia2.gms.client; - -import it.inaf.ia2.gms.client.call.AddInvitedRegistrationCall; -import it.inaf.ia2.gms.client.call.HttpClientWrapper; -import it.inaf.ia2.gms.client.call.AddMemberCall; -import it.inaf.ia2.gms.client.call.AddPermissionCall; -import it.inaf.ia2.gms.client.call.CreateGroupCall; -import it.inaf.ia2.gms.client.call.DeleteGroupCall; -import it.inaf.ia2.gms.client.call.GetGroupPermissionsCall; -import it.inaf.ia2.gms.client.call.GetMemberEmailAddresses; -import it.inaf.ia2.gms.client.call.GetUserGroupsCall; -import it.inaf.ia2.gms.client.call.GetUserPermissionsCall; -import it.inaf.ia2.gms.client.call.ListGroupsCall; -import it.inaf.ia2.gms.client.call.RemoveMemberCall; -import it.inaf.ia2.gms.client.call.RemovePermissionCall; -import it.inaf.ia2.gms.client.call.SetPermissionCall; -import it.inaf.ia2.gms.client.model.GroupPermission; -import it.inaf.ia2.gms.client.model.Permission; -import it.inaf.ia2.gms.client.model.UserPermission; -import java.util.List; -import java.util.Map; - -public class GmsClient { - - private final HttpClientWrapper httpClientWrapper; - - GmsClient(HttpClientWrapper httpClientWrapper) { - this.httpClientWrapper = httpClientWrapper; - } - - public GmsClient setAccessToken(String accessToken) { - httpClientWrapper.setAccessToken(accessToken); - return this; - } - - public List<String> getMyGroups(String prefix) { - return new GetUserGroupsCall(httpClientWrapper).getUserGroups(prefix); - } - - public List<String> listGroups(String prefix) { - return new ListGroupsCall(httpClientWrapper).listGroups(prefix); - } - - public List<String> getUserGroups(String userId, String prefix) { - return new GetUserGroupsCall(httpClientWrapper).getUserGroups(userId, prefix); - } - - public void createGroup(String completeGroupName, boolean leaf) { - new CreateGroupCall(httpClientWrapper).createGroup(completeGroupName, leaf); - } - - public void deleteGroup(String completeGroupName) { - new DeleteGroupCall(httpClientWrapper).deleteGroup(completeGroupName); - } - - public void addMember(String completeGroupName, String userId) { - new AddMemberCall(httpClientWrapper).addMember(completeGroupName, userId); - } - - public void removeMember(String completeGroupName, String userId) { - new RemoveMemberCall(httpClientWrapper).removeMember(completeGroupName, userId); - } - - public void addPermission(String completeGroupName, String userId, Permission permission) { - new AddPermissionCall(httpClientWrapper).addPermission(completeGroupName, userId, permission); - } - - public void setPermission(String completeGroupName, String userId, Permission permission) { - new SetPermissionCall(httpClientWrapper).setPermission(completeGroupName, userId, permission); - } - - public void removePermission(String completeGroupName, String userId) { - new RemovePermissionCall(httpClientWrapper).removePermission(completeGroupName, userId); - } - - public List<UserPermission> getUserPermissions(String userId) { - return new GetUserPermissionsCall(httpClientWrapper).getUserPermissions(userId); - } - - public List<GroupPermission> getGroupPermissions(String groupId) { - return new GetGroupPermissionsCall(httpClientWrapper).getGroupPermissions(groupId); - } - - public void addInvitedRegistration(String token, String email, Map<String, Permission> groupsPermissions) { - new AddInvitedRegistrationCall(httpClientWrapper).addInvitedRegistration(token, email, groupsPermissions); - } - - public List<String> getMemberEmailAddresses(String groupId, Permission permission) { - return new GetMemberEmailAddresses(httpClientWrapper).getMemberEmailAddresses(groupId, permission); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClientBuilder.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClientBuilder.java deleted file mode 100644 index 002081d041f2f0912b9678c9a83144537463ab80..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClientBuilder.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.inaf.ia2.gms.client; - -import it.inaf.ia2.gms.client.call.HttpClientWrapper; - -public class GmsClientBuilder { - - private String gmsBaseUrl; - private String rapBaseUrl; - private String clientId; - private String clientSecret; - - public GmsClientBuilder setGmsBaseUrl(String gmsBaseUrl) { - this.gmsBaseUrl = gmsBaseUrl; - return this; - } - - public GmsClientBuilder setRapBaseUrl(String rapBaseUrl) { - this.rapBaseUrl = rapBaseUrl; - return this; - } - - public GmsClientBuilder setClientId(String clientId) { - this.clientId = clientId; - return this; - } - - public GmsClientBuilder setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - public GmsClient build() { - HttpClientWrapper clientWrapper = new HttpClientWrapper(gmsBaseUrl); - if (rapBaseUrl != null && clientId != null && clientSecret != null) { - clientWrapper.setRapBaseUrl(rapBaseUrl) - .setClientId(clientId).setClientSecret(clientSecret); - } - return new GmsClient(clientWrapper); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java deleted file mode 100644 index e76242d179518ab4c98e12864012875cf785e728..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java +++ /dev/null @@ -1,35 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.BodyPublishers; -import java.net.http.HttpResponse; - -public class AddMemberCall extends BaseGmsCall { - - public AddMemberCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean addMember(String completeGroupName, String userId) { - - String endpoint = "membership"; - if (completeGroupName != null && !completeGroupName.isBlank()) { - endpoint += "/" + completeGroupName; - } - - HttpRequest groupsRequest = newHttpRequest(endpoint) - .header("Accept", "text/plain") - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(BodyPublishers.ofString("user_id=" + userId)) - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to add member to group"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java deleted file mode 100644 index 42acad909f324d1c0ea255fca62c433c2b5c161a..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import it.inaf.ia2.gms.client.model.Permission; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.BodyPublisher; -import java.net.http.HttpRequest.BodyPublishers; -import java.net.http.HttpResponse; - -public class AddPermissionCall extends BaseGmsCall { - - public AddPermissionCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean addPermission(String completeGroupName, String userId, Permission permission) { - - String endpoint = "permission"; - if (completeGroupName != null && !completeGroupName.isBlank()) { - endpoint += "/" + completeGroupName; - } - - BodyPublisher requestBody = BodyPublishers.ofString( - "user_id=" + userId + "&permission=" + permission); - - HttpRequest groupsRequest = newHttpRequest(endpoint) - .header("Accept", "text/plain") - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(requestBody) - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to add permission"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java deleted file mode 100644 index f5304b7d1623eed3af43d2914b29fd11cee9b7d8..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java +++ /dev/null @@ -1,44 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.io.InputStream; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.Builder; -import java.net.http.HttpResponse; -import java.util.Scanner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class BaseGmsCall { - - private static final Logger LOGGER = LoggerFactory.getLogger(BaseGmsCall.class); - - protected final HttpClientWrapper clientWrapper; - - public BaseGmsCall(HttpClientWrapper clientWrapper) { - this.clientWrapper = clientWrapper; - } - - protected HttpClient getClient() { - return clientWrapper.getClient(); - } - - protected Builder newHttpRequest(String endpoint) { - return clientWrapper.newHttpRequest(endpoint); - } - - protected static void logServerError(HttpRequest request, HttpResponse<String> response) { - LOGGER.error("Error while reading " + request.uri() - + "\nServer response status code is " + response.statusCode() - + "\nServer response text is " + response.body()); - } - - protected static void logServerErrorInputStream(HttpRequest request, HttpResponse<InputStream> response) { - Scanner s = new Scanner(response.body()).useDelimiter("\\A"); - String responseBody = s.hasNext() ? s.next() : ""; - String error = "Error while reading " + request.uri() - + "\nServer response status code is " + response.statusCode() - + "\nServer response text is " + responseBody; - LOGGER.error(error); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java deleted file mode 100644 index 1cbb2e1f0a96f9d19524f3b89be7d7ba78701aa5..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.BodyPublishers; -import java.net.http.HttpResponse; - -public class CreateGroupCall extends BaseGmsCall { - - public CreateGroupCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean createGroup(String completeGroupName, boolean leaf) { - - HttpRequest groupsRequest = newHttpRequest(completeGroupName) - .header("Accept", "text/plain") - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(BodyPublishers.ofString("leaf=" + leaf)) - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 201) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to create group"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java deleted file mode 100644 index 56b7b9893ccbe30ba3612a8d5bdc9d3a99601075..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java +++ /dev/null @@ -1,28 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - -public class DeleteGroupCall extends BaseGmsCall { - - public DeleteGroupCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean deleteGroup(String completeGroupName) { - - HttpRequest groupsRequest = newHttpRequest(completeGroupName) - .header("Accept", "text/plain") - .DELETE() - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 204) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to delete group"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/HttpClientWrapper.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/HttpClientWrapper.java deleted file mode 100644 index 7d90d7276ee959ad5de2e65d17d7ebbc2716256a..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/HttpClientWrapper.java +++ /dev/null @@ -1,105 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.Builder; -import java.net.http.HttpResponse; -import java.util.Base64; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HttpClientWrapper { - - private final String baseGmsUri; - private final HttpClient client; - - private String rapBaseUrl; - private String clientId; - private String clientSecret; - - private String accessToken; - - public HttpClientWrapper(String baseGmsUri) { - String uri = baseGmsUri; - if (!uri.endsWith("/")) { - uri += "/"; - } - this.baseGmsUri = uri; - - this.client = HttpClient.newBuilder() - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); - } - - public HttpClientWrapper setAccessToken(String accessToken) { - this.accessToken = accessToken; - return this; - } - - public HttpClientWrapper setRapBaseUrl(String rapBaseUrl) { - if (!rapBaseUrl.endsWith("/")) { - rapBaseUrl += "/"; - } - this.rapBaseUrl = rapBaseUrl; - return this; - } - - public HttpClientWrapper setClientId(String clientId) { - this.clientId = clientId; - return this; - } - - public HttpClientWrapper setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - return this; - } - - Builder newHttpRequest(String endpoint) { - if (accessToken == null) { - accessToken = getAccessTokenFromClientCredentials(); - } - return HttpRequest.newBuilder() - .uri(URI.create(baseGmsUri + endpoint)) - .header("Authorization", "Bearer " + accessToken); - } - - private String getAccessTokenFromClientCredentials() { - if (rapBaseUrl == null || clientId == null || clientSecret == null) { - throw new IllegalStateException("Access token is null and client credentials are not configured"); - } - - String basicAuthHeader = clientId + ":" + clientSecret; - - HttpRequest tokenRequest = HttpRequest.newBuilder() - .uri(URI.create(rapBaseUrl + "auth/oauth2/token")) - .header("Authorization", "Basic " + Base64.getEncoder().encodeToString(basicAuthHeader.getBytes())) - .header("Accept", "application/json") - .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString("grant_type=client_credentials")) - .build(); - - return client.sendAsync(tokenRequest, HttpResponse.BodyHandlers.ofString()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return getAccessTokenFromResponse(response.body()); - } - BaseGmsCall.logServerError(tokenRequest, response); - - throw new IllegalStateException("Unable to retrieve access token"); - }).join(); - } - - protected String getAccessTokenFromResponse(String body) { - Pattern codePattern = Pattern.compile(".*\"access_token\":\\s*\"([^\"]+).*"); - Matcher matcher = codePattern.matcher(body); - if (matcher.find()) { - return matcher.group(1); - } - throw new IllegalStateException("Unable to extract access token from body"); - } - - HttpClient getClient() { - return client; - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java deleted file mode 100644 index cc5b01a3ed2e8ec9c4f70c6cd32f29383c8dc09e..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java +++ /dev/null @@ -1,34 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - -public class RemoveMemberCall extends BaseGmsCall { - - public RemoveMemberCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean removeMember(String completeGroupName, String userId) { - - String endpoint = "membership"; - if (completeGroupName != null && !completeGroupName.isBlank()) { - endpoint += "/" + completeGroupName; - } - endpoint += "?user_id=" + userId; - - HttpRequest groupsRequest = newHttpRequest(endpoint) - .header("Accept", "text/plain") - .DELETE() - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 204) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to remove member from group"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java deleted file mode 100644 index 7f17e76e96cc34875a6b0447e18afa181404f783..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java +++ /dev/null @@ -1,35 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - -public class RemovePermissionCall extends BaseGmsCall { - - public RemovePermissionCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean removePermission(String completeGroupName, String userId) { - - String endpoint = "permission"; - if (completeGroupName != null && !completeGroupName.isBlank()) { - endpoint += "/" + completeGroupName; - } - endpoint += "?user_id=" + userId; - - HttpRequest groupsRequest = newHttpRequest(endpoint) - .header("Accept", "text/plain") - .DELETE() - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 204) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to remove permission"); - }).join(); - } - -} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java deleted file mode 100644 index c7eee108a06374c035e701f2018dee78e8d45324..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java +++ /dev/null @@ -1,39 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import static it.inaf.ia2.gms.client.call.BaseGmsCall.logServerErrorInputStream; -import it.inaf.ia2.gms.client.model.Permission; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - -public class SetPermissionCall extends BaseGmsCall { - - public SetPermissionCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); - } - - public boolean setPermission(String completeGroupName, String userId, Permission permission) { - - String endpoint = "permission"; - if (completeGroupName != null && !completeGroupName.isBlank()) { - endpoint += "/" + completeGroupName; - } - - HttpRequest.BodyPublisher requestBody = HttpRequest.BodyPublishers.ofString( - "user_id=" + userId + "&permission=" + permission); - - HttpRequest groupsRequest = newHttpRequest(endpoint) - .header("Accept", "text/plain") - .header("Content-Type", "application/x-www-form-urlencoded") - .PUT(requestBody) - .build(); - - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to set permission"); - }).join(); - } -} diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java deleted file mode 100644 index ce84caa0998ffded7ad83ec2cdf9f1fc1a4344b4..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java +++ /dev/null @@ -1,255 +0,0 @@ -package it.inaf.ia2.gms.client; - -import it.inaf.ia2.gms.client.call.HttpClientWrapper; -import it.inaf.ia2.gms.client.call.MockedHttpClientWrapper; -import it.inaf.ia2.gms.client.model.Permission; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodySubscriber; -import java.net.http.HttpResponse.BodySubscribers; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Flow; -import static org.junit.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.AdditionalMatchers; -import org.mockito.ArgumentMatcher; -import org.mockito.ArgumentMatchers; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class GmsClientTest { - - private static final String BASE_URL = "http://base-url"; - - private HttpClient httpClient; - private GmsClient client; - - @Before - public void setUp() { - - httpClient = mock(HttpClient.class); - - HttpClientWrapper clientWrapper = new MockedHttpClientWrapper(BASE_URL, httpClient); - clientWrapper.setAccessToken("foo"); - - client = new GmsClient(clientWrapper); - } - - @Test - public void testGetMyGroups() { - - String body = "LBT.INAF\n" - + "LBT.AZ"; - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200, body)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - List<String> groups = client.getMyGroups("LBT."); - - verify(httpClient, times(1)).sendAsync(endpointEq("GET", "search"), any()); - - assertEquals(2, groups.size()); - assertEquals("INAF", groups.get(0)); - assertEquals("AZ", groups.get(1)); - } - - @Test - public void testListGroups() { - - String body = "INAF\n" - + "AZ"; - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200, body)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - List<String> groups = client.listGroups("LBT."); - - verify(httpClient, times(1)).sendAsync(endpointEq("GET", "list/LBT."), any()); - - assertEquals(2, groups.size()); - assertEquals("INAF", groups.get(0)); - assertEquals("AZ", groups.get(1)); - } - - @Test - public void testCreateGroup() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.createGroup("LBT.INAF", false); - - verify(httpClient, times(1)).sendAsync(endpointEq("POST", "LBT.INAF"), any()); - } - - @Test - public void testDeleteGroup() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.deleteGroup("LBT.INAF"); - - verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "LBT.INAF"), any()); - } - - @Test - public void testAddMember() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.addMember("LBT.INAF", "user"); - - verify(httpClient, times(1)).sendAsync(endpointEq("POST", "membership/LBT.INAF"), any()); - } - - @Test - public void testRemoveMember() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.removeMember("LBT.INAF", "user"); - - verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "membership/LBT.INAF?user_id=user"), any()); - } - - @Test - public void testAddPermission() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.addPermission("LBT.INAF", "user", Permission.ADMIN); - - verify(httpClient, times(1)).sendAsync(endpointEq("POST", "permission/LBT.INAF"), any()); - } - - @Test - public void testRemovePermission() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - client.removePermission("LBT.INAF", "user"); - - verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "permission/LBT.INAF?user_id=user"), any()); - } - - @Test - public void testInvitedRegistration() { - - CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201)); - - when(httpClient.sendAsync(any(), any())).thenReturn(response); - Map<String, Permission> permissionsMap = new HashMap<>(); - permissionsMap.put("group1", Permission.MANAGE_MEMBERS); - permissionsMap.put("group2", Permission.MANAGE_MEMBERS); - client.addInvitedRegistration("bvjsgqu423", "email", permissionsMap); - // hash = AOyojiwaRR7BHPde6Tomg3+BMoQQggNM3wUHEarXuNQ= - - verify(httpClient, times(1)).sendAsync( - AdditionalMatchers.and( - endpointEq("POST", "invited-registration"), - ArgumentMatchers.argThat(req -> { - String reqbody = req.bodyPublisher().map(p -> { - var bodySubscriber = BodySubscribers.ofString(StandardCharsets.UTF_8); - var flowSubscriber = new StringSubscriber(bodySubscriber); - p.subscribe(flowSubscriber); - return bodySubscriber.getBody().toCompletableFuture().join(); - }).get(); - - // If the resulting hash contains a + symbol it has to be encoded to %2B, - // otherwise it will be interpreted as a space and wrong value will be - // stored into the database - String expectedBody = "token_hash=AOyojiwaRR7BHPde6Tomg3%2BBMoQQggNM3wUHEarXuNQ=" - + "&email=email&groups=group2 MANAGE_MEMBERS\n" - + "group1 MANAGE_MEMBERS"; - - return reqbody.equals(expectedBody); - })), any()); - } - - /** - * Credit: https://stackoverflow.com/a/55816685/771431 - */ - static final class StringSubscriber implements Flow.Subscriber<ByteBuffer> { - - final BodySubscriber<String> wrapped; - - StringSubscriber(BodySubscriber<String> wrapped) { - this.wrapped = wrapped; - } - - @Override - public void onSubscribe(Flow.Subscription subscription) { - wrapped.onSubscribe(subscription); - } - - @Override - public void onNext(ByteBuffer item) { - wrapped.onNext(List.of(item)); - } - - @Override - public void onError(Throwable throwable) { - wrapped.onError(throwable); - } - - @Override - public void onComplete() { - wrapped.onComplete(); - } - } - - private HttpResponse getMockedResponse(int statusCode, String body) { - HttpResponse response = getMockedResponse(statusCode); - InputStream in = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); - when(response.body()).thenReturn(in); - return response; - } - - private HttpResponse getMockedResponse(int statusCode) { - HttpResponse response = mock(HttpResponse.class); - when(response.statusCode()).thenReturn(statusCode); - return response; - } - - private HttpRequest endpointEq(String expectedMethod, String expectedEndpoint) { - return ArgumentMatchers.argThat(endpointEqArgumentMatcher(expectedMethod, expectedEndpoint)); - } - - private ArgumentMatcher<HttpRequest> endpointEqArgumentMatcher(String expectedMethod, String expectedEndpoint) { - - return new ArgumentMatcher<HttpRequest>() { - - private final String expectedUri = BASE_URL + "/" + expectedEndpoint; - - @Override - public boolean matches(HttpRequest request) { - return expectedMethod.equals(request.method()) && expectedUri.equals(request.uri().toString()); - } - - @Override - public String toString() { - return expectedMethod + " " + expectedUri; - } - }; - } -} diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/HttpClientWrapperTest.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/HttpClientWrapperTest.java deleted file mode 100644 index 4bc09244ebcbd82e1407ba97a64ef2a21a7e2a5c..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/HttpClientWrapperTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import static org.junit.Assert.assertEquals; -import org.junit.Test; - -public class HttpClientWrapperTest { - - @Test - public void testExtractAccessToken() { - String response = "{\"access_token\":\"TEST_TOKEN\",\"token_type\":\"Bearer\",\"expires_in\":3600}"; - - HttpClientWrapper clientWrapper = new HttpClientWrapper("http://localhost"); - assertEquals("TEST_TOKEN", clientWrapper.getAccessTokenFromResponse(response)); - } -} diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/MockedHttpClientWrapper.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/MockedHttpClientWrapper.java deleted file mode 100644 index d287f9039f03d5c1c156ff2b4f91362c09a5dedc..0000000000000000000000000000000000000000 --- a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/call/MockedHttpClientWrapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package it.inaf.ia2.gms.client.call; - -import java.net.http.HttpClient; - -public class MockedHttpClientWrapper extends HttpClientWrapper { - - private final HttpClient mockedClient; - - public MockedHttpClientWrapper(String baseGmsUri, HttpClient mockedClient) { - super(baseGmsUri); - this.mockedClient = mockedClient; - } - - @Override - HttpClient getClient() { - return mockedClient; - } -} diff --git a/gms-client/gms-client/pom.xml b/gms-client/gms-client/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0fb3618a910376617cf3bf90c7a6fda0c5355f76 --- /dev/null +++ b/gms-client/gms-client/pom.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>it.inaf.ia2</groupId> + <artifactId>gms-client</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.source>14</maven.compiler.source> + <maven.compiler.target>14</maven.compiler.target> + <junit-jupiter.version>5.6.0</junit-jupiter.version> + <mockito.version>3.5.13</mockito.version> + <finalName>${project.artifactId}-${project.version}</finalName> + </properties> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>rap-client</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit-jupiter.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-inline</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <finalName>${finalName}</finalName> + <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.6</version> + <executions> + <execution> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.22.2</version> + </plugin> + </plugins> + </build> + <repositories> + <repository> + <id>ia2.snapshot</id> + <url>http://repo.ia2.inaf.it/maven/repository/snapshots</url> + </repository> + </repositories> +</project> \ No newline at end of file diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/GmsClient.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/GmsClient.java new file mode 100644 index 0000000000000000000000000000000000000000..304df22c334f1d71a1cdc0225e27e69db1f1cbdb --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/GmsClient.java @@ -0,0 +1,160 @@ +package it.inaf.ia2.gms.client; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.inaf.ia2.client.BaseClient; +import it.inaf.ia2.gms.client.call.AddInvitedRegistrationCall; +import it.inaf.ia2.gms.client.call.AddMemberCall; +import it.inaf.ia2.gms.client.call.AddPermissionCall; +import it.inaf.ia2.gms.client.call.CreateGroupCall; +import it.inaf.ia2.gms.client.call.DeleteGroupCall; +import it.inaf.ia2.gms.client.call.GetGroupPermissionsCall; +import it.inaf.ia2.gms.client.call.GetGroupStatusCall; +import it.inaf.ia2.gms.client.call.GetMemberEmailAddresses; +import it.inaf.ia2.gms.client.call.GetUserGroupsCall; +import it.inaf.ia2.gms.client.call.GetUserPermissionsCall; +import it.inaf.ia2.gms.client.call.ListGroupsCall; +import it.inaf.ia2.gms.client.call.RemoveMemberCall; +import it.inaf.ia2.gms.client.call.RemovePermissionCall; +import it.inaf.ia2.gms.client.call.SetPermissionCall; +import it.inaf.ia2.gms.client.model.GroupPermission; +import it.inaf.ia2.gms.client.model.Permission; +import it.inaf.ia2.gms.client.model.UserPermission; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +public class GmsClient extends BaseClient { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private String accessToken; + + public GmsClient(String gmsBaseUri) { + super(gmsBaseUri); + } + + public GmsClient setAccessToken(String accessToken) { + this.accessToken = accessToken; + return this; + } + + public List<String> getMyGroups(String prefix) { + return new GetUserGroupsCall(this).getUserGroups(prefix); + } + + @Override + public HttpRequest.Builder newRequest(String endpoint) { + if (accessToken == null) { + throw new IllegalStateException("Access token is null"); + } + return super.newRequest(getUri(endpoint)) + .setHeader("Authorization", "Bearer " + accessToken); + } + + @Override + public HttpRequest.Builder newRequest(URI uri) { + if (accessToken == null) { + throw new IllegalStateException("Access token is null"); + } + return super.newRequest(uri) + .setHeader("Authorization", "Bearer " + accessToken); + } + + @Override + protected <T> String getInvalidStatusCodeExceptionMessage(HttpRequest request, HttpResponse<T> response) { + return response.headers().firstValue("Content-Type") + .map(contentType -> { + try { + if (contentType.startsWith("text/plain")) { + String errorResponseBody = null; + if (response.body() instanceof String) { + errorResponseBody = (String) response.body(); + } else if (response.body() instanceof InputStream) { + errorResponseBody = new String(((InputStream) response.body()).readAllBytes(), StandardCharsets.UTF_8); + } + if (errorResponseBody != null && !errorResponseBody.isBlank()) { + return errorResponseBody; + } + } else if (contentType.startsWith("application/json") + || contentType.startsWith("text/json")) { + Map<String, Object> errorResponseBody = null; + if (response.body() instanceof String) { + errorResponseBody = MAPPER.readValue((String) response.body(), Map.class); + } else if (response.body() instanceof InputStream) { + errorResponseBody = MAPPER.readValue((InputStream) response.body(), Map.class); + } + if (errorResponseBody != null && errorResponseBody.containsKey("error")) { + return (String) errorResponseBody.get("error"); + } + } + } catch (IOException ex) { + } + return null; + }).orElse(super.getInvalidStatusCodeExceptionMessage(request, response)); + } + + public List<String> listGroups(String prefix) { + return new ListGroupsCall(this).listGroups(prefix); + } + + public List<String> getUserGroups(String userId) { + return new GetUserGroupsCall(this).getUserGroups(userId); + } + + public List<String> getUserGroups(String userId, String prefix) { + return new GetUserGroupsCall(this).getUserGroups(userId, prefix); + } + + public void createGroup(String completeGroupName, boolean leaf) { + new CreateGroupCall(this).createGroup(completeGroupName, leaf); + } + + public void deleteGroup(String completeGroupName) { + new DeleteGroupCall(this).deleteGroup(completeGroupName); + } + + public void addMember(String completeGroupName, String userId) { + new AddMemberCall(this).addMember(completeGroupName, userId); + } + + public void removeMember(String completeGroupName, String userId) { + new RemoveMemberCall(this).removeMember(completeGroupName, userId); + } + + public String addPermission(String completeGroupName, String userId, Permission permission) { + return new AddPermissionCall(this).addPermission(completeGroupName, userId, permission); + } + + public String setPermission(String completeGroupName, String userId, Permission permission) { + return new SetPermissionCall(this).setPermission(completeGroupName, userId, permission); + } + + public void removePermission(String completeGroupName, String userId) { + new RemovePermissionCall(this).removePermission(completeGroupName, userId); + } + + public List<UserPermission> getUserPermissions(String userId) { + return new GetUserPermissionsCall(this).getUserPermissions(userId); + } + + public List<GroupPermission> getGroupPermissions(String groupId) { + return new GetGroupPermissionsCall(this).getGroupPermissions(groupId); + } + + public void addInvitedRegistration(String token, String email, Map<String, Permission> groupsPermissions) { + new AddInvitedRegistrationCall(this).addInvitedRegistration(token, email, groupsPermissions); + } + + public List<String> getMemberEmailAddresses(String groupId, Permission permission) { + return new GetMemberEmailAddresses(this).getMemberEmailAddresses(groupId, permission); + } + + public List<String[]> getStatus(String groupCompleteName) { + return new GetGroupStatusCall(this).getStatus(groupCompleteName); + } +} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java similarity index 66% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java index d0a1ad4ce706082acb6fc4ead041c149421370b3..533eea8f185b6ef4fa63954f1264b08dc9d0b73b 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddInvitedRegistrationCall.java @@ -1,8 +1,11 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import it.inaf.ia2.gms.client.model.Permission; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -10,10 +13,10 @@ import java.util.Base64; import java.util.Map; import java.util.stream.Collectors; -public class AddInvitedRegistrationCall extends BaseGmsCall { +public class AddInvitedRegistrationCall extends BaseCall<GmsClient> { - public AddInvitedRegistrationCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public AddInvitedRegistrationCall(GmsClient client) { + super(client); } public void addInvitedRegistration(String token, String email, Map<String, Permission> groupsPermissions) { @@ -29,20 +32,13 @@ public class AddInvitedRegistrationCall extends BaseGmsCall { .stream().map(e -> e.getKey() + " " + e.getValue()) .collect(Collectors.toList())); - HttpRequest groupsRequest = newHttpRequest(endpoint) + HttpRequest groupsRequest = client.newRequest(endpoint) .header("Accept", "text/plain") .header("Content-Type", "application/x-www-form-urlencoded") - .POST(HttpRequest.BodyPublishers.ofString(bodyParams)) + .POST(BodyPublishers.ofString(bodyParams)) .build(); - getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 201) { - return true; - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to create invited registration"); - }).join(); + client.call(groupsRequest, BodyHandlers.ofInputStream(), 201, res -> true); } private String getTokenHash(String token) { diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java new file mode 100644 index 0000000000000000000000000000000000000000..70bb477eb621127bacf3f24140c33a757da671c5 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddMemberCall.java @@ -0,0 +1,30 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; + +public class AddMemberCall extends BaseCall<GmsClient> { + + public AddMemberCall(GmsClient client) { + super(client); + } + + public boolean addMember(String completeGroupName, String userId) { + + String endpoint = "ws/jwt/membership"; + if (completeGroupName != null && !completeGroupName.isBlank()) { + endpoint += "/" + completeGroupName; + } + + HttpRequest groupsRequest = client.newRequest(endpoint) + .header("Accept", "text/plain") + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(BodyPublishers.ofString("user_id=" + userId)) + .build(); + + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, res -> true); + } +} diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java new file mode 100644 index 0000000000000000000000000000000000000000..ace30eccf540a97fb56e84fdee2dca5eea1d21a6 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/AddPermissionCall.java @@ -0,0 +1,35 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import it.inaf.ia2.gms.client.model.Permission; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; + +public class AddPermissionCall extends BaseCall<GmsClient> { + + public AddPermissionCall(GmsClient client) { + super(client); + } + + public String addPermission(String completeGroupName, String userId, Permission permission) { + + String endpoint = "ws/jwt/permission"; + if (completeGroupName != null && !completeGroupName.isBlank()) { + endpoint += "/" + completeGroupName; + } + + BodyPublisher requestBody = BodyPublishers.ofString( + "user_id=" + userId + "&permission=" + permission); + + HttpRequest groupsRequest = client.newRequest(endpoint) + .header("Accept", "text/plain") + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(requestBody) + .build(); + + return client.call(groupsRequest, BodyHandlers.ofString(), 200, res -> res); + } +} diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java new file mode 100644 index 0000000000000000000000000000000000000000..edb5158ce23c9027e23f0dea538a61f4a39d8026 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/CreateGroupCall.java @@ -0,0 +1,25 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; + +public class CreateGroupCall extends BaseCall<GmsClient> { + + public CreateGroupCall(GmsClient client) { + super(client); + } + + public boolean createGroup(String completeGroupName, boolean leaf) { + + HttpRequest groupsRequest = client.newRequest("ws/jwt/" + completeGroupName) + .header("Accept", "text/plain") + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(BodyPublishers.ofString("leaf=" + leaf)) + .build(); + + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 201, res -> true); + } +} diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java new file mode 100644 index 0000000000000000000000000000000000000000..bbde41dc7dfe37f830a3228a13a641b278f5dc23 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/DeleteGroupCall.java @@ -0,0 +1,23 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; + +public class DeleteGroupCall extends BaseCall<GmsClient> { + + public DeleteGroupCall(GmsClient client) { + super(client); + } + + public boolean deleteGroup(String completeGroupName) { + + HttpRequest groupsRequest = client.newRequest("ws/jwt/" + completeGroupName) + .header("Accept", "text/plain") + .DELETE() + .build(); + + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 204, res -> true); + } +} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java similarity index 58% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java index 6c0b99635b606af4b4bfd1d7a5a4445398718e8a..5e782d12c0d473c475e0522626fab8cc312da2da 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupPermissionsCall.java @@ -1,41 +1,36 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import it.inaf.ia2.gms.client.model.GroupPermission; import it.inaf.ia2.gms.client.model.Permission; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -public class GetGroupPermissionsCall extends BaseGmsCall { +public class GetGroupPermissionsCall extends BaseCall<GmsClient> { - public GetGroupPermissionsCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public GetGroupPermissionsCall(GmsClient client) { + super(client); } public List<GroupPermission> getGroupPermissions(String groupId) { List<GroupPermission> groupPermissions = new ArrayList<>(); - String endpoint = "permission"; + String endpoint = "ws/jwt/permission"; endpoint += "/" + groupId; - HttpRequest groupsRequest = newHttpRequest(endpoint) + HttpRequest groupsRequest = client.newRequest(endpoint) .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to retrieve groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -48,6 +43,6 @@ public class GetGroupPermissionsCall extends BaseGmsCall { } } return groupPermissions; - }).join(); + }); } } diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupStatusCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupStatusCall.java new file mode 100644 index 0000000000000000000000000000000000000000..e81ea2dc5c2cdbe411d93b13142d7cb75d884654 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetGroupStatusCall.java @@ -0,0 +1,27 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; + +public class GetGroupStatusCall extends BaseCall<GmsClient> { + + public GetGroupStatusCall(GmsClient client) { + super(client); + } + + public List<String[]> getStatus(String groupCompleteName) { + + String uri = "group/status?groupName=" + groupCompleteName; + + HttpRequest request = client.newRequest(uri) + .header("Accept", "application/json") + .GET() + .build(); + + return client.call(request, HttpResponse.BodyHandlers.ofInputStream(), 200, + in -> parseJsonList(in, String[].class)); + } +} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java similarity index 53% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java index a69913beda35fad563f60363cc3dc7759572297f..c240bcc725ca64fe750bd11722de3065daa6965e 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetMemberEmailAddresses.java @@ -1,16 +1,18 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import it.inaf.ia2.gms.client.model.Permission; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -public class GetMemberEmailAddresses extends BaseGmsCall { +public class GetMemberEmailAddresses extends BaseCall<GmsClient> { - public GetMemberEmailAddresses(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public GetMemberEmailAddresses(GmsClient client) { + super(client); } public List<String> getMemberEmailAddresses(String group, Permission permission) { @@ -22,21 +24,14 @@ public class GetMemberEmailAddresses extends BaseGmsCall { endpoint += "?permission=" + permission; } - HttpRequest request = newHttpRequest(endpoint) + HttpRequest request = client.newRequest(endpoint) .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(request, response); - throw new IllegalStateException("Unable to retrieve groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(request, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -45,6 +40,6 @@ public class GetMemberEmailAddresses extends BaseGmsCall { } } return emailAddresses; - }).join(); + }); } } diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java similarity index 56% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java index 57ab0ffe8b6ba2e1d88e6ca10ab255a59c05a802..a6a4b66438be803b9c0f4b49f5208f427e21bce4 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserGroupsCall.java @@ -1,15 +1,17 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -public class GetUserGroupsCall extends BaseGmsCall { +public class GetUserGroupsCall extends BaseCall<GmsClient> { - public GetUserGroupsCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public GetUserGroupsCall(GmsClient client) { + super(client); } /** @@ -20,21 +22,14 @@ public class GetUserGroupsCall extends BaseGmsCall { List<String> groups = new ArrayList<>(); - HttpRequest groupsRequest = newHttpRequest("search") + HttpRequest groupsRequest = client.newRequest("vo/search") .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to retrieve groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -50,34 +45,27 @@ public class GetUserGroupsCall extends BaseGmsCall { } } return groups; - }).join(); + }); } public List<String> getUserGroups(String userId, String prefix) { List<String> groups = new ArrayList<>(); - String endpoint = "membership"; + String endpoint = "ws/jwt/membership"; if (prefix != null && !prefix.isBlank()) { endpoint += "/" + prefix; } endpoint += "?user_id=" + userId; - HttpRequest groupsRequest = newHttpRequest(endpoint) + HttpRequest groupsRequest = client.newRequest(endpoint) .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to retrieve groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -86,6 +74,6 @@ public class GetUserGroupsCall extends BaseGmsCall { } } return groups; - }).join(); + }); } } diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java similarity index 58% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java index 34db2f11f5796ac57e7da7e11c0d2d5bd9bfaf2d..8f4e9ed7916d1af0a752321a9fb4797a70181b85 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/GetUserPermissionsCall.java @@ -1,41 +1,36 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import it.inaf.ia2.gms.client.model.Permission; import it.inaf.ia2.gms.client.model.UserPermission; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -public class GetUserPermissionsCall extends BaseGmsCall { +public class GetUserPermissionsCall extends BaseCall<GmsClient> { - public GetUserPermissionsCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public GetUserPermissionsCall(GmsClient client) { + super(client); } public List<UserPermission> getUserPermissions(String userId) { List<UserPermission> userPermissions = new ArrayList<>(); - String endpoint = "permission"; + String endpoint = "ws/jwt/permission"; endpoint += "?user_id=" + userId; - HttpRequest groupsRequest = newHttpRequest(endpoint) + HttpRequest groupsRequest = client.newRequest(endpoint) .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to retrieve groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -48,6 +43,6 @@ public class GetUserPermissionsCall extends BaseGmsCall { } } return userPermissions; - }).join(); + }); } } diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java similarity index 54% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java index 7170ebaac74285e8be472d82762e08888b3e40ca..2c3123ef896e211fe76c41fec92224d5030620c6 100644 --- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/ListGroupsCall.java @@ -1,15 +1,17 @@ package it.inaf.ia2.gms.client.call; +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -public class ListGroupsCall extends BaseGmsCall { +public class ListGroupsCall extends BaseCall<GmsClient> { - public ListGroupsCall(HttpClientWrapper clientWrapper) { - super(clientWrapper); + public ListGroupsCall(GmsClient client) { + super(client); } /** @@ -21,26 +23,19 @@ public class ListGroupsCall extends BaseGmsCall { List<String> groups = new ArrayList<>(); - String uri = "list"; + String uri = "ws/jwt/list"; if (prefix != null && !prefix.isBlank()) { uri += "/" + prefix; } - HttpRequest groupsRequest = newHttpRequest(uri) + HttpRequest groupsRequest = client.newRequest(uri) .header("Accept", "text/plain") .GET() .build(); - return getClient().sendAsync(groupsRequest, HttpResponse.BodyHandlers.ofInputStream()) - .thenApply(response -> { - if (response.statusCode() == 200) { - return response.body(); - } - logServerErrorInputStream(groupsRequest, response); - throw new IllegalStateException("Unable to list groups"); - }) - .thenApply(inputStream -> { - try (Scanner scan = new Scanner(inputStream)) { + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 200, + inputStream -> { + try ( Scanner scan = new Scanner(inputStream)) { while (scan.hasNextLine()) { String line = scan.nextLine(); if (!line.isEmpty()) { @@ -49,6 +44,6 @@ public class ListGroupsCall extends BaseGmsCall { } } return groups; - }).join(); + }); } } diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java new file mode 100644 index 0000000000000000000000000000000000000000..7daf15ecd214c59a820b8ad03ed071216b01b385 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemoveMemberCall.java @@ -0,0 +1,29 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; + +public class RemoveMemberCall extends BaseCall<GmsClient> { + + public RemoveMemberCall(GmsClient client) { + super(client); + } + + public boolean removeMember(String completeGroupName, String userId) { + + String endpoint = "ws/jwt/membership"; + if (completeGroupName != null && !completeGroupName.isBlank()) { + endpoint += "/" + completeGroupName; + } + endpoint += "?user_id=" + userId; + + HttpRequest groupsRequest = client.newRequest(endpoint) + .header("Accept", "text/plain") + .DELETE() + .build(); + + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 204, res -> true); + } +} diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java new file mode 100644 index 0000000000000000000000000000000000000000..3602a551cc25a27f31a971a3876b0e108064eec0 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/RemovePermissionCall.java @@ -0,0 +1,29 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; + +public class RemovePermissionCall extends BaseCall<GmsClient> { + + public RemovePermissionCall(GmsClient client) { + super(client); + } + + public boolean removePermission(String completeGroupName, String userId) { + + String endpoint = "ws/jwt/permission"; + if (completeGroupName != null && !completeGroupName.isBlank()) { + endpoint += "/" + completeGroupName; + } + endpoint += "?user_id=" + userId; + + HttpRequest groupsRequest = client.newRequest(endpoint) + .header("Accept", "text/plain") + .DELETE() + .build(); + + return client.call(groupsRequest, BodyHandlers.ofInputStream(), 204, res -> true); + } +} diff --git a/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java new file mode 100644 index 0000000000000000000000000000000000000000..97ceb21c0b40fd67ab698ae1bff321a4a060f790 --- /dev/null +++ b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/call/SetPermissionCall.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.client.BaseCall; +import it.inaf.ia2.gms.client.GmsClient; +import it.inaf.ia2.gms.client.model.Permission; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse.BodyHandlers; + +public class SetPermissionCall extends BaseCall<GmsClient> { + + public SetPermissionCall(GmsClient client) { + super(client); + } + + public String setPermission(String completeGroupName, String userId, Permission permission) { + + String endpoint = "ws/jwt/permission"; + if (completeGroupName != null && !completeGroupName.isBlank()) { + endpoint += "/" + completeGroupName; + } + + HttpRequest.BodyPublisher requestBody = HttpRequest.BodyPublishers.ofString( + "user_id=" + userId + "&permission=" + permission); + + HttpRequest groupsRequest = client.newRequest(endpoint) + .header("Accept", "text/plain") + .header("Content-Type", "application/x-www-form-urlencoded") + .PUT(requestBody) + .build(); + + return client.call(groupsRequest, BodyHandlers.ofString(), 200, res -> res); + } +} diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/GroupPermission.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/GroupPermission.java similarity index 100% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/GroupPermission.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/GroupPermission.java diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/Permission.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/Permission.java similarity index 100% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/Permission.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/Permission.java diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/UserPermission.java b/gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/UserPermission.java similarity index 100% rename from gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/model/UserPermission.java rename to gms-client/gms-client/src/main/java/it/inaf/ia2/gms/client/model/UserPermission.java diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/BaseGmsClientTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/BaseGmsClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..69e22fedbd40f7ec99add1b8cb0e989157d7e15a --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/BaseGmsClientTest.java @@ -0,0 +1,101 @@ +package it.inaf.ia2.gms.client; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import org.mockito.ArgumentMatcher; +import org.mockito.ArgumentMatchers; +import static org.mockito.ArgumentMatchers.any; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BaseGmsClientTest { + + private static final String BASE_URL = "http://base-url"; + + protected HttpClient httpClient; + protected GmsClient gmsClient; + + public void init() { + httpClient = mock(HttpClient.class); + gmsClient = getMockedGmsClient(httpClient); + } + + protected static String getResourceFileContent(String fileName) { + try ( InputStream in = BaseGmsClientTest.class.getClassLoader().getResourceAsStream(fileName)) { + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + protected static GmsClient getMockedGmsClient(HttpClient mockedHttpClient) { + + HttpClient.Builder builder = mock(HttpClient.Builder.class); + when(builder.followRedirects(any())).thenReturn(builder); + when(builder.version(any())).thenReturn(builder); + when(builder.build()).thenReturn(mockedHttpClient); + + try ( MockedStatic<HttpClient> staticMock = Mockito.mockStatic(HttpClient.class)) { + staticMock.when(HttpClient::newBuilder).thenReturn(builder); + return new GmsClient(BASE_URL).setAccessToken("foo"); + } + } + + protected static CompletableFuture<HttpResponse<InputStream>> getMockedStreamResponseFuture(int statusCode, String body) { + return CompletableFuture.completedFuture(getMockedStreamResponse(200, body)); + } + + protected static CompletableFuture<HttpResponse<String>> getMockedStringResponseFuture(int statusCode, String body) { + return CompletableFuture.completedFuture(getMockedStringResponse(200, body)); + } + + protected static HttpResponse<InputStream> getMockedStreamResponse(int statusCode, String body) { + HttpResponse response = getMockedResponse(statusCode); + InputStream in = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); + when(response.body()).thenReturn(in); + return response; + } + + protected static HttpResponse<String> getMockedStringResponse(int statusCode, String body) { + HttpResponse response = getMockedResponse(statusCode); + when(response.body()).thenReturn(body); + return response; + } + + protected static HttpResponse getMockedResponse(int statusCode) { + HttpResponse response = mock(HttpResponse.class); + when(response.statusCode()).thenReturn(statusCode); + return response; + } + + protected static HttpRequest endpointEq(String expectedMethod, String expectedEndpoint) { + return ArgumentMatchers.argThat(endpointEqArgumentMatcher(expectedMethod, expectedEndpoint)); + } + + protected static ArgumentMatcher<HttpRequest> endpointEqArgumentMatcher(String expectedMethod, String expectedEndpoint) { + + return new ArgumentMatcher<HttpRequest>() { + + private final String expectedUri = BASE_URL + "/" + expectedEndpoint; + + @Override + public boolean matches(HttpRequest request) { + return expectedMethod.equals(request.method()) && expectedUri.equals(request.uri().toString()); + } + + @Override + public String toString() { + return expectedMethod + " " + expectedUri; + } + }; + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddMemberTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddMemberTest.java new file mode 100644 index 0000000000000000000000000000000000000000..268b2f0f957e5908584de13e9b437984b377a8ee --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddMemberTest.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AddMemberTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testAddMember() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.addMember("LBT.INAF", "user"); + + verify(httpClient, times(1)).sendAsync(endpointEq("POST", "ws/jwt/membership/LBT.INAF"), any()); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddPermissionTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddPermissionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4c68372d3e8ebdb3e4c62c2ba791145f3556c450 --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/AddPermissionTest.java @@ -0,0 +1,34 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import it.inaf.ia2.gms.client.model.Permission; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AddPermissionTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testAddPermission() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(200)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.addPermission("LBT.INAF", "user", Permission.ADMIN); + + verify(httpClient, times(1)).sendAsync(endpointEq("POST", "ws/jwt/permission/LBT.INAF"), any()); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/CreateGroupTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/CreateGroupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8c62a3fc3e52fe29fb60a77ddb16f243161e0548 --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/CreateGroupTest.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class CreateGroupTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testCreateGroup() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.createGroup("LBT.INAF", false); + + verify(httpClient, times(1)).sendAsync(endpointEq("POST", "ws/jwt/LBT.INAF"), any()); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/DeleteGroupTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/DeleteGroupTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e5b1161ba230b8c11170ec0b9eb16a4e9cecdb0b --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/DeleteGroupTest.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class DeleteGroupTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testDeleteGroup() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.deleteGroup("LBT.INAF"); + + verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "ws/jwt/LBT.INAF"), any()); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/GetUserGroupsTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/GetUserGroupsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3b2dee689ffc556752cb0af3027803482f97d130 --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/GetUserGroupsTest.java @@ -0,0 +1,61 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class GetUserGroupsTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testGetMyGroups() { + + String body = "LBT.INAF\n" + + "LBT.AZ\n" + + ""; + + CompletableFuture response = CompletableFuture.completedFuture(getMockedStreamResponse(200, body)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + List<String> groups = gmsClient.getMyGroups("LBT."); + + verify(httpClient, times(1)).sendAsync(endpointEq("GET", "vo/search"), any()); + + assertEquals(2, groups.size()); + assertEquals("INAF", groups.get(0)); + assertEquals("AZ", groups.get(1)); + } + + @Test + public void testListGroups() { + + String body = "INAF\n" + + "AZ"; + + CompletableFuture response = CompletableFuture.completedFuture(getMockedStreamResponse(200, body)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + List<String> groups = gmsClient.listGroups("LBT."); + + verify(httpClient, times(1)).sendAsync(endpointEq("GET", "ws/jwt/list/LBT."), any()); + + assertEquals(2, groups.size()); + assertEquals("INAF", groups.get(0)); + assertEquals("AZ", groups.get(1)); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/InvitedRegistrationTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/InvitedRegistrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b234777c9ead015a0a06d9e247a9a68dbe2a9da4 --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/InvitedRegistrationTest.java @@ -0,0 +1,98 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import it.inaf.ia2.gms.client.model.Permission; +import java.net.http.HttpResponse; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Flow; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalMatchers; +import org.mockito.ArgumentMatchers; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class InvitedRegistrationTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testInvitedRegistration() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(201)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + Map<String, Permission> permissionsMap = new HashMap<>(); + permissionsMap.put("group1", Permission.MANAGE_MEMBERS); + permissionsMap.put("group2", Permission.MANAGE_MEMBERS); + gmsClient.addInvitedRegistration("bvjsgqu423", "email", permissionsMap); + // hash = AOyojiwaRR7BHPde6Tomg3+BMoQQggNM3wUHEarXuNQ= + + verify(httpClient, times(1)).sendAsync( + AdditionalMatchers.and( + endpointEq("POST", "invited-registration"), + ArgumentMatchers.argThat(req -> { + String reqbody = req.bodyPublisher().map(p -> { + var bodySubscriber = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8); + var flowSubscriber = new StringSubscriber(bodySubscriber); + p.subscribe(flowSubscriber); + return bodySubscriber.getBody().toCompletableFuture().join(); + }).get(); + + // If the resulting hash contains a + symbol it has to be encoded to %2B, + // otherwise it will be interpreted as a space and wrong value will be + // stored into the database + String expectedBody = "token_hash=AOyojiwaRR7BHPde6Tomg3%2BBMoQQggNM3wUHEarXuNQ=" + + "&email=email&groups=group2 MANAGE_MEMBERS\n" + + "group1 MANAGE_MEMBERS"; + + return reqbody.equals(expectedBody); + })), any()); + } + + /** + * Credit: https://stackoverflow.com/a/55816685/771431 + */ + static final class StringSubscriber implements Flow.Subscriber<ByteBuffer> { + + final HttpResponse.BodySubscriber<String> wrapped; + + StringSubscriber(HttpResponse.BodySubscriber<String> wrapped) { + this.wrapped = wrapped; + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + wrapped.onSubscribe(subscription); + } + + @Override + public void onNext(ByteBuffer item) { + wrapped.onNext(List.of(item)); + } + + @Override + public void onError(Throwable throwable) { + wrapped.onError(throwable); + } + + @Override + public void onComplete() { + wrapped.onComplete(); + } + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemoveMemberTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemoveMemberTest.java new file mode 100644 index 0000000000000000000000000000000000000000..845dbb21087a48ddc68d35a36dfafa95fd18873b --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemoveMemberTest.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class RemoveMemberTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testRemoveMember() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.removeMember("LBT.INAF", "user"); + + verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "ws/jwt/membership/LBT.INAF?user_id=user"), any()); + } +} diff --git a/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemovePermissionTest.java b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemovePermissionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c1a86b0c51085af59139c3f492ad6611c1a5868d --- /dev/null +++ b/gms-client/gms-client/src/test/java/it/inaf/ia2/gms/client/call/RemovePermissionTest.java @@ -0,0 +1,33 @@ +package it.inaf.ia2.gms.client.call; + +import it.inaf.ia2.gms.client.BaseGmsClientTest; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class RemovePermissionTest extends BaseGmsClientTest { + + @BeforeEach + @Override + public void init() { + super.init(); + } + + @Test + public void testRemovePermission() { + + CompletableFuture response = CompletableFuture.completedFuture(getMockedResponse(204)); + + when(httpClient.sendAsync(any(), any())).thenReturn(response); + gmsClient.removePermission("LBT.INAF", "user"); + + verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "ws/jwt/permission/LBT.INAF?user_id=user"), any()); + } +} diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue index c638b20ee8b9f494da85e93d60ffe7f5c29cbf42..8911d9792d504016a7bb3ecf6d12f3cdd7de1926 100644 --- a/gms-ui/src/components/GroupsBreadcrumb.vue +++ b/gms-ui/src/components/GroupsBreadcrumb.vue @@ -6,7 +6,7 @@ <span v-if="group.active">{{group.groupName}}</span> </li> </ol> - <a v-if="currentGroup" :href="'group/status/' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV"> + <a v-if="currentGroup" :href="'group/status?groupId=' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV"> <font-awesome-icon icon="download"></font-awesome-icon> </a> </nav> diff --git a/gms/pom.xml b/gms/pom.xml index af3d2cedda6621b8c6178b66cac6a104a65008ef..011a3eb4433a5152635ee5cf1be5f21a2d357e1c 100644 --- a/gms/pom.xml +++ b/gms/pom.xml @@ -40,7 +40,7 @@ </dependency> <dependency> <groupId>${project.groupId}</groupId> - <artifactId>AuthLib</artifactId> + <artifactId>auth-lib</artifactId> <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> @@ -68,36 +68,46 @@ </dependency> </dependencies> + <profiles> + <profile> + <id>build-gui</id> + <build> + <plugins> + <plugin> + <groupId>com.github.eirslett</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.6</version> + <configuration> + <nodeVersion>v12.6.0</nodeVersion> + <environmentVariables> + <VUE_APP_SHOW_USER_ID_IN_SEARCH>${show.user_id_in_search}</VUE_APP_SHOW_USER_ID_IN_SEARCH> + </environmentVariables> + </configuration> + <executions> + <execution> + <goals> + <goal>install-node-and-npm</goal> + </goals> + </execution> + <execution> + <id>npm install</id> + <goals> + <goal>npm</goal> + </goals> + <configuration> + <arguments>run build --prefix ../gms-ui</arguments> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> <finalName>gms</finalName> <plugins> - <plugin> - <groupId>com.github.eirslett</groupId> - <artifactId>frontend-maven-plugin</artifactId> - <version>1.7.6</version> - <configuration> - <nodeVersion>v12.6.0</nodeVersion> - <environmentVariables> - <VUE_APP_SHOW_USER_ID_IN_SEARCH>${show.user_id_in_search}</VUE_APP_SHOW_USER_ID_IN_SEARCH> - </environmentVariables> - </configuration> - <executions> - <execution> - <goals> - <goal>install-node-and-npm</goal> - </goals> - </execution> - <execution> - <id>npm install</id> - <goals> - <goal>npm</goal> - </goals> - <configuration> - <arguments>run build --prefix ../gms-ui</arguments> - </configuration> - </execution> - </executions> - </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> @@ -184,4 +194,10 @@ </plugin> </plugins> </build> + <repositories> + <repository> + <id>ia2.snapshot</id> + <url>http://repo.ia2.inaf.it/maven/repository/snapshots</url> + </repository> + </repositories> </project> diff --git a/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java b/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java index 258cc648701faa5edc39702daeee6010f75b3a10..367d4223e336b49e22ce37953952b95259cee7bd 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java +++ b/gms/src/main/java/it/inaf/ia2/gms/GmsApplication.java @@ -2,14 +2,18 @@ package it.inaf.ia2.gms; import it.inaf.ia2.aa.AuthConfig; import it.inaf.ia2.aa.ServiceLocator; -import it.inaf.ia2.aa.UriCustomizer; -import it.inaf.ia2.aa.jwt.QueryStringBuilder; +import it.inaf.ia2.aa.data.ServletCodeRequestData; +import it.inaf.ia2.client.QueryStringBuilder; +import it.inaf.ia2.client.UriCustomizer; import static it.inaf.ia2.gms.authn.ClientDbFilter.CLIENT_DB; import it.inaf.ia2.gms.exception.BadRequestException; +import it.inaf.ia2.rap.client.RapClient; +import java.net.URI; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -20,19 +24,32 @@ public class GmsApplication { public static void main(String[] args) { SpringApplication.run(GmsApplication.class, args); + } + + @Bean + public AuthConfig authConfig() { + return ServiceLocator.getInstance().getConfig(); + } + + @Bean + public RapClient rapClient(AuthConfig authConfig) { + + URI defaultAuthorizationUri = URI.create(authConfig.getRapBaseUri()) + .resolve(authConfig.getUserAuthorizationEndpoint()); - AuthConfig authConfig = ServiceLocator.getInstance().getConfig(); + URI defaultAccessTokenUri = URI.create(authConfig.getRapBaseUri()) + .resolve(authConfig.getAccessTokenEndpoint()); - final String defaultAuthorizationUri = authConfig.getUserAuthorizationUri(); + RapClient rapClient = ServiceLocator.getInstance().getRapClient(); - authConfig.setAuthorizationUriCustomizer(new UriCustomizer() { + rapClient.setAuthorizationUriCustomizer(new UriCustomizer<HttpServletRequest>() { @Override - public String getBaseUri(HttpServletRequest req) { + public URI getBaseUri(HttpServletRequest req) { // for a better security we should check for allowed redirects String redirect = req.getParameter("redirect"); if (redirect != null) { - return redirect; + return URI.create(redirect); } return defaultAuthorizationUri; } @@ -53,14 +70,17 @@ public class GmsApplication { } }); - final String defaultAccessTokenUri = authConfig.getAccessTokenUri(); - - authConfig.setAccessTokenUriCustomizer(req -> { - String redirect = req.getParameter("token_uri"); - if (redirect != null) { - return redirect; + rapClient.setAccessTokenUriCustomizer(new UriCustomizer<ServletCodeRequestData>() { + @Override + public URI getBaseUri(ServletCodeRequestData req) { + String redirect = req.getCodeRequest().getParameter("token_uri"); + if (redirect != null) { + return URI.create(redirect); + } + return defaultAccessTokenUri; } - return defaultAccessTokenUri; }); + + return rapClient; } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/ClientDbFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/ClientDbFilter.java index 251dd4669ba03152e0571a3b5a326edbf45f2d91..526ffac5f802ccf08a111afaafe19ab7925b419b 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/ClientDbFilter.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/ClientDbFilter.java @@ -1,41 +1,38 @@ package it.inaf.ia2.gms.authn; -import it.inaf.ia2.aa.ServiceLocator; -import it.inaf.ia2.aa.jwt.JwksClient; +import it.inaf.ia2.aa.AuthConfig; +import it.inaf.ia2.rap.client.RapClient; import java.io.IOException; +import java.net.URI; import javax.servlet.Filter; import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class ClientDbFilter implements Filter { public static final String CLIENT_DB = "client_db"; - private String defaultJwksUri; - private JwksClient jwksClient; + private final RapClient rapClient; + private final String defaultJwksUri; - @Override - public void init(FilterConfig filterConfig) throws ServletException { - defaultJwksUri = ServiceLocator.getInstance().getConfig().getJwksUri(); - jwksClient = ServiceLocator.getInstance().getJwksClient(); + public ClientDbFilter(AuthConfig authConfig, RapClient rapClient) { + this.rapClient = rapClient; + defaultJwksUri = URI.create(authConfig.getRapBaseUri()).resolve(authConfig.getJwksEndpoint()).toString(); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; - HttpServletResponse response = (HttpServletResponse) res; String clientDb = request.getParameter(CLIENT_DB); if (clientDb != null) { request.getSession().setAttribute(CLIENT_DB, clientDb); String newUrl = defaultJwksUri.replaceAll("\\?client_name=(.*)", "?client_name=" + clientDb); - jwksClient.addJwksUrl(newUrl); + rapClient.addJwksUri(URI.create(newUrl)); } fc.doFilter(req, res); diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java index a2a081a693173367e466b070a88378e43943602b..af5000bcdba79cff1d09dfcf334378e0d3277956 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/GmsLoginFilter.java @@ -26,6 +26,11 @@ public class GmsLoginFilter extends LoginFilter { private boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + if (request.getUserPrincipal() != null) { + // Principal set using JWT + return true; + } + // Allow CORS check if ("OPTIONS".equals(request.getMethod())) { return true; diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java index 9013c25432423528b134c3b96fc986b0910b8de3..f2c047c6cfdd7ae965443cc2c1768f21b9d55752 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java @@ -1,10 +1,8 @@ package it.inaf.ia2.gms.authn; -import io.jsonwebtoken.Jwt; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SigningKeyResolver; -import it.inaf.ia2.aa.ServiceLocator; +import it.inaf.ia2.aa.data.User; import it.inaf.ia2.gms.persistence.LoggingDAO; +import it.inaf.ia2.rap.client.RapClient; import java.io.IOException; import java.security.Principal; import java.util.Map; @@ -16,15 +14,16 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; public class JWTFilter implements Filter { private final LoggingDAO loggingDAO; - private final SigningKeyResolver signingKeyResolver; + private final RapClient rapClient; - public JWTFilter(LoggingDAO loggingDAO) { + public JWTFilter(LoggingDAO loggingDAO, RapClient rapClient) { this.loggingDAO = loggingDAO; - this.signingKeyResolver = ServiceLocator.getInstance().getTokenManager().getSigningKeyResolver(); + this.rapClient = rapClient; } @Override @@ -34,19 +33,28 @@ public class JWTFilter implements Filter { HttpServletResponse response = (HttpServletResponse) res; String authHeader = request.getHeader("Authorization"); + if (authHeader == null) { - loggingDAO.logAction("Attempt to access WS without token", request); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing Authorization token"); + + if (request.isRequestedSessionIdValid()) { + HttpSession session = request.getSession(false); + User user = (User) session.getAttribute("user_data"); + if (user != null) { + rapClient.setAccessToken(user.getAccessToken()); + ServletRequestWithSessionPrincipal wrappedRequest = new ServletRequestWithSessionPrincipal(request, user); + fc.doFilter(wrappedRequest, res); + return; + } + } + + fc.doFilter(req, res); return; } - authHeader = authHeader.replace("Bearer", "").trim(); + String token = authHeader.replace("Bearer", "").trim(); - Jwt jwt = Jwts.parser() - .setSigningKeyResolver(signingKeyResolver) - .parse(authHeader); - - Map<String, Object> claims = (Map<String, Object>) jwt.getBody(); + rapClient.setAccessToken(token); + Map<String, Object> claims = rapClient.parseIdTokenClaims(token); if (claims.get("sub") == null) { loggingDAO.logAction("Attempt to access WS with invalid token", request); @@ -60,9 +68,24 @@ public class JWTFilter implements Filter { fc.doFilter(wrappedRequest, res); } + private static class ServletRequestWithSessionPrincipal extends HttpServletRequestWrapper { + + private final User principal; + + public ServletRequestWithSessionPrincipal(HttpServletRequest request, User user) { + super(request); + this.principal = user; + } + + @Override + public Principal getUserPrincipal() { + return principal; + } + } + private static class ServletRequestWithJWTPrincipal extends HttpServletRequestWrapper { - private final Principal principal; + private final RapPrincipal principal; public ServletRequestWithJWTPrincipal(HttpServletRequest request, Map<String, Object> jwtClaims) { super(request); diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java b/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java index 996d011190bc35de63d2c63b5936a6051535e778..92e04da0c9fa6fbc040adc31caa1f3a7880fcfd4 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java @@ -1,6 +1,8 @@ package it.inaf.ia2.gms.authn; +import it.inaf.ia2.aa.AuthConfig; import it.inaf.ia2.gms.persistence.LoggingDAO; +import it.inaf.ia2.rap.client.RapClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -44,9 +46,9 @@ public class SecurityConfig { } @Bean - public FilterRegistrationBean clientDbFilter() { + public FilterRegistrationBean clientDbFilter(AuthConfig authConfig, RapClient rapClient) { FilterRegistrationBean bean = new FilterRegistrationBean(); - bean.setFilter(new ClientDbFilter()); + bean.setFilter(new ClientDbFilter(authConfig, rapClient)); bean.addUrlPatterns("/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; @@ -56,10 +58,10 @@ public class SecurityConfig { * Checks JWT for web services. */ @Bean - public FilterRegistrationBean serviceJWTFilter(LoggingDAO loggingDAO) { + public FilterRegistrationBean serviceJWTFilter(LoggingDAO loggingDAO, RapClient rapClient) { FilterRegistrationBean bean = new FilterRegistrationBean(); - bean.setFilter(new JWTFilter(loggingDAO)); - bean.addUrlPatterns("/ws/jwt/*"); + bean.setFilter(new JWTFilter(loggingDAO, rapClient)); + bean.addUrlPatterns("/*"); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java index 0de00ba5ab5505fbcaba89a9b6d9bc206d1f9b1f..6d4194efdf445900f8dc366b037bd07fc80213fd 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java @@ -1,6 +1,7 @@ package it.inaf.ia2.gms.authn; import it.inaf.ia2.aa.data.User; +import it.inaf.ia2.rap.client.RapClient; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -14,58 +15,44 @@ public class SessionData { private static final String USER_DATA = "user_data"; + private User user; + @Autowired private HttpServletRequest request; - private String userId; - private String userName; - private String accessToken; - private String refreshToken; - private long expiration; + @Autowired + private RapClient rapClient; @PostConstruct public void init() { - HttpSession session = request.getSession(false); if (session != null && session.getAttribute(USER_DATA) != null) { - User user = (User) session.getAttribute(USER_DATA); - userId = user.getName(); - userName = user.getUserLabel(); - accessToken = user.getAccessToken(); - refreshToken = user.getRefreshToken(); - setExpiresIn(user.getExpiresIn()); + setUser((User) session.getAttribute(USER_DATA)); } } - public String getUserId() { - return userId; - } - - public String getAccessToken() { - return accessToken; + public void setUser(User user) { + this.user = user; + rapClient.setAccessToken(user.getAccessToken()); } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public String getRefreshToken() { - return refreshToken; + public String getUserId() { + return user.getName(); } - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; + public String getUserName() { + return user.getUserLabel(); } - public String getUserName() { - return userName; + public String getAccessToken() { + return user.getAccessToken(); } - public void setExpiresIn(long expiresIn) { - this.expiration = System.currentTimeMillis() + expiresIn * 1000; + public String getRefreshToken() { + return user.getRefreshToken(); } public long getExpiresIn() { - return (expiration - System.currentTimeMillis()) / 1000; + return user.getExpiresIn(); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java index fe9137ed62cca9a1d3337718d95fdb967923a464..d4c855a5bd5010e8727802b6dc43acde4f9b668d 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java @@ -1,7 +1,7 @@ package it.inaf.ia2.gms.controller; +import com.fasterxml.jackson.databind.ObjectMapper; import com.opencsv.CSVWriter; -import it.inaf.ia2.gms.authn.SessionData; import it.inaf.ia2.gms.manager.GroupStatusManager; import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.model.request.AddGroupRequest; @@ -13,9 +13,13 @@ import it.inaf.ia2.gms.model.request.GroupsRequest; import it.inaf.ia2.gms.model.request.RenameGroupRequest; import it.inaf.ia2.gms.model.request.SearchFilterRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.service.GroupNameService; import it.inaf.ia2.gms.service.GroupsTreeBuilder; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.util.List; +import java.util.Optional; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -28,13 +32,16 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GroupsController { + private static final ObjectMapper MAPPER = new ObjectMapper(); + @Autowired - private SessionData session; + private HttpServletRequest servletRequest; @Autowired private GroupsManager groupsManager; @@ -48,6 +55,9 @@ public class GroupsController { @Autowired private GroupStatusManager groupStatusManager; + @Autowired + private GroupNameService groupNameService; + @GetMapping(value = "/groups", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> getGroupsTab(@Valid GroupsRequest request) { if (request.isOnlyPanel()) { @@ -93,21 +103,40 @@ public class GroupsController { return ResponseEntity.ok(groupsPanel); } - @GetMapping(value = "/group/status/{groupId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public void downloadStatus(@PathVariable("groupId") String groupId, HttpServletResponse response) throws Exception { + @GetMapping(value = "/group/status", produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, MediaType.APPLICATION_JSON_VALUE}) + public void downloadStatus(@RequestParam(value = "groupId", required = false) String groupId, + @RequestParam(value = "groupName", required = false) String groupName, + HttpServletRequest request, HttpServletResponse response) throws Exception { + + if (groupId == null && groupName == null) { + response.sendError(400, "Parameter groupId or groupName is required"); + return; + } + + if (groupId == null) { + GroupEntity group = groupNameService.getGroupFromNames(Optional.of(groupName)); + groupId = group.getId(); + } + + List<String[]> status = groupStatusManager.generateStatus(groupId); - try (OutputStream out = response.getOutputStream(); - CSVWriter writer = new CSVWriter(new OutputStreamWriter(out))) { + try ( OutputStream out = response.getOutputStream()) { - writer.writeNext(new String[]{"program", "email"}); + if ("application/json".equals(request.getHeader("Accept"))) { + MAPPER.writeValue(out, status); + } else { + try ( CSVWriter writer = new CSVWriter(new OutputStreamWriter(out))) { + writer.writeNext(new String[]{"program", "email"}); - for (String[] row : groupStatusManager.generateStatus(groupId)) { - writer.writeNext(row); + for (String[] row : status) { + writer.writeNext(row); + } + } } } } private <T extends PaginatedModelRequest & SearchFilterRequest> PaginatedData<GroupNode> getGroupsPanel(GroupEntity parentGroup, T request) { - return groupsTreeBuilder.listSubGroups(parentGroup, request, session.getUserId()); + return groupsTreeBuilder.listSubGroups(parentGroup, request, servletRequest.getUserPrincipal().getName()); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilder.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilder.java index 2718b19cd8d586c6fd8ca882b31466a4538f29bb..2da6216a9c827234cdcf4f7e068cda6c49fbd5c7 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilder.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilder.java @@ -1,6 +1,5 @@ package it.inaf.ia2.gms.controller; -import it.inaf.ia2.gms.authn.SessionData; import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.InvitedRegistrationManager; import it.inaf.ia2.gms.manager.PermissionsManager; @@ -10,6 +9,7 @@ import it.inaf.ia2.gms.model.response.GroupsTabResponse; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.GroupsTreeBuilder; +import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -17,7 +17,7 @@ import org.springframework.stereotype.Component; public class GroupsTabResponseBuilder { @Autowired - private SessionData session; + HttpServletRequest servletRequest; @Autowired private PermissionsManager permissionsManager; @@ -46,7 +46,7 @@ public class GroupsTabResponseBuilder { Permission permission = permissionsManager.getCurrentUserPermission(group); response.setPermission(permission); - response.setGroupsPanel(groupsListBuilder.listSubGroups(group, request, session.getUserId())); + response.setGroupsPanel(groupsListBuilder.listSubGroups(group, request, servletRequest.getUserPrincipal().getName())); response.setLeaf(group.isLeaf()); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java index 90a02bb60bdf1dc60fbf8bfcf88f3220ca244e8c..8536f98d76a61c25ad914f4b5aa265fc4df1fa0b 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java @@ -83,6 +83,10 @@ public class HomePageController { @GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE) public String index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // This page MUST NOT be cached to avoid losing the login redirect + response.setHeader("Cache-Control", "no-store, must-revalidate"); + response.setHeader("Expires", "0"); + Optional<List<InvitedRegistration>> optReg = invitedRegistrationManager.completeInvitedRegistrationIfNecessary(); if (optReg.isPresent()) { request.setAttribute("invited-registrations", optReg.get()); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java index 8607c4b2e1ca838e1dc166e2fb634b8d80135e2b..554d4437ef63752fda45efd5468b6cf01dc0b355 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/JWTWebServiceController.java @@ -7,7 +7,6 @@ import it.inaf.ia2.gms.manager.InvitedRegistrationManager; import it.inaf.ia2.gms.manager.MembershipManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.model.response.UserPermission; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO; @@ -19,6 +18,7 @@ import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.JoinService; import it.inaf.ia2.gms.service.PermissionUtils; import it.inaf.ia2.gms.service.SearchService; +import it.inaf.ia2.rap.data.RapUser; import java.io.IOException; import java.io.PrintWriter; import java.security.Principal; @@ -39,15 +39,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** - * Web service called by other web applications using JWT (delegation). + * This class needs some refactoring: it contains all endpoints that used JWT. + * Now all endpoints accept both a JWT token or a session, so some of them could + * be removed and others should be moved on dedicated classes. Some endpoints + * match 2 patters to achieve a smooth transition. */ @RestController -@RequestMapping("/ws/jwt") public class JWTWebServiceController { @Autowired @@ -63,7 +64,7 @@ public class JWTWebServiceController { private GroupsService groupsService; @Autowired - private GroupNameService groupNameService; + protected GroupNameService groupNameService; @Autowired private MembershipManager membershipManager; @@ -83,7 +84,7 @@ public class JWTWebServiceController { /** * This endpoint is compliant with the IVOA GMS standard. */ - @GetMapping(value = "/search", produces = MediaType.TEXT_PLAIN_VALUE) + @GetMapping(value = {"/ws/jwt/search", "/vo/search"}, produces = MediaType.TEXT_PLAIN_VALUE) public void getGroups(HttpServletResponse response) throws IOException { List<GroupEntity> memberships = membershipManager.getCurrentUserMemberships(); @@ -104,10 +105,10 @@ public class JWTWebServiceController { * be defined adding ".+", otherwise Spring will think it is a file * extension (thanks https://stackoverflow.com/a/16333149/771431) */ - @GetMapping(value = "/search/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) + @GetMapping(value = {"/ws/jwt/search/{group:.+}", "/vo/search/{group:.+}"}, produces = MediaType.TEXT_PLAIN_VALUE) public void isMemberOf(@PathVariable("group") String group, HttpServletResponse response) throws IOException { - List<String> groupNames = extractGroupNames(group); + List<String> groupNames = groupNameService.extractGroupNames(group); boolean isMember = membershipManager.isCurrentUserMemberOf("ROOT"); if (!isMember) { @@ -135,13 +136,12 @@ public class JWTWebServiceController { // else: empty response (as defined by GMS standard) } - @GetMapping(value = {"/list/{group:.+}", "/list"}, produces = MediaType.TEXT_PLAIN_VALUE) - public void listGroups(@PathVariable("group") Optional<String> group, Principal principal, HttpServletResponse response) throws IOException { + @GetMapping(value = {"/ws/jwt/list/{group:.+}", "/ws/jwt/list"}, produces = MediaType.TEXT_PLAIN_VALUE) + public void listGroups(@PathVariable("group") Optional<String> groupNames, Principal principal, HttpServletResponse response) throws IOException { String userId = principal.getName(); - List<String> groupNames = extractGroupNames(group); - GroupEntity parentGroup = getGroupFromNames(groupNames); + GroupEntity parentGroup = groupNameService.getGroupFromNames(groupNames); List<GroupEntity> allSubGroups = groupsDAO.getDirectSubGroups(parentGroup.getPath()); @@ -157,7 +157,7 @@ public class JWTWebServiceController { try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) { for (String groupName : groupNameService.getGroupsNames(visibleSubgroups)) { - pw.println(getShortGroupName(groupName, group)); + pw.println(groupNameService.getShortGroupName(groupName, groupNames)); } } } @@ -166,10 +166,10 @@ public class JWTWebServiceController { * Creates a group and its ancestors if they are missing. It doesn't fail if * the last group already exists. */ - @PostMapping(value = "/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) + @PostMapping(value = "/ws/jwt/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) public void createGroup(@PathVariable("group") String groupParam, HttpServletRequest request, HttpServletResponse response) throws IOException { - List<String> groupNames = extractGroupNames(groupParam); + List<String> groupNames = groupNameService.extractGroupNames(groupParam); String leafParam = request.getParameter("leaf"); boolean leaf = leafParam == null ? false : Boolean.valueOf(leafParam); @@ -191,29 +191,29 @@ public class JWTWebServiceController { } } - @DeleteMapping(value = "/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) + @DeleteMapping(value = "/ws/jwt/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) public void deleteGroup(@PathVariable("group") String groupParam, HttpServletResponse response) { - GroupEntity group = getGroupFromNames(extractGroupNames(groupParam)); + GroupEntity group = groupNameService.getGroupFromNames(Optional.of(groupParam)); groupsDAO.deleteGroup(group); response.setStatus(HttpServletResponse.SC_NO_CONTENT); } - @GetMapping(value = {"/membership/{group:.+}", "/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) - public void getMembership(@PathVariable("group") Optional<String> group, @RequestParam("user_id") String userId, HttpServletResponse response) throws IOException { + @GetMapping(value = {"/ws/jwt/membership/{group:.+}", "/ws/jwt/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) + public void getMembership(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") String userId, HttpServletResponse response) throws IOException { - GroupEntity parent = getGroupFromNames(extractGroupNames(group)); + GroupEntity parent = groupNameService.getGroupFromNames(groupNames); List<GroupEntity> groups = membershipManager.getUserGroups(parent, userId); try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) { for (String groupName : groupNameService.getGroupsNames(groups)) { - pw.println(getShortGroupName(groupName, group)); + pw.println(groupNameService.getShortGroupName(groupName, groupNames)); } } } - @PostMapping(value = {"/membership/{group:.+}", "/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) - public void addMember(@PathVariable("group") Optional<String> group, HttpServletRequest request, HttpServletResponse response) throws IOException { + @PostMapping(value = {"/ws/jwt/membership/{group:.+}", "/ws/jwt/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) + public void addMember(@PathVariable("group") Optional<String> groupNames, HttpServletRequest request, HttpServletResponse response) throws IOException { String targetUserId = request.getParameter("user_id"); if (targetUserId == null) { @@ -221,64 +221,64 @@ public class JWTWebServiceController { return; } - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(group)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); membershipManager.addMember(groupEntity, targetUserId); } - @DeleteMapping(value = {"/membership/{group:.+}", "/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) - public void removeMember(@PathVariable("group") Optional<String> group, @RequestParam("user_id") String userId, + @DeleteMapping(value = {"/ws/jwt/membership/{group:.+}", "/ws/jwt/membership"}, produces = MediaType.TEXT_PLAIN_VALUE) + public void removeMember(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") String userId, HttpServletRequest request, HttpServletResponse response) throws IOException { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(group)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); membershipManager.removeMember(groupEntity, userId); response.setStatus(HttpServletResponse.SC_NO_CONTENT); } - @GetMapping(value = {"/permission/{group:.+}", "/permission"}, produces = MediaType.TEXT_PLAIN_VALUE) + @GetMapping(value = {"/ws/jwt/permission/{group:.+}", "/ws/jwt/permission"}, produces = MediaType.TEXT_PLAIN_VALUE) public void getUserPermission(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") Optional<String> userId, HttpServletRequest request, HttpServletResponse response) throws IOException { + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); if (userId.isPresent()) { try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) { - for (UserPermission userPermission : searchService.getUserPermission(userId.get(), permissionsManager.getCurrentUserPermissions(getRoot()))) { + for (UserPermission userPermission : searchService.getUserPermission(groupEntity, userId.get(), permissionsManager.getCurrentUserPermissions(groupEntity))) { String group = String.join(".", userPermission.getGroupCompleteName()); pw.println(group + " " + userPermission.getPermission()); } } } else { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames)); try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) { - for (it.inaf.ia2.gms.model.UserPermission up : permissionsManager.getAllPermissions(groupEntity)) { + for (it.inaf.ia2.gms.model.RapUserPermission up : permissionsManager.getAllPermissions(groupEntity)) { pw.println(up.getUser().getId() + " " + up.getPermission()); } } } } - @PostMapping(value = {"/permission/{group:.+}", "/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @PostMapping(value = {"/ws/jwt/permission/{group:.+}", "/ws/jwt/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void addPermission(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") String targetUserId, @RequestParam("permission") Permission permission) throws IOException { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); permissionsManager.addPermission(groupEntity, targetUserId, permission); } - @PutMapping(value = {"/permission/{group:.+}", "/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @PutMapping(value = {"/ws/jwt/permission/{group:.+}", "/ws/jwt/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void setPermission(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") String targetUserId, @RequestParam("permission") Permission permission) throws IOException { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); permissionsManager.createOrUpdatePermission(groupEntity, targetUserId, permission); } - @DeleteMapping(value = {"/permission/{group:.+}", "/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE) + @DeleteMapping(value = {"/ws/jwt/permission/{group:.+}", "/ws/jwt/permission/"}, produces = MediaType.TEXT_PLAIN_VALUE) public void removePermission(@PathVariable("group") Optional<String> groupNames, @RequestParam("user_id") String userId, HttpServletRequest request, HttpServletResponse response) throws IOException { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(groupNames); permissionsManager.removePermission(groupEntity, userId); response.setStatus(HttpServletResponse.SC_NO_CONTENT); } - @GetMapping(value = "/check-invited-registration", produces = MediaType.TEXT_PLAIN_VALUE) + @GetMapping(value = {"/ws/jwt/check-invited-registration", "/check-invited-registration"}, produces = MediaType.TEXT_PLAIN_VALUE) public void completeInvitedRegistrationIfNecessary(Principal principal, HttpServletResponse response) throws IOException { String userId = principal.getName(); @@ -300,7 +300,7 @@ public class JWTWebServiceController { } } - @PostMapping(value = "/invited-registration", produces = MediaType.TEXT_PLAIN_VALUE) + @PostMapping(value = {"/ws/jwt/invited-registration", "/invited-registration"}, produces = MediaType.TEXT_PLAIN_VALUE) public void addInvitedRegistration(@RequestParam("token_hash") String tokenHash, @RequestParam("email") String email, @RequestParam("groups") String groupNamesAndPermissionsParam, HttpServletResponse response) { @@ -311,7 +311,7 @@ public class JWTWebServiceController { int lastSpaceIndex = param.lastIndexOf(" "); String groupName = param.substring(0, lastSpaceIndex); Permission permission = Permission.valueOf(param.substring(lastSpaceIndex + 1)); - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupName)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(Optional.of(groupName)); groupsPermissions.put(groupEntity, permission); } } @@ -321,10 +321,10 @@ public class JWTWebServiceController { response.setStatus(HttpServletResponse.SC_CREATED); } - @GetMapping(value = "/email/{group:.+}", produces = MediaType.TEXT_PLAIN_VALUE) + @GetMapping(value = {"/ws/jwt/email/{group:.+}", "/email/{group:.+}"}, produces = MediaType.TEXT_PLAIN_VALUE) public void getEmailOfMembers(@PathVariable("group") String groupNames, @RequestParam("permission") Optional<Permission> permission, HttpServletResponse response) throws IOException { - GroupEntity groupEntity = getGroupFromNames(extractGroupNames(groupNames)); + GroupEntity groupEntity = groupNameService.getGroupFromNames(Optional.of(groupNames)); Set<String> selectedUserIds = null; if (permission.isPresent()) { @@ -340,74 +340,13 @@ public class JWTWebServiceController { try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) { for (RapUser member : membershipManager.getMembers(groupEntity)) { if (selectedUserIds == null || selectedUserIds.contains(member.getId())) { - pw.println(member.getPrimaryEmail()); + pw.println(member.getPrimaryEmailAddress()); } } } } - private GroupEntity getGroupFromNames(List<String> groupNames) { - if (groupNames.isEmpty()) { - return getRoot(); - } - return getGroupFromNamesAndIndex(groupNames, groupNames.size() - 1); - } - - private GroupEntity getGroupFromNamesAndIndex(List<String> groupNames, int index) { - String parentPath = ""; // starting from ROOT - GroupEntity group = null; - for (int i = 0; i < index + 1; i++) { - String groupName = groupNames.get(i); - group = groupsDAO.findGroupByParentAndName(parentPath, groupName) - .orElseThrow(() -> new BadRequestException("Unable to find group " + groupName)); - parentPath = group.getPath(); - } - if (group == null) { - throw new IllegalStateException(); - } - return group; - } - - private GroupEntity getRoot() { - return groupsDAO.findGroupById("ROOT") - .orElseThrow(() -> new IllegalStateException("Missing root group")); - } - - private List<String> extractGroupNames(Optional<String> group) { - return extractGroupNames(group.orElse(null)); - } - - private List<String> extractGroupNames(String groupStr) { - - if (groupStr == null || groupStr.isEmpty()) { - return new ArrayList<>(); - } - - List<String> names = new ArrayList<>(); - String currentName = ""; - for (int i = 0; i < groupStr.length(); i++) { - char c = groupStr.charAt(i); - // dot is the group separator and it must be escaped if used inside - // group names - if (c == '.' && groupStr.charAt(i - 1) != '\\') { - names.add(currentName.replace("\\.", ".")); - currentName = ""; - } else { - currentName += c; - } - } - names.add(currentName); - return names; - } - - private String getShortGroupName(String completeGroupName, Optional<String> groupPrefix) { - if (groupPrefix.isPresent()) { - return completeGroupName.substring(groupPrefix.get().length() + 1); - } - return completeGroupName; - } - - @PostMapping(value = "/join", produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(value = {"/ws/jwt/join", "/join"}, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> join(RapPrincipal principal) { String fromUser = principal.getName(); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java index c41f012597cd6a7c5e59ce6d23db9c57049ab8b4..5e4b15fde34b823c852ad66b1523fe4d1873fda4 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java @@ -1,8 +1,10 @@ package it.inaf.ia2.gms.controller; +import it.inaf.ia2.aa.ServiceLocator; +import it.inaf.ia2.aa.UserManager; import it.inaf.ia2.gms.authn.SessionData; -import it.inaf.ia2.gms.rap.RapClient; import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -19,14 +21,17 @@ public class KeepAliveController { @Autowired private SessionData sessionData; - @Autowired - private RapClient rapClient; + private final UserManager userManager; + + public KeepAliveController() { + userManager = ServiceLocator.getInstance().getUserManager(); + } @GetMapping(value = "/keepAlive", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<?> keepAlive() { + public ResponseEntity<?> keepAlive(HttpServletRequest request) { LOG.trace("Keepalive called"); if (sessionData.getExpiresIn() < 60) { - rapClient.refreshToken(); + sessionData.setUser(userManager.refreshToken(request)); LOG.trace("RAP token refreshed"); } // empty JSON object response diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java index f31966fc7c63369f672c1a745c4c310c9eae9691..3db24db6b7298493fc9b9c4ada31e10b6316d1cb 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java @@ -4,12 +4,12 @@ import it.inaf.ia2.gms.manager.MembershipManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.request.AddMemberRequest; import it.inaf.ia2.gms.model.response.PaginatedData; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.model.request.PaginatedModelRequest; import it.inaf.ia2.gms.model.request.RemoveMemberRequest; import it.inaf.ia2.gms.model.request.TabRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.service.GroupsService; +import it.inaf.ia2.rap.data.RapUser; import java.util.Collections; import java.util.List; import javax.validation.Valid; diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java index 9742396d930bb9bc279952e7c10f2661a78edbc0..84e3faf3a5063af96e787b1f33ecdacf13854066 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java @@ -1,5 +1,6 @@ package it.inaf.ia2.gms.controller; +import it.inaf.ia2.gms.exception.BadRequestException; import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.request.AddPermissionRequest; @@ -7,15 +8,20 @@ import it.inaf.ia2.gms.model.request.MemberRequest; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.request.PaginatedModelRequest; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.UserPermission; +import it.inaf.ia2.gms.model.RapUserPermission; import it.inaf.ia2.gms.model.request.TabRequest; import it.inaf.ia2.gms.model.request.UpdatePermissionRequest; +import it.inaf.ia2.gms.model.response.UserPermission; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import it.inaf.ia2.gms.service.GroupNameService; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -39,10 +45,10 @@ public class PermissionsController { private PermissionsManager permissionsManager; @GetMapping(value = "/permissions", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<PaginatedData<UserPermission>> getPermissionsTab(TabRequest request) { + public ResponseEntity<PaginatedData<RapUserPermission>> getPermissionsTab(TabRequest request) { GroupEntity group = groupsManager.getGroupById(request.getGroupId()); - PaginatedData<UserPermission> permissionsPanel = getPermissionsPanel(group, request); + PaginatedData<RapUserPermission> permissionsPanel = getPermissionsPanel(group, request); return ResponseEntity.ok(permissionsPanel); } @@ -63,7 +69,7 @@ public class PermissionsController { } @PostMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<PaginatedData<UserPermission>> addPermission(@Valid @RequestBody AddPermissionRequest request) { + public ResponseEntity<PaginatedData<RapUserPermission>> addPermission(@Valid @RequestBody AddPermissionRequest request) { GroupEntity group = groupsManager.getGroupById(request.getGroupId()); if (request.isOverride()) { @@ -88,7 +94,7 @@ public class PermissionsController { } @DeleteMapping(value = "/permission", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<PaginatedData<UserPermission>> deletePermission(@Valid MemberRequest request) { + public ResponseEntity<PaginatedData<RapUserPermission>> deletePermission(@Valid MemberRequest request) { GroupEntity group = groupsManager.getGroupById(request.getGroupId()); permissionsManager.removePermission(group, request.getUserId()); @@ -96,8 +102,8 @@ public class PermissionsController { return ResponseEntity.ok(getPermissionsPanel(group, request)); } - private PaginatedData<UserPermission> getPermissionsPanel(GroupEntity group, PaginatedModelRequest request) { - List<UserPermission> permissions = permissionsManager.getAllPermissions(group); + private PaginatedData<RapUserPermission> getPermissionsPanel(GroupEntity group, PaginatedModelRequest request) { + List<RapUserPermission> permissions = permissionsManager.getAllPermissions(group); Collections.sort(permissions, (p1, p2) -> { return p1.getUser().getDisplayName().compareTo(p2.getUser().getDisplayName()); }); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java index c612e9c4baba290bcdcd75c8641cc88da7c38b30..a7fcc04bd49d13e89763ad95920004261b1d32b6 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java @@ -1,11 +1,10 @@ package it.inaf.ia2.gms.controller; -import it.inaf.ia2.gms.authn.SessionData; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.response.SearchResponseItem; import it.inaf.ia2.gms.model.response.UserSearchResponse; import it.inaf.ia2.gms.service.SearchService; +import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -18,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController; public class SearchController { @Autowired - private SessionData sessionData; + private HttpServletRequest servletRequest; @Autowired private SearchService searchService; @@ -27,14 +26,14 @@ public class SearchController { public ResponseEntity<PaginatedData<SearchResponseItem>> getSearchResults(@RequestParam("query") String query, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize) { - PaginatedData<SearchResponseItem> response = searchService.search(query, sessionData.getUserId(), page, pageSize); + PaginatedData<SearchResponseItem> response = searchService.search(query, servletRequest.getUserPrincipal().getName(), page, pageSize); return ResponseEntity.ok(response); } @GetMapping(value = "/search/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserSearchResponse> getSearchResultUser(@PathVariable("userId") String userId) { - UserSearchResponse response = searchService.getUserSearchResult(sessionData.getUserId(), userId); + UserSearchResponse response = searchService.getUserSearchResult(servletRequest.getUserPrincipal().getName(), userId); return ResponseEntity.ok(response); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java index e5d908c265228008b552db7aa3efa6cfd0ee826e..5d96ed400e5379c49c8b7b45e8422c6c44fce729 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java @@ -1,7 +1,7 @@ package it.inaf.ia2.gms.controller; -import it.inaf.ia2.gms.model.RapUser; -import it.inaf.ia2.gms.rap.RapClient; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.RapUser; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -18,6 +18,6 @@ public class UsersController { @GetMapping(value = "users", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<RapUser>> searchUsers(@RequestParam("search") String searchText) { - return ResponseEntity.ok(rapClient.searchUsers(searchText)); + return ResponseEntity.ok(rapClient.getUsers(searchText)); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java index 7e756500e4488c73773af36f1f2b75875738eb12..f3792ca8d1a87e804212b7b8731c560b94251159 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java @@ -2,14 +2,13 @@ package it.inaf.ia2.gms.manager; import it.inaf.ia2.gms.exception.UnauthorizedException; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.MembershipEntity; -import it.inaf.ia2.gms.rap.RapClient; -import it.inaf.ia2.gms.service.GroupNameService; import it.inaf.ia2.gms.service.GroupsService; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.RapUser; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -38,9 +37,6 @@ public class GroupStatusManager extends UserAwareComponent { @Autowired private MembershipsDAO membershipsDAO; - @Autowired - private GroupNameService groupNameService; - @Autowired private RapClient rapClient; @@ -50,14 +46,15 @@ public class GroupStatusManager extends UserAwareComponent { Permission groupPermission = permissionsManager.getCurrentUserPermission(parentGroup); - if (groupPermission != Permission.ADMIN) { - throw new UnauthorizedException("ADMIN permission is needed for performing this action"); + if (!Permission.includes(groupPermission, Permission.VIEW_MEMBERS)) { + throw new UnauthorizedException("VIEW_MEMBERS permission is needed for performing this action"); } List<GroupEntity> groups = groupsDAO.getAllChildren(parentGroup.getPath()); groups.add(parentGroup); - List<String> names = groupNameService.getGroupsNames(groups); + Map<String, String> names = groupsDAO.getGroupCompleteNamesFromId(groups.stream() + .map(g -> g.getId()).collect(Collectors.toSet())); List<MembershipEntity> memberships = membershipsDAO.findByGroups(groups.stream() .map(g -> g.getId()).collect(Collectors.toList())); @@ -77,24 +74,26 @@ public class GroupStatusManager extends UserAwareComponent { Map<String, String> usersMap = new HashMap<>(); for (RapUser user : rapClient.getUsers(memberships.stream() .map(u -> u.getUserId()).collect(Collectors.toSet()))) { - usersMap.put(user.getId(), user.getPrimaryEmail()); + usersMap.put(user.getId(), user.getPrimaryEmailAddress()); } List<String[]> rows = new ArrayList<>(); for (int i = 0; i < groups.size(); i++) { GroupEntity group = groups.get(i); - String groupName = names.get(i); - List<String> users = membersMap.get(group.getId()); - if (users != null) { - for (String userId : users) { - String email = usersMap.get(userId); - if (email == null) { - LOG.warn("Unable to retrieve information about user " + userId); - continue; + String groupName = names.get(group.getId()); + if (groupName != null) { + List<String> users = membersMap.get(group.getId()); + if (users != null) { + for (String userId : users) { + String email = usersMap.get(userId); + if (email == null) { + LOG.warn("Unable to retrieve information about user " + userId); + continue; + } + String[] row = new String[]{groupName, email}; + rows.add(row); } - String[] row = new String[]{groupName, email}; - rows.add(row); } } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java index dec9449f56626d78f3fdfd68f8a074f4d17a5192..7c9525d7e3ed319fb3c996df5e1c23ddc466369a 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java @@ -13,8 +13,8 @@ import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.InvitedRegistration; import it.inaf.ia2.gms.persistence.model.MembershipEntity; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.PermissionsService; +import it.inaf.ia2.rap.client.RapClient; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -54,7 +55,7 @@ public class InvitedRegistrationManager extends UserAwareComponent { private RapClient rapClient; @Autowired - private SessionData sessionData; + private HttpServletRequest servletRequest; @Autowired private LoggingDAO loggingDAO; @@ -104,7 +105,7 @@ public class InvitedRegistrationManager extends UserAwareComponent { public Optional<List<InvitedRegistration>> completeInvitedRegistrationIfNecessary() { - List<InvitedRegistration> invitedRegistrations = completeInvitedRegistrationIfNecessary(sessionData.getUserId()); + List<InvitedRegistration> invitedRegistrations = completeInvitedRegistrationIfNecessary(servletRequest.getUserPrincipal().getName()); InvitedRegistration invitedRegistrationFromToken = (InvitedRegistration) httpSession.getAttribute(INVITED_REGISTRATION); diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java index 022e54205e9074e28e7782e39d800024bc57add7..83340a52446292d8dabfb169438254ade5a0cc47 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java @@ -2,15 +2,15 @@ package it.inaf.ia2.gms.manager; import it.inaf.ia2.gms.exception.UnauthorizedException; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.LoggingDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.MembershipEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.PermissionUtils; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.RapUser; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java index e6a655190684bb06718650b3ddce5c4ca99ef693..00a254533c3198b68fa9773ed5dc48218b9aba91 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java @@ -2,14 +2,14 @@ package it.inaf.ia2.gms.manager; import it.inaf.ia2.gms.exception.UnauthorizedException; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; -import it.inaf.ia2.gms.model.UserPermission; +import it.inaf.ia2.gms.model.RapUserPermission; import it.inaf.ia2.gms.persistence.LoggingDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.PermissionUtils; import it.inaf.ia2.gms.service.PermissionsService; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.RapUser; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -34,7 +34,7 @@ public class PermissionsManager extends UserAwareComponent { this.loggingDAO = loggingDAO; } - public List<UserPermission> getAllPermissions(GroupEntity group) { + public List<RapUserPermission> getAllPermissions(GroupEntity group) { verifyUserCanManagePermissions(group); @@ -47,12 +47,12 @@ public class PermissionsManager extends UserAwareComponent { Map<String, RapUser> users = rapClient.getUsers(userIdentifiers).stream() .collect(Collectors.toMap(RapUser::getId, Function.identity())); - List<UserPermission> result = new ArrayList<>(); + List<RapUserPermission> result = new ArrayList<>(); for (PermissionEntity p : permissions) { RapUser rapUser = users.get(p.getUserId()); if (rapUser != null) { - UserPermission permission = new UserPermission(); + RapUserPermission permission = new RapUserPermission(); permission.setPermission(p.getPermission()); permission.setUser(rapUser); result.add(permission); @@ -117,6 +117,11 @@ public class PermissionsManager extends UserAwareComponent { } } + public List<PermissionEntity> findUserPermissions(GroupEntity group, String userId) { + verifyUserCanManagePermissions(group); + return permissionsService.findUserPermissions(group, userId); + } + public Permission getUserPermission(GroupEntity group, String userId) { return getUserPermission(group, userId, true); } diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/UserAwareComponent.java b/gms/src/main/java/it/inaf/ia2/gms/manager/UserAwareComponent.java index f6af17ca33b29f359814e53043a3aa3eceb938d0..ad7dece5ef1060520d47968c7158f908b1afab76 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/UserAwareComponent.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/UserAwareComponent.java @@ -1,23 +1,14 @@ package it.inaf.ia2.gms.manager; -import it.inaf.ia2.gms.authn.RapPrincipal; -import it.inaf.ia2.gms.authn.SessionData; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; public abstract class UserAwareComponent { - @Autowired(required = false) - private SessionData session; - @Autowired private HttpServletRequest request; protected String getCurrentUserId() { - if (request.getUserPrincipal() != null && request.getUserPrincipal() instanceof RapPrincipal) { - return request.getUserPrincipal().getName(); - } else { - return session.getUserId(); - } + return request.getUserPrincipal().getName(); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/GroupPermission.java b/gms/src/main/java/it/inaf/ia2/gms/model/GroupPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..0846da98be18559f67eb2a676a8b327e846dc8a5 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/model/GroupPermission.java @@ -0,0 +1,16 @@ +package it.inaf.ia2.gms.model; + +import it.inaf.ia2.gms.model.response.UserGroup; + +public class GroupPermission extends UserGroup { + + private Permission permission; + + public Permission getPermission() { + return permission; + } + + public void setPermission(Permission permission) { + this.permission = permission; + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java b/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java deleted file mode 100644 index c879da9477a774321659fdb6e08ae145459b933e..0000000000000000000000000000000000000000 --- a/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java +++ /dev/null @@ -1,59 +0,0 @@ -package it.inaf.ia2.gms.model; - -public class Identity { - - private IdentityType type; - private String typedId; - private String email; - private String name; - private String surname; - private boolean primary; - - public IdentityType getType() { - return type; - } - - public void setType(IdentityType type) { - this.type = type; - } - - public String getTypedId() { - return typedId; - } - - public void setTypedId(String typedId) { - this.typedId = typedId; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getSurname() { - return surname; - } - - public void setSurname(String surname) { - this.surname = surname; - } - - public boolean isPrimary() { - return primary; - } - - public void setPrimary(boolean primary) { - this.primary = primary; - } -} diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java b/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java deleted file mode 100644 index f5c80c80585b9905210f5cce4485279ab5b9f027..0000000000000000000000000000000000000000 --- a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java +++ /dev/null @@ -1,38 +0,0 @@ -package it.inaf.ia2.gms.model; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Arrays; - -public enum IdentityType { - - EDU_GAIN("eduGAIN"), - X509("X.509"), - ORCID("OrcID"), - GOOGLE("Google"), - LINKEDIN("LinkedIn"), - FACEBOOK("Facebook"), - LOCAL_IDP("LocalIdP"); - - private final String value; - - IdentityType(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - @JsonCreator - public static IdentityType forValue(String value) { - return Arrays.stream(IdentityType.values()) - .filter(it -> value.equals(it.value)).findFirst() - .orElseThrow(() -> new IllegalArgumentException("Unsupported IdentityType " + value)); - } - - @JsonValue - public String toValue() { - return value; - } -} diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java b/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java deleted file mode 100644 index 846b317d99017322f499d48a7b1bd8f5a8cdc31c..0000000000000000000000000000000000000000 --- a/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java +++ /dev/null @@ -1,63 +0,0 @@ -package it.inaf.ia2.gms.model; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -public class RapUser { - - private String id; - private List<Identity> identities; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public List<Identity> getIdentities() { - return identities; - } - - public void setIdentities(List<Identity> identities) { - this.identities = identities; - } - - public String getDisplayName() { - - String displayName = null; - - // trying to extract name and surname - for (Identity identity : identities) { - if (identity.getName() != null && identity.getSurname() != null) { - displayName = String.format("%s %s", identity.getName(), identity.getSurname()); - if (identity.isPrimary()) { // prefer always primary - break; - } - } - } - - if (displayName == null) { // No name and surname --> using primary email - displayName = getPrimaryEmail(); - } - - // Adding types - Set<String> types = identities.stream().map(i -> { - if (i.getType() == IdentityType.EDU_GAIN && i.getTypedId().endsWith("@ia2.inaf.it")) { - return "IA2"; - } - return i.getType().getValue(); - }).collect(Collectors.toSet()); - displayName += String.format(" (%s)", String.join(", ", types)); - - return displayName; - } - - public String getPrimaryEmail() { - Identity primaryIdentity = identities.stream().filter(i -> i.isPrimary()).findFirst() - .orElseThrow(() -> new IllegalStateException("No primary identity for user " + id)); - return primaryIdentity.getEmail(); - } -} diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/RapUserPermission.java b/gms/src/main/java/it/inaf/ia2/gms/model/RapUserPermission.java new file mode 100644 index 0000000000000000000000000000000000000000..8417970e0bb3cb650b68d408e0132764e92d5c01 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/model/RapUserPermission.java @@ -0,0 +1,25 @@ +package it.inaf.ia2.gms.model; + +import it.inaf.ia2.rap.data.RapUser; + +public class RapUserPermission { + + private RapUser user; + private Permission permission; + + public RapUser getUser() { + return user; + } + + public void setUser(RapUser user) { + this.user = user; + } + + public Permission getPermission() { + return permission; + } + + public void setPermission(Permission permission) { + this.permission = permission; + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/UserPermission.java b/gms/src/main/java/it/inaf/ia2/gms/model/UserPermission.java index 66ab93a33f3bf02c4337ccd7b033c9ee91e94fdb..38b95068b9189546822c6e474af0407c6babec84 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/model/UserPermission.java +++ b/gms/src/main/java/it/inaf/ia2/gms/model/UserPermission.java @@ -2,15 +2,15 @@ package it.inaf.ia2.gms.model; public class UserPermission { - private RapUser user; + private String userId; private Permission permission; - public RapUser getUser() { - return user; + public String getUserId() { + return userId; } - public void setUser(RapUser user) { - this.user = user; + public void setUserId(String userId) { + this.userId = userId; } public Permission getPermission() { diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java b/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java index 4d00c2489b6ff8f8c25f1aaf328e2b0aff2145b9..c3faa44f07338c9eddef86028af734a051dc1206 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java +++ b/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java @@ -1,6 +1,6 @@ package it.inaf.ia2.gms.model.response; -import it.inaf.ia2.gms.model.RapUser; +import it.inaf.ia2.rap.data.RapUser; import java.util.List; public class UserSearchResponse { diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java index 3cbffa59790a17845926da0690266a4bf1e93048..abe3041c36bcd623a5c579ea29d16699d3d9838d 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/GroupsDAO.java @@ -8,6 +8,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -159,6 +160,36 @@ public class GroupsDAO { }); } + /** + * @param groupIds + * @return map having group id as keys and group complete name as values + */ + public Map<String, String> getGroupCompleteNamesFromId(Set<String> groupIds) { + + Map<String, String> result = new HashMap<>(); + + if (groupIds.isEmpty()) { + return result; + } + + String sql = "SELECT id, complete_name FROM group_complete_name WHERE id IN (" + + String.join(",", Collections.nCopies(groupIds.size(), "?")) + ")"; + + jdbcTemplate.query(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + int i = 0; + for (String groupId : groupIds) { + ps.setString(++i, groupId); + } + return ps; + }, (rs, index) -> { + result.put(rs.getString("id"), rs.getString("complete_name")); + return null; + }); + + return result; + } + public Optional<GroupEntity> findGroupByParentAndName(String parentPath, String childName) { String sql = "SELECT id, path, is_leaf, locked from gms_group WHERE name = ? AND path ~ ?"; diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java index cf7cf8d53fb68ad3d662d3cd51f1331528a53676..dc26fd488dede187917961d2a3ebad10d73551b0 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java @@ -1,7 +1,5 @@ package it.inaf.ia2.gms.persistence; -import it.inaf.ia2.gms.authn.RapPrincipal; -import it.inaf.ia2.gms.authn.SessionData; import java.io.PrintWriter; import java.io.StringWriter; import java.sql.PreparedStatement; @@ -9,7 +7,6 @@ import javax.servlet.http.HttpServletRequest; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @@ -24,9 +21,6 @@ public class LoggingDAO { @Autowired(required = false) private HttpServletRequest request; - @Autowired(required = false) - private SessionData sessionData; - @Autowired public LoggingDAO(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); @@ -89,13 +83,8 @@ public class LoggingDAO { } private String getUser(HttpServletRequest request) { - if (request.getUserPrincipal() != null && request.getUserPrincipal() instanceof RapPrincipal) { + if (request != null && request.getUserPrincipal() != null) { return request.getUserPrincipal().getName(); - } else if (request.getSession(false) != null) { - try { - return sessionData.getUserId(); - } catch (BeanCreationException ex) { - } } return null; } diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java index 48bc56e4ce55d914c0d7d76eb0874384c3bf5770..7191496a4b8903e0731e598fd3f0cf92a27f965f 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java @@ -81,6 +81,10 @@ public class PermissionsDAO { }); } + /** + * Finds all direct user permissions for a given parent path (returns also + * all sub-groups permissions). + */ public List<PermissionEntity> findUserPermissions(String userId, String path) { String sql = "SELECT group_id, permission, group_path FROM gms_permission WHERE user_id = ?\n" diff --git a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java deleted file mode 100644 index 76cb9ba51274697087fbecb8b653a7b0f9b02c72..0000000000000000000000000000000000000000 --- a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java +++ /dev/null @@ -1,199 +0,0 @@ -package it.inaf.ia2.gms.rap; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import it.inaf.ia2.gms.authn.SessionData; -import it.inaf.ia2.gms.model.RapUser; -import java.util.ArrayList; -import java.util.Base64; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; - -@Component -public class RapClient { - - private static final Logger LOG = LoggerFactory.getLogger(RapClient.class); - - @Value("${rap.ws-url}") - private String rapBaseUrl; - - @Value("${security.oauth2.client.access-token-uri}") - private String accessTokenUri; - - @Value("${security.oauth2.client.client-id}") - private String clientId; - - @Value("${security.oauth2.client.client-secret}") - private String clientSecret; - - @Value("${security.oauth2.client.scope}") - private String scope; - - /* Use basic auth instead of JWT when asking for users - * Needed for Franco's version. */ - @Value("${rap.ws.basic-auth}") - private boolean basicAuth; - - @Autowired - private HttpServletRequest request; - - @Autowired(required = false) - private SessionData sessionData; - - private final RestTemplate rapRestTemplate; - - private final RestTemplate refreshTokenRestTemplate; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Autowired - public RapClient(RestTemplate rapRestTemplate) { - this.rapRestTemplate = rapRestTemplate; - this.refreshTokenRestTemplate = new RestTemplate(); - } - - public RapUser getUser(String userId) { - - String url = rapBaseUrl + "/user/" + userId; - - return httpCall(entity -> { - return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference<RapUser>() { - }).getBody(); - }); - } - - public List<RapUser> getUsers(Set<String> identifiers) { - - if (identifiers.isEmpty()) { - return new ArrayList<>(); - } - - String url = rapBaseUrl + "/user?identifiers=" + String.join(",", identifiers); - - return httpCall(entity -> { - return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference<List<RapUser>>() { - }).getBody(); - }); - } - - public List<RapUser> searchUsers(String searchText) { - - if (searchText == null || searchText.trim().isEmpty()) { - return new ArrayList<>(); - } - - String url = rapBaseUrl + "/user?search=" + searchText; - - return httpCall(entity -> { - return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference<List<RapUser>>() { - }).getBody(); - }); - } - - private <R> R httpCall(Function<HttpEntity<?>, R> function) { - return httpCall(function, null); - } - - private <R, T> R httpCall(Function<HttpEntity<?>, R> function, T body) { - try { - try { - return function.apply(getEntity(body)); - } catch (HttpClientErrorException.Unauthorized ex) { - if (request.getSession(false) == null || sessionData.getExpiresIn() > 0) { - // we can't refresh the token without a session - throw ex; - } - refreshToken(); - return function.apply(getEntity(body)); - } - } catch (HttpStatusCodeException ex) { - try { - Map<String, String> map = objectMapper.readValue(ex.getResponseBodyAsString(), Map.class); - if (map.containsKey("error")) { - String error = map.get("error"); - if (ex instanceof HttpClientErrorException) { - throw new HttpClientErrorException(ex.getStatusCode(), error); - } else if (ex instanceof HttpServerErrorException) { - throw new HttpServerErrorException(ex.getStatusCode(), error); - } - } - } catch (JsonProcessingException ignore) { - } - throw ex; - } - } - - private <T> HttpEntity<T> getEntity(T body) { - - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - - if (basicAuth) { // Franco's version - String auth = clientId + ":" + clientSecret; - String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes()); - headers.add("Authorization", "Basic " + encodedAuth); - - HttpSession session = request.getSession(false); - if (session != null) { - String clientDb = (String) session.getAttribute("client_db"); - if (clientDb != null) { - headers.add("client_db", clientDb); - LOG.debug("client_db=" + clientDb); - } - } - } else if (request.getSession(false) != null) { - headers.add("Authorization", "Bearer " + sessionData.getAccessToken()); - } else { - // from JWT web service - headers.add("Authorization", request.getHeader("Authorization")); - } - - return new HttpEntity<>(body, headers); - } - - public void refreshToken() { - - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - headers.setBasicAuth(clientId, clientSecret); - - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); - map.add("grant_type", "refresh_token"); - map.add("refresh_token", sessionData.getRefreshToken()); - map.add("scope", scope.replace(",", " ")); - - HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); - - ResponseEntity<Map> response = refreshTokenRestTemplate.postForEntity(accessTokenUri, request, Map.class); - - Map<String, Object> values = response.getBody(); - - sessionData.setAccessToken((String) values.get("access_token")); - sessionData.setRefreshToken((String) values.get("refresh_token")); - sessionData.setExpiresIn((int) values.get("expires_in")); - } -} diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupNameService.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupNameService.java index 5668fb21a63083f43acd0718666cb8f7d7a18242..98aaeadd5cf3beb79eba5d021a90334e72346e5b 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupNameService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupNameService.java @@ -1,15 +1,17 @@ package it.inaf.ia2.gms.service; +import it.inaf.ia2.gms.exception.BadRequestException; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; -import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -20,8 +22,12 @@ import org.springframework.stereotype.Service; @Service public class GroupNameService { + private final GroupsDAO groupsDAO; + @Autowired - private GroupsDAO groupsDAO; + public GroupNameService(GroupsDAO groupsDAO) { + this.groupsDAO = groupsDAO; + } public List<String> getGroupsNamesFromIdentifiers(Set<String> groupIdentifiers) { return getGroupsNames(groupsDAO.findGroupsByIds(groupIdentifiers)); @@ -37,113 +43,113 @@ public class GroupNameService { */ public List<String> getGroupsNames(List<GroupEntity> groups) { - // We need to return the complete group name, so it is necessary to load - // all the parents too. - Map<String, String> idNameMap = new HashMap<>(); - Set<String> allIdentifiers = getAllIdentifiers(groups); - for (GroupEntity group : groupsDAO.findGroupsByIds(allIdentifiers)) { - idNameMap.put(group.getId(), group.getName()); - } + Set<String> groupIds = groups.stream().map(g -> g.getId()).collect(Collectors.toSet()); - List<String> names = new ArrayList<>(); - for (GroupEntity group : groups) { - names.add(getGroupCompleteName(group, idNameMap)); - } - return names; - } + List<String> names = new ArrayList<>(groupsDAO.getGroupCompleteNamesFromId(groupIds).values()); - private Set<String> getAllIdentifiers(List<GroupEntity> groups) { + Collections.sort(names); - Set<String> allIdentifiers = new HashSet<>(); - for (GroupEntity group : groups) { - if (!"".equals(group.getPath())) { - String[] ids = group.getPath().split("\\."); - for (String id : ids) { - allIdentifiers.add(id); - } - } - } - - return allIdentifiers; + return names; } - private String getGroupCompleteName(GroupEntity group, Map<String, String> idNameMap) { + /** + * @param groups + * @return map having group id as keys and group names as values + */ + public Map<String, List<String>> getNames(Set<GroupEntity> groups) { - if ("ROOT".equals(group.getId())) { - return group.getName(); - } + Set<String> groupIds = groups.stream() + .map(g -> g.getId()).collect(Collectors.toSet()); - List<String> names = new ArrayList<>(); + return getNamesFromIds(groupIds); + } - for (String groupId : group.getPath().split("\\.")) { + public Map<String, List<String>> getNamesFromIds(Set<String> groupIds) { - String groupName = idNameMap.get(groupId); + Map<String, List<String>> result = new HashMap<>(); - // Dot inside names is considered a special character (because it is - // used to separate the group from its parents), so we use a - // backslash to escape it (client apps need to be aware of this). - groupName = groupName.replace("\\.", "\\\\."); + if (groupIds.contains("ROOT")) { + result.put("ROOT", Collections.singletonList(getRoot().getName())); + } - names.add(groupName); + for (Map.Entry<String, String> entry : groupsDAO.getGroupCompleteNamesFromId(groupIds).entrySet()) { + List<String> names = splitNames(entry.getValue()); + result.put(entry.getKey(), names); } - return String.join(".", names); + return result; } - /** - * @param groupsIdPath map having group id as keys and group paths as values - * @return map having group id as keys and group names as values - */ - public Map<String, List<String>> getNames(List<Map.Entry<String, String>> groupsIdPath) { + private List<String> splitNames(String completeGroupName) { + return Arrays.asList(completeGroupName.split("(?<!\\\\)\\.")); + } - Set<String> allIdentifiers = new HashSet<>(); - for (Map.Entry<String, String> entry : groupsIdPath) { - allIdentifiers.addAll(getIdentifiers(entry.getValue())); + public String getShortGroupName(String completeGroupName, Optional<String> groupPrefix) { + if (groupPrefix.isPresent()) { + return completeGroupName.substring(groupPrefix.get().length() + 1); } + return completeGroupName; + } + + public GroupEntity getGroupFromNames(Optional<String> group) { - Map<String, String> groupSingleNamesMap = getGroupSingleNamesMap(allIdentifiers); + List<String> groupNames = extractGroupNames(group); - Map<String, List<String>> groupCompleteNamesMap = new HashMap<>(); - for (Map.Entry<String, String> entry : groupsIdPath) { - List<String> groupCompleteName = getGroupCompleteName(groupSingleNamesMap, entry.getValue()); - groupCompleteNamesMap.put(entry.getKey(), groupCompleteName); + if (groupNames.isEmpty()) { + return getRoot(); } + return getGroupFromNamesAndIndex(groupNames, groupNames.size() - 1); + } - return groupCompleteNamesMap; + public GroupEntity getGroupFromNamesAndIndex(Optional<String> group, int index) { + List<String> groupNames = extractGroupNames(group); + return getGroupFromNamesAndIndex(groupNames, index); } - private Map<String, String> getGroupSingleNamesMap(Set<String> allIdentifiers) { - Map<String, String> groupNamesMap = new HashMap<>(); - for (GroupEntity group : groupsDAO.findGroupsByIds(allIdentifiers)) { - groupNamesMap.put(group.getId(), group.getName()); + private GroupEntity getGroupFromNamesAndIndex(List<String> groupNames, int index) { + String parentPath = ""; // starting from ROOT + GroupEntity group = null; + for (int i = 0; i < index + 1; i++) { + String groupName = groupNames.get(i); + group = groupsDAO.findGroupByParentAndName(parentPath, groupName) + .orElseThrow(() -> new BadRequestException("Unable to find group " + groupName)); + parentPath = group.getPath(); + } + if (group == null) { + throw new IllegalStateException(); } + return group; + } - return groupNamesMap; + private List<String> extractGroupNames(Optional<String> group) { + return extractGroupNames(group.orElse(null)); } - private List<String> getGroupCompleteName(Map<String, String> groupNamesMap, String groupPath) { + public List<String> extractGroupNames(String groupStr) { + + if (groupStr == null || groupStr.isEmpty()) { + return new ArrayList<>(); + } + List<String> names = new ArrayList<>(); - if (groupPath.isEmpty()) { - names.add("Root"); - } else { - List<String> identifiers = getIdentifiers(groupPath); - for (String groupId : identifiers) { - names.add(groupNamesMap.get(groupId)); + String currentName = ""; + for (int i = 0; i < groupStr.length(); i++) { + char c = groupStr.charAt(i); + // dot is the group separator and it must be escaped if used inside + // group names + if (c == '.' && groupStr.charAt(i - 1) != '\\') { + names.add(currentName.replace("\\.", ".")); + currentName = ""; + } else { + currentName += c; } } + names.add(currentName); return names; } - /** - * Returns the list of all identifiers including parent ones. - */ - private List<String> getIdentifiers(String groupPath) { - List<String> identifiers = new ArrayList<>(); - if (!groupPath.isEmpty()) { - for (String id : groupPath.split(Pattern.quote("."))) { - identifiers.add(id); - } - } - return identifiers; + private GroupEntity getRoot() { + return groupsDAO.findGroupById("ROOT") + .orElseThrow(() -> new IllegalStateException("Missing root group")); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java index 9f59907cc51db024182baba78611e6c58b902135..8ba7f3c95737f20403746e13fe26a0a68413d88d 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java @@ -13,11 +13,11 @@ import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; -import java.util.AbstractMap.SimpleEntry; +import it.inaf.ia2.rap.client.RapClient; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -58,7 +58,7 @@ public class SearchService { } private List<SearchResponseItem> searchUsers(String query) { - return rapClient.searchUsers(query).stream() + return rapClient.getUsers(query).stream() .map(u -> { SearchResponseItem item = new SearchResponseItem(); item.setType(SearchResponseType.USER); @@ -75,22 +75,16 @@ public class SearchService { // Select only the groups visible to the user List<PermissionEntity> permissions = permissionsDAO.findUserPermissions(userId); - - List<Map.Entry<String, String>> groupsIdPath = new ArrayList<>(); - for (GroupEntity group : allGroups) { - PermissionUtils.getGroupPermission(group, permissions).ifPresent(permission -> { - groupsIdPath.add(new SimpleEntry<>(group.getId(), group.getPath())); - }); - } + Set<GroupEntity> visibleGroups = getVisibleGroups(allGroups, permissions); List<SearchResponseItem> items = new ArrayList<>(); - Map<String, List<String>> groupNames = groupNameService.getNames(groupsIdPath); - for (Map.Entry<String, String> entry : groupsIdPath) { - String groupId = entry.getKey(); + Map<String, List<String>> groupNames = groupNameService.getNames(visibleGroups); + + for (GroupEntity group : visibleGroups) { SearchResponseItem item = new SearchResponseItem(); item.setType(SearchResponseType.GROUP); - item.setId(groupId); - List<String> names = groupNames.get(groupId); + item.setId(group.getId()); + List<String> names = groupNames.get(group.getId()); item.setLabel(String.join(" / ", names)); items.add(item); } @@ -115,7 +109,7 @@ public class SearchService { sortByGroupCompleteName(groups); response.setGroups(groups); - List<UserPermission> permissions = getUserPermission(targetUserId, actorPermissions); + List<UserPermission> permissions = getUserPermission(groupsManager.getRoot(), targetUserId, actorPermissions); sortByGroupCompleteName(permissions); response.setPermissions(permissions); @@ -129,15 +123,9 @@ public class SearchService { List<GroupEntity> allGroups = membershipsDAO.getUserMemberships(targetUserId); // Select only groups visible to the actor user - List<Map.Entry<String, String>> visibleGroupsIdPath = new ArrayList<>(); - for (GroupEntity group : allGroups) { - - PermissionUtils.getGroupPermission(group, actorPermissions).ifPresent(permission -> { - visibleGroupsIdPath.add(new SimpleEntry<>(group.getId(), group.getPath())); - }); - } + Set<GroupEntity> visibleGroups = getVisibleGroups(allGroups, actorPermissions); - return groupNameService.getNames(visibleGroupsIdPath).entrySet().stream() + return groupNameService.getNames(visibleGroups).entrySet().stream() .map(entry -> { UserGroup ug = new UserGroup(); ug.setGroupId(entry.getKey()); @@ -147,24 +135,28 @@ public class SearchService { .collect(Collectors.toList()); } - public List<UserPermission> getUserPermission(String targetUserId, List<PermissionEntity> actorPermissions) { + private Set<GroupEntity> getVisibleGroups(List<GroupEntity> allGroups, List<PermissionEntity> permissions) { + return allGroups.stream() + .filter(g -> PermissionUtils.getGroupPermission(g, permissions).isPresent()) + .collect(Collectors.toSet()); + } + + public List<UserPermission> getUserPermission(GroupEntity group, String targetUserId, List<PermissionEntity> actorPermissions) { List<UserPermission> permissions = new ArrayList<>(); // Super-admin user is able to see also other user permissions - PermissionUtils.getGroupPermission(groupsManager.getRoot(), actorPermissions).ifPresent(permission -> { + PermissionUtils.getGroupPermission(group, actorPermissions).ifPresent(permission -> { if (permission.equals(Permission.ADMIN)) { Map<String, PermissionEntity> targetUserPermissions = permissionsDAO.findUserPermissions(targetUserId).stream() .collect(Collectors.toMap(PermissionEntity::getGroupId, p -> p)); - List<Map.Entry<String, String>> groupsIdPath = new ArrayList<>(); - for (PermissionEntity p : targetUserPermissions.values()) { - groupsIdPath.add(new SimpleEntry<>(p.getGroupId(), p.getGroupPath())); - } + Set<String> groupIds = targetUserPermissions.values().stream() + .map(p -> p.getGroupId()).collect(Collectors.toSet()); - for (Map.Entry<String, List<String>> entry : groupNameService.getNames(groupsIdPath).entrySet()) { + for (Map.Entry<String, List<String>> entry : groupNameService.getNamesFromIds(groupIds).entrySet()) { UserPermission up = new UserPermission(); up.setGroupId(entry.getKey()); up.setGroupCompleteName(entry.getValue()); diff --git a/gms/src/main/resources/auth.properties b/gms/src/main/resources/auth.properties index 7bd1b03a1fa98b51489556599bb2898e8ebf3297..ff3fda0e610e1b8891c06aedc2d3d2ef3712f22e 100644 --- a/gms/src/main/resources/auth.properties +++ b/gms/src/main/resources/auth.properties @@ -1,5 +1,7 @@ client_id=gms client_secret=gms-secret +rap_uri=http://localhost/rap-ia2 +jwks_endpoint=/auth/oidc/jwks access_token_uri=http://localhost/rap-ia2/auth/oauth2/token user_authorization_uri=http://localhost/rap-ia2/auth/oauth2/authorize check_token_uri=http://localhost/rap-ia2/auth/oauth2/token diff --git a/gms/src/main/resources/sql/init.sql b/gms/src/main/resources/sql/init.sql index 05aec53ca465b3b7fe8241685973524e74e2daae..41360a8b799a960fd6a51a32a6a534effbe99eeb 100644 --- a/gms/src/main/resources/sql/init.sql +++ b/gms/src/main/resources/sql/init.sql @@ -63,3 +63,18 @@ CREATE TABLE invited_registration_request_group ( FOREIGN KEY (request_id) REFERENCES invited_registration_request(id), FOREIGN KEY (group_id) REFERENCES gms_group(id) ); + +CREATE VIEW group_complete_name AS +SELECT id, string_agg(name, '.') AS complete_name +FROM ( + SELECT replace(name, '.', '\.') AS name, p.id + FROM gms_group g + JOIN ( + SELECT UNNEST(string_to_array(path::varchar, '.')) AS rel_id, id + FROM gms_group + ) AS p ON g.id = p.rel_id + ORDER BY p.id, nlevel(g.path) +) AS j GROUP BY id +UNION +SELECT id, name AS complete_name FROM gms_group WHERE id = 'ROOT' +ORDER BY complete_name; diff --git a/gms/src/test/java/it/inaf/ia2/gms/GmsTestUtils.java b/gms/src/test/java/it/inaf/ia2/gms/GmsTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..ed79a9b487d01f080584b3eca67379d05dba4e4c --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/GmsTestUtils.java @@ -0,0 +1,19 @@ +package it.inaf.ia2.gms; + +import java.security.Principal; +import javax.servlet.http.HttpServletRequest; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GmsTestUtils { + + public static void mockPrincipal(HttpServletRequest mockedServletRequest) { + mockPrincipal(mockedServletRequest, "admin_id"); + } + + public static void mockPrincipal(HttpServletRequest mockedServletRequest, String userId) { + Principal principal = mock(Principal.class); + when(principal.getName()).thenReturn(userId); + when(mockedServletRequest.getUserPrincipal()).thenReturn(principal); + } +} diff --git a/gms/src/test/java/it/inaf/ia2/gms/authn/ClientDbFilterTest.java b/gms/src/test/java/it/inaf/ia2/gms/authn/ClientDbFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..171d0508dcdaf2091c13a57cca56c91ddb2242f0 --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/authn/ClientDbFilterTest.java @@ -0,0 +1,46 @@ +package it.inaf.ia2.gms.authn; + +import it.inaf.ia2.aa.AuthConfig; +import it.inaf.ia2.rap.client.RapClient; +import java.net.URI; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.eq; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ClientDbFilterTest { + + @Mock + private HttpServletRequest request; + + @Mock + private AuthConfig authConfig; + + @Mock + private RapClient rapClient; + + private ClientDbFilter filter; + + @Test + public void testJwksUriOverride() throws Exception { + + when(authConfig.getRapBaseUri()).thenReturn("http://ia2.inaf.it"); + when(authConfig.getJwksEndpoint()).thenReturn("/jwks?client_name=db0"); + when(request.getSession()).thenReturn(mock(HttpSession.class)); + when(request.getParameter(eq("client_db"))).thenReturn("other_db"); + + filter = new ClientDbFilter(authConfig, rapClient); + filter.doFilter(request, mock(HttpServletResponse.class), mock(FilterChain.class)); + + verify(rapClient).addJwksUri(eq(URI.create("http://ia2.inaf.it/jwks?client_name=other_db"))); + } +} diff --git a/gms/src/test/java/it/inaf/ia2/gms/authn/SessionDataTest.java b/gms/src/test/java/it/inaf/ia2/gms/authn/SessionDataTest.java index 00648021317db5c3704b58e56feb509e7072f644..75d44989f48e75cce0fd7d7c62b8079c91acd6e7 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/authn/SessionDataTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/authn/SessionDataTest.java @@ -1,6 +1,7 @@ package it.inaf.ia2.gms.authn; import it.inaf.ia2.aa.data.User; +import it.inaf.ia2.rap.client.RapClient; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import static org.junit.Assert.assertTrue; @@ -18,6 +19,9 @@ public class SessionDataTest { @Mock private HttpServletRequest request; + + @Mock + private RapClient rapClient; @InjectMocks private SessionData sessionData; diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java index f09287e45d31828aad7464f635384ecc9eca5fab..cdbdb8b42f3ce19f906ebcb99d35b604c01a58a2 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java @@ -1,7 +1,7 @@ package it.inaf.ia2.gms.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.GmsTestUtils; import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.GroupNode; @@ -14,6 +14,7 @@ import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.GroupsTreeBuilder; import java.util.ArrayList; import java.util.List; +import javax.servlet.http.HttpServletRequest; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import org.junit.Before; @@ -50,7 +51,7 @@ public class GroupsControllerTest { private GroupsService groupsService; @Mock - private SessionData session; + private HttpServletRequest servletRequest; @Mock private PermissionsManager permissionsManager; @@ -71,6 +72,7 @@ public class GroupsControllerTest { @Before public void init() { mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + GmsTestUtils.mockPrincipal(servletRequest); } @Test @@ -104,8 +106,6 @@ public class GroupsControllerTest { PaginatedData<GroupNode> paginatedData = new PaginatedData<>(nodes, 1, 10); when(groupsTreeBuilder.listSubGroups(any(), any(), any())).thenReturn(paginatedData); - when(session.getUserId()).thenReturn("admin_id"); - mockMvc.perform(post("/group") .content(mapper.writeValueAsString(request)) .contentType(MediaType.APPLICATION_JSON)) diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilderTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilderTest.java index 937fbe74b33dd9e0756099bcb62d47f14a00ffb1..07f1e8577751f8a8c61340f530a971457bb3b269 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilderTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsTabResponseBuilderTest.java @@ -1,6 +1,6 @@ package it.inaf.ia2.gms.controller; -import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.GmsTestUtils; import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.InvitedRegistrationManager; import it.inaf.ia2.gms.manager.PermissionsManager; @@ -13,6 +13,7 @@ import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.GroupsTreeBuilder; import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.Test; @@ -28,7 +29,7 @@ import org.mockito.junit.MockitoJUnitRunner; public class GroupsTabResponseBuilderTest { @Mock - private SessionData session; + private HttpServletRequest servletRequest; @Mock private GroupsManager groupsManager; @@ -51,7 +52,7 @@ public class GroupsTabResponseBuilderTest { @Test public void testGetGroupsTab() { - when(session.getUserId()).thenReturn("admin_id"); + GmsTestUtils.mockPrincipal(servletRequest); GroupEntity root = new GroupEntity(); root.setId("ROOT"); diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java index 625e72c14dc3f3feb6113d24a72e2a309fe791c4..1a086e3dcf0112b6437841e576ef81227f0e0393 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java @@ -4,15 +4,16 @@ import it.inaf.ia2.gms.manager.GroupsManager; import it.inaf.ia2.gms.manager.MembershipManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; -import it.inaf.ia2.gms.model.UserPermission; +import it.inaf.ia2.gms.model.RapUserPermission; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.MembershipEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; +import it.inaf.ia2.gms.service.GroupNameService; import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.JoinService; +import it.inaf.ia2.rap.data.RapUser; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; @@ -81,6 +82,7 @@ public class JWTWebServiceControllerTest { @Before public void init() { + controller.groupNameService = new GroupNameService(groupsDAO); mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); root = getRoot(); lbt = getLbtGroup(); @@ -193,8 +195,8 @@ public class JWTWebServiceControllerTest { when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbt)); when(groupsDAO.findGroupByParentAndName("lbt_id", "INAF")).thenReturn(Optional.of(inaf)); - List<UserPermission> permissions = new ArrayList<>(); - UserPermission up = new UserPermission(); + List<RapUserPermission> permissions = new ArrayList<>(); + RapUserPermission up = new RapUserPermission(); up.setUser(getRapUser()); up.setPermission(Permission.ADMIN); permissions.add(up); diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java index f419eba57f56c1c58302ffd750434f5eaf4bd39b..bfe0b1a1bb7b81ff65c4010b430779c011d2d1f5 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java @@ -1,12 +1,13 @@ package it.inaf.ia2.gms.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.GmsTestUtils; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.response.SearchResponseItem; import it.inaf.ia2.gms.model.response.UserSearchResponse; import it.inaf.ia2.gms.service.SearchService; import java.util.ArrayList; +import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,7 +30,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; public class SearchControllerTest { @Mock - private SessionData session; + private HttpServletRequest servletRequest; @Mock private SearchService searchService; @@ -44,8 +45,7 @@ public class SearchControllerTest { @Before public void init() { mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); - - when(session.getUserId()).thenReturn("admin_id"); + GmsTestUtils.mockPrincipal(servletRequest); } @Test @@ -56,7 +56,7 @@ public class SearchControllerTest { when(searchService.search(any(), any(), anyInt(), anyInt())).thenReturn(response); mockMvc.perform(get("/search?query=searchText&page=1&pageSize=10") - .contentType(MediaType.APPLICATION_JSON_UTF8)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()); verify(searchService, times(1)).search(eq("searchText"), eq("admin_id"), eq(1), eq(10)); @@ -68,7 +68,7 @@ public class SearchControllerTest { when(searchService.getUserSearchResult(any(), any())).thenReturn(new UserSearchResponse()); mockMvc.perform(get("/search/user/user_id") - .contentType(MediaType.APPLICATION_JSON_UTF8)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()); verify(searchService, times(1)).getUserSearchResult(eq("admin_id"), eq("user_id")); diff --git a/gms/src/test/java/it/inaf/ia2/gms/manager/InvitedRegistrationManagerTest.java b/gms/src/test/java/it/inaf/ia2/gms/manager/InvitedRegistrationManagerTest.java index 13cf87d37e771480d2d06898e3cbcf7e356f08fa..4ce897a21f44e4e3c047087336188913ba198e60 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/manager/InvitedRegistrationManagerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/manager/InvitedRegistrationManagerTest.java @@ -1,24 +1,25 @@ package it.inaf.ia2.gms.manager; -import it.inaf.ia2.gms.authn.SessionData; -import it.inaf.ia2.gms.model.Identity; -import it.inaf.ia2.gms.model.IdentityType; +import it.inaf.ia2.gms.GmsTestUtils; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.InvitedRegistrationDAO; import it.inaf.ia2.gms.persistence.LoggingDAO; import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.InvitedRegistration; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.PermissionsService; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.Identity; +import it.inaf.ia2.rap.data.IdentityType; +import it.inaf.ia2.rap.data.RapUser; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,7 @@ public class InvitedRegistrationManagerTest { @Mock private RapClient rapClient; @Mock - private SessionData sessionData; + private HttpServletRequest servletRequest; @Mock private LoggingDAO loggingDAO; @Mock @@ -95,14 +96,14 @@ public class InvitedRegistrationManagerTest { when(httpSession.getAttribute(eq("invited-registration"))).thenReturn(regFromToken); - when(sessionData.getUserId()).thenReturn(USER_ID); + GmsTestUtils.mockPrincipal(servletRequest, USER_ID); RapUser user = new RapUser(); user.setId(USER_ID); Identity identity = new Identity(); identity.setType(IdentityType.EDU_GAIN); identity.setEmail(EMAIL); - user.setIdentities(Collections.singletonList(identity)); + user.getIdentities().addAll(Collections.singletonList(identity)); when(rapClient.getUser(eq(USER_ID))).thenReturn(user); @@ -145,7 +146,7 @@ public class InvitedRegistrationManagerTest { when(httpSession.getAttribute(eq("invited-registration"))).thenReturn(regFromToken); - when(sessionData.getUserId()).thenReturn(USER_ID); + GmsTestUtils.mockPrincipal(servletRequest, USER_ID); RapUser user = new RapUser(); user.setId(USER_ID); diff --git a/gms/src/test/java/it/inaf/ia2/gms/manager/PermissionsManagerIntegrationTest.java b/gms/src/test/java/it/inaf/ia2/gms/manager/PermissionsManagerIntegrationTest.java index 9a25bc6eaf540ce6f9465fdfe44374f014eb56cd..b0b73601f1e7719528b9e88980b50f1815aa539b 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/manager/PermissionsManagerIntegrationTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/manager/PermissionsManagerIntegrationTest.java @@ -2,17 +2,18 @@ package it.inaf.ia2.gms.manager; import it.inaf.ia2.gms.DataSourceConfig; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; -import it.inaf.ia2.gms.model.UserPermission; +import it.inaf.ia2.gms.model.RapUserPermission; import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.LoggingDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.PermissionsService; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.RapUser; import java.util.Collections; import java.util.List; +import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.sql.DataSource; import static org.junit.Assert.assertEquals; @@ -52,7 +53,7 @@ public class PermissionsManagerIntegrationTest { // Mock RAP client RapUser rapUser = new RapUser(); rapUser.setId(USER_ID); - when(rapClient.getUsers(any())).thenReturn(Collections.singletonList(rapUser)); + when(rapClient.getUsers(any(Set.class))).thenReturn(Collections.singletonList(rapUser)); PermissionsService permissionsService = new PermissionsService(permissionsDAO, loggingDAO); PermissionsManager permissionsManager = new PermissionsManager(permissionsService, rapClient, loggingDAO); @@ -61,7 +62,7 @@ public class PermissionsManagerIntegrationTest { // Create root GroupEntity root = new GroupEntity(); root.setId("ROOT"); - root.setName("Root"); + root.setName("ROOT"); root.setPath(""); root = groupsDAO.createGroup(root); @@ -72,7 +73,7 @@ public class PermissionsManagerIntegrationTest { superAdminPermission.setGroupPath(root.getPath()); permissionsDAO.createOrUpdatePermission(superAdminPermission); - List<UserPermission> permissions = permissionsManager.getAllPermissions(root); + List<RapUserPermission> permissions = permissionsManager.getAllPermissions(root); assertEquals(1, permissions.size()); assertEquals(Permission.ADMIN, permissions.get(0).getPermission()); diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java index fd56e8a6302bab237d6d0ffb69930f77e8a59dd7..17fb805a987694fb50bcd2fda50c433a4d3b0839 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/GroupsDAOTest.java @@ -5,9 +5,11 @@ import it.inaf.ia2.gms.HooksConfig; import it.inaf.ia2.gms.model.GroupBreadcrumb; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.service.hook.GroupsHook; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import javax.sql.DataSource; import static org.junit.Assert.assertEquals; @@ -114,6 +116,15 @@ public class GroupsDAOTest { assertTrue(optGroup.isPresent()); assertEquals(lbtInaf.getId(), optGroup.get().getId()); + // Complete names + Set<String> groupIds = new HashSet<>(); + groupIds.add(groups.get(0).getId()); + groupIds.add(lbt.getId()); + Map<String, String> completeGroupNames = dao.getGroupCompleteNamesFromId(groupIds); + assertEquals(2, completeGroupNames.size()); + assertEquals("LBT", completeGroupNames.get(lbt.getId())); + assertEquals("LBT.INAF", completeGroupNames.get(groups.get(0).getId())); + // Children map Map<String, Boolean> childrenMap = dao.getHasChildrenMap(Sets.newSet(root.getId())); assertEquals(1, childrenMap.size()); @@ -151,4 +162,9 @@ public class GroupsDAOTest { private String getNewGroupId() { return UUID.randomUUID().toString().replaceAll("-", ""); } + + @Test + public void testGroupCompleteNamesEmptyInput() { + assertTrue(dao.getGroupCompleteNamesFromId(new HashSet<>()).isEmpty()); + } } diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java index baa08c183dd0e426f696857d3d8320d28c79a5fe..73d7d9985bb3dc0541b0c7aecc51d4e96c13b304 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java @@ -10,9 +10,9 @@ import it.inaf.ia2.gms.model.Permission; import it.inaf.ia2.gms.model.request.GroupsRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; import it.inaf.ia2.gms.service.GroupsTreeBuilder; import it.inaf.ia2.gms.service.PermissionsService; +import it.inaf.ia2.rap.client.RapClient; import java.util.List; import javax.sql.DataSource; import static org.junit.Assert.assertEquals; diff --git a/gms/src/test/java/it/inaf/ia2/gms/rap/RapClientTest.java b/gms/src/test/java/it/inaf/ia2/gms/rap/RapClientTest.java index bd317c09d73c7def90f287ede0cf2c05ab08c584..09becbfc53457dea0335e75665a11b0003ac6cb0 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/rap/RapClientTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/rap/RapClientTest.java @@ -1,7 +1,6 @@ package it.inaf.ia2.gms.rap; import it.inaf.ia2.gms.authn.SessionData; -import it.inaf.ia2.gms.model.RapUser; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -34,120 +33,120 @@ import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.HttpServerErrorException.InternalServerError; import org.springframework.web.client.RestTemplate; -@RunWith(MockitoJUnitRunner.class) +//@RunWith(MockitoJUnitRunner.class) public class RapClientTest { - @Mock - private HttpServletRequest request; - - @Mock - private SessionData sessionData; - - @Mock - private RestTemplate restTemplate; - - @Mock - private RestTemplate refreshTokenRestTemplate; - - private RapClient rapClient; - - @Before - public void init() { - rapClient = new RapClient(restTemplate); - ReflectionTestUtils.setField(rapClient, "request", request); - ReflectionTestUtils.setField(rapClient, "refreshTokenRestTemplate", refreshTokenRestTemplate); - ReflectionTestUtils.setField(rapClient, "scope", "openid"); - } - - @Test - public void testUnauthorizedNoRefreshJsonMsg() { - - String jsonError = "{\"error\":\"Unauthorized: foo\"}"; - - HttpClientErrorException exception = Unauthorized - .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); - - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { - }))).thenThrow(exception); - - try { - rapClient.getUser("123"); - } catch (HttpClientErrorException ex) { - assertEquals("401 Unauthorized: foo", ex.getMessage()); - } - } - - @Test - public void testUnauthorizedNoRefreshNotJsonMsg() { - - String errorMessage = "THIS IS NOT A JSON"; - - HttpClientErrorException exception = Unauthorized - .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, errorMessage.getBytes(), StandardCharsets.UTF_8); - - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { - }))).thenThrow(exception); - - try { - rapClient.getUser("123"); - } catch (HttpClientErrorException ex) { - assertNotNull(ex.getMessage()); - } - } - - @Test - public void testServerErrorJsonMsg() { - - String jsonError = "{\"error\":\"Fatal error\"}"; - - HttpServerErrorException exception = InternalServerError - .create(HttpStatus.INTERNAL_SERVER_ERROR, "500", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); - - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { - }))).thenThrow(exception); - - try { - rapClient.getUser("123"); - } catch (HttpServerErrorException ex) { - assertEquals("500 Fatal error", ex.getMessage()); - } - } - - @Test - public void testRefreshToken() { - - when(request.getSession(eq(false))).thenReturn(mock(HttpSession.class)); - when(sessionData.getExpiresIn()).thenReturn(-100l); - - ReflectionTestUtils.setField(rapClient, "sessionData", sessionData); - ReflectionTestUtils.setField(rapClient, "clientId", "clientId"); - ReflectionTestUtils.setField(rapClient, "clientSecret", "clientSecret"); - ReflectionTestUtils.setField(rapClient, "accessTokenUri", "https://sso.ia2.inaf.it"); - - String jsonError = "{\"error\":\"Unauthorized: token expired\"}"; - - HttpClientErrorException exception = Unauthorized - .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); - - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { - }))).thenThrow(exception) - .thenReturn(ResponseEntity.ok(new RapUser())); - - ResponseEntity refreshTokenResponse = mock(ResponseEntity.class); - Map<String, Object> mockedBody = new HashMap<>(); - mockedBody.put("access_token", "<access_token>"); - mockedBody.put("refresh_token", "<refresh_token>"); - mockedBody.put("expires_in", 3600); - when(refreshTokenResponse.getBody()).thenReturn(mockedBody); - - when(refreshTokenRestTemplate.postForEntity(anyString(), any(HttpEntity.class), any())) - .thenReturn(refreshTokenResponse); - - RapUser user = rapClient.getUser("123"); - assertNotNull(user); - - // verifies that token is refreshed - verify(sessionData, times(1)).setAccessToken(eq("<access_token>")); - verify(sessionData, times(1)).setExpiresIn(eq(3600l)); - } +// @Mock +// private HttpServletRequest request; +// +// @Mock +// private SessionData sessionData; +// +// @Mock +// private RestTemplate restTemplate; +// +// @Mock +// private RestTemplate refreshTokenRestTemplate; +// +// private RapClient rapClient; +// +// @Before +// public void init() { +// rapClient = new RapClient(restTemplate); +// ReflectionTestUtils.setField(rapClient, "request", request); +// ReflectionTestUtils.setField(rapClient, "refreshTokenRestTemplate", refreshTokenRestTemplate); +// ReflectionTestUtils.setField(rapClient, "scope", "openid"); +// } +// +// @Test +// public void testUnauthorizedNoRefreshJsonMsg() { +// +// String jsonError = "{\"error\":\"Unauthorized: foo\"}"; +// +// HttpClientErrorException exception = Unauthorized +// .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); +// +// when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { +// }))).thenThrow(exception); +// +// try { +// rapClient.getUser("123"); +// } catch (HttpClientErrorException ex) { +// assertEquals("401 Unauthorized: foo", ex.getMessage()); +// } +// } +// +// @Test +// public void testUnauthorizedNoRefreshNotJsonMsg() { +// +// String errorMessage = "THIS IS NOT A JSON"; +// +// HttpClientErrorException exception = Unauthorized +// .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, errorMessage.getBytes(), StandardCharsets.UTF_8); +// +// when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { +// }))).thenThrow(exception); +// +// try { +// rapClient.getUser("123"); +// } catch (HttpClientErrorException ex) { +// assertNotNull(ex.getMessage()); +// } +// } +// +// @Test +// public void testServerErrorJsonMsg() { +// +// String jsonError = "{\"error\":\"Fatal error\"}"; +// +// HttpServerErrorException exception = InternalServerError +// .create(HttpStatus.INTERNAL_SERVER_ERROR, "500", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); +// +// when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { +// }))).thenThrow(exception); +// +// try { +// rapClient.getUser("123"); +// } catch (HttpServerErrorException ex) { +// assertEquals("500 Fatal error", ex.getMessage()); +// } +// } +// +// @Test +// public void testRefreshToken() { +// +// when(request.getSession(eq(false))).thenReturn(mock(HttpSession.class)); +// when(sessionData.getExpiresIn()).thenReturn(-100l); +// +// ReflectionTestUtils.setField(rapClient, "sessionData", sessionData); +// ReflectionTestUtils.setField(rapClient, "clientId", "clientId"); +// ReflectionTestUtils.setField(rapClient, "clientSecret", "clientSecret"); +// ReflectionTestUtils.setField(rapClient, "accessTokenUri", "https://sso.ia2.inaf.it"); +// +// String jsonError = "{\"error\":\"Unauthorized: token expired\"}"; +// +// HttpClientErrorException exception = Unauthorized +// .create(HttpStatus.UNAUTHORIZED, "401", HttpHeaders.EMPTY, jsonError.getBytes(), StandardCharsets.UTF_8); +// +// when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(new ParameterizedTypeReference<RapUser>() { +// }))).thenThrow(exception) +// .thenReturn(ResponseEntity.ok(new RapUser())); +// +// ResponseEntity refreshTokenResponse = mock(ResponseEntity.class); +// Map<String, Object> mockedBody = new HashMap<>(); +// mockedBody.put("access_token", "<access_token>"); +// mockedBody.put("refresh_token", "<refresh_token>"); +// mockedBody.put("expires_in", 3600); +// when(refreshTokenResponse.getBody()).thenReturn(mockedBody); +// +// when(refreshTokenRestTemplate.postForEntity(anyString(), any(HttpEntity.class), any())) +// .thenReturn(refreshTokenResponse); +// +// RapUser user = rapClient.getUser("123"); +// assertNotNull(user); +// +// // verifies that token is refreshed +// verify(sessionData, times(1)).setAccessToken(eq("<access_token>")); +// verify(sessionData, times(1)).setExpiresIn(eq(3600l)); +// } } diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/GroupNameServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/GroupNameServiceTest.java index 1ec9f9aee50f263f31ff2ac0cfaad63f9c76aa18..6b4b94643c51e5b0b9ec4b40049004b6bb85c0e7 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/service/GroupNameServiceTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/service/GroupNameServiceTest.java @@ -4,12 +4,17 @@ import it.inaf.ia2.gms.persistence.GroupsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import java.util.AbstractMap; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Set; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import org.mockito.InjectMocks; import org.mockito.Mock; import static org.mockito.Mockito.when; @@ -27,56 +32,48 @@ public class GroupNameServiceTest { @Test public void getNamesTest() { - mockGroupsDAO(); + GroupEntity group = new GroupEntity(); + group.setName("Child\\.withDot"); + group.setId("def"); + group.setPath("abc.def"); - List<Map.Entry<String, String>> groupsIdPath = new ArrayList<>(); - groupsIdPath.add(new AbstractMap.SimpleEntry<>("def", "abc.def")); - - Map<String, List<String>> names = groupNameService.getNames(groupsIdPath); - assertEquals(1, names.size()); - assertEquals(2, names.get("def").size()); - assertEquals("Group 1", names.get("def").get(0)); - assertEquals("Group 2", names.get("def").get(1)); - } + Set<GroupEntity> groups = new HashSet<>(); + groups.add(group); - public void mockGroupsDAO() { + Map<String, String> daoResponse = new HashMap<>(); + daoResponse.put("def", "Parent_group.Child\\.withDot"); - List<GroupEntity> groups = new ArrayList<>(); + when(groupsDAO.getGroupCompleteNamesFromId(any())).thenReturn(daoResponse); - GroupEntity group1 = new GroupEntity(); - group1.setId("abc"); - group1.setName("Group 1"); - group1.setPath("abc"); - groups.add(group1); - - GroupEntity group2 = new GroupEntity(); - group2.setId("def"); - group2.setName("Group 2"); - group2.setPath("abc.def"); - groups.add(group2); - - when(groupsDAO.findGroupsByIds(any())).thenReturn(groups); + Map<String, List<String>> names = groupNameService.getNames(groups); + assertEquals(1, names.size()); + assertEquals(2, names.get("def").size()); + assertEquals("Parent_group", names.get("def").get(0)); + assertEquals("Child\\.withDot", names.get("def").get(1)); } @Test public void getRootTest() { - List<GroupEntity> groups = new ArrayList<>(); + Set<String> groupIds = new HashSet<>(); + groupIds.add("ROOT"); + + when(groupsDAO.getGroupCompleteNamesFromId(any())).thenReturn(new HashMap<>()); GroupEntity root = new GroupEntity(); root.setId("ROOT"); - root.setName("Root"); + root.setName("ROOT"); root.setPath(""); - groups.add(root); - when(groupsDAO.findGroupsByIds(any())).thenReturn(groups); + when(groupsDAO.findGroupById(eq("ROOT"))) + .thenReturn(Optional.of(root)); List<Map.Entry<String, String>> groupsIdPath = new ArrayList<>(); groupsIdPath.add(new AbstractMap.SimpleEntry<>("ROOT", "")); - Map<String, List<String>> names = groupNameService.getNames(groupsIdPath); + Map<String, List<String>> names = groupNameService.getNamesFromIds(groupIds); assertEquals(1, names.size()); assertEquals(1, names.get("ROOT").size()); - assertEquals("Root", names.get("ROOT").get(0)); + assertEquals("ROOT", names.get("ROOT").get(0)); } } diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java index 098d24c62d959296f238b88c12626b38b5c93930..de6961a5760d8efaaa8ad737ac76181e0c35be4f 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java @@ -1,10 +1,7 @@ package it.inaf.ia2.gms.service; import it.inaf.ia2.gms.manager.GroupsManager; -import it.inaf.ia2.gms.model.Identity; -import it.inaf.ia2.gms.model.IdentityType; import it.inaf.ia2.gms.model.Permission; -import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.response.SearchResponseItem; import it.inaf.ia2.gms.model.response.SearchResponseType; @@ -14,13 +11,16 @@ import it.inaf.ia2.gms.persistence.MembershipsDAO; import it.inaf.ia2.gms.persistence.PermissionsDAO; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.persistence.model.PermissionEntity; -import it.inaf.ia2.gms.rap.RapClient; +import it.inaf.ia2.rap.client.RapClient; +import it.inaf.ia2.rap.data.Identity; +import it.inaf.ia2.rap.data.IdentityType; +import it.inaf.ia2.rap.data.RapUser; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.Set; import static org.junit.Assert.assertEquals; import org.junit.Before; import org.junit.Test; @@ -64,18 +64,18 @@ public class SearchServiceTest { when(groupNameService.getNames(any())).then(invocation -> { Map<String, List<String>> result = new HashMap<>(); - List<Map.Entry<String, String>> arg = invocation.getArgument(0); - for (Entry<String, String> entry : arg) { + Set<GroupEntity> arg = invocation.getArgument(0); + for (GroupEntity group : arg) { List<String> names = new ArrayList<>(); - switch (entry.getKey()) { + switch (group.getId()) { case "ROOT": - names.add("Root"); + names.add("ROOT"); break; case "group1_id": names.add("Group 1"); break; } - result.put(entry.getKey(), names); + result.put(group.getId(), names); } return result; }); @@ -93,7 +93,7 @@ public class SearchServiceTest { identity.setTypedId("user@inaf.it"); user.setIdentities(Collections.singletonList(identity)); - when(rapClient.searchUsers(any())).thenReturn(Collections.singletonList(user)); + when(rapClient.getUsers(any(String.class))).thenReturn(Collections.singletonList(user)); GroupEntity group1 = new GroupEntity(); group1.setId("group1_id"); @@ -136,6 +136,11 @@ public class SearchServiceTest { @Test public void testGetUserSearchResult() { + Map<String, List<String>> nameResult = new HashMap<>(); + nameResult.put("group1_id", Collections.singletonList("Group 1")); + + when(groupNameService.getNamesFromIds(any())).thenReturn(nameResult); + GroupEntity group1 = new GroupEntity(); group1.setId("group1_id"); group1.setName("Group 1"); @@ -164,7 +169,7 @@ public class SearchServiceTest { GroupEntity root = new GroupEntity(); root.setId("ROOT"); - root.setName("Root"); + root.setName("ROOT"); root.setPath(""); when(groupsManager.getRoot()).thenReturn(root);