Skip to content

Commit

Permalink
fixes #213 update the JwtVerifierHandler to use SecurityConfig
Browse files Browse the repository at this point in the history
  • Loading branch information
stevehu committed Jun 12, 2022
1 parent 3fea1fb commit 4c728a5
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 26 deletions.
4 changes: 2 additions & 2 deletions access-control/src/main/resources/config/access-control.yml
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand All @@ -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<String, Object> 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;
Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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<String> specScopes = null;
Collection<SecurityRequirement> securityRequirements = operation.getSecurityRequirements();
Expand Down Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions openapi-security/src/main/resources/config/openapi-security.yml
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
@@ -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());

}
}
13 changes: 9 additions & 4 deletions openapi-security/src/test/resources/config/openapi-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion specification/src/main/resources/config/specification.yml
Original file line number Diff line number Diff line change
@@ -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}

0 comments on commit 4c728a5

Please sign in to comment.