diff --git a/openapi-security/src/test/java/com/networknt/openapi/JwtVerifierHandlerMultipleSpecsTest.java b/openapi-security/src/test/java/com/networknt/openapi/JwtVerifierHandlerMultipleSpecsTest.java index f763b04..9d79aae 100644 --- a/openapi-security/src/test/java/com/networknt/openapi/JwtVerifierHandlerMultipleSpecsTest.java +++ b/openapi-security/src/test/java/com/networknt/openapi/JwtVerifierHandlerMultipleSpecsTest.java @@ -32,43 +32,62 @@ * This is a test class that focuses the multiple specifications with scope verification. It has a customized * openapi-handler.yml config file injected during the setup. * - * This test is disabled as it has to access jwk endpoint during the middle of the request. * */ -@Ignore public class JwtVerifierHandlerMultipleSpecsTest { static final Logger logger = LoggerFactory.getLogger(JwtVerifierHandlerMultipleSpecsTest.class); - static Undertow server = null; - + static Undertow server1 = null; + static Undertow server2 = null; @BeforeClass public static void setUp() { - if(server == null) { - logger.info("starting server"); + if (server1 == null) { + logger.info("starting server1"); HttpHandler handler = getTestHandler(); JwtVerifyHandler jwtVerifyHandler = new JwtVerifyHandler(); jwtVerifyHandler.setNext(handler); OpenApiHandler openApiHandler = new OpenApiHandler(OpenApiHandlerConfig.load("openapi-handler-multiple")); openApiHandler.setNext(jwtVerifyHandler); - server = Undertow.builder() + server1 = Undertow.builder() .addHttpListener(7081, "localhost") .setHandler(openApiHandler) .build(); - server.start(); + server1.start(); } + + if (server2 == null) { + logger.info("starting server2"); + HttpHandler handler = getJwksHandler(); + server2 = Undertow.builder() + .addHttpListener(7082, "localhost") + .setHandler(handler) + .build(); + server2.start(); + } + } @AfterClass public static void tearDown() throws Exception { - if(server != null) { + if (server1 != null) { try { Thread.sleep(100); } catch (InterruptedException ignored) { } - server.stop(); - logger.info("The server is stopped."); + server1.stop(); + logger.info("The server1 is stopped."); } + if (server2 != null) { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + + } + server2.stop(); + logger.info("The server2 is stopped."); + } + } static RoutingHandler getTestHandler() { @@ -84,11 +103,16 @@ static RoutingHandler getTestHandler() { exchange.endExchange(); } }) + .add(Methods.GET, "/petstore/pets", exchange -> exchange.getResponseSender().send("get")); + } + + static RoutingHandler getJwksHandler() { + return Handlers.routing() .add(Methods.GET, "/oauth2/N2CMw0HGQXeLvC1wBfln2A/keys", exchange -> { exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); exchange.getResponseSender().send("{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"Tj_l_tIBTginOtQbL0Pv5w\",\"n\":\"0YRbWAb1FGDpPUUcrIpJC6BwlswlKMS-z2wMAobdo0BNxNa7hG_gIHVPkXu14Jfo1JhUhS4wES3DdY3a6olqPcRN1TCCUVHd-1TLd1BBS-yq9tdJ6HCewhe5fXonaRRKwutvoH7i_eR4m3fQ1GoVzVAA3IngpTr4ptnM3Ef3fj-5wZYmitzrRUyQtfARTl3qGaXP_g8pHFAP0zrNVvOnV-jcNMKm8YZNcgcs1SuLSFtUDXpf7Nr2_xOhiNM-biES6Dza1sMLrlxULFuctudO9lykB7yFh3LHMxtIZyIUHuy0RbjuOGC5PmDowLttZpPI_j4ynJHAaAWr8Ddz764WdQ\",\"e\":\"AQAB\"}]}"); - }) - .add(Methods.GET, "/petstore/pets", exchange -> exchange.getResponseSender().send("get")); + }); + } @Test diff --git a/openapi-security/src/test/java/com/networknt/openapi/JwtVerifyHandlerTest.java b/openapi-security/src/test/java/com/networknt/openapi/JwtVerifyHandlerTest.java index 83ecc2d..6911ce1 100644 --- a/openapi-security/src/test/java/com/networknt/openapi/JwtVerifyHandlerTest.java +++ b/openapi-security/src/test/java/com/networknt/openapi/JwtVerifyHandlerTest.java @@ -46,43 +46,62 @@ import java.util.concurrent.atomic.AtomicReference; /** - * Created by steve on 01/09/16. Disabled as it has threading issue as the same request will access the server twice - * to get the token in the middle. + * Created by steve on 01/09/16. */ -@Ignore public class JwtVerifyHandlerTest { static final Logger logger = LoggerFactory.getLogger(JwtVerifyHandlerTest.class); - static Undertow server = null; - + static Undertow server1 = null; + static Undertow server2 = null; @BeforeClass public static void setUp() { - if (server == null) { - logger.info("starting server"); + if (server1 == null) { + logger.info("starting server1"); HttpHandler handler = getTestHandler(); JwtVerifyHandler jwtVerifyHandler = new JwtVerifyHandler(); jwtVerifyHandler.setNext(handler); OpenApiHandler openApiHandler = new OpenApiHandler(); openApiHandler.setNext(jwtVerifyHandler); - server = Undertow.builder() + server1 = Undertow.builder() .addHttpListener(7081, "localhost") .setHandler(openApiHandler) .build(); - server.start(); + server1.start(); } + + if (server2 == null) { + logger.info("starting server2"); + HttpHandler handler = getJwksHandler(); + server2 = Undertow.builder() + .addHttpListener(7082, "localhost") + .setHandler(handler) + .build(); + server2.start(); + } + } @AfterClass public static void tearDown() throws Exception { - if (server != null) { + if (server1 != null) { try { Thread.sleep(100); } catch (InterruptedException ignored) { } - server.stop(); - logger.info("The server is stopped."); + server1.stop(); + logger.info("The server1 is stopped."); } + if (server2 != null) { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + + } + server2.stop(); + logger.info("The server2 is stopped."); + } + } static RoutingHandler getTestHandler() { @@ -98,11 +117,16 @@ static RoutingHandler getTestHandler() { exchange.endExchange(); } }) + .add(Methods.GET, "/v1/pets", exchange -> exchange.getResponseSender().send("get")); + } + + static RoutingHandler getJwksHandler() { + return Handlers.routing() .add(Methods.GET, "/oauth2/N2CMw0HGQXeLvC1wBfln2A/keys", exchange -> { exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); exchange.getResponseSender().send("{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"Tj_l_tIBTginOtQbL0Pv5w\",\"n\":\"0YRbWAb1FGDpPUUcrIpJC6BwlswlKMS-z2wMAobdo0BNxNa7hG_gIHVPkXu14Jfo1JhUhS4wES3DdY3a6olqPcRN1TCCUVHd-1TLd1BBS-yq9tdJ6HCewhe5fXonaRRKwutvoH7i_eR4m3fQ1GoVzVAA3IngpTr4ptnM3Ef3fj-5wZYmitzrRUyQtfARTl3qGaXP_g8pHFAP0zrNVvOnV-jcNMKm8YZNcgcs1SuLSFtUDXpf7Nr2_xOhiNM-biES6Dza1sMLrlxULFuctudO9lykB7yFh3LHMxtIZyIUHuy0RbjuOGC5PmDowLttZpPI_j4ynJHAaAWr8Ddz764WdQ\",\"e\":\"AQAB\"}]}"); - }) - .add(Methods.GET, "/v1/pets", exchange -> exchange.getResponseSender().send("get")); + }); + } @Test diff --git a/openapi-security/src/test/java/com/networknt/openapi/UnifiedSecurityHandlerTest.java b/openapi-security/src/test/java/com/networknt/openapi/UnifiedSecurityHandlerTest.java index eb4dbd2..dad3c70 100644 --- a/openapi-security/src/test/java/com/networknt/openapi/UnifiedSecurityHandlerTest.java +++ b/openapi-security/src/test/java/com/networknt/openapi/UnifiedSecurityHandlerTest.java @@ -33,11 +33,7 @@ /** * This is a test case for UnifiedSecurityHandler. It is using the petstore.yaml file in the test resources - * Due to the jwk is in the same server and it is called within the context of the petstore access, there are - * some threading issue that cause some test case failures. So, I have to run the test case one by one to make - * all passed. For build, we have disabled this test case for now. */ -@Ignore public class UnifiedSecurityHandlerTest { static final Logger logger = LoggerFactory.getLogger(UnifiedSecurityHandlerTest.class); @@ -50,6 +46,44 @@ public class UnifiedSecurityHandlerTest { static final int httpsPort = server.getServerConfig().getHttpsPort(); static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort; + static Undertow server2 = null; + @BeforeClass + public static void setUp() { + if (server2 == null) { + logger.info("starting server2"); + HttpHandler handler = getJwksHandler(); + server2 = Undertow.builder() + .addHttpListener(7082, "localhost") + .setHandler(handler) + .build(); + server2.start(); + } + + } + + @AfterClass + public static void tearDown() throws Exception { + if (server2 != null) { + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + + } + server2.stop(); + logger.info("The server2 is stopped."); + } + + } + + static RoutingHandler getJwksHandler() { + return Handlers.routing() + .add(Methods.GET, "/oauth2/N2CMw0HGQXeLvC1wBfln2A/keys", exchange -> { + exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); + exchange.getResponseSender().send("{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"Tj_l_tIBTginOtQbL0Pv5w\",\"n\":\"0YRbWAb1FGDpPUUcrIpJC6BwlswlKMS-z2wMAobdo0BNxNa7hG_gIHVPkXu14Jfo1JhUhS4wES3DdY3a6olqPcRN1TCCUVHd-1TLd1BBS-yq9tdJ6HCewhe5fXonaRRKwutvoH7i_eR4m3fQ1GoVzVAA3IngpTr4ptnM3Ef3fj-5wZYmitzrRUyQtfARTl3qGaXP_g8pHFAP0zrNVvOnV-jcNMKm8YZNcgcs1SuLSFtUDXpf7Nr2_xOhiNM-biES6Dza1sMLrlxULFuctudO9lykB7yFh3LHMxtIZyIUHuy0RbjuOGC5PmDowLttZpPI_j4ynJHAaAWr8Ddz764WdQ\",\"e\":\"AQAB\"}]}"); + }); + + } + static RoutingHandler getTestHandler() { return Handlers.routing() .add(Methods.GET, "/v1/pets/{petId}", exchange -> { diff --git a/openapi-security/src/test/resources/config/openapi-handler-multiple.yml b/openapi-security/src/test/resources/config/openapi-handler-multiple.yml index 52aeb67..002a69a 100644 --- a/openapi-security/src/test/resources/config/openapi-handler-multiple.yml +++ b/openapi-security/src/test/resources/config/openapi-handler-multiple.yml @@ -7,4 +7,3 @@ multipleSpec: ${openapi-handler.multipleSpec:true} pathSpecMapping: /petstore: openapi-petstore /market: openapi-market - /oauth2: openapi-oauth2 diff --git a/openapi-security/src/test/resources/config/openapi-oauth2.yaml b/openapi-security/src/test/resources/config/openapi-oauth2.yaml deleted file mode 100644 index fc62882..0000000 --- a/openapi-security/src/test/resources/config/openapi-oauth2.yaml +++ /dev/null @@ -1,318 +0,0 @@ ---- -openapi: "3.0.0" -info: - version: "1.0.0" - title: "OAuth2 Service" - description: "OAuth2 Service that include code, token and keys." - contact: - email: "stevehu@gmail.com" - license: - name: "Apache 2.0" - url: "http://www.apache.org/licenses/LICENSE-2.0.html" -servers: - - url: "https://oauth2.networknt.com/oauth2" -paths: - /{hostId}/code: - get: - description: "Return 302 redirect with authorization code" - operationId: "getAuthCode" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - - name: "response_type" - in: "query" - description: "The response type for authorization code" - required: true - schema: - type: "string" - enum: - - "code" - - name: "client_id" - in: "query" - description: "The client id for authorization code" - required: true - schema: - type: "string" - - name: "redirect_uri" - in: "query" - description: "The redirect uri for authorization code" - required: false - schema: - type: "string" - - name: "username" - in: "query" - description: "The user name for authorization code" - required: false - schema: - type: "string" - - name: "password" - in: "query" - description: "The password for authorization code in clear text" - required: false - schema: - type: "string" - - name: "user_type" - in: "query" - description: "The type of user that drives authentication and authorization" - required: false - schema: - type: "string" - - name: "roles" - in: "query" - description: "User roles concat with a space for fine-grained authorization" - required: false - schema: - type: "string" - - name: "state" - in: "query" - description: "to prevent cross-site request forgery" - required: false - schema: - type: "string" - - name: "scope" - in: "query" - description: "scope of the request" - required: false - schema: - type: "string" - - name: "code_challenge" - in: "query" - description: "PKCE code challenge" - required: false - schema: - type: "string" - - name: "code_challenge_method" - in: "query" - description: "PKCE code challenge method" - required: false - schema: - type: "string" - responses: - "302": - description: "Successful Operation" - post: - description: "Return 302 redirect with authorization code" - operationId: "postAuthCode" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - responses: - "302": - description: "Successful Operation" - requestBody: - content: - application/x-www-form-urlencoded: - schema: - type: "object" - properties: - j_username: - description: "User name" - type: "string" - j_password: - description: "Password" - type: "string" - response_type: - description: "Response type" - type: "string" - enum: - - "code" - client_id: - description: "Client Id" - type: "string" - redirect_uri: - description: "Redirect Uri" - type: "string" - state: - description: "to prevent cross-site request forgery" - type: "string" - scope: - description: "scope of the request" - type: "string" - code_challenge: - description: "PKCE code challenge" - type: "string" - code_challenge_method: - description: "PKCE code challenge method" - type: "string" - required: - - "j_username" - - "j_password" - - "response_type" - - "client_id" - /{hostId}/keys: - get: - description: "Get the statndard jwks" - operationId: "getJwks" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - responses: - "200": - description: "Successful response" - content: - application/json: - schema: - $ref: "#/components/schemas/Jwk" - "400": - description: "Bad request" - "404": - description: "Key not found" - security: - - key_auth: - - "oauth.key.r" - - "oauth.key.w" - /{hostId}/token: - post: - description: "JSON object that contains access token" - operationId: "postToken" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - responses: - "200": - description: "Successful Operation" - requestBody: - content: - application/json: - schema: - type: "object" - properties: - grant_type: - type: "string" - enum: - - "authorization_code" - - "client_credentials" - - "password" - - "refresh_token" - - "client_authenticated_user" - client_id: - description: "used as alternative to authentication header for client\ - \ authentication" - type: "string" - client_secret: - description: "used as alternative to authentication header for client\ - \ authentication" - type: "string" - code: - description: "used in authorization_code to specify the code" - type: "string" - username: - description: "mandatory in password grant type" - type: "string" - password: - description: "mandatory in password grant type" - type: "string" - user_type: - description: "mandatory user type in password grant type" - type: "string" - roles: - description: "optional user roles in password grant type" - type: "string" - scope: - description: "used by all flows to specify scope in the access token" - type: "string" - redirect_uri: - description: "used in authorization code if code endpoint with rediret_uri" - type: "string" - refresh_token: - description: "refresh token used to get another access token" - type: "string" - code_verifier: - description: "PKCE code verifier" - type: "string" - required: - - "grant_type" - /{hostId}/deref/{token}: - get: - description: "exchange by-reference token to a JWT" - operationId: "derefToken" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - - name: "token" - in: "path" - description: "by-reference token" - required: true - schema: - type: "string" - responses: - "200": - description: "successful operation" - /{hostId}/signing: - post: - description: "Sign a JSON object and return a JWT" - operationId: "postSigning" - parameters: - - name: "hostId" - description: "The host id this service belongs to" - in: "path" - required: true - schema: - type: "string" - responses: - "200": - description: "Successful Operation" - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/SignRequest" - description: "Signing request object" - required: true -components: - securitySchemes: - key_auth: - type: "oauth2" - flows: - implicit: - authorizationUrl: "http://localhost:8888/oauth2/code" - scopes: - oauth.key.w: "write key" - oauth.key.r: "read key" - schemas: - Jwk: - type: "object" - required: - - "keyId" - - "certificate" - properties: - keyId: - type: "string" - description: "a unique id" - certificate: - type: "string" - description: "certificate" - SignRequest: - type: "object" - required: - - "expires" - - "payload" - properties: - expires: - type: "integer" - format: "int32" - description: "expires in seconds" - payload: - type: "object" - description: "payload that needs to be signed" - diff --git a/openapi-security/src/test/resources/config/values.yml b/openapi-security/src/test/resources/config/values.yml index 7b833fb..bc4cc5a 100644 --- a/openapi-security/src/test/resources/config/values.yml +++ b/openapi-security/src/test/resources/config/values.yml @@ -43,7 +43,7 @@ apikey.pathPrefixAuths: apiKey: CRYPT:3ddd6c8b9bf2afc24d1c94af1dffd518:1bf0cafb19c53e61ddeae626f8906d43 # client.yml -client.tokenKeyServerUrl: http://localhost:7081 +client.tokenKeyServerUrl: http://localhost:7082 client.tokenKeyUri: /oauth2/N2CMw0HGQXeLvC1wBfln2A/keys client.tokenKeyClientId: f7d42348-c647-4efb-a52d-4c5787421e72 client.tokenKeyClientSecret: f6h1FTI8Q3-7UScPZDzfXA