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());
}
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();
}
}
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");
}
}
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);
}
}
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);
}
Aggregations