Search in sources :

Example 36 with InboundMessageContext

use of org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext in project carbon-apimgt by wso2.

the class GraphQLResponseProcessor method handleResponse.

/**
 * Handle inbound websocket responses of GraphQL subscriptions and perform authentication, authorization
 * and throttling. This identifies operation from the subscription responses using the unique message id parameter.
 *
 * @param msgSize               Message size of graphQL subscription response payload
 * @param msgText               The GraphQL subscription response payload text
 * @param inboundMessageContext InboundMessageContext
 * @return InboundProcessorResponseDTO
 */
@Override
public InboundProcessorResponseDTO handleResponse(int msgSize, String msgText, InboundMessageContext inboundMessageContext) {
    InboundProcessorResponseDTO responseDTO = InboundWebsocketProcessorUtil.authenticateToken(inboundMessageContext);
    JSONObject graphQLMsg = new JSONObject(msgText);
    if (!responseDTO.isError() && checkIfSubscribeMessageResponse(graphQLMsg)) {
        if (graphQLMsg.has(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID) && graphQLMsg.getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID) != null) {
            String operationId = graphQLMsg.getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID);
            GraphQLOperationDTO graphQLOperationDTO = inboundMessageContext.getVerbInfoForGraphQLMsgId(graphQLMsg.getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID));
            // validate scopes based on subscription payload when security is enabled
            String authType = graphQLOperationDTO.getVerbInfoDTO().getAuthType();
            if (!StringUtils.capitalize(APIConstants.AUTH_TYPE_NONE.toLowerCase()).equals(authType)) {
                responseDTO = InboundWebsocketProcessorUtil.validateScopes(inboundMessageContext, graphQLOperationDTO.getOperation(), operationId);
            }
            if (!responseDTO.isError()) {
                // throttle for matching resource
                return InboundWebsocketProcessorUtil.doThrottleForGraphQL(msgSize, graphQLOperationDTO.getVerbInfoDTO(), inboundMessageContext, operationId);
            }
        } else {
            responseDTO = InboundWebsocketProcessorUtil.getBadRequestFrameErrorDTO("Missing mandatory id field in the message");
        }
    }
    return responseDTO;
}
Also used : GraphQLOperationDTO(org.wso2.carbon.apimgt.gateway.dto.GraphQLOperationDTO) JSONObject(org.json.JSONObject) InboundProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO)

Example 37 with InboundMessageContext

use of org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext 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 38 with InboundMessageContext

use of org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext in project carbon-apimgt by wso2.

the class InboundWebsocketProcessorUtil method publishGoogleAnalyticsData.

/**
 * Publish Google Analytics data.
 *
 * @param inboundMessageContext InboundMessageContext
 * @param remoteAddress         Remote IP address
 * @throws WebSocketApiException if an  error occurs
 */
public static void publishGoogleAnalyticsData(InboundMessageContext inboundMessageContext, String remoteAddress) throws WebSocketApiException {
    // publish Google Analytics data
    GoogleAnalyticsData.DataBuilder gaData;
    try {
        gaData = new GoogleAnalyticsData.DataBuilder(null, null, null, null).setDocumentPath(inboundMessageContext.getFullRequestPath()).setDocumentHostName(DataPublisherUtil.getHostAddress()).setSessionControl("end").setCacheBuster(APIMgtGoogleAnalyticsUtils.getCacheBusterId()).setIPOverride(remoteAddress);
        APIMgtGoogleAnalyticsUtils gaUtils = new APIMgtGoogleAnalyticsUtils();
        gaUtils.init(inboundMessageContext.getTenantDomain());
        gaUtils.publishGATrackingData(gaData, inboundMessageContext.getRequestHeaders().get(HttpHeaders.USER_AGENT), inboundMessageContext.getRequestHeaders().get(HttpHeaders.AUTHORIZATION));
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
        throw new WebSocketApiException("Error while publishing google analytics data for API " + inboundMessageContext.getApiContext());
    }
}
Also used : WebSocketApiException(org.wso2.carbon.apimgt.gateway.handlers.streaming.websocket.WebSocketApiException) APIMgtGoogleAnalyticsUtils(org.wso2.carbon.apimgt.gateway.utils.APIMgtGoogleAnalyticsUtils) UnsupportedEncodingException(java.io.UnsupportedEncodingException) GoogleAnalyticsData(org.wso2.carbon.ganalytics.publisher.GoogleAnalyticsData) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException)

Example 39 with InboundMessageContext

use of org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext in project carbon-apimgt by wso2.

the class InboundWebsocketProcessorUtil method validateAuthenticationContext.

