Skip to content

Commit 4ac5e3a

Browse files
authored
Allow creating a new token from the input string for admin (#549)
Motivation: When migrating a Central Dogma into another Central Dogma, a repository could be easily migrated by copying its file folder under `/data`. However, it is difficult to merge the tokens of two systems because all tokens are managed by `tokens` repository. Modifications: - Allow creating a new token from `sceret` parameter for admin - Increase timeout of `ZooKeeperCommandExecutorTest.hierarchicalQuorumsWithFailOver()` for receiving flakiness - Result: - You can now create a new token with non-random string starting with `appToken-` for admin - Fixes #550
1 parent 135d9af commit 4ac5e3a

File tree

4 files changed

+110
-5
lines changed

4 files changed

+110
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2021 LINE Corporation
3+
*
4+
* LINE Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.centraldogma.it;
18+
19+
import static com.linecorp.centraldogma.testing.internal.auth.TestAuthMessageUtil.login;
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.extension.RegisterExtension;
24+
25+
import com.linecorp.armeria.client.WebClient;
26+
import com.linecorp.armeria.common.AggregatedHttpResponse;
27+
import com.linecorp.armeria.common.HttpRequest;
28+
import com.linecorp.armeria.common.HttpStatus;
29+
import com.linecorp.armeria.common.MediaType;
30+
import com.linecorp.armeria.common.auth.OAuth2Token;
31+
import com.linecorp.centraldogma.internal.Jackson;
32+
import com.linecorp.centraldogma.internal.api.v1.AccessToken;
33+
import com.linecorp.centraldogma.server.CentralDogmaBuilder;
34+
import com.linecorp.centraldogma.testing.internal.auth.TestAuthMessageUtil;
35+
import com.linecorp.centraldogma.testing.internal.auth.TestAuthProviderFactory;
36+
import com.linecorp.centraldogma.testing.junit.CentralDogmaExtension;
37+
38+
class NonRandomTokenTest {
39+
40+
@RegisterExtension
41+
static final CentralDogmaExtension dogma = new CentralDogmaExtension() {
42+
@Override
43+
protected void configure(CentralDogmaBuilder builder) {
44+
builder.administrators(TestAuthMessageUtil.USERNAME);
45+
builder.authProviderFactory(new TestAuthProviderFactory());
46+
}
47+
};
48+
49+
@Test
50+
void createNonRandomToken() throws Exception {
51+
final WebClient client = dogma.httpClient();
52+
final AggregatedHttpResponse response = login(client,
53+
TestAuthMessageUtil.USERNAME,
54+
TestAuthMessageUtil.PASSWORD);
55+
56+
assertThat(response.status()).isEqualTo(HttpStatus.OK);
57+
final String sessionId = Jackson.readValue(response.content().array(), AccessToken.class)
58+
.accessToken();
59+
final WebClient adminClient = WebClient.builder(client.uri())
60+
.auth(OAuth2Token.of(sessionId)).build();
61+
62+
final HttpRequest request = HttpRequest.builder()
63+
.post("/api/v1/tokens")
64+
.content(MediaType.FORM_DATA,
65+
"secret=appToken-secret&isAdmin=true&appId=foo")
66+
.build();
67+
AggregatedHttpResponse res = adminClient.execute(request).aggregate().join();
68+
assertThat(res.status()).isEqualTo(HttpStatus.CREATED);
69+
res = adminClient.get("/api/v1/tokens").aggregate().join();
70+
assertThat(res.contentUtf8()).contains("\"secret\":\"appToken-secret\"");
71+
}
72+
}

server/src/main/java/com/linecorp/centraldogma/server/internal/api/TokenService.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.Collection;
2323
import java.util.concurrent.CompletableFuture;
2424

25+
import javax.annotation.Nullable;
26+
2527
import com.fasterxml.jackson.databind.JsonNode;
2628
import com.google.common.collect.ImmutableList;
2729
import com.google.common.collect.ImmutableMap;
@@ -42,6 +44,7 @@
4244
import com.linecorp.armeria.server.annotation.ResponseConverter;
4345
import com.linecorp.armeria.server.annotation.StatusCode;
4446
import com.linecorp.centraldogma.common.Author;
47+
import com.linecorp.centraldogma.common.Revision;
4548
import com.linecorp.centraldogma.internal.Jackson;
4649
import com.linecorp.centraldogma.server.command.CommandExecutor;
4750
import com.linecorp.centraldogma.server.internal.api.converter.CreateApiResponseConverter;
@@ -104,10 +107,21 @@ public CompletableFuture<Collection<Token>> listTokens(User loginUser) {
104107
@ResponseConverter(CreateApiResponseConverter.class)
105108
public CompletableFuture<HttpResult<Token>> createToken(@Param String appId,
106109
@Param boolean isAdmin,
110+
@Param @Nullable String secret,
107111
Author author, User loginUser) {
108112
checkArgument(!isAdmin || loginUser.isAdmin(),
109113
"Only administrators are allowed to create an admin-level token.");
110-
return mds.createToken(author, appId, isAdmin)
114+
115+
checkArgument(secret == null || loginUser.isAdmin(),
116+
"Only administrators are allowed to create a new token from the given secret string");
117+
118+
final CompletableFuture<Revision> tokenFuture;
119+
if (secret != null) {
120+
tokenFuture = mds.createToken(author, appId, secret, isAdmin);
121+
} else {
122+
tokenFuture = mds.createToken(author, appId, isAdmin);
123+
}
124+
return tokenFuture
111125
.thenCompose(unused -> mds.findTokenByAppId(appId))
112126
.thenApply(token -> {
113127
final ResponseHeaders headers = ResponseHeaders.of(HttpStatus.CREATED,

server/src/test/java/com/linecorp/centraldogma/server/internal/api/TokenServiceTest.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ static void setUp() {
6363

6464
@Test
6565
void adminToken() {
66-
final Token token = tokenService.createToken("forAdmin1", true, adminAuthor, admin).join()
66+
final Token token = tokenService.createToken("forAdmin1", true, null, adminAuthor, admin).join()
6767
.content();
6868
assertThat(token.isActive()).isTrue();
69-
assertThatThrownBy(() -> tokenService.createToken("forAdmin2", true, guestAuthor, guest)
69+
assertThatThrownBy(() -> tokenService.createToken("forAdmin2", true, null, guestAuthor, guest)
7070
.join())
7171
.isInstanceOf(IllegalArgumentException.class);
7272

@@ -87,9 +87,9 @@ void adminToken() {
8787

8888
@Test
8989
void userToken() {
90-
final Token userToken1 = tokenService.createToken("forUser1", false, adminAuthor, admin)
90+
final Token userToken1 = tokenService.createToken("forUser1", false, null, adminAuthor, admin)
9191
.join().content();
92-
final Token userToken2 = tokenService.createToken("forUser2", false, guestAuthor, guest)
92+
final Token userToken2 = tokenService.createToken("forUser2", false, null, guestAuthor, guest)
9393
.join().content();
9494
assertThat(userToken1.isActive()).isTrue();
9595
assertThat(userToken2.isActive()).isTrue();
@@ -111,4 +111,21 @@ void userToken() {
111111
assertThat(t.deactivation()).isEqualTo(userToken2.deactivation());
112112
});
113113
}
114+
115+
@Test
116+
void nonRandomToken() {
117+
final Token token = tokenService.createToken("forAdmin1", true, "appToken-secret", adminAuthor,
118+
admin)
119+
.join().content();
120+
assertThat(token.isActive()).isTrue();
121+
122+
final Collection<Token> tokens = tokenService.listTokens(admin).join();
123+
assertThat(tokens.stream().filter(t -> !StringUtil.isNullOrEmpty(t.secret()))).hasSize(1);
124+
125+
assertThatThrownBy(() -> tokenService.createToken("forUser1", true, "appToken-secret", guestAuthor,
126+
guest)
127+
.join())
128+
.isInstanceOf(IllegalArgumentException.class);
129+
tokenService.deleteToken(ctx, "forAdmin1", adminAuthor, admin).join();
130+
}
114131
}

server/src/test/java/com/linecorp/centraldogma/server/internal/replication/ZooKeeperCommandExecutorTest.java

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreV2;
4444
import org.junit.jupiter.api.Test;
45+
import org.junit.jupiter.api.Timeout;
4546
import org.junit.jupiter.api.function.ThrowingConsumer;
4647
import org.slf4j.Logger;
4748
import org.slf4j.LoggerFactory;
@@ -254,6 +255,7 @@ void hierarchicalQuorums() throws Throwable {
254255
}
255256

256257
@Test
258+
@Timeout(120)
257259
void hierarchicalQuorumsWithFailOver() throws Throwable {
258260
try (Cluster cluster = Cluster.builder()
259261
.numReplicas(9)

0 commit comments

Comments
 (0)