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;
}
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;
}
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;
}
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;
}
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;
}
Aggregations