Search in sources :

Example 16 with InboundMessageContext

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

the class GraphQLRequestProcessor method handleRequest.

/**
 * Handle inbound websocket requests for GraphQL subscriptions and perform authentication, authorization,
 * payload validation, query depth and complexity analysis and throttling.
 *
 * @param msgSize               Message size of graphQL subscription payload
 * @param msgText               The GraphQL subscription payload text
 * @param inboundMessageContext InboundMessageContext
 * @return InboundProcessorResponseDTO
 */
@Override
public InboundProcessorResponseDTO handleRequest(int msgSize, String msgText, InboundMessageContext inboundMessageContext) {
    InboundProcessorResponseDTO responseDTO;
    JSONObject graphQLMsg = new JSONObject(msgText);
    responseDTO = InboundWebsocketProcessorUtil.authenticateToken(inboundMessageContext);
    Parser parser = new Parser();
    // for gql subscription operation payloads
    if (!responseDTO.isError() && checkIfSubscribeMessage(graphQLMsg)) {
        String operationId = graphQLMsg.getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID);
        if (validatePayloadFields(graphQLMsg)) {
            String graphQLSubscriptionPayload = ((JSONObject) graphQLMsg.get(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_PAYLOAD)).getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_QUERY);
            Document document = parser.parseDocument(graphQLSubscriptionPayload);
            // Extract the operation type and operations from the payload
            OperationDefinition operation = getOperationFromPayload(document);
            if (operation != null) {
                if (checkIfValidSubscribeOperation(operation)) {
                    responseDTO = validateQueryPayload(inboundMessageContext, document, operationId);
                    if (!responseDTO.isError()) {
                        // subscription operation name
                        String subscriptionOperation = GraphQLProcessorUtil.getOperationList(operation, inboundMessageContext.getGraphQLSchemaDTO().getTypeDefinitionRegistry());
                        // extract verb info dto with throttle policy for matching verb
                        VerbInfoDTO verbInfoDTO = InboundWebsocketProcessorUtil.findMatchingVerb(subscriptionOperation, inboundMessageContext);
                        String authType = verbInfoDTO.getAuthType();
                        // validate scopes based on subscription payload when security is enabled
                        if (!StringUtils.capitalize(APIConstants.AUTH_TYPE_NONE.toLowerCase()).equals(authType)) {
                            responseDTO = InboundWebsocketProcessorUtil.validateScopes(inboundMessageContext, subscriptionOperation, operationId);
                        }
                        if (!responseDTO.isError()) {
                            SubscriptionAnalyzer subscriptionAnalyzer = new SubscriptionAnalyzer(inboundMessageContext.getGraphQLSchemaDTO().getGraphQLSchema());
                            // analyze query depth and complexity
                            responseDTO = validateQueryDepthAndComplexity(subscriptionAnalyzer, inboundMessageContext, graphQLSubscriptionPayload, operationId);
                            if (!responseDTO.isError()) {
                                // throttle for matching resource
                                responseDTO = InboundWebsocketProcessorUtil.doThrottleForGraphQL(msgSize, verbInfoDTO, inboundMessageContext, operationId);
                                // add verb info dto for the successful invoking subscription operation request
                                inboundMessageContext.addVerbInfoForGraphQLMsgId(graphQLMsg.getString(GraphQLConstants.SubscriptionConstants.PAYLOAD_FIELD_NAME_ID), new GraphQLOperationDTO(verbInfoDTO, subscriptionOperation));
                            }
                        }
                    }
                } else {
                    responseDTO = InboundWebsocketProcessorUtil.getBadRequestGraphQLFrameErrorDTO("Invalid operation. Only allowed Subscription type operations", operationId);
                }
            } else {
                responseDTO = InboundWebsocketProcessorUtil.getBadRequestGraphQLFrameErrorDTO("Operation definition cannot be empty", operationId);
            }
        } else {
            responseDTO = InboundWebsocketProcessorUtil.getBadRequestGraphQLFrameErrorDTO("Invalid operation payload", operationId);
        }
    }
    return responseDTO;
}
Also used : GraphQLOperationDTO(org.wso2.carbon.apimgt.gateway.dto.GraphQLOperationDTO) JSONObject(org.json.JSONObject) VerbInfoDTO(org.wso2.carbon.apimgt.impl.dto.VerbInfoDTO) InboundProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO) Document(graphql.language.Document) OperationDefinition(graphql.language.OperationDefinition) SubscriptionAnalyzer(org.wso2.carbon.apimgt.gateway.handlers.graphQL.analyzer.SubscriptionAnalyzer) Parser(graphql.parser.Parser)

