Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
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
- Loading branch information
1 parent
5e1223e
commit 236d4ba
Showing
28 changed files
with
1,969 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /* | ||
| * Copyright 2019 ThoughtWorks, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| apply plugin: 'jacoco' | ||
| apply plugin: 'groovy' | ||
|
|
||
| dependencies { | ||
| compile project(':api:api-base') | ||
|
|
||
| testCompile project(path: ':api:api-base', configuration: 'testOutput') | ||
|
|
||
| testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: project.versions.junit5 | ||
| testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.versions.junit5 | ||
| } |
135 changes: 135 additions & 0 deletions
135
...token-v1/src/main/java/com/thoughtworks/go/apiv1/accessToken/AccessTokenControllerV1.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| /* | ||
| * Copyright 2019 ThoughtWorks, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.thoughtworks.go.apiv1.accessToken; | ||
|
|
||
| import com.thoughtworks.go.api.ApiController; | ||
| import com.thoughtworks.go.api.ApiVersion; | ||
| import com.thoughtworks.go.api.representers.JsonReader; | ||
| import com.thoughtworks.go.api.spring.ApiAuthenticationHelper; | ||
| import com.thoughtworks.go.api.util.GsonTransformer; | ||
| import com.thoughtworks.go.apiv1.accessToken.representers.AccessTokenRepresenter; | ||
| import com.thoughtworks.go.apiv1.accessToken.representers.AccessTokensRepresenter; | ||
| import com.thoughtworks.go.config.exceptions.RecordNotFoundException; | ||
| import com.thoughtworks.go.domain.AccessToken; | ||
| import com.thoughtworks.go.server.newsecurity.models.AuthenticationToken; | ||
| import com.thoughtworks.go.server.newsecurity.utils.SessionUtils; | ||
| import com.thoughtworks.go.server.service.AccessTokenService; | ||
| import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult; | ||
| import com.thoughtworks.go.spark.Routes; | ||
| import com.thoughtworks.go.spark.spring.SparkSpringController; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.stereotype.Component; | ||
| import spark.Request; | ||
| import spark.Response; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.List; | ||
|
|
||
| import static spark.Spark.*; | ||
|
|
||
| @Component | ||
| public class AccessTokenControllerV1 extends ApiController implements SparkSpringController { | ||
|
|
||
| private final ApiAuthenticationHelper apiAuthenticationHelper; | ||
| private AccessTokenService accessTokenService; | ||
|
|
||
| @Autowired | ||
| public AccessTokenControllerV1(ApiAuthenticationHelper apiAuthenticationHelper, AccessTokenService AccessTokenService) { | ||
| super(ApiVersion.v1); | ||
| this.apiAuthenticationHelper = apiAuthenticationHelper; | ||
| this.accessTokenService = AccessTokenService; | ||
| } | ||
|
|
||
| @Override | ||
| public String controllerBasePath() { | ||
| return Routes.AccessToken.BASE; | ||
| } | ||
|
|
||
| @Override | ||
| public void setupRoutes() { | ||
| path(controllerBasePath(), () -> { | ||
| before("", mimeType, this::setContentType); | ||
| before("/*", mimeType, this::setContentType); | ||
|
|
||
| before("", mimeType, this.apiAuthenticationHelper::checkUserAnd403); | ||
| before("/*", mimeType, this.apiAuthenticationHelper::checkUserAnd403); | ||
|
|
||
| get("", mimeType, this::getAllAccessTokens); | ||
| post("", mimeType, this::createAccessToken); | ||
| patch(String.format("%s%s/revoke", Routes.AccessToken.USERNAME, Routes.AccessToken.TOKEN_NAME), mimeType, this::revokeAccessToken); | ||
| get(Routes.AccessToken.TOKEN_NAME, mimeType, this::getAccessToken); | ||
|
|
||
| exception(RecordNotFoundException.class, this::notFound); | ||
| }); | ||
| } | ||
|
|
||
| public String createAccessToken(Request request, Response response) throws Exception { | ||
| HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); | ||
|
|
||
| final JsonReader reader = GsonTransformer.getInstance().jsonReaderFrom(request.body()); | ||
|
|
||
| String tokenName = reader.getString("name"); | ||
| String tokenDescription = reader.optString("description").orElse(null); | ||
|
|
||
| AccessToken created = accessTokenService.create(tokenName, tokenDescription, currentUsername(), currentUserAuthConfigId(request), result); | ||
|
|
||
| if (result.isSuccessful()) { | ||
| return renderAccessToken(request, response, created, true); | ||
| } | ||
|
|
||
| return renderHTTPOperationResult(result, request, response); | ||
| } | ||
|
|
||
| public String getAccessToken(Request request, Response response) throws Exception { | ||
| final AccessToken token = accessTokenService.find(request.params("token_name"), currentUsername().getUsername().toString()); | ||
|
|
||
| if (token == null) { | ||
| throw new RecordNotFoundException(); | ||
| } | ||
|
|
||
| return renderAccessToken(request, response, token, false); | ||
| } | ||
|
|
||
| public String getAllAccessTokens(Request request, Response response) throws Exception { | ||
| List<AccessToken> allTokens = accessTokenService.findAllTokensForUser(currentUsername()); | ||
| return writerForTopLevelObject(request, response, outputWriter -> AccessTokensRepresenter.toJSON(outputWriter, allTokens)); | ||
| } | ||
|
|
||
| public String revokeAccessToken(Request request, Response response) throws Exception { | ||
| String tokenName = request.params("token_name"); | ||
| String username = request.params("username"); | ||
|
|
||
| HttpLocalizedOperationResult result = new HttpLocalizedOperationResult(); | ||
| accessTokenService.revokeAccessToken(tokenName, username, result); | ||
|
|
||
| if (result.isSuccessful()) { | ||
| return renderAccessToken(request, response, accessTokenService.find(tokenName, username), true); | ||
| } | ||
|
|
||
| return renderHTTPOperationResult(result, request, response); | ||
| } | ||
|
|
||
| private String renderAccessToken(Request request, Response response, AccessToken token, boolean includeTokenValue) throws IOException { | ||
| return writerForTopLevelObject(request, response, outputWriter -> AccessTokenRepresenter.toJSON(outputWriter, token, includeTokenValue)); | ||
| } | ||
|
|
||
| private String currentUserAuthConfigId(Request request) { | ||
| AuthenticationToken<?> authenticationToken = SessionUtils.getAuthenticationToken(request.raw()); | ||
| return authenticationToken.getAuthConfigId(); | ||
| } | ||
|
|
||
| } |
44 changes: 44 additions & 0 deletions
44
.../main/java/com/thoughtworks/go/apiv1/accessToken/representers/AccessTokenRepresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * Copyright 2019 ThoughtWorks, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.thoughtworks.go.apiv1.accessToken.representers; | ||
|
|
||
| import com.thoughtworks.go.api.base.OutputWriter; | ||
| import com.thoughtworks.go.domain.AccessToken; | ||
| import com.thoughtworks.go.spark.Routes; | ||
|
|
||
| public class AccessTokenRepresenter { | ||
| public static void toJSON(OutputWriter outputWriter, AccessToken token, boolean includeTokenValue) { | ||
| outputWriter.addLinks(linksWriter -> linksWriter | ||
| .addLink("self", Routes.AccessToken.name(token.getName())) | ||
| .addAbsoluteLink("doc", Routes.AccessToken.DOC) | ||
| .addLink("find", Routes.AccessToken.find())); | ||
|
|
||
| outputWriter.add("name", token.getName()) | ||
| .add("description", token.getDescription()) | ||
| .add("auth_config_id", token.getAuthConfigId()) | ||
| .addChild("_meta", metaWriter -> { | ||
| metaWriter.add("is_revoked", token.isRevoked()) | ||
| .add("revoked_at", token.getRevokedAt()) | ||
| .add("created_at", token.getCreatedAt()) | ||
| .add("last_used_at", token.getLastUsed()); | ||
| }); | ||
|
|
||
| if (includeTokenValue) { | ||
| outputWriter.add("token", token.getOriginalValue()); | ||
| } | ||
| } | ||
| } |
37 changes: 37 additions & 0 deletions
37
...main/java/com/thoughtworks/go/apiv1/accessToken/representers/AccessTokensRepresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /* | ||
| * Copyright 2019 ThoughtWorks, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.thoughtworks.go.apiv1.accessToken.representers; | ||
|
|
||
| import com.thoughtworks.go.api.base.OutputWriter; | ||
| import com.thoughtworks.go.domain.AccessToken; | ||
| import com.thoughtworks.go.domain.AccessToken; | ||
| import com.thoughtworks.go.spark.Routes; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class AccessTokensRepresenter { | ||
| public static void toJSON(OutputWriter outputWriter, List<AccessToken> allTokens) { | ||
| outputWriter.addLinks(outputLinkWriter -> outputLinkWriter | ||
| .addLink("self", Routes.AccessToken.BASE) | ||
| .addAbsoluteLink("doc", Routes.AccessToken.DOC)) | ||
| .addChild("_embedded", embeddedWriter -> | ||
| embeddedWriter.addChildList("access_tokens", AccessTokenWriter -> { | ||
| allTokens.forEach(token -> AccessTokenWriter.addChild(artifactStoreWriter -> AccessTokenRepresenter.toJSON(artifactStoreWriter, token, false))); | ||
| }) | ||
| ); | ||
| } | ||
| } |
Oops, something went wrong.