Search in sources :

Example 51 with APISecurityException

use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.

the class OAuthTokenGeneratorTest method testOauthBackendSecurityWithClientCredentialsGrant.

/**
 * Test OAuth backend security with client credentials grant type
 */
@Test
public void testOauthBackendSecurityWithClientCredentialsGrant() throws ParseException, IOException, APIManagementException, APISecurityException {
    // Assign values for test specific properties of mock token response and oAuthEndpoint object.
    mockTokenResponse.setExpiresIn("1800");
    long validTill = System.currentTimeMillis() / 1000 + Long.parseLong(mockTokenResponse.getExpiresIn());
    mockTokenResponse.setValidTill(validTill);
    mockTokenResponse.setRefreshToken("testRefreshToken");
    oAuthEndpoint.setId("testID1");
    oAuthEndpoint.setGrantType("CLIENT_CREDENTIALS");
    // First token generation operation. Token endpoint will be called and the token response will be cached.
    TokenResponse tokenResponse = OAuthTokenGenerator.generateToken(oAuthEndpoint, latch);
    Assert.assertNotNull(tokenResponse);
    Assert.assertNotNull(tokenCache.getTokenMap().get(oAuthEndpoint.getId()));
    // Second token generation operation. Since the token response was cached, the token endpoint will not be
    // called during this operation.
    tokenResponse = OAuthTokenGenerator.generateToken(oAuthEndpoint, latch);
    Assert.assertNotNull(tokenResponse);
    // Token endpoint will be called only one time (during the first token generation operation).
    PowerMockito.verifyStatic(OAuthClient.class, Mockito.times(1));
    OAuthClient.generateToken(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.anyString());
}
Also used : TokenResponse(org.wso2.carbon.apimgt.gateway.mediators.oauth.client.TokenResponse) Test(org.junit.Test) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest)

Example 52 with APISecurityException

use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.

the class InboundWebsocketProcessorUtil method isAuthenticated.

/**
 * Authenticate inbound websocket request handshake.
 *
 * @param inboundMessageContext InboundMessageContext
 * @return whether authenticated or not
 * @throws APIManagementException if an internal error occurs
 * @throws APISecurityException   if authentication fails
 */