/**
 * Validates AuthenticationContext and set APIKeyValidationInfoDTO to InboundMessageContext.
 *
 * @param authenticationContext Validated AuthenticationContext
 * @param inboundMessageContext InboundMessageContext
 * @return true if authenticated
 */
public static boolean validateAuthenticationContext(AuthenticationContext authenticationContext, InboundMessageContext inboundMessageContext) {
    if (authenticationContext == null || !authenticationContext.isAuthenticated()) {
        return false;
    }
    // The information given by the AuthenticationContext is set to an APIKeyValidationInfoDTO object
    // so to feed information analytics and throttle data publishing
    APIKeyValidationInfoDTO info = new APIKeyValidationInfoDTO();
    info.setAuthorized(authenticationContext.isAuthenticated());
    info.setApplicationTier(authenticationContext.getApplicationTier());
    info.setTier(authenticationContext.getTier());
    info.setSubscriberTenantDomain(authenticationContext.getSubscriberTenantDomain());
    info.setSubscriber(authenticationContext.getSubscriber());
    info.setStopOnQuotaReach(authenticationContext.isStopOnQuotaReach());
    info.setApiName(authenticationContext.getApiName());
    info.setApplicationId(authenticationContext.getApplicationId());
    info.setType(authenticationContext.getKeyType());
    info.setApiPublisher(authenticationContext.getApiPublisher());
    info.setApplicationName(authenticationContext.getApplicationName());
    info.setConsumerKey(authenticationContext.getConsumerKey());
    info.setEndUserName(authenticationContext.getUsername());
    info.setApiTier(authenticationContext.getApiTier());
    info.setGraphQLMaxDepth(authenticationContext.getGraphQLMaxDepth());
    info.setGraphQLMaxComplexity(authenticationContext.getGraphQLMaxComplexity());
    inboundMessageContext.setKeyType(info.getType());
    inboundMessageContext.setInfoDTO(info);
    inboundMessageContext.setAuthContext(authenticationContext);
    inboundMessageContext.setInfoDTO(info);
    return authenticationContext.isAuthenticated();
}
Also used : APIKeyValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)

Example 40 with InboundMessageContext

use of org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext in project carbon-apimgt by wso2.

the class InboundWebsocketProcessorUtil method doThrottleForGraphQL.

/**
 * Checks if the request is throttled for GraphQL subscriptions.
 *
 * @param msgSize               Websocket msg size
 * @param verbInfoDTO           VerbInfoDTO for invoking operation.
 * @param inboundMessageContext InboundMessageContext
 * @param operationId           Operation ID
 * @return InboundProcessorResponseDTO
 */
public static InboundProcessorResponseDTO doThrottleForGraphQL(int msgSize, VerbInfoDTO verbInfoDTO, InboundMessageContext inboundMessageContext, String operationId) {
    GraphQLProcessorResponseDTO responseDTO = new GraphQLProcessorResponseDTO();
    responseDTO.setId(operationId);
    return InboundWebsocketProcessorUtil.doThrottle(msgSize, verbInfoDTO, inboundMessageContext, responseDTO);
}
Also used : GraphQLProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO)

Aggregations

InboundProcessorResponseDTO (org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO)32 InboundMessageContext (org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext)31 Test (org.junit.Test)26 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)26 VerbInfoDTO (org.wso2.carbon.apimgt.impl.dto.VerbInfoDTO)15 GraphQLProcessorResponseDTO (org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO)12 APIKeyValidationInfoDTO (org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)8 GraphQLOperationDTO (org.wso2.carbon.apimgt.gateway.dto.GraphQLOperationDTO)7 GraphQLSchema (graphql.schema.GraphQLSchema)6 SchemaParser (graphql.schema.idl.SchemaParser)6 TypeDefinitionRegistry (graphql.schema.idl.TypeDefinitionRegistry)6 JSONObject (org.json.simple.JSONObject)6 JSONParser (org.json.simple.parser.JSONParser)6 GraphQLSchemaDTO (org.wso2.carbon.apimgt.api.gateway.GraphQLSchemaDTO)6 TextWebSocketFrame (io.netty.handler.codec.http.websocketx.TextWebSocketFrame)5 APIManagementException (org.wso2.carbon.apimgt.api.APIManagementException)5 APISecurityException (org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException)5 WebSocketApiException (org.wso2.carbon.apimgt.gateway.handlers.streaming.websocket.WebSocketApiException)4 CloseWebSocketFrame (io.netty.handler.codec.http.websocketx.CloseWebSocketFrame)3 WebSocketFrame (io.netty.handler.codec.http.websocketx.WebSocketFrame)3