Search in sources :

Example 16 with InboundProcessorResponseDTO

use of org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO 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 InboundProcessorResponseDTO

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

the class InboundWebsocketProcessorUtil method getGraphQLFrameErrorDTO.

/**
 * Get GraphQL subscription error frame DTO for error code and message closeConnection parameters.
 *
 * @param errorCode       Error code
 * @param errorMessage    Error message
 * @param closeConnection Whether to close connection after throwing the error frame
 * @param operationId     Operation ID
 * @return InboundProcessorResponseDTO
 */
public static GraphQLProcessorResponseDTO getGraphQLFrameErrorDTO(int errorCode, String errorMessage, boolean closeConnection, String operationId) {
    GraphQLProcessorResponseDTO graphQLProcessorResponseDTO = new GraphQLProcessorResponseDTO();
    graphQLProcessorResponseDTO.setError(true);
    graphQLProcessorResponseDTO.setErrorCode(errorCode);
    graphQLProcessorResponseDTO.setErrorMessage(errorMessage);
    graphQLProcessorResponseDTO.setCloseConnection(closeConnection);
    graphQLProcessorResponseDTO.setId(operationId);
    return graphQLProcessorResponseDTO;
}
Also used : GraphQLProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO)

Example 18 with InboundProcessorResponseDTO

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

the class InboundWebsocketProcessorUtil method getBadRequestGraphQLFrameErrorDTO.

/**
 * Get bad request (error code 4010) error frame DTO for GraphQL subscriptions. The closeConnection parameter is
 * false.
 *
 * @param errorMessage Error message
 * @param operationId  Operation ID
 * @return InboundProcessorResponseDTO
 */
public static InboundProcessorResponseDTO getBadRequestGraphQLFrameErrorDTO(String errorMessage, String operationId) {
    GraphQLProcessorResponseDTO inboundProcessorResponseDTO = new GraphQLProcessorResponseDTO();
    inboundProcessorResponseDTO.setError(true);
    inboundProcessorResponseDTO.setErrorCode(WebSocketApiConstants.FrameErrorConstants.BAD_REQUEST);
    inboundProcessorResponseDTO.setErrorMessage(errorMessage);
    inboundProcessorResponseDTO.setId(operationId);
    return inboundProcessorResponseDTO;
}
Also used : GraphQLProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO)

Example 19 with InboundProcessorResponseDTO

use of org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO 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)

Example 20 with InboundProcessorResponseDTO

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

the class InboundWebsocketProcessorUtil method validateScopes.

/**
 * Validates scopes for subscription operations.
 *
 * @param inboundMessageContext InboundMessageContext
 * @param subscriptionOperation Subscription operation
 * @param operationId           GraphQL message Id
 * @return InboundProcessorResponseDTO
 */
public static InboundProcessorResponseDTO validateScopes(InboundMessageContext inboundMessageContext, String subscriptionOperation, String operationId) {
    InboundProcessorResponseDTO responseDTO = new GraphQLProcessorResponseDTO();
    // validate scopes based on subscription payload
    try {
        if (!InboundWebsocketProcessorUtil.authorizeGraphQLSubscriptionEvents(subscriptionOperation, inboundMessageContext)) {
            String errorMessage = WebSocketApiConstants.FrameErrorConstants.RESOURCE_FORBIDDEN_ERROR_MESSAGE + StringUtils.SPACE + subscriptionOperation;
            log.error(errorMessage);
            responseDTO = InboundWebsocketProcessorUtil.getGraphQLFrameErrorDTO(WebSocketApiConstants.FrameErrorConstants.RESOURCE_FORBIDDEN_ERROR, errorMessage, false, operationId);
        }
    } catch (APIManagementException e) {
        log.error(WebSocketApiConstants.FrameErrorConstants.API_AUTH_GENERAL_MESSAGE, e);
        responseDTO = InboundWebsocketProcessorUtil.getFrameErrorDTO(WebSocketApiConstants.FrameErrorConstants.API_AUTH_GENERAL_ERROR, WebSocketApiConstants.FrameErrorConstants.API_AUTH_GENERAL_MESSAGE, true);
    } catch (APISecurityException e) {
        log.error(WebSocketApiConstants.FrameErrorConstants.RESOURCE_FORBIDDEN_ERROR_MESSAGE, e);
        responseDTO = InboundWebsocketProcessorUtil.getGraphQLFrameErrorDTO(WebSocketApiConstants.FrameErrorConstants.RESOURCE_FORBIDDEN_ERROR, e.getMessage(), false, operationId);
    }
    return responseDTO;
}
Also used : APISecurityException(org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException) GraphQLProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO) APIManagementException(org.wso2.carbon.apimgt.api.APIManagementException) InboundProcessorResponseDTO(org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO)

Aggregations

InboundProcessorResponseDTO (org.wso2.carbon.apimgt.gateway.inbound.websocket.InboundProcessorResponseDTO)35 InboundMessageContext (org.wso2.carbon.apimgt.gateway.inbound.InboundMessageContext)28 Test (org.junit.Test)26 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)26 VerbInfoDTO (org.wso2.carbon.apimgt.impl.dto.VerbInfoDTO)13 GraphQLProcessorResponseDTO (org.wso2.carbon.apimgt.gateway.inbound.websocket.GraphQLProcessorResponseDTO)12 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 APIKeyValidationInfoDTO (org.wso2.carbon.apimgt.impl.dto.APIKeyValidationInfoDTO)6 TextWebSocketFrame (io.netty.handler.codec.http.websocketx.TextWebSocketFrame)5 APISecurityException (org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException)4 CloseWebSocketFrame (io.netty.handler.codec.http.websocketx.CloseWebSocketFrame)3 WebSocketFrame (io.netty.handler.codec.http.websocketx.WebSocketFrame)3 JSONObject (org.json.JSONObject)3 APIManagementException (org.wso2.carbon.apimgt.api.APIManagementException)3