Skip to content

Commit d85ee87

Browse files
committed
Add katta setup cli.
1 parent e677ad5 commit d85ee87

File tree

11 files changed

+838
-0
lines changed

11 files changed

+838
-0
lines changed

admin-cli/pom.xml

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>cloud.katta</groupId>
8+
<artifactId>katta-clientlib</artifactId>
9+
<version>1.0.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>admin-cli</artifactId>
12+
13+
<properties>
14+
<maven.compiler.source>21</maven.compiler.source>
15+
<maven.compiler.target>21</maven.compiler.target>
16+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
17+
<picocli.version>4.7.6</picocli.version>
18+
</properties>
19+
20+
<dependencies>
21+
<dependency>
22+
<groupId>cloud.katta</groupId>
23+
<artifactId>katta-clientlib-hub</artifactId>
24+
<version>${project.version}</version>
25+
</dependency>
26+
<dependency>
27+
<groupId>cloud.katta</groupId>
28+
<artifactId>katta-clientlib-hub</artifactId>
29+
<version>${project.version}</version>
30+
<classifier>tests</classifier>
31+
<type>test-jar</type>
32+
<scope>test</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>ch.cyberduck</groupId>
36+
<artifactId>core</artifactId>
37+
<type>test-jar</type>
38+
<scope>test</scope>
39+
</dependency>
40+
<dependency>
41+
<groupId>ch.cyberduck</groupId>
42+
<artifactId>test</artifactId>
43+
<type>pom</type>
44+
<scope>test</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>io.github.coffeelibs</groupId>
48+
<artifactId>tiny-oauth2-client</artifactId>
49+
<version>0.8.1</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>commons-codec</groupId>
53+
<artifactId>commons-codec</artifactId>
54+
<version>1.18.0</version>
55+
</dependency>
56+
<dependency>
57+
<groupId>software.amazon.awssdk</groupId>
58+
<artifactId>iam</artifactId>
59+
<version>2.31.27</version>
60+
<scope>compile</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.json</groupId>
64+
<artifactId>json</artifactId>
65+
<version>20250107</version>
66+
<scope>compile</scope>
67+
</dependency>
68+
<dependency>
69+
<groupId>org.apache.commons</groupId>
70+
<artifactId>commons-compress</artifactId>
71+
<version>1.27.0</version>
72+
<scope>compile</scope>
73+
</dependency>
74+
<dependency>
75+
<groupId>info.picocli</groupId>
76+
<artifactId>picocli</artifactId>
77+
<version>${picocli.version}</version>
78+
</dependency>
79+
<dependency>
80+
<groupId>io.rest-assured</groupId>
81+
<artifactId>rest-assured</artifactId>
82+
<version>5.5.0</version>
83+
</dependency>
84+
85+
<dependency>
86+
<groupId>com.fasterxml.jackson.core</groupId>
87+
<artifactId>jackson-databind</artifactId>
88+
<version>2.18.2</version>
89+
<scope>compile</scope>
90+
</dependency>
91+
<dependency>
92+
<groupId>org.testcontainers</groupId>
93+
<artifactId>testcontainers</artifactId>
94+
</dependency>
95+
</dependencies>
96+
97+
<build>
98+
<plugins>
99+
<plugin>
100+
<groupId>org.apache.maven.plugins</groupId>
101+
<artifactId>maven-dependency-plugin</artifactId>
102+
<version>3.8.1</version>
103+
<executions>
104+
<execution>
105+
<id>unpack</id>
106+
<phase>process-sources</phase>
107+
<goals>
108+
<goal>unpack</goal>
109+
</goals>
110+
<configuration>
111+
<artifactItems>
112+
<artifactItem>
113+
<groupId>cloud.katta</groupId>
114+
<artifactId>katta-clientlib-hub</artifactId>
115+
<version>${project.version}</version>
116+
<classifier>tests</classifier>
117+
<type>test-jar</type>
118+
<overWrite>false</overWrite>
119+
<outputDirectory>${project.build.directory}/test-classes</outputDirectory>
120+
<includes>**/*</includes>
121+
</artifactItem>
122+
</artifactItems>
123+
<includes>**/*.java</includes>
124+
<excludes>**/*.properties</excludes>
125+
<outputDirectory>${project.build.directory}/wars</outputDirectory>
126+
<overWriteReleases>false</overWriteReleases>
127+
<overWriteSnapshots>true</overWriteSnapshots>
128+
</configuration>
129+
</execution>
130+
</executions>
131+
</plugin>
132+
<plugin>
133+
<groupId>org.apache.maven.plugins</groupId>
134+
<artifactId>maven-enforcer-plugin</artifactId>
135+
<version>3.5.0</version>
136+
<executions>
137+
<execution>
138+
<id>enforce-bytecode-version</id>
139+
<goals>
140+
<goal>enforce</goal>
141+
</goals>
142+
<configuration>
143+
<skip>true</skip>
144+
</configuration>
145+
</execution>
146+
</executions>
147+
<dependencies>
148+
<dependency>
149+
<groupId>org.codehaus.mojo</groupId>
150+
<artifactId>extra-enforcer-rules</artifactId>
151+
<version>1.10.0</version>
152+
</dependency>
153+
</dependencies>
154+
</plugin>
155+
</plugins>
156+
</build>
157+
</project>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
3+
*/
4+
5+
package cloud.katta.cli;
6+
7+
import cloud.katta.cli.commands.*;
8+
import picocli.CommandLine;
9+
10+
@CommandLine.Command(name = "katta-admin-cli",
11+
mixinStandardHelpOptions = true,
12+
subcommands = {
13+
AwsSTSSetup.class, CommandLine.HelpCommand.class, StorageProfileAWSSTSSetup.class, StorageProfileAWSStaticSetup.class,
14+
AuthorizationCode.class,
15+
StorageProfileArchive.class
16+
})
17+
public class KattaSetupCli {
18+
19+
public static void main(String... args) {
20+
var app = new KattaSetupCli();
21+
int exitCode = new CommandLine(app)
22+
.setPosixClusteredShortOptionsAllowed(false)
23+
.execute(args);
24+
System.exit(exitCode);
25+
}
26+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
3+
*/
4+
5+
package cloud.katta.cli.commands;
6+
7+
import org.apache.commons.lang3.StringUtils;
8+
9+
import java.io.IOException;
10+
import java.net.URI;
11+
import java.net.http.HttpClient;
12+
import java.net.http.HttpResponse;
13+
14+
import com.fasterxml.jackson.core.JsonProcessingException;
15+
import com.fasterxml.jackson.databind.ObjectMapper;
16+
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
17+
import picocli.CommandLine;
18+
19+
public class AbstractAuthorizationCode {
20+
21+
@CommandLine.Option(names = {"--tokenUrl"}, description = "Keycloak realm URL with scheme. Example: \"https://testing.katta.cloud/kc/realms/tamarind/protocol/openid-connect/token\"", required = false)
22+
String tokenUrl;
23+
24+
@CommandLine.Option(names = {"--authUrl"}, description = "Keycloak realm URL with scheme. Example: \"https://testing.katta.cloud/kc/realms/tamarind/protocol/openid-connect/auth\"", required = false)
25+
String authUrl;
26+
27+
@CommandLine.Option(names = {"--clientId"}, description = "Keycloak realm URL with scheme. Example: \"cryptomator\"", required = false)
28+
String clientId;
29+
30+
@CommandLine.Option(names = {"--accessToken"}, description = "The access token. Requires admin role in the hub.", required = false)
31+
String accessToken;
32+
33+
protected String login() throws IOException, InterruptedException {
34+
if(StringUtils.isEmpty(accessToken)) {
35+
var authResponse = TinyOAuth2.client(clientId)
36+
.withTokenEndpoint(URI.create(tokenUrl))
37+
.authorizationCodeGrant(URI.create(authUrl))
38+
.authorize(HttpClient.newHttpClient(), uri -> {
39+
System.out.println("Please login on " + uri);
40+
});
41+
return extractAccessToken(authResponse);
42+
}
43+
else {
44+
return accessToken;
45+
}
46+
}
47+
48+
private static String extractAccessToken(HttpResponse<String> response) throws JsonProcessingException {
49+
var statusCode = response.statusCode();
50+
if(statusCode != 200) {
51+
System.err.println("""
52+
Request was responded with code %d and body:\n%s\n""".formatted(statusCode, response.body()));
53+
return null;
54+
}
55+
return new ObjectMapper().reader().readTree(response.body()).get("access_token").asText();
56+
}
57+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package cloud.katta.cli.commands;
2+
3+
import java.net.URI;
4+
import java.net.http.HttpClient;
5+
import java.net.http.HttpResponse;
6+
import java.util.concurrent.Callable;
7+
8+
import com.fasterxml.jackson.core.JsonProcessingException;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
import io.github.coffeelibs.tinyoauth2client.TinyOAuth2;
11+
import picocli.CommandLine;
12+
13+
// https://github.com/cryptomator/hub-cli/commit/bffcf2805530976c4a758990958ff75f9df68c0e#diff-c349f933a7698e31cfe25bd0a638ae487a02ac6fcb429bcce3e315aa8832be8b
14+
@CommandLine.Command(name = "authorizationCode", description = "Get token using authorization code flow.", mixinStandardHelpOptions = true)
15+
public class AuthorizationCode implements Callable<Void> {
16+
17+
@CommandLine.Option(names = {"--tokenUrl"}, description = "Keycloak realm URL with scheme. Example: \"https://testing.katta.cloud/kc/realms/tamarind/protocol/openid-connect/token\"", required = true)
18+
String tokenUrl;
19+
20+
@CommandLine.Option(names = {"--authUrl"}, description = "Keycloak realm URL with scheme. Example: \"https://testing.katta.cloud/kc/realms/tamarind/protocol/openid-connect/auth\"", required = true)
21+
String authUrl;
22+
23+
@CommandLine.Option(names = {"--clientId"}, description = "Keycloak realm URL with scheme. Example: \"cryptomator\"", required = true)
24+
String clientId;
25+
26+
27+
@Override
28+
public Void call() throws Exception {
29+
var authResponse = TinyOAuth2.client(clientId)
30+
.withTokenEndpoint(URI.create(tokenUrl))
31+
.authorizationCodeGrant(URI.create(authUrl))
32+
.authorize(HttpClient.newHttpClient(), uri -> {
33+
System.out.println("Please login on " + uri);
34+
});
35+
System.out.println(authResponse);
36+
printAccessToken(authResponse);
37+
return null;
38+
}
39+
40+
private static int printAccessToken(HttpResponse<String> response) throws JsonProcessingException {
41+
var statusCode = response.statusCode();
42+
if(statusCode != 200) {
43+
System.err.println("""
44+
Request was responded with code %d and body:\n%s\n""".formatted(statusCode, response.body()));
45+
return statusCode;
46+
}
47+
var token = new ObjectMapper().reader().readTree(response.body()).get("access_token").asText();
48+
System.out.println(token);
49+
return 0;
50+
}
51+
}

0 commit comments

Comments
 (0)