Commit 898a6c68 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Merge branch 'master' into franco

parents e60cd7fb 55432f71
......@@ -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
......
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
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
......@@ -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>
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) {
......
spring.main.banner-mode=off
logging.level.root=OFF
\ No newline at end of file
<?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
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);
}
}
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);
}
}
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);
}
}
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();
}
}
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;
}
}
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));
}
}
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;
}
}
<?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>