diff --git a/access-control/src/main/resources/config/access-control.yml b/access-control/src/main/resources/config/access-control.yml index 6dd00602..cf85595c 100644 --- a/access-control/src/main/resources/config/access-control.yml +++ b/access-control/src/main/resources/config/access-control.yml @@ -1,8 +1,8 @@ # AccessControlHandler will be the last middleware handler before the proxy on the sidecar or the last # one before the business handler to handle the fine-grained authorization in the business domain. # Enable Access Control Handler -enabled: ${accessControl.enabled:true} +enabled: ${access-control.enabled:true} # If there is no access rule defined for the endpoint, default access is denied. Users can overwrite # this default action by setting this config value to false. If true, the handle will force users to # define the rules for each endpoint when the access control handler is enabled. -defaultDeny: ${accessControl.defaultDeny:true} +defaultDeny: ${access-control.defaultDeny:true} diff --git a/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java b/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java index 4db00c31..6f1a879b 100644 --- a/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java +++ b/openapi-security/src/main/java/com/networknt/openapi/JwtVerifyHandler.java @@ -29,6 +29,7 @@ import com.networknt.oas.model.SecurityRequirement; import com.networknt.security.IJwtVerifyHandler; import com.networknt.security.JwtVerifier; +import com.networknt.security.SecurityConfig; import com.networknt.utility.Constants; import com.networknt.utility.ModuleRegistry; import io.undertow.Handlers; @@ -59,9 +60,6 @@ public class JwtVerifyHandler implements MiddlewareHandler, IJwtVerifyHandler { static final String HANDLER_CONFIG = "handler"; static final String OPENAPI_SECURITY_CONFIG = "openapi-security"; - static final String ENABLE_VERIFY_SCOPE = "enableVerifyScope"; - static final String ENABLE_VERIFY_JWT_SCOPE_TOKEN = "enableExtractScopeToken"; - static final String IGNORE_JWT_EXPIRY = "ignoreJwtExpiry"; static final String STATUS_INVALID_AUTH_TOKEN = "ERR10000"; static final String STATUS_AUTH_TOKEN_EXPIRED = "ERR10001"; @@ -73,16 +71,12 @@ public class JwtVerifyHandler implements MiddlewareHandler, IJwtVerifyHandler { static final String STATUS_INVALID_REQUEST_PATH = "ERR10007"; static final String STATUS_METHOD_NOT_ALLOWED = "ERR10008"; - static Map config; + static SecurityConfig config; String basePath; // make this static variable public so that it can be accessed from the server-info module public static JwtVerifier jwtVerifier; static { - // check if openapi-security.yml exist - config = Config.getInstance().getJsonMapConfig(OPENAPI_SECURITY_CONFIG); - // fallback to generic security.yml - if(config == null) config = Config.getInstance().getJsonMapConfig(JwtVerifier.SECURITY_CONFIG); - + config = SecurityConfig.load(OPENAPI_SECURITY_CONFIG); } private volatile HttpHandler next; @@ -109,7 +103,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception { HeaderMap headerMap = exchange.getRequestHeaders(); String authorization = headerMap.getFirst(Headers.AUTHORIZATION); String jwt = jwtVerifier.getJwtFromAuthorization(authorization); - boolean ignoreExpiry = config.get(IGNORE_JWT_EXPIRY) == null ? false : (boolean)config.get(IGNORE_JWT_EXPIRY); + boolean ignoreExpiry = config.isIgnoreJwtExpiry(); if(jwt != null) { try { JwtClaims claims = jwtVerifier.verifyJwt(jwt, ignoreExpiry, true); @@ -131,7 +125,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception { auditInfo.put(Constants.SUBJECT_CLAIMS, claims); String callerId = headerMap.getFirst(HttpStringConstants.CALLER_ID); if(callerId != null) auditInfo.put(Constants.CALLER_ID_STRING, callerId); - if(config != null && (Boolean)config.get(ENABLE_VERIFY_JWT_SCOPE_TOKEN) && OpenApiHelper.openApi3 != null) { + if(config != null && config.isEnableVerifyScope() && OpenApiHelper.openApi3 != null) { Operation operation = null; OpenApiOperation openApiOperation = (OpenApiOperation)auditInfo.get(Constants.OPENAPI_OPERATION_STRING); if(openApiOperation == null) { @@ -195,7 +189,7 @@ public void handleRequest(final HttpServerExchange exchange) throws Exception { } // validate the scope against the scopes configured in the OpenAPI spec - if((Boolean)config.get(ENABLE_VERIFY_SCOPE)) { + if(config.isEnableVerifyScope()) { // get scope defined in OpenAPI spec for this endpoint. Collection specScopes = null; Collection securityRequirements = operation.getSecurityRequirements(); @@ -293,22 +287,17 @@ public MiddlewareHandler setNext(final HttpHandler next) { @Override public boolean isEnabled() { - Object object = config.get(JwtVerifier.ENABLE_VERIFY_JWT); - return object != null && Boolean.valueOf(object.toString()) ; + return config.isEnableVerifyJwt(); } @Override public void register() { - ModuleRegistry.registerModule(JwtVerifyHandler.class.getName(), config, null); ModuleRegistry.registerModule(JwtVerifyHandler.class.getName(), Config.getInstance().getJsonMapConfigNoCache(OPENAPI_SECURITY_CONFIG), null); } @Override public void reload() { - // check if openapi-security.yml exist - config = Config.getInstance().getJsonMapConfig(OPENAPI_SECURITY_CONFIG); - // fallback to generic security.yml - if(config == null) config = Config.getInstance().getJsonMapConfig(JwtVerifier.SECURITY_CONFIG); + config.reload(OPENAPI_SECURITY_CONFIG); } @Override diff --git a/openapi-security/src/main/resources/config/openapi-security.yml b/openapi-security/src/main/resources/config/openapi-security.yml new file mode 100644 index 00000000..6b6f8239 --- /dev/null +++ b/openapi-security/src/main/resources/config/openapi-security.yml @@ -0,0 +1,52 @@ +# Security configuration for openapi-security in light-rest-4j. It is a specific config +# for OpenAPI framework security. It is introduced to support multiple frameworks in the +# same server instance. If this file cannot be found, the generic security.yml will be +# loaded for backward compatibility. +--- +# Enable JWT verification flag. +enableVerifyJwt: ${openapi-security.enableVerifyJwt:true} + +# Extract JWT scope token from the X-Scope-Token header and validate the JWT token +enableExtractScopeToken: ${openapi-security.enableExtractScopeToken:true} + +# Enable JWT scope verification. Only valid when enableVerifyJwt is true. +enableVerifyScope: ${openapi-security.enableVerifyScope:true} + +# Enable JWT scope verification. +# Only valid when (enableVerifyJwt is true) AND (enableVerifyScope is true) +enableVerifyJwtScopeToken: ${openapi-security.enableVerifyJwtScopeToken:true} + +# If set true, the JWT verifier handler will pass if the JWT token is expired already. Unless +# you have a strong reason, please use it only on the dev environment if your OAuth 2 provider +# doesn't support long-lived token for dev environment or test automation. +ignoreJwtExpiry: ${openapi-security.ignoreJwtExpiry:false} + +# User for test only. should be always be false on official environment. +enableMockJwt: ${openapi-security.enableMockJwt:false} + +# JWT signature public certificates. kid and certificate path mappings. +jwt: + certificate: ${openapi-security.certificate:100=primary.crt&101=secondary.crt} +# '100': primary.crt +# '101': secondary.crt + clockSkewInSeconds: ${openapi-security.clockSkewInSeconds:60} + # Key distribution server standard: JsonWebKeySet for other OAuth 2.0 provider| X509Certificate for light-oauth2 + keyResolver: ${openapi-security.keyResolver:X509Certificate} + +# Enable or disable JWT token logging for audit. This is to log the entire token +# or choose the next option that only logs client_id, user_id and scope. +logJwtToken: ${openapi-security.logJwtToken:true} + +# Enable or disable client_id, user_id and scope logging if you don't want to log +# the entire token. Choose this option or the option above. +logClientUserScope: ${openapi-security.logClientUserScope:false} + +# Enable JWT token cache to speed up verification. This will only verify expired time +# and skip the signature verification as it takes more CPU power and long time. +enableJwtCache: ${openapi-security.enableJwtCache:true} + +# If you are using light-oauth2, then you don't need to have oauth subfolder for public +# key certificate to verify JWT token, the key will be retrieved from key endpoint once +# the first token is arrived. Default to false for dev environment without oauth2 server +# or official environment that use other OAuth 2.0 providers. +bootstrapFromKeyService: ${openapi-security.bootstrapFromKeyService:false} diff --git a/openapi-security/src/test/java/com/networknt/openapi/SecurityConfigTest.java b/openapi-security/src/test/java/com/networknt/openapi/SecurityConfigTest.java new file mode 100644 index 00000000..d0262c68 --- /dev/null +++ b/openapi-security/src/test/java/com/networknt/openapi/SecurityConfigTest.java @@ -0,0 +1,16 @@ +package com.networknt.openapi; + +import com.networknt.security.SecurityConfig; +import org.junit.Assert; +import org.junit.Test; + +public class SecurityConfigTest { + @Test + public void testLoadConfig() { + SecurityConfig config = SecurityConfig.load("openapi-security"); + Assert.assertTrue(config.isEnableVerifyJwt()); + Assert.assertFalse(config.isEnableVerifyJwtScopeToken()); + Assert.assertEquals(2, config.getCertificate().size()); + + } +} diff --git a/openapi-security/src/test/resources/config/openapi-security.yml b/openapi-security/src/test/resources/config/openapi-security.yml index 215ab026..8353273e 100644 --- a/openapi-security/src/test/resources/config/openapi-security.yml +++ b/openapi-security/src/test/resources/config/openapi-security.yml @@ -14,16 +14,21 @@ enableVerifyScope: ${openapi-security.enableVerifyScope:true} # Enable JWT scope verification. # Only valid when (enableVerifyJwt is true) AND (enableVerifyScope is true) -enableVerifyJWTScopeToken: ${openapi-security.enableVerifyJWTScopeToken:true} +enableVerifyJwtScopeToken: ${openapi-security.enableVerifyJwtScopeToken:true} + +# If set true, the JWT verifier handler will pass if the JWT token is expired already. Unless +# you have a strong reason, please use it only on the dev environment if your OAuth 2 provider +# doesn't support long-lived token for dev environment or test automation. +ignoreJwtExpiry: ${security.ignoreJwtExpiry:false} # User for test only. should be always be false on official environment. enableMockJwt: ${openapi-security.enableMockJwt:false} # JWT signature public certificates. kid and certificate path mappings. jwt: - certificate: - '100': primary.crt - '101': secondary.crt + certificate: ${openapi-security.certificate:100=primary.crt&101=secondary.crt} +# '100': primary.crt +# '101': secondary.crt clockSkewInSeconds: ${openapi-security.clockSkewInSeconds:60} # Key distribution server standard: JsonWebKeySet for other OAuth 2.0 provider| X509Certificate for light-oauth2 keyResolver: ${openapi-security.keyResolver:X509Certificate} diff --git a/specification/src/main/resources/config/specification.yml b/specification/src/main/resources/config/specification.yml index 7784b8d3..945bdbbe 100644 --- a/specification/src/main/resources/config/specification.yml +++ b/specification/src/main/resources/config/specification.yml @@ -1,6 +1,6 @@ --- # Specification type and file name definition # The filename with path of the specification file, and usually it is openapi.yaml -fileName: ${specification.fileName:config/openapi.yaml} +fileName: ${specification.fileName:openapi.yaml} # The content type of the specification file. In most cases, we are using yaml format. contentType: ${specification.contentType:text/yaml}