use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.
the class APIKeyValidator method getKeyValidationInfo.
/**
* Get the API key validated against the specified API
*
* @param context API context
* @param apiKey API key to be validated
* @param apiVersion API version number
* @param keyManagers list of key managers to authenticate the API
* @return An APIKeyValidationInfoDTO object
* @throws APISecurityException If an error occurs while accessing backend services
*/
public APIKeyValidationInfoDTO getKeyValidationInfo(String context, String apiKey, String apiVersion, String authenticationScheme, String matchingResource, String httpVerb, boolean defaultVersionInvoked, List<String> keyManagers) throws APISecurityException {
String prefixedVersion = apiVersion;
// Check if client has invoked the default version API.
if (defaultVersionInvoked) {
// Prefix the version so that it looks like _default_1.0 (_default_<version>)).
// This is so that the Key Validator knows that this request is coming through a default api version
prefixedVersion = APIConstants.DEFAULT_VERSION_PREFIX + prefixedVersion;
}
String cacheKey = APIUtil.getAccessTokenCacheKey(apiKey, context, prefixedVersion, matchingResource, httpVerb, authenticationScheme);
// If Gateway key caching is enabled.
if (gatewayKeyCacheEnabled) {
// Get the access token from the first level cache.
String cachedToken = (String) getGatewayTokenCache().get(apiKey);
// If the access token exists in the first level cache.
if (cachedToken != null) {
APIKeyValidationInfoDTO info = (APIKeyValidationInfoDTO) getGatewayKeyCache().get(cacheKey);
if (info != null) {
if (APIUtil.isAccessTokenExpired(info)) {
log.info("Invalid OAuth Token : Access Token " + GatewayUtils.getMaskedToken(apiKey) + " " + "expired.");
info.setAuthorized(false);
// in cache, if token is expired remove cache entry.
getGatewayKeyCache().remove(cacheKey);
// Remove from the first level token cache as well.
getGatewayTokenCache().remove(apiKey);
// Put into invalid token cache
getInvalidTokenCache().put(apiKey, cachedToken);
}
return info;
}
} else {
// Check token available in invalidToken Cache
String revokedCachedToken = (String) getInvalidTokenCache().get(apiKey);
if (revokedCachedToken != null) {
// Token is revoked/invalid or expired
APIKeyValidationInfoDTO apiKeyValidationInfoDTO = new APIKeyValidationInfoDTO();
apiKeyValidationInfoDTO.setAuthorized(false);
apiKeyValidationInfoDTO.setValidationStatus(APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS);
return apiKeyValidationInfoDTO;
}
}
}
String tenantDomain = getTenantDomain();
APIKeyValidationInfoDTO info = doGetKeyValidationInfo(context, prefixedVersion, apiKey, authenticationScheme, matchingResource, httpVerb, tenantDomain, keyManagers);
if (info != null) {
if (gatewayKeyCacheEnabled) {
if (info.getValidationStatus() == APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS) {
// if Token is not valid token (expired,invalid,revoked) put into invalid token cache
getInvalidTokenCache().put(apiKey, tenantDomain);
} else {
// Add into 1st level cache and Key cache
getGatewayTokenCache().put(apiKey, tenantDomain);
getGatewayKeyCache().put(cacheKey, info);
}
// If this is NOT a super-tenant API that is being invoked
if (!MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) {
// to remove the entry when the need occurs to clear this particular cache entry.
try {
startTenantFlow();
if (info.getValidationStatus() == APIConstants.KeyValidationStatus.API_AUTH_INVALID_CREDENTIALS) {
// if Token is not valid token (expired,invalid,revoked) put into invalid token cache in
// tenant cache
getInvalidTokenCache().put(apiKey, tenantDomain);
} else {
// add into to tenant token cache
getGatewayTokenCache().put(apiKey, tenantDomain);
}
} finally {
endTenantFlow();
}
}
}
return info;
} else {
String warnMsg = "API key validation service returns null object";
log.warn(warnMsg);
throw new APISecurityException(APISecurityConstants.API_AUTH_GENERAL_ERROR, warnMsg);
}
}
use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.
the class APIKeyValidator method getVerbInfoDTOFromAPIData.
/**
* @param messageContext The message context
* @param context API context of API
* @param apiVersion Version of API
* @param requestPath Incoming request path
* @param httpMethod http method of request
* @return verbInfoDTO which contains throttling tier for given resource and verb+resource key
*/
public VerbInfoDTO getVerbInfoDTOFromAPIData(MessageContext messageContext, String context, String apiVersion, String requestPath, String httpMethod) throws APISecurityException {
String cacheKey = context + ':' + apiVersion;
APIInfoDTO apiInfoDTO = null;
if (isGatewayAPIResourceValidationEnabled) {
apiInfoDTO = (APIInfoDTO) getResourceCache().get(cacheKey);
}
if (apiInfoDTO == null) {
apiInfoDTO = doGetAPIInfo(messageContext, context, apiVersion);
if (isGatewayAPIResourceValidationEnabled) {
getResourceCache().put(cacheKey, apiInfoDTO);
}
}
// Match the case where the direct api context is matched
if ("/".equals(requestPath)) {
String requestCacheKey = context + '/' + apiVersion + requestPath + ':' + httpMethod;
// Get decision from cache.
VerbInfoDTO matchingVerb = null;
if (isGatewayAPIResourceValidationEnabled) {
matchingVerb = (VerbInfoDTO) getResourceCache().get(requestCacheKey);
}
// On a cache hit
if (matchingVerb != null) {
matchingVerb.setRequestKey(requestCacheKey);
return matchingVerb;
} else {
if (apiInfoDTO.getResources() != null) {
for (ResourceInfoDTO resourceInfoDTO : apiInfoDTO.getResources()) {
String urlPattern = resourceInfoDTO.getUrlPattern();
// If the request patch is '/', it can only be matched with a resource whose url-context is '/*'
if ("/*".equals(urlPattern)) {
for (VerbInfoDTO verbDTO : resourceInfoDTO.getHttpVerbs()) {
if (verbDTO.getHttpVerb().equals(httpMethod)) {
// Store verb in cache
if (isGatewayAPIResourceValidationEnabled) {
getResourceCache().put(requestCacheKey, verbDTO);
}
verbDTO.setRequestKey(requestCacheKey);
return verbDTO;
}
}
}
}
}
}
}
// Remove the ending '/' from request
requestPath = RESTUtils.trimTrailingSlashes(requestPath);
while (requestPath.length() > 1) {
String requestCacheKey = context + '/' + apiVersion + requestPath + ':' + httpMethod;
// Get decision from cache.
VerbInfoDTO matchingVerb = null;
if (isGatewayAPIResourceValidationEnabled) {
matchingVerb = (VerbInfoDTO) getResourceCache().get(requestCacheKey);
}
// On a cache hit
if (matchingVerb != null) {
matchingVerb.setRequestKey(requestCacheKey);
return matchingVerb;
} else // On a cache miss
{
for (ResourceInfoDTO resourceInfoDTO : apiInfoDTO.getResources()) {
String urlPattern = resourceInfoDTO.getUrlPattern();
if (urlPattern.endsWith("/*")) {
// Remove the ending '/*'
urlPattern = urlPattern.substring(0, urlPattern.length() - 2);
}
// If the urlPattern ends with a '/', remove that as well.
urlPattern = RESTUtils.trimTrailingSlashes(urlPattern);
if (requestPath.endsWith(urlPattern)) {
for (VerbInfoDTO verbDTO : resourceInfoDTO.getHttpVerbs()) {
if (verbDTO.getHttpVerb().equals(httpMethod)) {
// Store verb in cache
if (isGatewayAPIResourceValidationEnabled) {
getResourceCache().put(requestCacheKey, verbDTO);
}
verbDTO.setRequestKey(requestCacheKey);
return verbDTO;
}
}
}
}
}
// Remove the section after the last occurrence of the '/' character
int index = requestPath.lastIndexOf('/');
requestPath = requestPath.substring(0, index <= 0 ? 0 : index);
}
// nothing found. return the highest level of security
return null;
}
use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.
the class BasicAuthCredentialValidator method validateScopes.
/**
* Validates the roles of the given user against the roles of the scopes of the API resource.
*
* @param username given username
* @param openAPI OpenAPI of the API
* @param synCtx The message to be authenticated
* @param userRoleList The list of roles of the user
* @return true if the validation passed
* @throws APISecurityException If an authentication failure or some other error occurs
*/
@MethodStats
public boolean validateScopes(String username, OpenAPI openAPI, MessageContext synCtx, BasicAuthValidationInfoDTO basicAuthValidationInfoDTO) throws APISecurityException {
String[] userRoleList = basicAuthValidationInfoDTO.getUserRoleList();
String apiContext = (String) synCtx.getProperty(RESTConstants.REST_API_CONTEXT);
String apiVersion = (String) synCtx.getProperty(RESTConstants.SYNAPSE_REST_API_VERSION);
String apiElectedResource = (String) synCtx.getProperty(APIConstants.API_ELECTED_RESOURCE);
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
org.apache.axis2.context.MessageContext axis2MessageContext = ((Axis2MessageContext) synCtx).getAxis2MessageContext();
String httpMethod = (String) axis2MessageContext.getProperty(APIConstants.DigestAuthConstants.HTTP_METHOD);
String resourceKey = apiContext + ":" + apiVersion + ":" + apiElectedResource + ":" + httpMethod;
Map<String, Scope> scopeMap = apiKeyValidator.retrieveScopes(tenantDomain);
String resourceCacheKey = resourceKey + ":" + username;
if (gatewayKeyCacheEnabled && getGatewayBasicAuthResourceCache().get(resourceCacheKey) != null && basicAuthValidationInfoDTO.isCached()) {
return true;
}
if (openAPI != null) {
// retrieve the user roles related to the scope of the API resource
List<String> resourceScopes = OpenAPIUtils.getScopesOfResource(openAPI, synCtx);
if (resourceScopes != null && resourceScopes.size() > 0) {
for (String resourceScope : resourceScopes) {
Scope scope = scopeMap.get(resourceScope);
if (scope != null) {
if (scope.getRoles().isEmpty()) {
log.debug("Scope " + resourceScope + " didn't have roles");
if (gatewayKeyCacheEnabled) {
getGatewayBasicAuthResourceCache().put(resourceCacheKey, resourceKey);
}
return true;
} else {
// any of the role of the user
if (validateInternalUserRoles(scope.getRoles(), userRoleList)) {
if (gatewayKeyCacheEnabled) {
getGatewayBasicAuthResourceCache().put(resourceCacheKey, resourceKey);
}
return true;
}
// check if the roles related to the API resource contains any of the role of the user
for (String role : userRoleList) {
if (scope.getRoles().contains(role)) {
if (gatewayKeyCacheEnabled) {
getGatewayBasicAuthResourceCache().put(resourceCacheKey, resourceKey);
}
return true;
}
}
}
}
}
} else {
if (log.isDebugEnabled()) {
log.debug("Basic Authentication: No scopes for the API resource: ".concat(resourceKey));
}
return true;
}
} else if (APIConstants.GRAPHQL_API.equals(synCtx.getProperty(APIConstants.API_TYPE))) {
HashMap<String, String> operationScopeMappingList = (HashMap<String, String>) synCtx.getProperty(APIConstants.SCOPE_OPERATION_MAPPING);
String[] operationList = ((String) synCtx.getProperty(APIConstants.API_ELECTED_RESOURCE)).split(",");
for (String operation : operationList) {
String operationScope = operationScopeMappingList.get(operation);
if (operationScope != null) {
if (scopeMap.containsKey(operationScope)) {
List<String> operationRoles = scopeMap.get(operationScope).getRoles();
boolean userHasOperationRole = false;
if (operationRoles.isEmpty()) {
userHasOperationRole = true;
} else {
for (String role : userRoleList) {
if (operationRoles.contains(role)) {
userHasOperationRole = true;
break;
}
}
}
if (!userHasOperationRole) {
throw new APISecurityException(APISecurityConstants.INVALID_SCOPE, "Scope validation failed");
}
} else {
throw new APISecurityException(APISecurityConstants.API_AUTH_GENERAL_ERROR, APISecurityConstants.API_AUTH_GENERAL_ERROR_MESSAGE);
}
}
}
if (gatewayKeyCacheEnabled) {
getGatewayBasicAuthResourceCache().put(resourceCacheKey, resourceKey);
}
return true;
} else {
if (log.isDebugEnabled()) {
log.debug("Basic Authentication: No OpenAPI found in the gateway for the API: ".concat(apiContext).concat(":").concat(apiVersion));
}
return true;
}
if (log.isDebugEnabled()) {
log.debug("Basic Authentication: Scope validation failed for the API resource: ".concat(apiElectedResource));
}
throw new APISecurityException(APISecurityConstants.INVALID_SCOPE, "Scope validation failed");
}
use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.
the class BasicAuthCredentialValidator method validate.
/**
* Validates the given username and password against the users in the user store.
*
* @param username given username
* @param password given password
* @return true if the validation passed
* @throws APISecurityException If an authentication failure or some other error occurs
*/
@MethodStats
public BasicAuthValidationInfoDTO validate(String username, String password) throws APISecurityException {
boolean isAuthenticated;
String cachedPasswordHash = null;
String providedPasswordHash = null;
String invalidCachedPasswordHash;
if (gatewayKeyCacheEnabled) {
providedPasswordHash = GatewayUtils.hashString(password.getBytes(StandardCharsets.UTF_8));
BasicAuthValidationInfoDTO cachedValidationInfoObj = (BasicAuthValidationInfoDTO) getGatewayUsernameCache().get(username);
if (cachedValidationInfoObj != null) {
cachedPasswordHash = cachedValidationInfoObj.getHashedPassword();
cachedValidationInfoObj.setCached(true);
}
if (cachedPasswordHash != null && cachedPasswordHash.equals(providedPasswordHash)) {
log.debug("Basic Authentication: <Valid Username Cache> Username & password authenticated");
return cachedValidationInfoObj;
} else {
BasicAuthValidationInfoDTO invalidCacheValidationInfoObj = (BasicAuthValidationInfoDTO) getInvalidUsernameCache().get(username);
if (invalidCacheValidationInfoObj != null) {
invalidCacheValidationInfoObj.setCached(true);
invalidCachedPasswordHash = invalidCacheValidationInfoObj.getHashedPassword();
if (invalidCachedPasswordHash != null && invalidCachedPasswordHash.equals(providedPasswordHash)) {
log.debug("Basic Authentication: <Invalid Username Cache> Username & password authentication failed");
invalidCacheValidationInfoObj.setAuthenticated(// If (username->password) is in the invalid cache
false);
return invalidCacheValidationInfoObj;
}
}
}
}
BasicAuthValidationInfoDTO basicAuthValidationInfoDTO;
try {
org.wso2.carbon.apimgt.impl.dto.xsd.BasicAuthValidationInfoDTO generatedInfoDTO = apiKeyMgtRemoteUserStoreMgtServiceStub.getUserAuthenticationInfo(username, password);
basicAuthValidationInfoDTO = convertToDTO(generatedInfoDTO);
isAuthenticated = basicAuthValidationInfoDTO.isAuthenticated();
} catch (APIKeyMgtRemoteUserStoreMgtServiceAPIManagementException | RemoteException e) {
log.error("Basic Authentication: Error while accessing backend services to validate user authentication for user : " + username);
throw new APISecurityException(APISecurityConstants.API_AUTH_GENERAL_ERROR, e.getMessage(), e);
}
if (gatewayKeyCacheEnabled) {
basicAuthValidationInfoDTO.setHashedPassword(providedPasswordHash);
if (isAuthenticated) {
// put (username->password) into the valid cache
getGatewayUsernameCache().put(username, basicAuthValidationInfoDTO);
} else {
// put (username->password) into the invalid cache
getInvalidUsernameCache().put(username, basicAuthValidationInfoDTO);
}
}
return basicAuthValidationInfoDTO;
}
use of org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException in project carbon-apimgt by wso2.
the class JWTValidator method authenticateForGraphQLSubscription.
/**
* Authenticate for GraphQL subscriptions API requests. This method validates the token signature, expire time and
* subscription. The token request scopes are added to the AuthenticationContxt to validate later.
*
* @param signedJWTInfo SignedJWTInfo
* @param apiContext API context
* @param apiVersion API version
* @return AuthenticationContext
* @throws APISecurityException if an error occurs
*/
public AuthenticationContext authenticateForGraphQLSubscription(SignedJWTInfo signedJWTInfo, String apiContext, String apiVersion) throws APISecurityException {
String tokenSignature = signedJWTInfo.getSignedJWT().getSignature().toString();
JWTClaimsSet jwtClaimsSet = signedJWTInfo.getJwtClaimsSet();
String jti = jwtClaimsSet.getJWTID();
JWTValidationInfo jwtValidationInfo = validateTokenForWS(signedJWTInfo, tokenSignature, jti);
if (jwtValidationInfo != null && jwtValidationInfo.isValid()) {
APIKeyValidationInfoDTO apiKeyValidationInfoDTO = validateSubscriptionsForWS(jwtValidationInfo, apiContext, apiVersion);
if (apiKeyValidationInfoDTO.isAuthorized()) {
if (log.isDebugEnabled()) {
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.error(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