From e2e639f339e501eba9666cecd2b11d0b0545a8bc Mon Sep 17 00:00:00 2001 From: "Badr.NassLahsen" Date: Thu, 9 Mar 2023 21:03:19 +0100 Subject: [PATCH] fix update #2094 --- .../SpringDocSecurityOAuth2Customizer.java | 8 +- ...gDocOAuth2AuthorizationServerMetadata.java | 154 ++++++++++++ .../SpringDocOAuth2TokenIntrospection.java | 141 +++++++++++ .../src/test/resources/results/app10.json | 222 +++++++++--------- 4 files changed, 410 insertions(+), 115 deletions(-) create mode 100644 springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java create mode 100644 springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2TokenIntrospection.java diff --git a/springdoc-openapi-security/src/main/java/org/springdoc/security/SpringDocSecurityOAuth2Customizer.java b/springdoc-openapi-security/src/main/java/org/springdoc/security/SpringDocSecurityOAuth2Customizer.java index d98bd1e71..9c9b44b75 100644 --- a/springdoc-openapi-security/src/main/java/org/springdoc/security/SpringDocSecurityOAuth2Customizer.java +++ b/springdoc-openapi-security/src/main/java/org/springdoc/security/SpringDocSecurityOAuth2Customizer.java @@ -22,6 +22,8 @@ import org.slf4j.LoggerFactory; import org.springdoc.core.SpringDocAnnotationsUtils; import org.springdoc.core.customizers.GlobalOpenApiCustomizer; +import org.springdoc.security.oauth2.SpringDocOAuth2AuthorizationServerMetadata; +import org.springdoc.security.oauth2.SpringDocOAuth2TokenIntrospection; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -31,8 +33,6 @@ import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata; -import org.springframework.security.oauth2.server.authorization.OAuth2TokenIntrospection; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken; import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter; @@ -119,7 +119,7 @@ private void getOAuth2TokenIntrospectionEndpointFilter(OpenAPI openAPI, Security Object oAuth2EndpointFilter = new SpringDocSecurityOAuth2EndpointUtils(OAuth2TokenIntrospectionEndpointFilter.class).findEndpoint(securityFilterChain); if (oAuth2EndpointFilter != null) { - ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(OAuth2TokenIntrospection.class, openAPI.getComponents(), null), openAPI); + ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2TokenIntrospection.class, openAPI.getComponents(), null), openAPI); Operation operation = buildOperation(apiResponses); Schema schema = new ObjectSchema() .addProperty("token", new StringSchema()) @@ -143,7 +143,7 @@ private void getOAuth2AuthorizationServerMetadataEndpoint(OpenAPI openAPI, Secur Object oAuth2EndpointFilter = new SpringDocSecurityOAuth2EndpointUtils(OAuth2AuthorizationServerMetadataEndpointFilter.class).findEndpoint(securityFilterChain); if (oAuth2EndpointFilter != null) { - ApiResponses apiResponses = buildApiResponses(SpringDocAnnotationsUtils.resolveSchemaFromType(OAuth2AuthorizationServerMetadata.class, openAPI.getComponents(), null)); + ApiResponses apiResponses = buildApiResponses(SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2AuthorizationServerMetadata.class, openAPI.getComponents(), null)); Operation operation = buildOperation(apiResponses); buildPath(oAuth2EndpointFilter, "requestMatcher", openAPI, operation, HttpMethod.GET); } diff --git a/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java b/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java new file mode 100644 index 000000000..b919dbce3 --- /dev/null +++ b/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2AuthorizationServerMetadata.java @@ -0,0 +1,154 @@ +package org.springdoc.security.oauth2; + +import java.net.URL; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimAccessor; +import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames; + +/** + * @author bnasslahsen + */ +@Schema(name = "OAuth2AuthorizationServerMetadata") +public class SpringDocOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor { + + + @Override + public Map getClaims() { + return null; + } + + @Override + public T getClaim(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaim(claim); + } + + @Override + public boolean hasClaim(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.hasClaim(claim); + } + + @Override + public Boolean containsClaim(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.containsClaim(claim); + } + + @Override + public String getClaimAsString(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsString(claim); + } + + @Override + public Boolean getClaimAsBoolean(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsBoolean(claim); + } + + @Override + public Instant getClaimAsInstant(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsInstant(claim); + } + + @Override + public URL getClaimAsURL(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsURL(claim); + } + + @Override + public Map getClaimAsMap(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsMap(claim); + } + + @Override + public List getClaimAsStringList(String claim) { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsStringList(claim); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.ISSUER) + public URL getIssuer() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getIssuer(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT) + public URL getAuthorizationEndpoint() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getAuthorizationEndpoint(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT) + public URL getTokenEndpoint() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenEndpoint(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) + public List getTokenEndpointAuthenticationMethods() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenEndpointAuthenticationMethods(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) + public URL getJwkSetUrl() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getJwkSetUrl(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) + public List getScopes() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getScopes(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED) + public List getResponseTypes() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getResponseTypes(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED) + public List getGrantTypes() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getGrantTypes(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT) + public URL getTokenRevocationEndpoint() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenRevocationEndpoint(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) + public List getTokenRevocationEndpointAuthenticationMethods() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenRevocationEndpointAuthenticationMethods(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT) + public URL getTokenIntrospectionEndpoint() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenIntrospectionEndpoint(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) + public List getTokenIntrospectionEndpointAuthenticationMethods() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenIntrospectionEndpointAuthenticationMethods(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT) + public URL getClientRegistrationEndpoint() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClientRegistrationEndpoint(); + } + + @Override + @JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED) + public List getCodeChallengeMethods() { + return OAuth2AuthorizationServerMetadataClaimAccessor.super.getCodeChallengeMethods(); + } +} diff --git a/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2TokenIntrospection.java b/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2TokenIntrospection.java new file mode 100644 index 000000000..e6a4430f2 --- /dev/null +++ b/springdoc-openapi-security/src/main/java/org/springdoc/security/oauth2/SpringDocOAuth2TokenIntrospection.java @@ -0,0 +1,141 @@ +package org.springdoc.security.oauth2; + +import java.net.URL; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor; +import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames; + +/** + * @author bnasslahsen + */ +@Schema(name = "OAuth2TokenIntrospection") +public class SpringDocOAuth2TokenIntrospection implements OAuth2TokenIntrospectionClaimAccessor { + + @Override + public Map getClaims() { + return null; + } + + @Override + public T getClaim(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaim(claim); + } + + @Override + public boolean hasClaim(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.hasClaim(claim); + } + + @Override + public Boolean containsClaim(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.containsClaim(claim); + } + + @Override + public String getClaimAsString(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsString(claim); + } + + @Override + public Boolean getClaimAsBoolean(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsBoolean(claim); + } + + @Override + public Instant getClaimAsInstant(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsInstant(claim); + } + + @Override + public URL getClaimAsURL(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsURL(claim); + } + + @Override + public Map getClaimAsMap(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsMap(claim); + } + + @Override + public List getClaimAsStringList(String claim) { + return OAuth2TokenIntrospectionClaimAccessor.super.getClaimAsStringList(claim); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.ACTIVE) + public boolean isActive() { + return OAuth2TokenIntrospectionClaimAccessor.super.isActive(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.USERNAME) + public String getUsername() { + return OAuth2TokenIntrospectionClaimAccessor.super.getUsername(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.CLIENT_ID) + public String getClientId() { + return OAuth2TokenIntrospectionClaimAccessor.super.getClientId(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.SCOPE) + public List getScopes() { + return OAuth2TokenIntrospectionClaimAccessor.super.getScopes(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE) + public String getTokenType() { + return OAuth2TokenIntrospectionClaimAccessor.super.getTokenType(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.EXP) + public Instant getExpiresAt() { + return OAuth2TokenIntrospectionClaimAccessor.super.getExpiresAt(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.IAT) + public Instant getIssuedAt() { + return OAuth2TokenIntrospectionClaimAccessor.super.getIssuedAt(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.NBF) + public Instant getNotBefore() { + return OAuth2TokenIntrospectionClaimAccessor.super.getNotBefore(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.SUB) + public String getSubject() { + return OAuth2TokenIntrospectionClaimAccessor.super.getSubject(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.AUD) + public List getAudience() { + return OAuth2TokenIntrospectionClaimAccessor.super.getAudience(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.ISS) + public URL getIssuer() { + return OAuth2TokenIntrospectionClaimAccessor.super.getIssuer(); + } + + @Override + @JsonProperty(OAuth2TokenIntrospectionClaimNames.JTI) + public String getId() { + return OAuth2TokenIntrospectionClaimAccessor.super.getId(); + } +} diff --git a/springdoc-openapi-security/src/test/resources/results/app10.json b/springdoc-openapi-security/src/test/resources/results/app10.json index 38ac8b8f9..af11778c1 100644 --- a/springdoc-openapi-security/src/test/resources/results/app10.json +++ b/springdoc-openapi-security/src/test/resources/results/app10.json @@ -332,14 +332,38 @@ "private": { "type": "boolean" }, - "keyType": { - "$ref": "#/components/schemas/KeyType" + "algorithm": { + "$ref": "#/components/schemas/Algorithm" }, - "keyUse": { - "$ref": "#/components/schemas/KeyUse" + "x509CertURL": { + "type": "string", + "format": "uri" }, - "keyID": { - "type": "string" + "keyOperations": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "sign", + "verify", + "encrypt", + "decrypt", + "wrapKey", + "unwrapKey", + "deriveKey", + "deriveBits" + ] + } + }, + "x509CertThumbprint": { + "$ref": "#/components/schemas/Base64URL" + }, + "requiredParams": { + "type": "object", + "additionalProperties": { + "type": "object" + } }, "parsedX509CertChain": { "type": "array", @@ -379,33 +403,6 @@ } } }, - "sigAlgName": { - "type": "string" - }, - "subjectDN": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "issuerDN": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "notBefore": { - "type": "string", - "format": "date-time" - }, - "notAfter": { - "type": "string", - "format": "date-time" - }, "extendedKeyUsage": { "type": "array", "items": { @@ -459,6 +456,33 @@ "type": "boolean" } }, + "sigAlgName": { + "type": "string" + }, + "subjectDN": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "issuerDN": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "notBefore": { + "type": "string", + "format": "date-time" + }, + "notAfter": { + "type": "string", + "format": "date-time" + }, "signature": { "type": "array", "items": { @@ -525,44 +549,20 @@ } } }, - "requiredParams": { - "type": "object", - "additionalProperties": { - "type": "object" - } - }, - "x509CertThumbprint": { - "$ref": "#/components/schemas/Base64URL" - }, - "keyOperations": { - "uniqueItems": true, - "type": "array", - "items": { - "type": "string", - "enum": [ - "sign", - "verify", - "encrypt", - "decrypt", - "wrapKey", - "unwrapKey", - "deriveKey", - "deriveBits" - ] - } - }, - "x509CertURL": { - "type": "string", - "format": "uri" - }, "x509CertChain": { "type": "array", "items": { "$ref": "#/components/schemas/Base64" } }, - "algorithm": { - "$ref": "#/components/schemas/Algorithm" + "keyType": { + "$ref": "#/components/schemas/KeyType" + }, + "keyUse": { + "$ref": "#/components/schemas/KeyUse" + }, + "keyID": { + "type": "string" }, "x509CertSHA256Thumbprint": { "$ref": "#/components/schemas/Base64URL" @@ -619,73 +619,73 @@ "type": "object" } }, - "tokenRevocationEndpointAuthenticationMethods": { + "revocation_endpoint_auth_methods_supported": { "type": "array", "items": { "type": "string" } }, - "tokenIntrospectionEndpointAuthenticationMethods": { + "introspection_endpoint_auth_methods_supported": { "type": "array", "items": { "type": "string" } }, - "issuer": { + "authorization_endpoint": { "type": "string", "format": "url" }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "authorizationEndpoint": { + "token_endpoint": { "type": "string", "format": "url" }, - "tokenEndpoint": { + "revocation_endpoint": { "type": "string", "format": "url" }, - "tokenRevocationEndpoint": { - "type": "string", - "format": "url" - }, - "jwkSetUrl": { - "type": "string", - "format": "url" + "response_types_supported": { + "type": "array", + "items": { + "type": "string" + } }, - "responseTypes": { + "grant_types_supported": { "type": "array", "items": { "type": "string" } }, - "grantTypes": { + "code_challenge_methods_supported": { "type": "array", "items": { "type": "string" } }, - "codeChallengeMethods": { + "jwks_uri": { + "type": "string", + "format": "url" + }, + "scopes_supported": { "type": "array", "items": { "type": "string" } }, - "tokenIntrospectionEndpoint": { + "issuer": { + "type": "string", + "format": "url" + }, + "introspection_endpoint": { "type": "string", "format": "url" }, - "tokenEndpointAuthenticationMethods": { + "token_endpoint_auth_methods_supported": { "type": "array", "items": { "type": "string" } }, - "clientRegistrationEndpoint": { + "registration_endpoint": { "type": "string", "format": "url" } @@ -781,50 +781,50 @@ "type": "object" } }, - "notBefore": { + "nbf": { "type": "string", "format": "date-time" }, - "id": { + "jti": { "type": "string" }, "active": { "type": "boolean" }, - "issuer": { - "type": "string", - "format": "url" - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } + "client_id": { + "type": "string" }, "username": { "type": "string" }, - "clientId": { + "iat": { + "type": "string", + "format": "date-time" + }, + "exp": { + "type": "string", + "format": "date-time" + }, + "token_type": { "type": "string" }, - "audience": { + "aud": { "type": "array", "items": { "type": "string" } }, - "issuedAt": { - "type": "string", - "format": "date-time" + "scope": { + "type": "array", + "items": { + "type": "string" + } }, - "expiresAt": { + "iss": { "type": "string", - "format": "date-time" - }, - "tokenType": { - "type": "string" + "format": "url" }, - "subject": { + "sub": { "type": "string" } }