Skip to content

Commit 236d4ba

Browse files
GaneshSPatilbdpiprava
authored andcommitted
Access Token APIs (#5797)
* Define a AuthTokenSqlMapDao to store and retrieve token information from DB * Introduce Auth Token API to manage auth tokens * Store the hashed token instead of storing the raw value * Some tests * Store information of the user who has created the auth token * Use SHA-256 instead of MD5 for hashing token value * Change username data type from VARCHAR to VARCHAR_IGNORECASE * Introduce AuthTokenMother to create authToken in tests * Fix auth token controller specs * Remove unused class variable from test * Introduce auth token index endpoint to fetch all the tokens belonging to me * Add extra attributes to auth token * Salt Id * Salt Value * Auth Config Id * Revoked At * Add support for revoking access tokens Add a method to validate and fetch access token object provided access token * Rename controller package and module * rename service and dao * Rename auth token package * Rename rules under url rewrite * Renamed few more occurences of auth token to acccess tokens
1 parent 5e1223e commit 236d4ba

File tree

28 files changed

+1969
-0
lines changed

28 files changed

+1969
-0
lines changed

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ api/api-admins-config-v2/logs/
9393
api/api-admins-config-v2/out/
9494
api/api-admins-config-v2/target/
9595
api/api-artifact-store-config-v1/config/
96+
api/api-access-token-v1/logs/
97+
api/api-access-token-v1/out/
98+
api/api-access-token-v1/target/
9699
api/api-artifact-store-config-v1/logs/
97100
api/api-artifact-store-config-v1/out/
98101
api/api-artifact-store-config-v1/target/

Diff for: api/api-access-token-v1/build.gradle

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
apply plugin: 'jacoco'
18+
apply plugin: 'groovy'
19+
20+
dependencies {
21+
compile project(':api:api-base')
22+
23+
testCompile project(path: ':api:api-base', configuration: 'testOutput')
24+
25+
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: project.versions.junit5
26+
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.versions.junit5
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.thoughtworks.go.apiv1.accessToken;
18+
19+
import com.thoughtworks.go.api.ApiController;
20+
import com.thoughtworks.go.api.ApiVersion;
21+
import com.thoughtworks.go.api.representers.JsonReader;
22+
import com.thoughtworks.go.api.spring.ApiAuthenticationHelper;
23+
import com.thoughtworks.go.api.util.GsonTransformer;
24+
import com.thoughtworks.go.apiv1.accessToken.representers.AccessTokenRepresenter;
25+
import com.thoughtworks.go.apiv1.accessToken.representers.AccessTokensRepresenter;
26+
import com.thoughtworks.go.config.exceptions.RecordNotFoundException;
27+
import com.thoughtworks.go.domain.AccessToken;
28+
import com.thoughtworks.go.server.newsecurity.models.AuthenticationToken;
29+
import com.thoughtworks.go.server.newsecurity.utils.SessionUtils;
30+
import com.thoughtworks.go.server.service.AccessTokenService;
31+
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
32+
import com.thoughtworks.go.spark.Routes;
33+
import com.thoughtworks.go.spark.spring.SparkSpringController;
34+
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.stereotype.Component;
36+
import spark.Request;
37+
import spark.Response;
38+
39+
import java.io.IOException;
40+
import java.util.List;
41+
42+
import static spark.Spark.*;
43+
44+
@Component
45+
public class AccessTokenControllerV1 extends ApiController implements SparkSpringController {
46+
47+
private final ApiAuthenticationHelper apiAuthenticationHelper;
48+
private AccessTokenService accessTokenService;
49+
50+
@Autowired
51+
public AccessTokenControllerV1(ApiAuthenticationHelper apiAuthenticationHelper, AccessTokenService AccessTokenService) {
52+
super(ApiVersion.v1);
53+
this.apiAuthenticationHelper = apiAuthenticationHelper;
54+
this.accessTokenService = AccessTokenService;
55+
}
56+
57+
@Override
58+
public String controllerBasePath() {
59+
return Routes.AccessToken.BASE;
60+
}
61+
62+
@Override
63+
public void setupRoutes() {
64+
path(controllerBasePath(), () -> {
65+
before("", mimeType, this::setContentType);
66+
before("/*", mimeType, this::setContentType);
67+
68+
before("", mimeType, this.apiAuthenticationHelper::checkUserAnd403);
69+
before("/*", mimeType, this.apiAuthenticationHelper::checkUserAnd403);
70+
71+
get("", mimeType, this::getAllAccessTokens);
72+
post("", mimeType, this::createAccessToken);
73+
patch(String.format("%s%s/revoke", Routes.AccessToken.USERNAME, Routes.AccessToken.TOKEN_NAME), mimeType, this::revokeAccessToken);
74+
get(Routes.AccessToken.TOKEN_NAME, mimeType, this::getAccessToken);
75+
76+
exception(RecordNotFoundException.class, this::notFound);
77+
});
78+
}
79+
80+
public String createAccessToken(Request request, Response response) throws Exception {
81+
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
82+
83+
final JsonReader reader = GsonTransformer.getInstance().jsonReaderFrom(request.body());
84+
85+
String tokenName = reader.getString("name");
86+
String tokenDescription = reader.optString("description").orElse(null);
87+
88+
AccessToken created = accessTokenService.create(tokenName, tokenDescription, currentUsername(), currentUserAuthConfigId(request), result);
89+
90+
if (result.isSuccessful()) {
91+
return renderAccessToken(request, response, created, true);
92+
}
93+
94+
return renderHTTPOperationResult(result, request, response);
95+
}
96+
97+
public String getAccessToken(Request request, Response response) throws Exception {
98+
final AccessToken token = accessTokenService.find(request.params("token_name"), currentUsername().getUsername().toString());
99+
100+
if (token == null) {
101+
throw new RecordNotFoundException();
102+
}
103+
104+
return renderAccessToken(request, response, token, false);
105+
}
106+
107+
public String getAllAccessTokens(Request request, Response response) throws Exception {
108+
List<AccessToken> allTokens = accessTokenService.findAllTokensForUser(currentUsername());
109+
return writerForTopLevelObject(request, response, outputWriter -> AccessTokensRepresenter.toJSON(outputWriter, allTokens));
110+
}
111+
112+
public String revokeAccessToken(Request request, Response response) throws Exception {
113+
String tokenName = request.params("token_name");
114+
String username = request.params("username");
115+
116+
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
117+
accessTokenService.revokeAccessToken(tokenName, username, result);
118+
119+
if (result.isSuccessful()) {
120+
return renderAccessToken(request, response, accessTokenService.find(tokenName, username), true);
121+
}
122+
123+
return renderHTTPOperationResult(result, request, response);
124+
}
125+
126+
private String renderAccessToken(Request request, Response response, AccessToken token, boolean includeTokenValue) throws IOException {
127+
return writerForTopLevelObject(request, response, outputWriter -> AccessTokenRepresenter.toJSON(outputWriter, token, includeTokenValue));
128+
}
129+
130+
private String currentUserAuthConfigId(Request request) {
131+
AuthenticationToken<?> authenticationToken = SessionUtils.getAuthenticationToken(request.raw());
132+
return authenticationToken.getAuthConfigId();
133+
}
134+
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.thoughtworks.go.apiv1.accessToken.representers;
18+
19+
import com.thoughtworks.go.api.base.OutputWriter;
20+
import com.thoughtworks.go.domain.AccessToken;
21+
import com.thoughtworks.go.spark.Routes;
22+
23+
public class AccessTokenRepresenter {
24+
public static void toJSON(OutputWriter outputWriter, AccessToken token, boolean includeTokenValue) {
25+
outputWriter.addLinks(linksWriter -> linksWriter
26+
.addLink("self", Routes.AccessToken.name(token.getName()))
27+
.addAbsoluteLink("doc", Routes.AccessToken.DOC)
28+
.addLink("find", Routes.AccessToken.find()));
29+
30+
outputWriter.add("name", token.getName())
31+
.add("description", token.getDescription())
32+
.add("auth_config_id", token.getAuthConfigId())
33+
.addChild("_meta", metaWriter -> {
34+
metaWriter.add("is_revoked", token.isRevoked())
35+
.add("revoked_at", token.getRevokedAt())
36+
.add("created_at", token.getCreatedAt())
37+
.add("last_used_at", token.getLastUsed());
38+
});
39+
40+
if (includeTokenValue) {
41+
outputWriter.add("token", token.getOriginalValue());
42+
}
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://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,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.thoughtworks.go.apiv1.accessToken.representers;
18+
19+
import com.thoughtworks.go.api.base.OutputWriter;
20+
import com.thoughtworks.go.domain.AccessToken;
21+
import com.thoughtworks.go.domain.AccessToken;
22+
import com.thoughtworks.go.spark.Routes;
23+
24+
import java.util.List;
25+
26+
public class AccessTokensRepresenter {
27+
public static void toJSON(OutputWriter outputWriter, List<AccessToken> allTokens) {
28+
outputWriter.addLinks(outputLinkWriter -> outputLinkWriter
29+
.addLink("self", Routes.AccessToken.BASE)
30+
.addAbsoluteLink("doc", Routes.AccessToken.DOC))
31+
.addChild("_embedded", embeddedWriter ->
32+
embeddedWriter.addChildList("access_tokens", AccessTokenWriter -> {
33+
allTokens.forEach(token -> AccessTokenWriter.addChild(artifactStoreWriter -> AccessTokenRepresenter.toJSON(artifactStoreWriter, token, false)));
34+
})
35+
);
36+
}
37+
}

0 commit comments

Comments
 (0)