Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#195 fix token-exchange endpoint request parameter should be in reque… #196

Merged
merged 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/push-with-v-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }}
GPG_OWNERTRUST: ${{ secrets.GPG_OWNERTRUST }}

- name: Step 4 - Build client
run: ./scripts/build_client.sh

- name: Get the tag name
run: echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV

Expand Down
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
<spring-boot.version>3.2.5</spring-boot.version>
<keystore-management.version>0.0.11</keystore-management.version>
<shedlock.version>5.3.0</shedlock.version>
<openapi.version>2.4.0</openapi.version>
<swagger-annotations.version>2.2.20</swagger-annotations.version>
<openapi.version>2.5.0</openapi.version>
<swagger-annotations.version>2.2.22</swagger-annotations.version>
<jackson.version>2.15.1</jackson.version>
<jackson-databind.version>2.15.1</jackson-databind.version>
<keycloak.version>22.0.4</keycloak.version>
Expand All @@ -73,8 +73,8 @@
<maven-release-plugin.version>3.0.1</maven-release-plugin.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<buildnumber-maven-plugin.version>3.2.0</buildnumber-maven-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<maven-gpg-plugin.version>3.1.0</maven-gpg-plugin.version>
<maven-deploy-plugin.version>3.1.2</maven-deploy-plugin.version>
<maven-gpg-plugin.version>3.2.4</maven-gpg-plugin.version>
<maven-javadoc-plugin.version>3.6.0</maven-javadoc-plugin.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.adorsys.sts.secretserver;

