Skip to content
Permalink
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
GaneshSPatil authored and bdpiprava committed Feb 1, 2019
1 parent 5e1223e commit 236d4ba
Show file tree
Hide file tree
Showing 28 changed files with 1,969 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -93,6 +93,9 @@ api/api-admins-config-v2/logs/
api/api-admins-config-v2/out/
api/api-admins-config-v2/target/
api/api-artifact-store-config-v1/config/
api/api-access-token-v1/logs/
api/api-access-token-v1/out/
api/api-access-token-v1/target/
api/api-artifact-store-config-v1/logs/
api/api-artifact-store-config-v1/out/
api/api-artifact-store-config-v1/target/
Expand Down
27 changes: 27 additions & 0 deletions api/api-access-token-v1/build.gradle
@@ -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
}
@@ -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();
}

}
@@ -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());
}
}
}
@@ -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)));
})
);
}
}

0 comments on commit 236d4ba

Please sign in to comment.