public static boolean isAuthenticated(InboundMessageContext inboundMessageContext) throws APISecurityException, APIManagementException {
    try {
        PrivilegedCarbonContext.startTenantFlow();
        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(inboundMessageContext.getTenantDomain(), true);
        APIKeyValidationInfoDTO info;
        String authorizationHeader = inboundMessageContext.getRequestHeaders().get(HttpHeaders.AUTHORIZATION);
        inboundMessageContext.getRequestHeaders().put(HttpHeaders.AUTHORIZATION, authorizationHeader);
        String[] auth = authorizationHeader.split(StringUtils.SPACE);
        List<String> keyManagerList = DataHolder.getInstance().getKeyManagersFromUUID(inboundMessageContext.getElectedAPI().getUuid());
        if (APIConstants.CONSUMER_KEY_SEGMENT.equals(auth[0])) {
            String cacheKey;
            boolean isJwtToken = false;
            String apiKey = auth[1];
            if (WebsocketUtil.isRemoveOAuthHeadersFromOutMessage()) {
                inboundMessageContext.getRequestHeaders().remove(HttpHeaders.AUTHORIZATION);
            }
            // Initial guess of a JWT token using the presence of a DOT.
            if (StringUtils.isNotEmpty(apiKey) && apiKey.contains(APIConstants.DOT)) {
                try {
                    // Check if the header part is decoded
                    if (StringUtils.countMatches(apiKey, APIConstants.DOT) != 2) {
                        log.debug("Invalid JWT token. The expected token format is <header.payload.signature>");
                        throw new APISecurityException(APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token");
                    }
                    inboundMessageContext.setSignedJWTInfo(getSignedJwtInfo(apiKey));
                    String keyManager = ServiceReferenceHolder.getInstance().getJwtValidationService().getKeyManagerNameIfJwtValidatorExist(inboundMessageContext.getSignedJWTInfo());
                    if (StringUtils.isNotEmpty(keyManager)) {
                        if (log.isDebugEnabled()) {
                            log.debug("KeyManager " + keyManager + "found for authenticate token " + GatewayUtils.getMaskedToken(apiKey));
                        }
                        if (keyManagerList.contains(APIConstants.KeyManager.API_LEVEL_ALL_KEY_MANAGERS) || keyManagerList.contains(keyManager)) {
                            if (log.isDebugEnabled()) {
                                log.debug("Elected KeyManager " + keyManager + "found in API level list " + String.join(",", keyManagerList));
                            }
                            isJwtToken = true;
                        } else {
                            if (log.isDebugEnabled()) {
                                log.debug("Elected KeyManager " + keyManager + " not found in API level list " + String.join(",", keyManagerList));
                            }
                            throw new APISecurityException(APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token");
                        }
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("KeyManager not found for accessToken " + GatewayUtils.getMaskedToken(apiKey));
                        }
                    }
                } catch (ParseException e) {
                    log.debug("Not a JWT token. Failed to decode the token header.", e);
                } catch (APIManagementException e) {
                    log.error("Error while checking validation of JWT", e);
                    throw new APISecurityException(APISecurityConstants.API_AUTH_GENERAL_ERROR, APISecurityConstants.API_AUTH_GENERAL_ERROR_MESSAGE);
                }
            }
            // Find the authentication scheme based on the token type
            if (isJwtToken) {
                log.debug("The token was identified as a JWT token");
                if (APIConstants.GRAPHQL_API.equals(inboundMessageContext.getElectedAPI().getApiType())) {
                    return InboundWebsocketProcessorUtil.authenticateGraphQLJWTToken(inboundMessageContext);
                } else {
                    return InboundWebsocketProcessorUtil.authenticateWSJWTToken(inboundMessageContext);
                }
            } else {
                log.debug("The token was identified as an OAuth token");
                // If the key have already been validated
                if (WebsocketUtil.isGatewayTokenCacheEnabled()) {
                    cacheKey = WebsocketUtil.getAccessTokenCacheKey(apiKey, inboundMessageContext.getApiContext(), inboundMessageContext.getMatchingResource());
                    info = WebsocketUtil.validateCache(apiKey, cacheKey);
                    if (info != null) {
                        inboundMessageContext.setKeyType(info.getType());
                        inboundMessageContext.setInfoDTO(info);
                        return info.isAuthorized();
                    }
                }
                info = getApiKeyDataForWSClient(apiKey, inboundMessageContext.getTenantDomain(), inboundMessageContext.getApiContext(), inboundMessageContext.getVersion(), keyManagerList);
                if (info == null || !info.isAuthorized()) {
                    return false;
                }
                if (WebsocketUtil.isGatewayTokenCacheEnabled()) {
                    cacheKey = WebsocketUtil.getAccessTokenCacheKey(apiKey, inboundMessageContext.getApiContext(), inboundMessageContext.getMatchingResource());
                    WebsocketUtil.putCache(info, apiKey, cacheKey);
                }
                inboundMessageContext.setKeyType(info.getType());
                inboundMessageContext.setToken(info.getEndUserToken());
                inboundMessageContext.setInfoDTO(info);
                return true;
            }
        } else {
            return false;
        }
    } finally {
        PrivilegedCarbonContext.endTenantFlow();
    }
}
Also used : APISecurityException(org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException) APIManagementException(org.wso2.carbon.apimgt.api.APIManagementException) ParseException(java.text.ParseException) APIKeyValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)

Example 53 with APISecurityException

use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.

the class BasicAuthAuthenticator method authenticate.

/**
 * Authenticates the given request to see if an API consumer is allowed to access
 * a particular API or not.
 *
 * @param synCtx The message to be authenticated
 * @return an AuthenticationResponse object which contains the authentication status
 */