Example 17 with InboundMessageContext

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

the class InboundWebsocketProcessorUtil method authenticateWSJWTToken.

/**
 * Authenticates JWT token in incoming Websocket handshake requests.
 *
 * @param inboundMessageContext InboundMessageContext
 * @return true if authenticated
 * @throws APIManagementException if an internal error occurs
 * @throws APISecurityException   if authentication fails
 */
public static boolean authenticateWSJWTToken(InboundMessageContext inboundMessageContext) throws APIManagementException, APISecurityException {
    AuthenticationContext authenticationContext;
    JWTValidator jwtValidator = new JWTValidator(new APIKeyValidator(), inboundMessageContext.getTenantDomain());
    authenticationContext = jwtValidator.authenticateForWebSocket(inboundMessageContext.getSignedJWTInfo(), inboundMessageContext.getApiContext(), inboundMessageContext.getVersion(), inboundMessageContext.getMatchingResource());
    return validateAuthenticationContext(authenticationContext, inboundMessageContext);
}
Also used : AuthenticationContext(org.wso2.carbon.apimgt.gateway.handlers.security.AuthenticationContext) APIKeyValidator(org.wso2.carbon.apimgt.gateway.handlers.security.APIKeyValidator) JWTValidator(org.wso2.carbon.apimgt.gateway.handlers.security.jwt.JWTValidator)

Example 18 with InboundMessageContext

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

the class InboundWebsocketProcessorUtil method authenticateGraphQLJWTToken.

/**
 * Authenticates JWT token in incoming GraphQL subscription requests.
 *
 * @param inboundMessageContext InboundMessageContext
 * @return true if authenticated
 * @throws APIManagementException if an internal error occurs
 * @throws APISecurityException   if authentication fails
 */
public static boolean authenticateGraphQLJWTToken(InboundMessageContext inboundMessageContext) throws APIManagementException, APISecurityException {
    AuthenticationContext authenticationContext;
    PrivilegedCarbonContext.startTenantFlow();
    PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(inboundMessageContext.getTenantDomain(), true);
    JWTValidator jwtValidator = new JWTValidator(new APIKeyValidator(), inboundMessageContext.getTenantDomain());
    authenticationContext = jwtValidator.authenticateForGraphQLSubscription(inboundMessageContext.getSignedJWTInfo(), inboundMessageContext.getApiContext(), inboundMessageContext.getVersion());
    return validateAuthenticationContext(authenticationContext, inboundMessageContext);
}
Also used : AuthenticationContext(org.wso2.carbon.apimgt.gateway.handlers.security.AuthenticationContext) APIKeyValidator(org.wso2.carbon.apimgt.gateway.handlers.security.APIKeyValidator) JWTValidator(org.wso2.carbon.apimgt.gateway.handlers.security.jwt.JWTValidator)

Example 19 with InboundMessageContext

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

the class InboundWebsocketProcessorUtil method authorizeGraphQLSubscriptionEvents.

/**
 * Validate scopes of JWT token for incoming GraphQL subscription messages.
 *
 * @param matchingResource      Invoking GraphQL subscription operation
 * @param inboundMessageContext InboundMessageContext
 * @return true if authorized
 * @throws APIManagementException if an internal error occurs
 * @throws APISecurityException   if authorization fails
 */
public static boolean authorizeGraphQLSubscriptionEvents(String matchingResource, InboundMessageContext inboundMessageContext) throws APIManagementException, APISecurityException {
    JWTValidator jwtValidator = new JWTValidator(new APIKeyValidator(), inboundMessageContext.getTenantDomain());
    jwtValidator.validateScopesForGraphQLSubscriptions(inboundMessageContext.getApiContext(), inboundMessageContext.getVersion(), matchingResource, inboundMessageContext.getSignedJWTInfo(), inboundMessageContext.getAuthContext());
    return true;
}
Also used : APIKeyValidator(org.wso2.carbon.apimgt.gateway.handlers.security.APIKeyValidator) JWTValidator(org.wso2.carbon.apimgt.gateway.handlers.security.jwt.JWTValidator)

Example 20 with InboundMessageContext

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

the class InboundWebsocketProcessorUtil method doThrottle.

/**
 * Checks if the request is throttled.
 *
 * @param msgSize               Websocket msg size
 * @param verbInfoDTO           VerbInfoDTO for invoking operation. Pass null for websocket API throttling.
 * @param inboundMessageContext InboundMessageContext
 * @return false if throttled
 */