import de.adorsys.sts.common.config.TokenResource;
import de.adorsys.sts.token.tokenexchange.TokenExchangeService;
import de.adorsys.sts.token.tokenexchange.server.TokenExchangeController;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -11,4 +12,7 @@
@TokenResource
@RequestMapping("${sts.secret-server.endpoint:/secret-server/token-exchange}")
public class SecretServerRestController extends TokenExchangeController {
public SecretServerRestController(TokenExchangeService tokenExchangeService) {
super(tokenExchangeService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,117 +3,50 @@
import de.adorsys.sts.ResponseUtils;
import de.adorsys.sts.token.InvalidParameterException;
import de.adorsys.sts.token.MissingParameterException;
import de.adorsys.sts.token.api.TokenRequestForm;
import de.adorsys.sts.token.api.TokenResponse;
import de.adorsys.sts.token.tokenexchange.TokenExchangeConstants;
import de.adorsys.sts.token.tokenexchange.TokenExchangeRequest;
import de.adorsys.sts.token.tokenexchange.TokenExchangeService;
import de.adorsys.sts.token.tokenexchange.TokenValidationException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestBody;

@Slf4j
@RequiredArgsConstructor
max402 marked this conversation as resolved.
Show resolved Hide resolved
public class TokenExchangeController {

private static final Logger logger = LoggerFactory.getLogger(TokenExchangeController.class);

@Autowired
private TokenExchangeService tokenExchangeService;
private final TokenExchangeService tokenExchangeService;

@PostMapping(consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
@Operation(summary = "Exchange Token", description = "Create an access or refresh token given a valide subject token.", responses = {
@ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = "application/json", schema = @Schema(implementation = TokenResponse.class))),
@ApiResponse(responseCode = "400", description = "Bad request", headers = @Header(name = "error", description = "invalid request"))
})
public ResponseEntity<Object> tokenExchange(
@Parameter(
name = "grant_type",
description = "Indicates that a token exchange is being performed.",
required = true,
example = TokenExchangeConstants.TOKEN_EXCHANGE_OAUTH_GRANT_TYPE)
@RequestParam(
value = "grant_type",
defaultValue = TokenExchangeConstants.TOKEN_EXCHANGE_OAUTH_GRANT_TYPE
) String grantType,

@Parameter(
name = "resource",
description = "Indicates the physical location of the target service or resource where the client intends to use the requested security token. This enables the authorization server to apply policy as appropriate for the target, such as determining the type and content of the token to be issued or if and how the token is to be encrypted.",
example = "http://localhost:8080/multibanking-service")
@RequestParam(name = "resource", required = false) String[] resources,

@Parameter(
name = "audience",
description = "The logical name of the target service where the client intends to use the requested security token. This serves a purpose similar to the resource parameter, but with the client providing a logical name rather than a physical location.",
example = "http://localhost:8080/multibanking-service")
@RequestParam(name = "audience", required = false) String[] audiences,

@Parameter(
name = "scope",
description = "A list of space-delimited, case-sensitive strings that allow the client to specify the desired scope of the requested security token in the context of the service or resource where the token will be used.",
example = "user banking")
@RequestParam(name = "scope", required = false) String scope,

@Parameter(
name = "requested_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml.",
required = false,
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
@RequestParam(name = "requested_token_type", required = false) String requestedTokenType,

@Parameter(
name = "subject_token",
description = "A security token that represents the identity of the party on behalf of whom the request is being made. Typically, the subject of this token will be the subject of the security token issued in response to this request.",
required = true,
example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJNYXhNdXN0ZXJtYW4iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTQ5NTM5MTAxM30.mN9eFMnEuYgh_KCULI8Gpm1X49wWaA67Ps1M7EFV0BQ")
@RequestParam("subject_token") String subjectToken,

@Parameter(
name = "subject_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml. This can be urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:refresh_token.",
required = true,
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
@RequestParam(value = "subject_token_type", defaultValue = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE) String subjectTokenType,

@Parameter(
name = "actor_token",
description = "A security token that represents the identity of the acting party. Typically this will be the party that is authorized to use the requested security token and act on behalf of the subject.",
required = false,
example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJNYXhNdXN0ZXJtYW4iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTQ5NTM5MTAxM30.mN9eFMnEuYgh_KCULI8Gpm1X49wWaA67Ps1M7EFV0BQ")
@RequestParam(name = "actor_token", required = false) String actorToken,

@Parameter(
name = "actor_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml. This can be urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:refresh_token.",
required = false,
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
@RequestParam(name = "actor_token_type", required = false) String actorTokenType,
HttpServletRequest servletRequest
) {
if (logger.isTraceEnabled()) logger.trace("POST tokenExchange started...");
public ResponseEntity<Object> tokenExchange(@RequestBody @ModelAttribute TokenRequestForm tokenRequestForm, HttpServletRequest servletRequest) {
if (log.isTraceEnabled()) log.trace("POST tokenExchange started...");

TokenExchangeRequest tokenExchange = TokenExchangeRequest.builder()
max402 marked this conversation as resolved.
Show resolved Hide resolved
.grantType(grantType)
.resources(resources)
.subjectToken(subjectToken)
.subjectTokenType(subjectTokenType)
.actorToken(actorToken)
.actorTokenType(actorTokenType)
.grantType(tokenRequestForm.getGrantType())
.resources(tokenRequestForm.getResources())
.subjectToken(tokenRequestForm.getSubjectToken())
.subjectTokenType(tokenRequestForm.getSubjectTokenType())
.actorToken(tokenRequestForm.getActorToken())
.actorTokenType(tokenRequestForm.getActorTokenType())
.issuer(ResponseUtils.getIssuer(servletRequest))
.scope(scope)
.requestedTokenType(requestedTokenType)
.audiences(audiences)
.scope(tokenRequestForm.getScope())
.requestedTokenType(tokenRequestForm.getRequestedTokenType())
.audiences(tokenRequestForm.getAudiences())
.build();

try {
Expand All @@ -127,7 +60,7 @@ public ResponseEntity<Object> tokenExchange(
ResponseEntity<Object> errorData = ResponseUtils.invalidParam(e.getMessage());
return ResponseEntity.badRequest().body(errorData);
} finally {
if (logger.isTraceEnabled()) logger.trace("POST tokenExchange finished.");
if (log.isTraceEnabled()) log.trace("POST tokenExchange finished.");
max402 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.adorsys.sts.token.tokenexchange.server;

import de.adorsys.sts.common.config.TokenResource;
import de.adorsys.sts.token.tokenexchange.TokenExchangeService;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -12,4 +13,8 @@
public class TokenExchangeRestController extends TokenExchangeController {
max402 marked this conversation as resolved.
Show resolved Hide resolved

public static final String DEFAULT_PATH = "/token/token-exchange";

public TokenExchangeRestController(TokenExchangeService tokenExchangeService) {
super(tokenExchangeService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package de.adorsys.sts.token.api;

import de.adorsys.sts.token.tokenexchange.TokenExchangeConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.beans.ConstructorProperties;

@Getter
@AllArgsConstructor(onConstructor_ = @ConstructorProperties({"grant_type", "resource", "audience", "scope", "requested_token_type", "subject_token", "subject_token_type", "actor_token", "actor_token_type"}))
@Schema(description = "Carries request form properties of a token-exchange request",
requiredProperties = {"grant_type", "subject_token", "subject_token_type"})
public class TokenRequestForm {

@Schema(name = "grant_type",
description = "Indicates that a token exchange is being performed.",
example = TokenExchangeConstants.TOKEN_EXCHANGE_OAUTH_GRANT_TYPE)
private String grantType;

@Schema(name = "resource",
description = "Indicates the physical location of the target service or resource where the client intends to use the requested security token. This enables the authorization server to apply policy as appropriate for the target, such as determining the type and content of the token to be issued or if and how the token is to be encrypted.",
example = "http://localhost:8080/multibanking-service")
private String[] resources;

@Schema(name = "audience",
description = "The logical name of the target service where the client intends to use the requested security token. This serves a purpose similar to the resource parameter, but with the client providing a logical name rather than a physical location.",
example = "http://localhost:8080/multibanking-service")
private String[] audiences;

@Schema(name = "scope",
description = "A list of space-delimited, case-sensitive strings that allow the client to specify the desired scope of the requested security token in the context of the service or resource where the token will be used.",
example = "user banking")
private String scope;

@Schema(name = "requested_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml.",
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
private String requestedTokenType;

@Schema(name = "subject_token",
description = "A security token that represents the identity of the party on behalf of whom the request is being made. Typically, the subject of this token will be the subject of the security token issued in response to this request.",
example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJNYXhNdXN0ZXJtYW4iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTQ5NTM5MTAxM30.mN9eFMnEuYgh_KCULI8Gpm1X49wWaA67Ps1M7EFV0BQ")
private String subjectToken;

@Schema(name = "subject_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml. This can be urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:refresh_token.",
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
private String subjectTokenType;

@Schema(name = "actor_token",
description = "A security token that represents the identity of the acting party. Typically this will be the party that is authorized to use the requested security token and act on behalf of the subject.",
example = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJNYXhNdXN0ZXJtYW4iLCJyb2xlIjoiVVNFUiIsImV4cCI6MTQ5NTM5MTAxM30.mN9eFMnEuYgh_KCULI8Gpm1X49wWaA67Ps1M7EFV0BQ")
private String actorToken;

@Schema(name = "actor_token_type",
description = "An identifier for the type of the requested security token. If the requested type is unspecified, the issued token type is at the discretion of the authorization server and may be dictated by knowledge of the requirements of the service or resource indicated by the resource or audience parameter. This can be urn:ietf:params:oauth:token-type:jwt or urn:ietf:params:oauth:token-type:saml. This can be urn:ietf:params:oauth:token-type:access_token or urn:ietf:params:oauth:token-type:refresh_token.",
example = TokenExchangeConstants.JWT_OAUTH_TOKEN_TYPE)
private String actorTokenType;
}