@MethodStats
public AuthenticationResponse authenticate(MessageContext synCtx) {
    if (log.isDebugEnabled()) {
        log.info("Basic Authentication initialized");
    }
    openAPI = (OpenAPI) synCtx.getProperty(APIMgtGatewayConstants.OPEN_API_OBJECT);
    if (openAPI == null && !APIConstants.GRAPHQL_API.equals(synCtx.getProperty(APIConstants.API_TYPE))) {
        log.error("OpenAPI definition is missing in the gateway. Basic authentication cannot be performed.");
        return new AuthenticationResponse(false, isMandatory, true, APISecurityConstants.API_AUTH_MISSING_OPEN_API_DEF, "Basic authentication cannot be performed.");
    }
    // Extract basic authorization header while removing it from the authorization header
    String basicAuthHeader = extractBasicAuthHeader(synCtx);
    String apiContext = (String) synCtx.getProperty(RESTConstants.REST_API_CONTEXT);
    String apiVersion = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API_VERSION);
    String httpMethod = (String) ((Axis2MessageContext) synCtx).getAxis2MessageContext().getProperty(Constants.Configuration.HTTP_METHOD);
    String matchingResource = (String) synCtx.getProperty(APIConstants.API_ELECTED_RESOURCE);
    // Check for resource level authentication
    String authenticationScheme;
    List<VerbInfoDTO> verbInfoList;
    if (APIConstants.GRAPHQL_API.equals(synCtx.getProperty(APIConstants.API_TYPE))) {
        HashMap<String, Boolean> operationAuthSchemeMappingList = (HashMap<String, Boolean>) synCtx.getProperty(APIConstants.OPERATION_AUTH_SCHEME_MAPPING);
        HashMap<String, String> operationThrottlingMappingList = (HashMap<String, String>) synCtx.getProperty(APIConstants.OPERATION_THROTTLING_MAPPING);
        String[] operationList = matchingResource.split(",");
        verbInfoList = new ArrayList<>(1);
        authenticationScheme = APIConstants.AUTH_NO_AUTHENTICATION;
        for (String operation : operationList) {
            boolean operationAuthSchemeEnabled = operationAuthSchemeMappingList.get(operation);
            VerbInfoDTO verbInfoDTO = new VerbInfoDTO();
            if (operationAuthSchemeEnabled) {
                verbInfoDTO.setAuthType(APIConstants.AUTH_APPLICATION_OR_USER_LEVEL_TOKEN);
                authenticationScheme = APIConstants.AUTH_APPLICATION_OR_USER_LEVEL_TOKEN;
            } else {
                verbInfoDTO.setAuthType(APIConstants.AUTH_NO_AUTHENTICATION);
            }
            verbInfoDTO.setThrottling(operationThrottlingMappingList.get(operation));
            verbInfoDTO.setRequestKey(apiContext + "/" + apiVersion + operation + ":" + httpMethod);
            verbInfoList.add(verbInfoDTO);
        }
    } else {
        authenticationScheme = OpenAPIUtils.getResourceAuthenticationScheme(openAPI, synCtx);
        verbInfoList = new ArrayList<>(1);
        VerbInfoDTO verbInfoDTO = new VerbInfoDTO();
        verbInfoDTO.setAuthType(authenticationScheme);
        verbInfoDTO.setThrottling(OpenAPIUtils.getResourceThrottlingTier(openAPI, synCtx));
        verbInfoDTO.setRequestKey(apiContext + "/" + apiVersion + matchingResource + ":" + httpMethod);
        verbInfoList.add(verbInfoDTO);
    }
    String[] credentials;
    try {
        credentials = extractBasicAuthCredentials(basicAuthHeader);
    } catch (APISecurityException ex) {
        return new AuthenticationResponse(false, isMandatory, true, ex.getErrorCode(), ex.getMessage());
    }
    String username = getEndUserName(credentials[0]);
    String password = credentials[1];
    // If end user tenant domain does not match the API publisher's tenant domain, return error
    if (!MultitenantUtils.getTenantDomain(username).equals(synCtx.getProperty(PUBLISHER_TENANT_DOMAIN))) {
        log.error("Basic Authentication failure: tenant domain mismatch for user :" + username);
        return new AuthenticationResponse(false, isMandatory, true, APISecurityConstants.API_AUTH_FORBIDDEN, APISecurityConstants.API_AUTH_FORBIDDEN_MESSAGE);
    }
    BasicAuthValidationInfoDTO basicAuthValidationInfoObj;
    try {
        if (basicAuthCredentialValidator == null) {
            basicAuthCredentialValidator = new BasicAuthCredentialValidator();
        }
        basicAuthValidationInfoObj = basicAuthCredentialValidator.validate(username, password);
    } catch (APISecurityException ex) {
        return new AuthenticationResponse(false, isMandatory, true, ex.getErrorCode(), ex.getMessage());
    }
    if (!basicAuthValidationInfoObj.isAuthenticated()) {
        log.error("Basic Authentication failure: Username and Password mismatch");
        return new AuthenticationResponse(false, isMandatory, true, APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, APISecurityConstants.API_AUTH_INVALID_CREDENTIALS_MESSAGE);
    } else {
        // username password matches
        if (log.isDebugEnabled()) {
            log.debug("Basic Authentication: Username and Password authenticated");
        }
        // scope validation
        boolean scopesValid = false;
        try {
            scopesValid = basicAuthCredentialValidator.validateScopes(username, openAPI, synCtx, basicAuthValidationInfoObj);
        } catch (APISecurityException ex) {
            return new AuthenticationResponse(false, isMandatory, true, ex.getErrorCode(), ex.getMessage());
        }
        String domainQualifiedUserName = basicAuthValidationInfoObj.getDomainQualifiedUsername();
        if (scopesValid) {
            if (APISecurityUtils.getAuthenticationContext(synCtx) == null) {
                // Create a dummy AuthenticationContext object with hard coded values for
                // Tier and KeyType. This is because we cannot determine the Tier nor Key
                // Type without subscription information..
                AuthenticationContext authContext = new AuthenticationContext();
                authContext.setAuthenticated(true);
                authContext.setTier(APIConstants.UNAUTHENTICATED_TIER);
                authContext.setStopOnQuotaReach(// Since we don't have details on unauthenticated tier we setting stop on quota reach true
                true);
                synCtx.setProperty(APIConstants.VERB_INFO_DTO, verbInfoList);
                // In basic authentication scenario, we will use the username for throttling.
                authContext.setApiKey(domainQualifiedUserName);
                authContext.setKeyType(APIConstants.API_KEY_TYPE_PRODUCTION);
                authContext.setUsername(domainQualifiedUserName);
                authContext.setCallerToken(null);
                authContext.setApplicationName(APIConstants.BASIC_AUTH_APPLICATION_NAME);
                // Set username as application ID in basic auth scenario
                authContext.setApplicationId(domainQualifiedUserName);
                // Set username as application ID in basic auth scenario
                authContext.setApplicationUUID(domainQualifiedUserName);
                // Set application owner in basic auth scenario
                authContext.setSubscriber(APIConstants.BASIC_AUTH_APPLICATION_OWNER);
                authContext.setConsumerKey(null);
                authContext.setApiTier(apiLevelPolicy);
                APISecurityUtils.setAuthenticationContext(synCtx, authContext, null);
            }
            log.debug("Basic Authentication: Scope validation passed");
            return new AuthenticationResponse(true, isMandatory, false, 0, null);
        }
        return new AuthenticationResponse(false, isMandatory, true, APISecurityConstants.INVALID_SCOPE, "Scope validation failed");
    }
}
Also used : APISecurityException(org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException) AuthenticationContext(org.wso2.carbon.apimgt.gateway.handlers.security.AuthenticationContext) HashMap(java.util.HashMap) AuthenticationResponse(org.wso2.carbon.apimgt.gateway.handlers.security.AuthenticationResponse) BasicAuthValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.BasicAuthValidationInfoDTO) VerbInfoDTO(org.wso2.carbon.apimgt.impl.dto.VerbInfoDTO) Axis2MessageContext(org.apache.synapse.core.axis2.Axis2MessageContext) MethodStats(org.wso2.carbon.apimgt.gateway.MethodStats)