public static InboundProcessorResponseDTO doThrottle(int msgSize, VerbInfoDTO verbInfoDTO, InboundMessageContext inboundMessageContext, InboundProcessorResponseDTO responseDTO) {
    APIKeyValidationInfoDTO infoDTO = inboundMessageContext.getInfoDTO();
    String applicationLevelTier = infoDTO.getApplicationTier();
    String apiLevelTier = infoDTO.getApiTier() == null && verbInfoDTO == null ? APIConstants.UNLIMITED_TIER : infoDTO.getApiTier();
    String subscriptionLevelTier = infoDTO.getTier();
    String resourceLevelTier;
    String authorizedUser;
    if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equalsIgnoreCase(infoDTO.getSubscriberTenantDomain())) {
        authorizedUser = infoDTO.getSubscriber() + "@" + infoDTO.getSubscriberTenantDomain();
    } else {
        authorizedUser = infoDTO.getSubscriber();
    }
    String apiName = infoDTO.getApiName();
    String apiVersion = inboundMessageContext.getVersion();
    String appTenant = infoDTO.getSubscriberTenantDomain();
    String apiTenant = inboundMessageContext.getTenantDomain();
    String appId = infoDTO.getApplicationId();
    String applicationLevelThrottleKey = appId + ":" + authorizedUser;
    String apiLevelThrottleKey = inboundMessageContext.getApiContext() + ":" + apiVersion;
    String resourceLevelThrottleKey;
    // If API level throttle policy is present then it will apply and no resource level policy will apply for it
    if (StringUtils.isNotEmpty(apiLevelTier) && verbInfoDTO == null) {
        resourceLevelThrottleKey = apiLevelThrottleKey;
        resourceLevelTier = apiLevelTier;
    } else {
        resourceLevelThrottleKey = verbInfoDTO.getRequestKey();
        resourceLevelTier = verbInfoDTO.getThrottling();
    }
    String subscriptionLevelThrottleKey = appId + ":" + inboundMessageContext.getApiContext() + ":" + apiVersion;
    String messageId = UIDGenerator.generateURNString();
    String remoteIP = inboundMessageContext.getUserIP();
    if (log.isDebugEnabled()) {
        log.debug("Remote IP address : " + remoteIP);
    }
    if (remoteIP.indexOf(":") > 0) {
        remoteIP = remoteIP.substring(1, remoteIP.indexOf(":"));
    }
    JSONObject jsonObMap = new JSONObject();
    Utils.setRemoteIp(jsonObMap, remoteIP);
    jsonObMap.put(APIThrottleConstants.MESSAGE_SIZE, msgSize);
    try {
        PrivilegedCarbonContext.startTenantFlow();
        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(inboundMessageContext.getTenantDomain(), true);
        boolean isThrottled = WebsocketUtil.isThrottled(resourceLevelThrottleKey, subscriptionLevelThrottleKey, applicationLevelThrottleKey);
        if (isThrottled) {
            responseDTO.setError(true);
            responseDTO.setErrorCode(WebSocketApiConstants.FrameErrorConstants.THROTTLED_OUT_ERROR);
            responseDTO.setErrorMessage(WebSocketApiConstants.FrameErrorConstants.THROTTLED_OUT_ERROR_MESSAGE);
        }
    } finally {
        PrivilegedCarbonContext.endTenantFlow();
    }
    Object[] objects = new Object[] { messageId, applicationLevelThrottleKey, applicationLevelTier, apiLevelThrottleKey, apiLevelTier, subscriptionLevelThrottleKey, subscriptionLevelTier, resourceLevelThrottleKey, resourceLevelTier, authorizedUser, inboundMessageContext.getApiContext(), apiVersion, appTenant, apiTenant, appId, apiName, jsonObMap.toString() };
    org.wso2.carbon.databridge.commons.Event event = new org.wso2.carbon.databridge.commons.Event("org.wso2.throttle.request.stream:1.0.0", System.currentTimeMillis(), null, null, objects);
    if (ServiceReferenceHolder.getInstance().getThrottleDataPublisher() == null) {
        log.error("Cannot publish events to traffic manager because ThrottleDataPublisher " + "has not been initialised");
    }
    ServiceReferenceHolder.getInstance().getThrottleDataPublisher().getDataPublisher().tryPublish(event);
    return responseDTO;
}
Also used : JSONObject(org.json.JSONObject) JSONObject(org.json.JSONObject) APIKeyValidationInfoDTO(org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)

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