Example 54 with APISecurityException

use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.

the class JWTValidator method validateScopes.

/**
 * Validate scopes bound to the resource of the API being invoked against the scopes specified
 * in the JWT token payload.
 *
 * @param apiContext        API Context
 * @param apiVersion        API Version
 * @param matchingResource  Accessed API resource
 * @param httpMethod        API resource's HTTP method
 * @param jwtValidationInfo Validated JWT Information
 * @param jwtToken          JWT Token
 * @throws APISecurityException in case of scope validation failure
 */
private void validateScopes(String apiContext, String apiVersion, String matchingResource, String httpMethod, JWTValidationInfo jwtValidationInfo, SignedJWTInfo jwtToken) throws APISecurityException {
    String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
    // Generate TokenValidationContext
    TokenValidationContext tokenValidationContext = new TokenValidationContext();
    APIKeyValidationInfoDTO apiKeyValidationInfoDTO = new APIKeyValidationInfoDTO();
    Set<String> scopeSet = new HashSet<>();
    scopeSet.addAll(jwtValidationInfo.getScopes());
    apiKeyValidationInfoDTO.setScopes(scopeSet);
    tokenValidationContext.setValidationInfoDTO(apiKeyValidationInfoDTO);
    tokenValidationContext.setAccessToken(jwtToken.getToken());
    tokenValidationContext.setHttpVerb(httpMethod);
    tokenValidationContext.setMatchingResource(matchingResource);
    tokenValidationContext.setContext(apiContext);
    tokenValidationContext.setVersion(apiVersion);
    boolean valid = this.apiKeyValidator.validateScopes(tokenValidationContext, tenantDomain);
    if (valid) {
        if (log.isDebugEnabled()) {
            log.debug("Scope validation successful for the resource: " + matchingResource + ", user: " + jwtValidationInfo.getUser());
        }
    } else {
        String message = "User is NOT authorized to access the Resource: " + matchingResource + ". Scope validation failed.";
        log.debug(message);
        throw new APISecurityException(APISecurityConstants.INVALID_SCOPE, message);
    }
}
Also used : APISecurityException(org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException) TokenValidationContext(org.wso2.carbon.apimgt.keymgt.service.TokenValidationContext) APIKeyValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO) HashSet(java.util.HashSet)

Example 55 with APISecurityException

use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.

the class JWTValidator method authenticateForWebSocket.

/**
 * Authenticates the given WebSocket handshake request with a JWT token to see if an API consumer is allowed to
 * access a particular API or not.
 *
 * @param signedJWTInfo    The JWT token sent with the API request
 * @param apiContext       The context of the invoked API
 * @param apiVersion       The version of the invoked API
 * @param matchingResource template of matching api resource
 * @return an AuthenticationContext object which contains the authentication information
 * @throws APISecurityException in case of authentication failure
 */
@MethodStats
public AuthenticationContext authenticateForWebSocket(SignedJWTInfo signedJWTInfo, String apiContext, String apiVersion, String matchingResource) throws APISecurityException {
    String tokenSignature = signedJWTInfo.getSignedJWT().getSignature().toString();
    JWTClaimsSet jwtClaimsSet = signedJWTInfo.getJwtClaimsSet();
    String jti = getJWTTokenIdentifier(signedJWTInfo);
    JWTValidationInfo jwtValidationInfo = validateTokenForWS(signedJWTInfo, tokenSignature, jti);
    if (jwtValidationInfo != null && jwtValidationInfo.isValid()) {
        APIKeyValidationInfoDTO apiKeyValidationInfoDTO = validateSubscriptionsForWS(jwtValidationInfo, apiContext, apiVersion);
        if (apiKeyValidationInfoDTO.isAuthorized()) {
            validateScopes(apiContext, apiVersion, matchingResource, WebSocketApiConstants.WEBSOCKET_DUMMY_HTTP_METHOD_NAME, jwtValidationInfo, signedJWTInfo);
            log.debug("JWT authentication successful. user: " + apiKeyValidationInfoDTO.getEndUserName());
            String endUserToken = generateBackendJWTForWS(jwtValidationInfo, apiKeyValidationInfoDTO, apiContext, apiVersion, tokenSignature);
            return generateAuthenticationContextForWS(jti, jwtValidationInfo, apiKeyValidationInfoDTO, endUserToken, apiVersion);
        } else {
            String message = "User is NOT authorized to access the Resource. API Subscription validation failed.";
            log.debug(message);
            throw new APISecurityException(apiKeyValidationInfoDTO.getValidationStatus(), message);
        }
    } else if (!jwtValidationInfo.isValid()) {
        throw new APISecurityException(APISecurityConstants.API_AUTH_INVALID_CREDENTIALS, "Invalid JWT token");
    }
    throw new APISecurityException(APISecurityConstants.API_AUTH_GENERAL_ERROR, APISecurityConstants.API_AUTH_GENERAL_ERROR_MESSAGE);
}
Also used : APISecurityException(org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException) JWTClaimsSet(com.nimbusds.jwt.JWTClaimsSet) APIKeyValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO) JWTValidationInfo(org.wso2.carbon.apimgt.common.gateway.dto.JWTValidationInfo) MethodStats(org.wso2.carbon.apimgt.gateway.MethodStats)

Aggregations

APISecurityException (org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException)34 Test (org.junit.Test)28 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)28 APIKeyValidationInfoDTO (org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)28 Axis2MessageContext (org.apache.synapse.core.axis2.Axis2MessageContext)26 Cache (javax.cache.Cache)22 AuthenticationContext (org.wso2.carbon.apimgt.gateway.handlers.security.AuthenticationContext)16 JWTValidationInfo (org.wso2.carbon.apimgt.common.gateway.dto.JWTValidationInfo)15 APIKeyValidator (org.wso2.carbon.apimgt.gateway.handlers.security.APIKeyValidator)15 MessageContext (org.apache.synapse.MessageContext)14 APIManagerConfiguration (org.wso2.carbon.apimgt.impl.APIManagerConfiguration)14 SignedJWTInfo (org.wso2.carbon.apimgt.impl.jwt.SignedJWTInfo)12 SignedJWT (com.nimbusds.jwt.SignedJWT)11 HashMap (java.util.HashMap)11 VerbInfoDTO (org.wso2.carbon.apimgt.impl.dto.VerbInfoDTO)11 ArrayList (java.util.ArrayList)10 APIManagementException (org.wso2.carbon.apimgt.api.APIManagementException)10 ExtendedJWTConfigurationDto (org.wso2.carbon.apimgt.impl.dto.ExtendedJWTConfigurationDto)10 JWTValidationService (org.wso2.carbon.apimgt.impl.jwt.JWTValidationService)10 TokenValidationContext (org.wso2.carbon.apimgt.keymgt.service.TokenValidationContext)10