diff --git a/financial-services-accelerator/accelerators/fs-is/repository/resources/apis/accelerator-extensions-v1.0.4.yaml b/financial-services-accelerator/accelerators/fs-is/repository/resources/apis/accelerator-extensions-v1.0.4.yaml new file mode 100644 index 000000000..984676145 --- /dev/null +++ b/financial-services-accelerator/accelerators/fs-is/repository/resources/apis/accelerator-extensions-v1.0.4.yaml @@ -0,0 +1,2703 @@ +openapi: 3.0.1 +info: + title: API contract for financial accelerator extension points in WSO2 IS and APIM + description: This API defines the REST API contract for services that implements logic to extend the Open Data accelerator flow. + contact: + name: WSO2 + url: https://wso2.com/solutions/financial-services/open-banking/ + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: v1.0.3 +servers: + - url: https://virtserver.swaggerhub.com/wso2-f5b/OB4/1.0.0 + description: SwaggerHub API Auto Mocking +security: + - BasicAuth: [] + - OAuth2: [] +tags: + - name: Client + description: APIs for dynamically registering/updating client related extensions + - name: Application + description: APIs for registering/updating applications through devportal related extensions + - name: Consent + description: APIs for consent flow extensions + - name: Token + description: APIs for token flow extensions + - name: Authorize + description: APIs for authorize flow extensions + - name: Error Handling + description: APIs for handling accelerator errors +paths: + /map-accelerator-error-response: + post: + tags: + - Error Handling + summary: map accelerator level error formats to custom error formats + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMapperRequestBody' + example: + requestId: Ec1wMjmiG8 + data: + error: + code: "401" + description: Invalid client ID provided. + operation: consent_create + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForErrorMapper' + example: + responseId: Ec1wMjmiG8 + errorCode: 400 + data: + customErrorCode: invalid + customErrorDescription: Invalid client ID provided. + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + errorMessage: server_error + errorDescription: Failed to process the response + /pre-process-client-creation: + post: + tags: + - Client + summary: handle pre validations & obtain custom data to store in dynamic client registration step + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ClientProcessRequestBody' + examples: + ProcessClientCreation: + summary: ProcessClientCreation + value: + requestId: Ec1wMjmiG8 + data: + clientData: + key: value + softwareStatement: + key: value + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForClientProcess' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + clientData: + key: value + failedExample: + summary: Failed response + value: + responseId: Ec1wMjmiG8 + status: ERROR + errorCode: 401 + data: + error: invalid_client_metadata + errorDescription: Invalid scope values found + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /pre-process-client-update: + post: + tags: + - Client + summary: handle pre validations & obtain custom data to store in dynamic client update step + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ClientProcessRequestBody' + examples: + ProcessClientUpdate: + summary: ProcessClientUpdate + value: + requestId: Ec1wMjmiG8 + data: + clientData: + key: value + softwareStatement: + key: value + existingClientData: + key: value + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForClientProcess' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + clientData: + key: value + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: invalid_client_metadata + errorDescription: Invalid scope values found + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /pre-process-client-retrieval: + post: + tags: + - Client + summary: handle post client retrieval response generation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ClientProcessRequestBody' + examples: + ProcessClientRetrieval: + summary: ProcessClientRetrieval + value: + requestId: Ec1wMjmiG8 + data: + existingClientData: + key: value + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForClientProcess' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + clientData: + key: value + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: invalid_client_metadata + errorDescription: Invalid scope values found + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /pre-process-application-creation: + post: + tags: + - Application + summary: handle pre validations & changes to the consumer application creation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AppCreateProcessRequestBody' + examples: + ProcessApplicationCreation: + summary: ProcessApplicationCreation + value: + requestId: Ec1wMjmiG8 + data: + appData: + key: value + additionalProperties: + key: value + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForApplicationCreation' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + clientId: value + additionalAppData: + key: value + failedExample: + summary: Failed response + value: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: Invalid certificate found + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /pre-process-application-update: + post: + tags: + - Application + summary: handle pre validations & changes to the consumer application update + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AppUpdateProcessRequestBody' + examples: + ProcessApplicationUpdate: + summary: ProcessApplicationUpdate + value: + requestId: Ec1wMjmiG8 + data: + appData: + key: value + additionalProperties: + key: value + existingAppData: + key: value + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForApplicationUpdate' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + additionalAppData: + key: value + failedExample: + summary: Failed response + value: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: Invalid certificate found + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /pre-process-consent-creation: + post: + tags: + - Consent + summary: handle pre validations & obtain custom consent data to be stored + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessConsentCreationRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForPreProcessConsentCreation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-consent-creation-response: + post: + tags: + - Consent + summary: "handle post-consent generation -response generation,validations" + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EnrichConsentCreationRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForResponseAlternation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /pre-process-consent-update: + post: + tags: + - Consent + summary: handle pre validations & obtain custom consent data to be updated + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessConsentUpdateRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForPreProcessConsentUpdate' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-consent-update-response: + post: + tags: + - Consent + summary: "handle post-consent update - response generation,validations" + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EnrichConsentUpdateRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForResponseAlternation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /pre-process-consent-file-upload: + post: + tags: + - Consent + summary: Handle pre validations related to file upload requests. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessFileUploadRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForPreProcessFileUpload' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-consent-file-response: + post: + tags: + - Consent + summary: Modify the response sent in the file upload request after successfully storing the file. + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EnrichFileUploadResponseRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForResponseAlternation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /pre-process-consent-retrieval: + post: + tags: + - Consent + summary: handle pre-consent retrieval validations + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessConsentRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForResponseAlternation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /validate-consent-file-retrieval: + post: + tags: + - Consent + summary: Handle validations related to file retrieval and return a response + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessConsentRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /pre-process-consent-revoke: + post: + tags: + - Consent + summary: handle pre-consent revocation validations when a TPP calls consent /DELETE + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PreProcessConsentRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForConsentRevocation' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-consent-search-response: + post: + tags: + - Consent + summary: handle consent search required extension to fetch additional data + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EnrichConsentSearchRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForConsentSearch' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /populate-consent-authorize-screen: + post: + tags: + - Consent + summary: handle validations before consent authorization and consent data to load in consent authorization UI + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PopulateConsentAuthorizeScreenRequestBody' + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForPopulateConsentAuthorizeScreen' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Permissions are missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /persist-authorized-consent: + post: + tags: + - Consent + summary: handle consent persistence logic and enrich response with user authorization and account mapping data + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PersistAuthorizedConsentRequestBody' + required: true + responses: + "200": + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForPersistAuthorizedConsent' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Bad Request + errorDescription: Request does not comply with the schema + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: Internal server error + errorDescription: Error occurred while handling the request + /validate-consent-access: + post: + tags: + - Consent + summary: handle custom consent data validations before data access + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ValidateConsentAccessRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 401 + data: + errorMessage: invalid_permissions + errorDescription: "The requested permissions are invalid, unknown" + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Permissions are missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: server_error + errorDescription: Failed to process the response + /issue-refresh-token: + post: + tags: + - Token + summary: Handles refresh token issuance and validations + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/IssueRefreshTokenRequestBody' + required: true + responses: + "200": + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForIssueRefreshToken' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Permissions are missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /validate-authorization-request: + post: + tags: + - Authorize + summary: Handles pre-user authorization requests + operationId: preUserAuthorization + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ValidateAuthorizationRequestBody' + required: true + responses: + "200": + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForValidateAuthorizationRequest' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 401 + errorMessage: invalid_permissions + errorDescription: "The requested permissions are invalid, unknown" + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Permissions are missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /validate-event-subscription: + post: + tags: + - Event Subscription + summary: handle event subscription validations & storing data + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EventSubscriptionRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForEventSubscriptionValidation' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + successExampleWithDetails: + summary: Success response With Details + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + callbackUrl: https://test.com + version: "3.1" + eventTypes: + - eventType + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: Invalid Request + errorMessage: Invalid event subscription payload + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-event-subscription-response: + post: + tags: + - Event Subscription + summary: handle post event-subscription-creation response generation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EventSubscriptionRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForEnrichEventSubscription' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + eventSubscriptionResponse: {} + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: Invalid Request + errorMessage: Invalid event subscription payload + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /validate-event-creation: + post: + tags: + - Event Creation + summary: handle event creation validations & storing data + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EventCreationRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForEventValidation' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: Invalid Request + errorMessage: Invalid event creation payload + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /validate-event-polling: + post: + tags: + - Event Polling + summary: handle event polling validations & storing data + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EventPollingRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForEventValidation' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: Invalid Request + errorMessage: Invalid event polling payload + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + data: + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response + /enrich-event-polling-response: + post: + tags: + - Event Polling + summary: handle post event-polling response generation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EventPollingRequestBody' + required: true + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Response200ForEnrichEventPolling' + examples: + successExample: + summary: Success response + value: + responseId: Ec1wMjmiG8 + status: SUCCESS + data: + eventPollingResponse: {} + failedExample: + summary: Failed response + value: + status: ERROR + errorCode: 400 + data: + error: Invalid Request + errorMessage: Invalid event polling payload + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: invalid_request + errorDescription: Data is missing + "500": + description: Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + responseId: Ec1wMjmiG8 + status: ERROR + errorMessage: server_error + errorDescription: Failed to process the response +components: + schemas: + Response200ForErrorMapper: + type: object + required: + - responseId + - data + properties: + responseId: + type: string + errorCode: + type: integer + data: + type: object + description: Defines the custom error response. + ErrorMapperRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/ErrorMapperData' + ErrorMapperData: + type: object + properties: + error: + $ref: '#/components/schemas/Error' + description: Defines the context data related to the errors. + Error: + type: object + properties: + code: + type: string + description: Error code identifying the specific issue. + description: + type: string + description: Detailed description of the error. + operation: + type: string + description: The operation where the error occurred. + description: Defines an error object with details. + Request: + type: object + properties: + consentInitiationData: + type: object + description: The initiation payload used by third parties which includes detailed information on data access request. + requestHeaders: + $ref: '#/components/schemas/RequestHeaders' + consentResourcePath: + type: string + description: To identify requested consent type + RequestForEnrichConsentCreationResponse: + type: object + properties: + consentId: + type: string + description: To identify requested + consentResource: + $ref: '#/components/schemas/StoredDetailedConsentResourceData' + consentResourcePath: + type: string + description: consent resource path + RequestForPreProcessConsentUpdate: + type: object + properties: + consentUpdateData: + type: object + description: The upload payload used by third parties which includes detailed information on data access request. + storedConsentResource: + $ref: '#/components/schemas/StoredBasicConsentResourceData' + requestHeaders: + $ref: '#/components/schemas/RequestHeaders' + consentResourcePath: + type: string + description: To identify requested consent type + RequestForEnrichConsentUpdateResponse: + type: object + properties: + consentId: + type: string + description: To identify requested + consentResource: + $ref: '#/components/schemas/StoredBasicConsentResourceData' + consentResourcePath: + type: string + description: consent resource path + RequestForPreProcessFileUpload: + type: object + properties: + consentId: + type: string + description: To identify requested + consentResource: + $ref: '#/components/schemas/StoredDetailedConsentResourceData' + fileContent: + type: string + description: content of the uploaded file + consentResourcePath: + type: string + description: consent resource path + requestHeaders: + $ref: '#/components/schemas/RequestHeaders' + RequestForEnrichFileUploadResponse: + type: object + properties: + consentId: + type: string + description: To identify consent. + fileUploadCreatedTime: + type: string + description: Timestamp which the file was stored in the database. + PreProcessConsentRetrievalData: + type: object + properties: + consentId: + type: string + description: The consent id + consentResource: + $ref: '#/components/schemas/StoredBasicConsentResourceData' + requestHeaders: + $ref: '#/components/schemas/RequestHeaders' + consentResourcePath: + type: string + description: Resource url + ConsentSearchData: + type: object + properties: + searchType: + type: string + enum: + - BULK_SEARCH + - AMENDMENT_HISTORY + searchResult: + type: object + description: payload + enrichmentParams: + type: object + description: query params + PopulateConsentAuthorizeScreenData: + type: object + properties: + consentId: + type: string + example: An UUID + userId: + type: string + example: Username + requestParameters: + type: object + description: Custom object with request parameters + consentResource: + $ref: '#/components/schemas/StoredDetailedConsentResourceData' + ValidateConsentAccessData: + type: object + properties: + consentId: + type: string + description: The consent id + consentResource: + $ref: '#/components/schemas/StoredDetailedConsentResourceData' + dataRequestPayload: + type: object + description: The receipt used by Third parties which includes detailed information on data access request + SuccessResponse: + type: object + required: + - responseId + - status + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + SuccessResponseConsentRevocation: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseConsentRevocationData' + SuccessResponsePopulateConsentAuthorizeScreen: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponsePopulateConsentAuthorizeScreenData' + SuccessResponseForResponseAlternation: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseForResponseAlternationData' + SuccessResponseForConsentSearch: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseForConsentSearchData' + SuccessResponsePreProcessConsentCreation: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseWithDetailedConsentData' + SuccessResponsePreProcessConsentUpdate: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseWithBasicConsentData' + SuccessResponsePreProcessFileUpload: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponsePreProcessFileUploadData' + FailedResponse: + required: + - responseId + - data + - errorCode + - status + type: object + properties: + responseId: + type: string + status: + type: string + description: "Indicates the outcome of the request. For a failed operation, this should be set to ERROR." + enum: + - ERROR + errorCode: + type: integer + description: If any HTTP error code to return. + data: + type: object + description: :"Custom error object to response back" + FailedResponseInConsentAuthorize: + type: object + required: + - responseId + - data + - status + properties: + responseId: + type: string + status: + type: string + description: "Indicates the outcome of the request. For a failed operation, this should be set to ERROR." + enum: + - ERROR + data: + $ref: '#/components/schemas/FailedResponseInConsentAuthorizeData' + FailedResponseInConsentAuthorizeData: + required: + - errorMessage + type: object + properties: + errorMessage: + type: string + description: Error message to be displayed in the URL + newConsentStatus: + type: string + description: New consent status to be set to the consent + ErrorResponse: + required: + - data + - status + type: object + properties: + status: + type: string + description: "Indicates the outcome of the request. For an error operation, this should be set to ERROR." + enum: + - ERROR + data: + type: object + description: :"Custom error object to response back" + PreProcessConsentCreationRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/Request' + EnrichConsentCreationRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/RequestForEnrichConsentCreationResponse' + PreProcessConsentUpdateRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/RequestForPreProcessConsentUpdate' + EnrichConsentUpdateRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/RequestForEnrichConsentUpdateResponse' + EnrichFileUploadResponseRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/RequestForEnrichFileUploadResponse' + PreProcessFileUploadRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/RequestForPreProcessFileUpload' + PreProcessConsentRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/PreProcessConsentRetrievalData' + EnrichConsentSearchRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/ConsentSearchData' + PopulateConsentAuthorizeScreenRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/PopulateConsentAuthorizeScreenData' + ValidateConsentAccessRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/ValidateConsentAccessData' + IssueRefreshTokenRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/IssueRefreshTokenRequestData' + IssueRefreshTokenRequestData: + type: object + properties: + grantType: + type: string + example: authorization_code + consentCreatedTime: + type: integer + format: int64 + consentValidityPeriod: + type: integer + format: int64 + defaultRefreshTokenValidityPeriod: + type: integer + format: int64 + Response200ForPreProcessConsentCreation: + oneOf: + - $ref: '#/components/schemas/SuccessResponsePreProcessConsentCreation' + - $ref: '#/components/schemas/FailedResponse' + Response200ForPreProcessConsentUpdate: + oneOf: + - $ref: '#/components/schemas/SuccessResponsePreProcessConsentUpdate' + - $ref: '#/components/schemas/FailedResponse' + Response200ForPreProcessFileUpload: + oneOf: + - $ref: '#/components/schemas/SuccessResponsePreProcessFileUpload' + - $ref: '#/components/schemas/FailedResponse' + Response200ForResponseAlternation: + oneOf: + - $ref: '#/components/schemas/SuccessResponseForResponseAlternation' + - $ref: '#/components/schemas/FailedResponse' + Response200ForConsentSearch: + oneOf: + - $ref: '#/components/schemas/SuccessResponseForConsentSearch' + - $ref: '#/components/schemas/FailedResponse' + Response200: + oneOf: + - $ref: '#/components/schemas/SuccessResponse' + - $ref: '#/components/schemas/FailedResponse' + Response200ForIssueRefreshToken: + oneOf: + - $ref: '#/components/schemas/SuccessResponseIssueRefreshToken' + - $ref: '#/components/schemas/FailedResponse' + SuccessResponseIssueRefreshToken: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseIssueRefreshTokenData' + SuccessResponseIssueRefreshTokenData: + type: object + properties: + issueRefreshToken: + type: boolean + refreshTokenValidityPeriod: + type: integer + format: int64 + Response200ForValidateAuthorizationRequest: + oneOf: + - $ref: '#/components/schemas/SuccessResponse' + - $ref: '#/components/schemas/FailedResponse' + ValidateAuthorizationRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/ValidateAuthorizationRequestBodyData' + ValidateAuthorizationRequestBodyData: + type: object + properties: + requestObject: + type: object + description: full request object + Response200ForConsentRevocation: + oneOf: + - $ref: '#/components/schemas/SuccessResponseConsentRevocation' + - $ref: '#/components/schemas/FailedResponse' + Response200ForPopulateConsentAuthorizeScreen: + oneOf: + - $ref: '#/components/schemas/SuccessResponsePopulateConsentAuthorizeScreen' + - $ref: '#/components/schemas/FailedResponseInConsentAuthorize' + SuccessResponseConsentRevocationData: + type: object + properties: + revocationStatusName: + type: string + description: Name for the revoked status + requireTokenRevocation: + type: string + description: Require access token to be revoked + SuccessResponseForResponseAlternationData: + type: object + properties: + responseHeaders: + type: object + description: Headers to be included in the response. + modifiedResponse: + type: object + description: Generated custom response body + SuccessResponseForConsentSearchData: + type: object + properties: + enrichedSearchResult: + type: object + description: Enriched search result + SuccessResponseWithDetailedConsentData: + type: object + properties: + consentResource: + $ref: '#/components/schemas/DetailedConsentResourceData' + SuccessResponseWithBasicConsentData: + type: object + properties: + consentResource: + $ref: '#/components/schemas/BasicConsentResourceData' + SuccessResponsePreProcessFileUploadData: + type: object + properties: + consentStatus: + type: string + description: "New consent status after the file upload is successful." + userId: + type: string + description: "Id of the user doing the file upload. Used for auditing purposes." + PersistAuthorizedConsentRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/PersistAuthorizedConsent' + PersistAuthorizedConsent: + type: object + properties: + consentId: + type: string + isApproved: + type: boolean + userGrantedData: + $ref: '#/components/schemas/UserGrantedData' + consentResource: + $ref: '#/components/schemas/StoredDetailedConsentResourceData' + UserGrantedData: + type: object + properties: + requestParameters: + type: object + authorizedResources: + $ref: '#/components/schemas/AuthorizedResources' + userId: + type: string + Response200ForPersistAuthorizedConsent: + oneOf: + - $ref: '#/components/schemas/SuccessResponsePersistAuthorizedConsent' + - $ref: '#/components/schemas/FailedResponseInConsentAuthorize' + SuccessResponsePersistAuthorizedConsent: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + oneOf: + - $ref: '#/components/schemas/SuccessResponseWithDetailedConsentData' + - $ref: '#/components/schemas/SuccessResponseWithDetailedConsentDataAndAmendments' + SuccessResponseWithDetailedConsentDataAndAmendments: + type: object + properties: + consentResource: + $ref: '#/components/schemas/DetailedConsentResourceDataWithAmendments' + BasicConsentResourceData: + type: object + properties: + type: + type: string + status: + type: string + validityTime: + type: integer + format: int64 + recurringIndicator: + type: boolean + frequency: + type: integer + receipt: + type: object + attributes: + type: object + DetailedConsentResourceData: + type: object + properties: + type: + type: string + status: + type: string + validityTime: + type: integer + format: int64 + recurringIndicator: + type: boolean + frequency: + type: integer + receipt: + type: object + attributes: + type: object + authorizations: + type: array + items: + $ref: '#/components/schemas/Authorization' + DetailedConsentResourceDataWithAmendments: + type: object + properties: + type: + type: string + status: + type: string + validityTime: + type: integer + format: int64 + recurringIndicator: + type: boolean + frequency: + type: integer + receipt: + type: object + attributes: + type: object + authorizations: + type: array + items: + $ref: '#/components/schemas/Authorization' + amendments: + type: array + items: + $ref: '#/components/schemas/AmendedAuthorization' + StoredBasicConsentResourceData: + type: object + properties: + id: + type: string + receipt: + type: object + createdTime: + type: integer + format: int32 + updatedTime: + type: integer + format: int32 + clientId: + type: string + type: + type: string + status: + type: string + frequency: + type: integer + validityTime: + type: integer + format: int32 + recurringIndicator: + type: boolean + attributes: + type: object + StoredDetailedConsentResourceData: + type: object + properties: + id: + type: string + receipt: + type: object + createdTime: + type: integer + format: int32 + updatedTime: + type: integer + format: int32 + clientId: + type: string + type: + type: string + status: + type: string + frequency: + type: integer + validityTime: + type: integer + format: int32 + recurringIndicator: + type: boolean + attributes: + type: object + authorizations: + type: array + items: + $ref: '#/components/schemas/StoredAuthorization' + fileContent: + type: string + Authorization: + type: object + properties: + userId: + type: string + type: + type: string + status: + type: string + resources: + type: array + items: + $ref: '#/components/schemas/Resource' + AmendedAuthorization: + type: object + properties: + id: + type: string + type: + type: string + status: + type: string + resources: + type: array + items: + $ref: '#/components/schemas/Resource' + amendedResources: + type: array + items: + $ref: '#/components/schemas/AmendedResource' + StoredAuthorization: + type: object + properties: + id: + type: string + userId: + type: string + type: + type: string + status: + type: string + resources: + type: array + items: + $ref: '#/components/schemas/StoredResource' + Resource: + type: object + properties: + accountId: + type: string + permission: + type: string + status: + type: string + AmendedResource: + type: object + properties: + id: + type: string + permission: + type: string + status: + type: string + StoredResource: + type: object + properties: + id: + type: string + accountId: + type: string + permission: + type: string + status: + type: string + Response200ForClientProcess: + oneOf: + - $ref: '#/components/schemas/SuccessResponseClientProcess' + - $ref: '#/components/schemas/FailedResponseClientProcess' + ClientProcessRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/ClientProcessData' + ClientProcessData: + type: object + properties: + clientData: + type: object + description: Client Registration Data. Mandatory for pre-process-client-creation and pre-process-client-update. + softwareStatement: + type: object + description: "Parameters of the decoded SSA. Mandatory for pre-process-client-creation, pre-process-client-update and pre-process-client-retrieval." + existingClientData: + type: object + description: properties of the existing client application. Mandatory for pre-process-client-update. + description: Defines the context data related to the client registration. + SuccessResponseClientProcess: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseClientProcess_data' + SuccessResponseClientProcess_data: + type: object + properties: + clientData: + type: object + description: Defines the success response. + FailedResponseClientProcess: + required: + - data + - status + type: object + properties: + status: + type: string + description: "Indicates the outcome of the request. For a failed operation, this should be set to ERROR." + enum: + - ERROR + errorCode: + type: integer + description: If any HTTP error code to return. + data: + $ref: '#/components/schemas/FailedResponseClientProcess_data' + FailedResponseClientProcess_data: + type: object + properties: + error: + type: string + description: Provides the error code for error. + enum: + - invalid_client_metadata + - invalid_redirect_uri + - invalid_software_statement + errorDescription: + type: string + description: Offers a detailed explanation of the error. + EventSubscriptionRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/EventSubscriptionRequest' + EventSubscriptionRequest: + type: object + properties: + eventType: + type: string + enum: + - SubscriptionCreation + - SingleSubscriptionRetrieval + - BulkSubscriptionRetrieval + - SubscriptionRetrievalForEventTypes + - SubscriptionUpdate + - SubscriptionDelete + eventSubscriptionData: + type: object + description: Event Subscription Payload + EventCreationRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/EventCreationRequest' + EventCreationRequest: + type: object + properties: + eventData: + type: object + description: Event creation Payload + EventPollingRequestBody: + type: object + required: + - requestId + - data + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/EventPollingRequest' + EventPollingRequest: + type: object + properties: + eventPollingData: + type: object + description: Event polling data + Response200ForEventSubscriptionValidation: + oneOf: + - $ref: '#/components/schemas/SuccessResponse' + - $ref: '#/components/schemas/SuccessResponseForEventWithDetails' + - $ref: '#/components/schemas/FailedResponse' + Response200ForEventValidation: + oneOf: + - $ref: '#/components/schemas/SuccessResponse' + - $ref: '#/components/schemas/FailedResponse' + Response200ForEnrichEventSubscription: + oneOf: + - $ref: '#/components/schemas/SuccessResponseForEnrichEventSubscription' + - $ref: '#/components/schemas/FailedResponse' + Response200ForEnrichEventPolling: + oneOf: + - $ref: '#/components/schemas/SuccessResponseForEnrichEventPolling' + - $ref: '#/components/schemas/FailedResponse' + SuccessResponseForEventWithDetails: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseForEventWithDetails_data' + SuccessResponseForEventWithDetails_data: + type: object + properties: + callbackUrl: + type: string + version: + type: string + eventTypes: + type: array + items: + type: string + SuccessResponseForEnrichEventSubscription: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseForEnrichEventSubscription_data' + SuccessResponseForEnrichEventSubscription_data: + type: object + properties: + eventSubscriptionResponse: + type: object + description: Event Subscription Response + SuccessResponseForEnrichEventPolling: + type: object + required: + - responseId + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseForEnrichEventPolling_data' + SuccessResponseForEnrichEventPolling_data: + type: object + properties: + eventPollingResponse: + type: object + description: Event Polling Response + RequestHeaders: + type: object + description: Request headers sent by the TPP. Filtered set of headers are sent to the external service. + SuccessResponsePopulateConsentAuthorizeScreenData: + type: object + properties: + consentData: + type: object + additionalProperties: true + properties: + type: + type: string + description: The type of consent + basicConsentData: + type: object + description: Structured descriptive text shown on the consent page, split into sections. Each key is a section title, and its value is a list of bullet points displayed under that section. + additionalProperties: + items: + type: string + permissions: + type: array + description: List of permissions for the consent (optional) + items: + type: object + required: + - uid + - displayValues + properties: + uid: + type: string + description: Unique ID for the permission + displayValues: + type: array + description: Permission display values + items: + type: string + initiatedAccounts: + type: array + description: Accounts initiated with this permission + items: + $ref: '#/components/schemas/Account' + initiatedAccountsForConsent: + type: array + description: Initialized accounts for the overall consent (optional) + items: + $ref: '#/components/schemas/Account' + allowMultipleAccounts: + type: boolean + description: Indicates if multiple consumer accounts can be selected per consent / permission + handleAccountSelectionSeparately: + type: boolean + description: Indicates if account selection must be handled separately in the UI + isReauthorization: + type: boolean + description: Indicates if this is a reauthorization flow (optional) + consentMetadata: + type: object + description: Hidden consent metadata to be forwarded to consent persistence. + consumerData: + type: object + additionalProperties: true + description: Consumer related data fetched from the banking backend. + properties: + accounts: + type: array + description: List of all user accounts/resources selectable in the UI + items: + type: object + required: + - displayName + - selected + allOf: + - $ref: '#/components/schemas/Account' + properties: + selected: + type: boolean + description: Whether the account is selected by default + AuthorizedResources: + type: object + additionalProperties: true + properties: + approval: + type: boolean + description: Whether the user approved the consent + isReauthorization: + type: boolean + description: Indicates if this was a reauthorization flow (optional) + type: + type: string + description: Type of consent granted (e.g., 'accounts', 'payments', etc.) + authorizedData: + type: array + description: List of granted permissions and corresponding user-selected account data + items: + type: object + required: + - accounts + properties: + permissions: + type: array + description: Granted permissions (optional if no permissions exist) + items: + type: string + accounts: + type: array + description: Accounts selected for the permissions + items: + $ref: '#/components/schemas/Account' + metadata: + type: object + description: Consent authorization related metadata. + Account: + type: object + description: A user account or resource representation + required: + - displayName + properties: + displayName: + type: string + description: Account display name + additionalProperties: true + + AppCreateProcessRequestBody: + type: object + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/AppCreateProcessData' + AppCreateProcessData: + type: object + required: + - appData + - additionalProperties + properties: + appData: + type: object + description: OAuth Application Data. Mandatory for pre-process-application-creation. + additionalProperties: + type: object + description: Additional properties retrieved from devportal UI. Mandatory for pre-process-application-creation. + description: Defines the context data related to the application registration. + AppUpdateProcessRequestBody: + type: object + properties: + requestId: + type: string + description: A unique correlation identifier + example: Ec1wMjmiG8 + data: + $ref: '#/components/schemas/AppUpdateProcessData' + AppUpdateProcessData: + type: object + required: + - appData + - additionalProperties + - existingAppData + properties: + appData: + type: object + description: OAuth Application Data. Mandatory for pre-process-application-update. + additionalProperties: + type: object + description: Additional properties retrieved from devportal UI. Mandatory for pre-process-application-update. + existingAppData: + type: object + description: Existing OAuth Application Data. Mandatory for pre-process-application-update. + description: Defines the context data related to the application update. + Response200ForApplicationCreation: + oneOf: + - $ref: '#/components/schemas/SuccessResponseApplicationCreation' + - $ref: '#/components/schemas/FailedResponseApplicationProcess' + SuccessResponseApplicationCreation: + type: object + required: + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseApplicationCreation_data' + SuccessResponseApplicationCreation_data: + type: object + properties: + clientId: + type: string + description: Unique id to be stored as the clientId + additionalAppData: + type: object + description: Defines the additional properties to store against the application. + Response200ForApplicationUpdate: + oneOf: + - $ref: '#/components/schemas/SuccessResponseApplicationUpdate' + - $ref: '#/components/schemas/FailedResponseApplicationProcess' + SuccessResponseApplicationUpdate: + type: object + required: + - status + - data + properties: + responseId: + type: string + status: + type: string + enum: + - SUCCESS + data: + $ref: '#/components/schemas/SuccessResponseApplicationUpdate_data' + SuccessResponseApplicationUpdate_data: + type: object + properties: + additionalAppData: + type: object + description: Defines the additional properties to store against the application. + FailedResponseApplicationProcess: + required: + - data + - status + type: object + properties: + status: + type: string + description: "Indicates the outcome of the request. For a failed operation, this should be set to ERROR." + enum: + - ERROR + errorMessage: + $ref: '#/components/schemas/FailedResponseApplicationProcessData' + FailedResponseApplicationProcessData: + required: + - errorMessage + type: object + properties: + errorMessage: + type: string + description: Error message to be returned + securitySchemes: + BasicAuth: + type: http + scheme: basic + OAuth2: + type: oauth2 + flows: + clientCredentials: + tokenUrl: https://example.com/oauth/token + scopes: + process: process request generate response diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/extension/model/ServiceExtensionTypeEnum.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/extension/model/ServiceExtensionTypeEnum.java index afba501c0..98fc24ab0 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/extension/model/ServiceExtensionTypeEnum.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.common/src/main/java/org/wso2/financial/services/accelerator/common/extension/model/ServiceExtensionTypeEnum.java @@ -46,7 +46,9 @@ public enum ServiceExtensionTypeEnum { ENRICH_EVENT_POLLING_RESPONSE("enrich_event_polling_response"), MAP_ACCELERATOR_ERROR_RESPONSE("map_accelerator_error_response"), PRE_PROCESS_APPLICATION_CREATION("pre_process_application_creation"), - PRE_PROCESS_APPLICATION_UPDATE("pre_process_application_update"); + PRE_PROCESS_APPLICATION_UPDATE("pre_process_application_update"), + PRE_PROCESS_CONSENT_UPDATE("pre_process_consent_update"), + ENRICH_CONSENT_UPDATE_RESPONSE("enrich_consent_update_response"); private final String type; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.dao/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/dao/queries/ConsentMgtCommonDBQueries.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.dao/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/dao/queries/ConsentMgtCommonDBQueries.java index 5c9574063..2310c56d6 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.dao/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/dao/queries/ConsentMgtCommonDBQueries.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.dao/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/dao/queries/ConsentMgtCommonDBQueries.java @@ -41,7 +41,7 @@ public String getGetConsentWithConsentAttributesPreparedStatement() { return "SELECT FS_CONSENT.CONSENT_ID, RECEIPT, CREATED_TIME, UPDATED_TIME, CLIENT_ID, CONSENT_TYPE, " + "CURRENT_STATUS, CONSENT_FREQUENCY, VALIDITY_TIME, RECURRING_INDICATOR, " + - "FS_CONSENT_ATTRIBUTE.ATT_KEY, FS_CONSENT_ATTRIBUTE.ATT_VALUE FROM FS_CONSENT RIGHT JOIN " + + "FS_CONSENT_ATTRIBUTE.ATT_KEY, FS_CONSENT_ATTRIBUTE.ATT_VALUE FROM FS_CONSENT LEFT JOIN " + "FS_CONSENT_ATTRIBUTE ON FS_CONSENT.CONSENT_ID = FS_CONSENT_ATTRIBUTE.CONSENT_ID WHERE FS_CONSENT" + ".CONSENT_ID = ?"; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ConsentExtensionUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ConsentExtensionUtils.java index 377b00aab..a8d1fc495 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ConsentExtensionUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ConsentExtensionUtils.java @@ -120,6 +120,34 @@ public static JSONObject getInitiationResponse(Object responseObj, DetailedConse return response; } + /** + * Method to construct Initiation response. + * + * @param responseObj Response of the request + * @param createdConsent Consent response received from service layer + * @return JSONObject Initiation Response + */ + public static JSONObject getInitiationResponse(Object responseObj, ConsentResource createdConsent) { + + JSONObject response = (JSONObject) responseObj; + JSONObject dataObject = response.getJSONObject(ConsentExtensionConstants.DATA); + dataObject.put(ConsentExtensionConstants.CONSENT_ID, createdConsent.getConsentID()); + dataObject.put(ConsentExtensionConstants.CREATION_DATE_TIME, convertToISO8601(createdConsent.getCreatedTime())); + dataObject.put(ConsentExtensionConstants.STATUS_UPDATE_DATE_TIME, + convertToISO8601(createdConsent.getUpdatedTime())); + dataObject.put(ConsentExtensionConstants.STATUS, createdConsent.getCurrentStatus()); + + response.remove(ConsentExtensionConstants.DATA); + response.put(ConsentExtensionConstants.DATA, dataObject); + + if (log.isDebugEnabled()) { + log.debug(String.format("Initiation response constructed for consent ID: %s with status: %s", + createdConsent.getConsentID().replaceAll("[\r\n]", ""), + createdConsent.getCurrentStatus().replaceAll("[\r\n]", ""))); + } + return response; + } + /** * Method to construct Retrieval Initiation response. * diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ExternalAPIUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ExternalAPIUtil.java index ace9753c3..7ed8e96db 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ExternalAPIUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/ExternalAPIUtil.java @@ -29,6 +29,7 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.authorize.model.ConsentPersistData; import org.wso2.financial.services.accelerator.consent.mgt.extensions.authorize.model.ExternalAPIPreConsentPersistRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.authorize.model.ExternalAPIPreConsentPersistResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIConsentResourceResponseDTO; import java.util.ArrayList; @@ -439,4 +440,24 @@ private static ConsentMappingResource constructAmendedMappingResource( return resource; } + /** + * Combines all resolved data into a final {@link ConsentResource}. + * + * @param basicConsentResource The consent resource received from the external API pre-consent-update step. + * @param consentID Consent ID. + * @param clientID Client ID. + * @param createTime Consent Created timestamp. + * @return A fully constructed {@link ConsentResource}. + */ + public static ConsentResource buildConsentResource(ExternalAPIBasicConsentResourceResponseDTO basicConsentResource, + String consentID, String clientID, long createTime) { + + String receipt = basicConsentResource.getReceipt() != null ? + new JSONObject(basicConsentResource.getReceipt()).toString() : "{}"; + return new ConsentResource(consentID, clientID, receipt, basicConsentResource.getType(), + basicConsentResource.getFrequency(), basicConsentResource.getValidityTime(), + basicConsentResource.getRecurringIndicator(), basicConsentResource.getStatus(), + createTime, 0); + } + } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceRequestDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceRequestDTO.java new file mode 100644 index 000000000..099a0a8a6 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceRequestDTO.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model; + +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentResource; + +import java.util.Collections; +import java.util.Map; + +/** + * Basic Consent resource model for the API extension consent management. + */ +public class ExternalAPIBasicConsentResourceRequestDTO { + + private String id; + private String clientId; + private String type; + private String status; + private int frequency; + private long validityTime; + private long createdTime; + private long updatedTime; + private boolean recurringIndicator; + private Map receipt; + private Map attributes; + + public ExternalAPIBasicConsentResourceRequestDTO(ConsentResource consentResource) { + + this.id = consentResource.getConsentID(); + this.clientId = consentResource.getClientID(); + this.type = consentResource.getConsentType(); + this.status = consentResource.getCurrentStatus(); + this.frequency = consentResource.getConsentFrequency(); + this.validityTime = consentResource.getValidityPeriod(); + this.createdTime = consentResource.getCreatedTime(); + this.updatedTime = consentResource.getUpdatedTime(); + this.recurringIndicator = consentResource.isRecurringIndicator(); + + if (consentResource.getReceipt() != null && !consentResource.getReceipt().isEmpty()) { + JSONObject receiptJson = new JSONObject(consentResource.getReceipt()); + this.receipt = receiptJson.toMap(); + } else { + this.receipt = Collections.emptyMap(); + } + this.attributes = consentResource.getConsentAttributes(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public int getFrequency() { + return frequency; + } + + public void setFrequency(int frequency) { + this.frequency = frequency; + } + + public long getValidityTime() { + return validityTime; + } + + public void setValidityTime(long validityTime) { + this.validityTime = validityTime; + } + + public long getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(long createdTime) { + this.createdTime = createdTime; + } + + public long getUpdatedTime() { + return updatedTime; + } + + public void setUpdatedTime(long updatedTime) { + this.updatedTime = updatedTime; + } + + public boolean getRecurringIndicator() { + return recurringIndicator; + } + + public void setRecurringIndicator(boolean recurringIndicator) { + this.recurringIndicator = recurringIndicator; + } + + public Map getReceipt() { + return receipt; + } + + public void setReceipt(Map receipt) { + this.receipt = receipt; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceResponseDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceResponseDTO.java new file mode 100644 index 000000000..da839d019 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/common/model/ExternalAPIBasicConsentResourceResponseDTO.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model; + +import java.util.Map; + +/** + * Basic Consent resource response model for the API extension consent management. + */ +public class ExternalAPIBasicConsentResourceResponseDTO { + + private String type; + private String status; + private Integer frequency; + private Long validityTime; + private Boolean recurringIndicator; + private Map receipt; + private Map attributes; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Integer getFrequency() { + return frequency; + } + + public void setFrequency(Integer frequency) { + this.frequency = frequency; + } + + public Long getValidityTime() { + return validityTime; + } + + public void setValidityTime(Long validityTime) { + this.validityTime = validityTime; + } + + public Boolean getRecurringIndicator() { + return recurringIndicator; + } + + public void setRecurringIndicator(Boolean recurringIndicator) { + this.recurringIndicator = recurringIndicator; + } + + public Map getReceipt() { + return receipt; + } + + public void setReceipt(Map receipt) { + this.receipt = receipt; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/impl/DefaultConsentManageHandler.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/impl/DefaultConsentManageHandler.java index b7ba55933..8631cbcda 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/impl/DefaultConsentManageHandler.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/impl/DefaultConsentManageHandler.java @@ -38,10 +38,13 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ExternalAPIUtil; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ResponseStatus; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.idempotency.IdempotencyValidator; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIConsentResourceRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIConsentResourceResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.internal.ConsentExtensionsDataHolder; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.ConsentManageHandler; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ConsentManageAuthorizationUpdateDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ConsentManageData; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ConsentPayloadValidationResult; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIConsentRetrieveRequestDTO; @@ -49,9 +52,12 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIConsentRevokeResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIModifiedResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentGenerateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentUpdateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.utils.ConsentManageConstants; @@ -79,6 +85,8 @@ public class DefaultConsentManageHandler implements ConsentManageHandler { boolean isExternalPreFileUploadEnabled; boolean isExternalPostFileUploadEnabled; boolean isExternalPreFileRetrievalEnabled; + boolean isExternalPreConsentUpdateEnabled; + boolean isExternalPostConsentUpdateEnabled; String idempotencyHeaderName; IdempotencyValidator idempotencyValidator; @@ -101,6 +109,10 @@ public DefaultConsentManageHandler() { .contains(ServiceExtensionTypeEnum.ENRICH_CONSENT_FILE_RESPONSE); isExternalPreFileRetrievalEnabled = configParser.getServiceExtensionTypes() .contains(ServiceExtensionTypeEnum.VALIDATE_CONSENT_FILE_RETRIEVAL); + isExternalPreConsentUpdateEnabled = configParser.getServiceExtensionTypes() + .contains(ServiceExtensionTypeEnum.PRE_PROCESS_CONSENT_UPDATE); + isExternalPostConsentUpdateEnabled = configParser.getServiceExtensionTypes() + .contains(ServiceExtensionTypeEnum.ENRICH_CONSENT_UPDATE_RESPONSE); idempotencyHeaderName = configParser.getIdempotencyHeaderName(); idempotencyValidator = new IdempotencyValidator(); } @@ -149,16 +161,28 @@ public void handleGet(ConsentManageData consentManageData) throws ConsentExcepti ConsentOperationEnum.CONSENT_RETRIEVE); try { + + if (ConsentManageUtils.isInternalConsentRequest(consentManageData)) { + // Allowing consent retrieval for internal purpose. Retrieving the detailed consent and response back + // for internal consent retrieval requests + log.info(String.format("Processing internal consent retrieval request for consentId: %s", + consentId.replaceAll("[\r\n]+", " "))); + DetailedConsentResource detailedConsentResource = consentCoreService.getDetailedConsent(consentId); + consentManageData.setResponsePayload(new JSONObject(detailedConsentResource)); + consentManageData.setResponseStatus(ResponseStatus.OK); + return; + } + ConsentResource consent = consentCoreService.getConsent(consentId, false); if (consent == null) { - log.error("Consent not found"); + log.error(String.format("Consent not found for consent ID: %s", consentId.replaceAll("[\r\n]+", " "))); throw new ConsentException(ResponseStatus.BAD_REQUEST, "Consent not found", ConsentOperationEnum.CONSENT_RETRIEVE); } // Check whether the client id is matching if (!consent.getClientID().equals(consentManageData.getClientId())) { - log.error("Client ID mismatch"); - throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID mismatch", + log.error(ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR); + throw new ConsentException(ResponseStatus.BAD_REQUEST, ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR, ConsentOperationEnum.CONSENT_RETRIEVE); } @@ -228,7 +252,7 @@ public void handlePost(ConsentManageData consentManageData) throws ConsentExcept if (!FinancialServicesUtils.isValidClientId(consentManageData.getClientId())) { log.error("Client ID does not exist in the system."); throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID does not exist in the system.", - ConsentOperationEnum.CONSENT_RETRIEVE); + ConsentOperationEnum.CONSENT_CREATE); } //Validate Initiation headers @@ -352,7 +376,7 @@ public void handleDelete(ConsentManageData consentManageData) throws ConsentExce if (!FinancialServicesUtils.isValidClientId(consentManageData.getClientId())) { log.error("Client ID does not exist in the system."); throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID does not exist in the system.", - ConsentOperationEnum.CONSENT_RETRIEVE); + ConsentOperationEnum.CONSENT_DELETE); } //Validate Initiation headers @@ -388,9 +412,9 @@ public void handleDelete(ConsentManageData consentManageData) throws ConsentExce if (!consentResource.getClientID().equals(consentManageData.getClientId())) { //Throwing this error in a generic manner since client will not be able to identify if consent // exists if consent does not belong to them - log.error(ConsentManageConstants.NO_CONSENT_FOR_CLIENT_ERROR); - throw new ConsentException(ResponseStatus.BAD_REQUEST, - ConsentManageConstants.NO_CONSENT_FOR_CLIENT_ERROR, ConsentOperationEnum.CONSENT_DELETE); + log.error(ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR); + throw new ConsentException(ResponseStatus.BAD_REQUEST, ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR, + ConsentOperationEnum.CONSENT_DELETE); } if (isExtensionsEnabled && isExternalPreConsentRevocationEnabled) { @@ -446,9 +470,134 @@ public void handleDelete(ConsentManageData consentManageData) throws ConsentExce @Override public void handlePut(ConsentManageData consentManageData) throws ConsentException { - log.error("Method PUT is not supported"); - throw new ConsentException(ResponseStatus.METHOD_NOT_ALLOWED, "Method PUT is not supported", + if (consentManageData.getHeaders().containsKey(ConsentExtensionConstants.INTERACTION_ID_HEADER)) { + consentManageData.setResponseHeader(ConsentExtensionConstants.INTERACTION_ID_HEADER, + consentManageData.getHeaders().get(ConsentExtensionConstants.INTERACTION_ID_HEADER)); + } else { + consentManageData.setResponseHeader(ConsentExtensionConstants.INTERACTION_ID_HEADER, + UUID.randomUUID().toString()); + } + + //Check whether client ID exists + if (StringUtils.isEmpty(consentManageData.getClientId())) { + log.error("Client ID missing in the request."); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID missing in the request.", + ConsentOperationEnum.CONSENT_UPDATE); + } + + //Check whether client ID is valid + if (!FinancialServicesUtils.isValidClientId(consentManageData.getClientId())) { + log.error("Client ID does not exist in the system."); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID does not exist in the system.", + ConsentOperationEnum.CONSENT_UPDATE); + } + + if (consentManageData.getRequestPath() == null) { + log.error("Resource Path Not Found"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Resource Path Not Found", + ConsentOperationEnum.CONSENT_UPDATE); + } + + String consentId = ConsentManageUtils.extractConsentIdFromPath(consentManageData.getRequestPath(), ConsentOperationEnum.CONSENT_UPDATE); + + try { + + // Allowing the developers to use /consent/{ConsentId}/authorisations/{authId} endpoint with + // wso2 internal header to update authorisation resources for a particular authorisation ID + if (ConsentManageUtils.isInternalConsentRequest(consentManageData) && + consentManageData.getRequestPath().contains("authorisations")) { + // Allowing consent update for internal purpose. + ConsentManageAuthorizationUpdateDTO updatedAuthResource = ConsentManageUtils + .updateConsentAuthResource(consentManageData, consentId, consentCoreService); + consentManageData.setResponsePayload(new JSONObject(updatedAuthResource)); + consentManageData.setResponseStatus(ResponseStatus.OK); + return; + } + + ConsentResource storedConsentResource = consentCoreService.getConsent(consentId, true); + + if (storedConsentResource == null) { + log.error("Consent not found"); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Consent not found", + ConsentOperationEnum.CONSENT_UPDATE); + } + + if (!storedConsentResource.getClientID().equals(consentManageData.getClientId())) { + //Throwing this error in a generic manner since client will not be able to identify if consent + // exists if consent does not belong to them + log.error(ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR); + throw new ConsentException(ResponseStatus.BAD_REQUEST, ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR, + ConsentOperationEnum.CONSENT_UPDATE); + } + + ConsentResource updatedConsent; + if (isExtensionsEnabled && isExternalPreConsentUpdateEnabled) { + // Call external service before updating consent + ExternalAPIBasicConsentResourceRequestDTO externalAPIConsentResource = + new ExternalAPIBasicConsentResourceRequestDTO(storedConsentResource); + ExternalAPIPreConsentUpdateRequestDTO preRequestDTO = + new ExternalAPIPreConsentUpdateRequestDTO(consentManageData, externalAPIConsentResource); + ExternalAPIPreConsentUpdateResponseDTO preResponseDTO = ExternalAPIConsentManageUtils. + callExternalService(preRequestDTO); + updatedConsent = constructUpdatingConsent(preResponseDTO, storedConsentResource); + } else { + String consentType = ConsentManageUtils.getConsentManageValidator().getConsentType(consentManageData); + //Validate Initiation request + ConsentPayloadValidationResult validationResponse = ConsentManageUtils.getConsentManageValidator() + .validateRequestPayload(consentManageData, consentType); + if (!validationResponse.isValid()) { + log.error(validationResponse.getErrorMessage().replaceAll("[\r\n]+", " ")); + throw new ConsentException(validationResponse.getHttpCode(), validationResponse.getErrorCode(), + validationResponse.getErrorMessage()); + } + + ConsentResource updatingConsent = new ConsentResource(consentManageData.getClientId(), + consentManageData.getPayload().toString(), storedConsentResource.getConsentType(), + storedConsentResource.getCurrentStatus()); + updatingConsent.setConsentID(consentId); + updatingConsent.setValidityPeriod(ConsentManageUtils.getValidityTime(consentManageData.getPayload(), + consentType)); + + updatedConsent = consentCoreService.updateConsent(updatingConsent); + } + + if (isExtensionsEnabled && isExternalPostConsentUpdateEnabled) { + // Call external service after generating consent + ConsentResource createdConsentResource = consentCoreService.getConsent( + updatedConsent.getConsentID(), true); + ExternalAPIBasicConsentResourceRequestDTO externalAPIConsentResource = + new ExternalAPIBasicConsentResourceRequestDTO(createdConsentResource); + ExternalAPIPostConsentUpdateRequestDTO postRequestDTO = new ExternalAPIPostConsentUpdateRequestDTO( + externalAPIConsentResource, consentManageData.getRequestPath()); + ExternalAPIModifiedResponseDTO postResponseDTO = ExternalAPIConsentManageUtils. + callExternalService(postRequestDTO); + + if (postResponseDTO.getModifiedResponse() != null) { + consentManageData.setResponsePayload(postResponseDTO.getModifiedResponse()); + } else { + consentManageData.setResponsePayload(new JSONObject()); + } + if (postResponseDTO.getResponseHeaders() != null) { + consentManageData.setResponseHeaders(postResponseDTO.getResponseHeaders()); + } else { + consentManageData.setResponseHeaders(new HashMap<>()); + } + } else { + consentManageData.setResponsePayload(ConsentExtensionUtils + .getInitiationResponse(consentManageData.getPayload(), updatedConsent)); + } + consentManageData.setResponseStatus(ResponseStatus.OK); + + } catch (ConsentManagementException e) { + log.error("Error Occurred while creating the consent", e); + throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, + "Error Occurred while updating the consent", ConsentOperationEnum.CONSENT_UPDATE); + } catch (FinancialServicesException e) { + log.error("Error Occurred while updating the consent", e); + throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, e.getMessage(), + ConsentOperationEnum.CONSENT_UPDATE); + } } @Override @@ -473,7 +622,7 @@ public void handleFileUploadPost(ConsentManageData consentManageData) throws Con //Check whether client ID exists if (StringUtils.isEmpty(consentManageData.getClientId())) { log.error("Client ID is missing in the request."); - throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID id missing in the request.", + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID missing in the request.", ConsentOperationEnum.CONSENT_FILE_UPLOAD); } @@ -590,7 +739,7 @@ public void handleFileGet(ConsentManageData consentManageData) throws ConsentExc //Check whether client ID exists if (StringUtils.isEmpty(consentManageData.getClientId())) { log.error("Client ID is missing in the request."); - throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID id missing in the request.", + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID missing in the request.", ConsentOperationEnum.CONSENT_FILE_RETRIEVAL); } @@ -619,8 +768,8 @@ public void handleFileGet(ConsentManageData consentManageData) throws ConsentExc } // Check whether the client id is matching if (!consent.getClientID().equals(consentManageData.getClientId())) { - log.error("Client ID mismatch"); - throw new ConsentException(ResponseStatus.BAD_REQUEST, "Client ID mismatch", + log.error(ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR); + throw new ConsentException(ResponseStatus.BAD_REQUEST, ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR, ConsentOperationEnum.CONSENT_FILE_RETRIEVAL); } ConsentFile consentFile = consentCoreService.getConsentFile(consentId); @@ -672,4 +821,28 @@ private DetailedConsentResource generateConsent(ExternalAPIPreConsentGenerateRes } } + /** + * Constructs the consent resource to be updated based on the response from external service and updates the consent + * + * @param responseDTO Response from external service for pre consent update call + * @param storedConsentResource Consent resource stored in the system before update + * @return Consent resource to update + * @throws ConsentException If error occurs while constructing the consent resource or updating the consent + */ + private ConsentResource constructUpdatingConsent(ExternalAPIPreConsentUpdateResponseDTO responseDTO, + ConsentResource storedConsentResource) throws ConsentException { + + ExternalAPIBasicConsentResourceResponseDTO externalAPIConsentResource = responseDTO.getConsentResource(); + try { + ConsentResource consentResource = ExternalAPIUtil.buildConsentResource( + externalAPIConsentResource, storedConsentResource.getConsentID(), + storedConsentResource.getClientID(), storedConsentResource.getCreatedTime()); + return consentCoreService.updateConsent(consentResource); + } catch (ConsentManagementException e) { + log.error("Error persisting consent", e); + throw new ConsentException(ResponseStatus.INTERNAL_SERVER_ERROR, "Error persisting consent", + ConsentOperationEnum.CONSENT_UPDATE); + } + } + } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ConsentManageAuthorizationUpdateDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ConsentManageAuthorizationUpdateDTO.java new file mode 100644 index 000000000..70afbce60 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ConsentManageAuthorizationUpdateDTO.java @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model; + +import java.util.List; + +/** + * DTO for consent authorization update in consent management. + */ +public class ConsentManageAuthorizationUpdateDTO { + + private String authorizationID; + private String consentID; + private String userID; + private String authorizationStatus; + private String authorizationType; + private long updatedTime; + List mappingResources; + + public ConsentManageAuthorizationUpdateDTO() { + + } + + public ConsentManageAuthorizationUpdateDTO(String consentID, String userID, String authorizationStatus, + String authorizationType, long updatedTime) { + this.consentID = consentID; + this.userID = userID; + this.authorizationStatus = authorizationStatus; + this.authorizationType = authorizationType; + this.updatedTime = updatedTime; + + } + public String getAuthorizationID() { + + return authorizationID; + } + + public String getAuthorizationType() { + + return authorizationType; + } + + public void setAuthorizationType(String authorizationType) { + + this.authorizationType = authorizationType; + } + + public void setAuthorizationID(String authorizationID) { + + this.authorizationID = authorizationID; + } + + public String getConsentID() { + + return consentID; + } + + public void setConsentID(String consentID) { + + this.consentID = consentID; + } + + public String getUserID() { + + return userID; + } + + public void setUserID(String userID) { + + this.userID = userID; + } + + public String getAuthorizationStatus() { + + return authorizationStatus; + } + + public void setAuthorizationStatus(String authorizationStatus) { + + this.authorizationStatus = authorizationStatus; + } + + public long getUpdatedTime() { + + return updatedTime; + } + + public void setUpdatedTime(long updatedTime) { + + this.updatedTime = updatedTime; + } + + public List getMappingResources() { + + return mappingResources; + } + + public void setMappingResources(List mappingResources) { + + this.mappingResources = mappingResources; + } + + /** + * Mapping resource class to hold the mapping information related to the authorization. + */ + public static class MappingResource { + + private String mappingID; + private String authorizationID; + private String accountID; + private String permission; + private String mappingStatus; + + public MappingResource() { + + } + + public MappingResource(String authorizationID, String accountID, String permission, + String mappingStatus) { + this.authorizationID = authorizationID; + this.accountID = accountID; + this.permission = permission; + this.mappingStatus = mappingStatus; + } + + public String getMappingID() { + + return mappingID; + } + + public void setMappingID(String mappingID) { + + this.mappingID = mappingID; + } + + public String getAuthorizationID() { + + return authorizationID; + } + + public void setAuthorizationID(String authorizationID) { + + this.authorizationID = authorizationID; + } + + public String getAccountID() { + + return accountID; + } + + public void setAccountID(String accountID) { + + this.accountID = accountID; + } + + public String getPermission() { + + return permission; + } + + public void setPermission(String permission) { + + this.permission = permission; + } + + public String getMappingStatus() { + + return mappingStatus; + } + + public void setMappingStatus(String mappingStatus) { + + this.mappingStatus = mappingStatus; + } + } + +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPostConsentUpdateRequestDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPostConsentUpdateRequestDTO.java new file mode 100644 index 000000000..69a7466fe --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPostConsentUpdateRequestDTO.java @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model; + +import org.json.JSONObject; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceRequestDTO; + +/** + * ExternalAPIPostConsentUpdateRequestDTO. + */ +public class ExternalAPIPostConsentUpdateRequestDTO { + + private String consentId; + private ExternalAPIBasicConsentResourceRequestDTO consentResource; + private String consentResourcePath; + + + public ExternalAPIPostConsentUpdateRequestDTO(ExternalAPIBasicConsentResourceRequestDTO consentResource, + String resourcePath) { + + this.consentId = consentResource.getId(); + this.consentResource = consentResource; + this.consentResourcePath = resourcePath; + + } + + public String getConsentId() { + return consentId; + } + + public void setConsentId(String consentId) { + this.consentId = consentId; + } + + public ExternalAPIBasicConsentResourceRequestDTO getConsentResource() { + return consentResource; + } + + public void setConsentResource(ExternalAPIBasicConsentResourceRequestDTO consentResource) { + this.consentResource = consentResource; + } + + public String getConsentResourcePath() { + return consentResourcePath; + } + + public void setConsentResourcePath(String consentResourcePath) { + this.consentResourcePath = consentResourcePath; + } + + /** + * Convert the dto to a JSON object with correct consent resource format. + * + * @return JSON object + */ + public JSONObject toJson() { + + JSONObject dtoJson = new JSONObject(this); + JSONObject consentResourceJson = new JSONObject(consentResource); + dtoJson.put("consentResource", consentResourceJson); + return dtoJson; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateRequestDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateRequestDTO.java new file mode 100644 index 000000000..2383d6a23 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateRequestDTO.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model; + +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceRequestDTO; + +import java.util.Map; + +/** + * ExternalAPIPreConsentUpdateRequestDTO. + */ +public class ExternalAPIPreConsentUpdateRequestDTO { + + //Payload can either be a JSONObject or a JSONArray + private ExternalAPIBasicConsentResourceRequestDTO consentResource; + private Object consentUpdateData; + private String consentResourcePath; + private Map requestHeaders; + + public ExternalAPIPreConsentUpdateRequestDTO(ConsentManageData consentManageData, + ExternalAPIBasicConsentResourceRequestDTO consentResource) { + + this.consentResource = consentResource; + this.consentUpdateData = consentManageData.getPayload(); + this.consentResourcePath = consentManageData.getRequestPath(); + this.requestHeaders = consentManageData.getAllowedExtensionHeaders(); + } + + public ExternalAPIBasicConsentResourceRequestDTO getConsentResource() { + return consentResource; + } + + public void setConsentResource(ExternalAPIBasicConsentResourceRequestDTO consentResource) { + this.consentResource = consentResource; + } + + public Object getConsentUpdateData() { + return consentUpdateData; + } + + public void setConsentUpdateData(Object consentUpdateData) { + this.consentUpdateData = consentUpdateData; + } + + public String getConsentResourcePath() { + return consentResourcePath; + } + + public void setConsentResourcePath(String consentResourcePath) { + this.consentResourcePath = consentResourcePath; + } + + public Map getRequestHeaders() { + return requestHeaders; + } + + public void setRequestHeaders(Map requestHeaders) { + this.requestHeaders = requestHeaders; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateResponseDTO.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateResponseDTO.java new file mode 100644 index 000000000..8b1a01784 --- /dev/null +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/model/ExternalAPIPreConsentUpdateResponseDTO.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + *

+ * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model; + +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceResponseDTO; + +/** + * ExternalAPIPreConsentUpdateResponseDTO. + */ +public class ExternalAPIPreConsentUpdateResponseDTO { + + private ExternalAPIBasicConsentResourceResponseDTO consentResource; + + public ExternalAPIBasicConsentResourceResponseDTO getConsentResource() { + return consentResource; + } + + public void setConsentResource(ExternalAPIBasicConsentResourceResponseDTO consentResource) { + this.consentResource = consentResource; + } +} diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageConstants.java index 83405d968..78c0179ac 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageConstants.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageConstants.java @@ -25,4 +25,7 @@ public class ConsentManageConstants { public static final String NO_CONSENT_FOR_CLIENT_ERROR = "No valid consent found for given information"; public static final String CONSENT_TYPE_MISMATCH_ERROR = "Consent Type mismatch"; + public static final String INTERNAL_API_REQUEST_HEADER = "x-wso2-internal-request"; + public static final String CLIENT_ID_MISMATCH_ERROR = "Client Id in the request does not match with the client Id" + + " associated to the consent"; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageUtils.java index 18a41c3f0..6ab112324 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ConsentManageUtils.java @@ -18,11 +18,19 @@ package org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.utils; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONObject; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.constant.FinancialServicesConstants; +import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; +import org.wso2.financial.services.accelerator.consent.mgt.dao.models.AuthorizationResource; +import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentMappingResource; +import org.wso2.financial.services.accelerator.consent.mgt.dao.models.DetailedConsentResource; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ConsentException; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ConsentExtensionConstants; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ConsentExtensionExporter; @@ -30,6 +38,9 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.ResponseStatus; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.ConsentManageValidator; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.builder.ConsentManageBuilder; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ConsentManageAuthorizationUpdateDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ConsentManageData; +import org.wso2.financial.services.accelerator.consent.mgt.service.ConsentCoreService; import java.time.Instant; import java.time.OffsetDateTime; @@ -37,10 +48,13 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Utility class for consent manage module. @@ -50,6 +64,7 @@ public class ConsentManageUtils { private static final Log log = LogFactory.getLog(ConsentManageUtils.class); private static final FinancialServicesConfigParser parser = FinancialServicesConfigParser.getInstance(); private static final Pattern UUID_PATTERN = Pattern.compile(FinancialServicesConstants.UUID_REGEX); + private static final Gson gson = new Gson(); public static boolean isConsentExpirationTimeValid(String expDateVal) { @@ -283,4 +298,211 @@ public static long getValidityTime(Object payload, String consentType) { } } } + + /** + * Method to get whether the request is an internal consent API request. + * + * @param consentManageData consentManageData + * @return boolean whether the request is an internal consent API request + */ + public static boolean isInternalConsentRequest(ConsentManageData consentManageData) { + Map headers = consentManageData.getHeaders(); + if (headers != null && headers.containsKey(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER)) { + return Boolean.parseBoolean(headers.get(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER)); + } + return false; + } + + /** + * Method to update the authorization resource of a consent based on the auth id and + * consent id in the request path and return the updated authorization resource. + * The payload of the /consent/{ConsentId}/authorisations/{authId} API should be in the format + * {@link ConsentManageAuthorizationUpdateDTO}. + * + * @param consentManageData data containing the request path and headers + * @param consentId consent id extracted from the request path + * @param consentCoreService service to retrieve and update the consent and authorization resource + * @return updated AuthorizationResource object + * @throws ConsentException in case of errors in retrieving/updating the consent or authorization resource + */ + public static ConsentManageAuthorizationUpdateDTO updateConsentAuthResource( + ConsentManageData consentManageData, String consentId, ConsentCoreService consentCoreService) + throws ConsentException { + + log.info(String.format("Updating authorization resource for consent: %s", consentId.replaceAll("[\r\n]", ""))); + try { + ConsentManageAuthorizationUpdateDTO authDetails = gson.fromJson(consentManageData.getPayload().toString(), + ConsentManageAuthorizationUpdateDTO.class); + + // Splitting resource path /consent/{ConsentId}/authorisations/{authId} from "/authorisations/" + // to get the auth Id + String authId = extractAuthIdFromPath(consentManageData.getRequestPath()); + + // Creating an AuthorizationResource object with the details from the request payload and the consent id. + // Setting updated time as 0 since the updating time will be set in the DAO layer + AuthorizationResource updatingAuthResource = new AuthorizationResource(consentId, authDetails.getUserID(), + authDetails.getAuthorizationStatus(), authDetails.getAuthorizationType(), 0); + updatingAuthResource.setAuthorizationID(authId); + + DetailedConsentResource detailedConsentResource = consentCoreService.getDetailedConsent(consentId); + if (detailedConsentResource.getAuthorizationResources().isEmpty()) { + log.error(String.format("No authorization resource found for consent id: %s", + consentId.replaceAll("[\r\n]", ""))); + throw new ConsentException(ResponseStatus.BAD_REQUEST, + "No authorization resource found for consent id: " + consentId, + ConsentOperationEnum.CONSENT_UPDATE); + } + + Optional matchingAuthResource = detailedConsentResource.getAuthorizationResources() + .stream() + .filter(resource -> authId.equals(resource.getAuthorizationID())) + .findFirst(); + + if (matchingAuthResource.isPresent()) { + // Update the authorization resource if authId is found + consentCoreService.updateAuthorizationResources(Collections.singletonList(updatingAuthResource)); + consentCoreService.updateAuthorizationUser(authId, updatingAuthResource.getUserID()); + if (log.isDebugEnabled()) { + log.debug(String.format("Authorization resource updated successfully for auth id: %s", + authId.replaceAll("[\r\n]", ""))); + } + AuthorizationResource updatedAuthResource = consentCoreService.getAuthorizationResource(authId); + List createdMappingResources = null; + if (CollectionUtils.isNotEmpty(authDetails.getMappingResources())) { + // Deactivate the current account mappings of the authorization resource if available + deactivateAccountMappings(detailedConsentResource, consentCoreService, authId); + // Create new account mappings based on the request payload + createdMappingResources = createConsentMappings(authDetails.getMappingResources(), + consentCoreService, authId); + } + return constructUpdateResponse(updatedAuthResource, createdMappingResources); + } else { + // Throw an error if authId is not found + String errorMsg = String.format("No matching authorization resource found for consent id: %s and" + + " auth id: %s", consentId.replaceAll("[\r\n]", ""), + authId.replaceAll("[\r\n]", "")); + log.error(errorMsg); + throw new ConsentManagementException(errorMsg); + } + } catch (JsonSyntaxException e) { + log.error("Payload is not in the correct format", e); + throw new ConsentException(ResponseStatus.BAD_REQUEST, "Payload is not in the correct format", + ConsentOperationEnum.CONSENT_UPDATE); + } catch (ConsentManagementException e) { + log.error(String.format("Error occurred while updating consent authorization resource: %s", + e.getMessage().replaceAll("[\r\n]", ""))); + throw new ConsentException(ResponseStatus.BAD_REQUEST, e.getMessage(), ConsentOperationEnum.CONSENT_UPDATE); + } + + } + + /** + * Method to extract auth id from the request path. The request path will be in the format + * /consent/{ConsentId}/authorisations/{authId}. + * + * @param requestPath request path of the API request + * @return auth id extracted from the request path + * @throws ConsentManagementException in case the auth id is not found in the request path + */ + private static String extractAuthIdFromPath(String requestPath) throws ConsentManagementException { + + String[] pathParts = requestPath.split("/authorisations/"); + String authId = pathParts.length > 1 ? pathParts[1] : null; + if (StringUtils.isBlank(authId)) { + log.error("Auth id is missing in the request path."); + throw new ConsentManagementException("Invalid request path: Auth id is missing."); + } + return authId; + } + + /** + * Method to deactivate all the account mappings of a consent. + * + * @param storedResource detailed consent resource containing the account mappings to be deactivated + * @param consentCoreService service to call the method to deactivate the account mappings + * @param authId auth id for which the mappings need to be deactivated (used for logging purposes) + * @throws ConsentManagementException in case of errors in deactivating the account mappings + */ + private static void deactivateAccountMappings(DetailedConsentResource storedResource, + ConsentCoreService consentCoreService, String authId) + throws ConsentManagementException { + + ArrayList mappingIdsToDeactivate = new ArrayList<>(); + storedResource.getConsentMappingResources().stream() + .filter(consentMappingResource -> + consentMappingResource.getAuthorizationID().equals(authId)) + .map(ConsentMappingResource::getMappingID) + .forEach(mappingIdsToDeactivate::add); + if (CollectionUtils.isNotEmpty(mappingIdsToDeactivate)) { + consentCoreService.deactivateAccountMappings(mappingIdsToDeactivate); + if (log.isDebugEnabled()) { + log.debug(String.format("Authorization resource mappings deactivate successfully for auth id: %s", + authId.replaceAll("[\r\n]", ""))); + } + return; + } + if (log.isDebugEnabled()) { + log.debug(String.format("Authorization resource mappings not available for auth id: %s hence skipping" + + " deactivation", authId.replaceAll("[\r\n]", ""))); + } + } + + /** + * Method to create new account mappings for an authorization resource based on the request payload. + * + * @param authMappingResources list of resources in the request payload for which the mappings need to be created + * @param consentCoreService service to call the method to create the account mappings + * @param authId auth id for which the mappings need to be created (used for logging purposes) + * @return list of created ConsentMappingResource objects + * @throws ConsentManagementException in case of errors in creating the account mappings + */ + private static List createConsentMappings( + List authMappingResources, + ConsentCoreService consentCoreService, String authId) + throws ConsentManagementException { + + List updatingMappingResources = authMappingResources.stream() + .map(resource -> new ConsentMappingResource(authId, resource.getAccountID(), + resource.getPermission(), resource.getMappingStatus())) + .collect(Collectors.toList()); + List createdMappings = consentCoreService + .createConsentMappingResources(updatingMappingResources); + if (log.isDebugEnabled()) { + log.debug(String.format("Authorization resource mappings created successfully for auth id: %s", + authId.replaceAll("[\r\n]", ""))); + } + return createdMappings; + } + + /** + * Method to construct the response object of the /consent/{ConsentId}/authorisations/{authId} API after updating + * the authorization resource and creating the account mappings based on the request payload. + * + * @param updatedAuthResource updated AuthorizationResource object + * @param createdMappingResources list of created ConsentMappingResource objects for the authorization resource + * @return ConsentManageAuthorizationUpdateDTO object to be sent in the response of the API + */ + private static ConsentManageAuthorizationUpdateDTO constructUpdateResponse( + AuthorizationResource updatedAuthResource, List createdMappingResources) { + + ConsentManageAuthorizationUpdateDTO authorization = new ConsentManageAuthorizationUpdateDTO( + updatedAuthResource.getConsentID(), updatedAuthResource.getUserID(), + updatedAuthResource.getAuthorizationStatus(), updatedAuthResource.getAuthorizationType(), + updatedAuthResource.getUpdatedTime()); + authorization.setAuthorizationID(updatedAuthResource.getAuthorizationID()); + + if (CollectionUtils.isNotEmpty(createdMappingResources)) { + List resources = createdMappingResources.stream() + .map(mapping -> { + ConsentManageAuthorizationUpdateDTO.MappingResource resource = + new ConsentManageAuthorizationUpdateDTO.MappingResource(mapping.getAuthorizationID(), + mapping.getAccountID(), mapping.getPermission(), mapping.getMappingStatus()); + resource.setMappingID(mapping.getMappingID()); + return resource; + }) + .collect(Collectors.toList()); + authorization.setMappingResources(resources); + } + return authorization; + } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ExternalAPIConsentManageUtils.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ExternalAPIConsentManageUtils.java index 6ccfd0254..458231a19 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ExternalAPIConsentManageUtils.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/utils/ExternalAPIConsentManageUtils.java @@ -33,9 +33,12 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIConsentRevokeResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIModifiedResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentGenerateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentUpdateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadResponseDTO; @@ -168,6 +171,39 @@ public static ExternalAPIModifiedResponseDTO callExternalServiceForFileRetrieval return gson.fromJson(responseJson.toString(), ExternalAPIModifiedResponseDTO.class); } + /** + * Method to call external service for pre consent update. + * + * @param requestDTO - Request DTO + * @return - Response DTO + * @throws FinancialServicesException - If there is an error while calling the external service + */ + public static ExternalAPIPreConsentUpdateResponseDTO callExternalService( + ExternalAPIPreConsentUpdateRequestDTO requestDTO) throws FinancialServicesException { + + log.debug("Calling external service for pre consent update"); + JSONObject requestJson = new JSONObject(requestDTO); + JSONObject responseJson = callExternalService(requestJson, ServiceExtensionTypeEnum.PRE_PROCESS_CONSENT_UPDATE); + return gson.fromJson(responseJson.toString(), ExternalAPIPreConsentUpdateResponseDTO.class); + } + + /** + * Method to call external service for post consent update. + * + * @param requestDTO - Request DTO + * @return - Response DTO + * @throws FinancialServicesException - If there is an error while calling the external service + */ + public static ExternalAPIModifiedResponseDTO callExternalService( + ExternalAPIPostConsentUpdateRequestDTO requestDTO) throws FinancialServicesException { + + log.debug("Calling external service for post consent update"); + JSONObject requestJson = requestDTO.toJson(); + JSONObject responseJson = callExternalService(requestJson, + ServiceExtensionTypeEnum.ENRICH_CONSENT_UPDATE_RESPONSE); + return gson.fromJson(responseJson.toString(), ExternalAPIModifiedResponseDTO.class); + } + /** * Method to call external service using a Json. * diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/DefaultConsentManageHandlerTest.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/DefaultConsentManageHandlerTest.java index 54ded133f..6e8012b89 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/DefaultConsentManageHandlerTest.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/manage/DefaultConsentManageHandlerTest.java @@ -22,10 +22,10 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; import org.wso2.financial.services.accelerator.common.config.FinancialServicesConfigParser; import org.wso2.financial.services.accelerator.common.constant.FinancialServicesConstants; import org.wso2.financial.services.accelerator.common.exception.ConsentManagementException; @@ -45,11 +45,15 @@ import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIConsentRevokeResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIModifiedResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentGenerateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostConsentUpdateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPostFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateRequestDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadRequestDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreFileUploadResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.utils.ConsentManageConstants; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.utils.ExternalAPIConsentManageUtils; import org.wso2.financial.services.accelerator.consent.mgt.extensions.util.DataProviders; import org.wso2.financial.services.accelerator.consent.mgt.extensions.util.TestConstants; @@ -57,6 +61,7 @@ import org.wso2.financial.services.accelerator.consent.mgt.service.impl.ConsentCoreServiceImpl; import java.lang.reflect.Field; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -76,8 +81,6 @@ public class DefaultConsentManageHandlerTest { DefaultConsentManageHandler defaultConsentManageHandler; DefaultConsentManageHandler externalServiceConsentManageHandler; @Mock - ConsentManageData consentManageDataMock; - @Mock ConsentCoreServiceImpl consentCoreServiceMock; private static Map headers; private static Map configs; @@ -86,9 +89,8 @@ public class DefaultConsentManageHandlerTest { private MockedStatic financialServicesUtilsMockedStatic; @BeforeClass - public void initTest() throws ConsentManagementException, IdentityOAuth2Exception { + public void initTest() throws ConsentManagementException { - consentManageDataMock = mock(ConsentManageData.class); consentCoreServiceMock = mock(ConsentCoreServiceImpl.class); consentExtensionsDataHolder = mockStatic(ConsentExtensionsDataHolder.class); configParser = mockStatic(FinancialServicesConfigParser.class); @@ -110,6 +112,14 @@ public void initTest() throws ConsentManagementException, IdentityOAuth2Exceptio .getConsentAttributes(anyString()); doReturn(true).when(consentCoreServiceMock).revokeConsent(anyString(), any(), any(), anyBoolean()); + doReturn(TestUtil.getSampleConsentResource(TestConstants.AWAITING_AUTH_STATUS)).when(consentCoreServiceMock) + .updateConsent(any()); + doReturn(true).when(consentCoreServiceMock).updateAuthorizationResources(any()); + doReturn(TestUtil.getSampleAuthorizationResource(TestConstants.SAMPLE_CONSENT_ID, + TestConstants.SAMPLE_AUTH_ID)).when(consentCoreServiceMock).getAuthorizationResource(any()); + doReturn(true).when(consentCoreServiceMock).deactivateAccountMappings(any()); + doReturn(Collections.singletonList(TestUtil.getSampleConsentMappingResource(TestConstants.SAMPLE_AUTH_ID))) + .when(consentCoreServiceMock).createConsentMappingResources(any()); configs = new HashMap(); configs.put(FinancialServicesConstants.MAX_INSTRUCTED_AMOUNT, "1000"); @@ -137,6 +147,8 @@ public void initTest() throws ConsentManagementException, IdentityOAuth2Exceptio setPrivateBoolean(externalServiceConsentManageHandler, "isExternalPreFileUploadEnabled", true); setPrivateBoolean(externalServiceConsentManageHandler, "isExternalPostFileUploadEnabled", true); setPrivateBoolean(externalServiceConsentManageHandler, "isExternalPreFileRetrievalEnabled", true); + setPrivateBoolean(externalServiceConsentManageHandler, "isExternalPreConsentUpdateEnabled", true); + setPrivateBoolean(externalServiceConsentManageHandler, "isExternalPostConsentUpdateEnabled", true); financialServicesUtilsMockedStatic = Mockito.mockStatic(FinancialServicesUtils.class); financialServicesUtilsMockedStatic.when(() -> FinancialServicesUtils.isValidClientId(anyString())) @@ -151,79 +163,117 @@ public void tearDown() { financialServicesUtilsMockedStatic.close(); } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandlePostWithoutClientId() { - setConsentManageBuilder(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(null).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandlePostWithInvalidHeaders() { - setConsentManageBuilderForErrorScenario(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilderForErrorScenario(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid headers"); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandlePostWithoutRequestPath() { - setConsentManageBuilder(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getRequestPath(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource Path Not Found"); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandlePostWithInvalidRequestPath() { - setConsentManageBuilder(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(headers).when(consentManageDataMock).getHeaders(); - - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(headers).when(consentManageDataMock).getHeaders(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid request path found."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandlePostWithInvalidPayload() { - setConsentManageBuilder(); - doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) - .getPayload(); - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(ConsentExtensionConstants.ACCOUNT_CONSENT_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) + .getPayload(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Payload is not in the correct format"); + } } - @Test(expectedExceptions = ConsentException.class, dataProvider = "AccountInitiationDataProvider", - dataProviderClass = DataProviders.class) - public void testHandlePostWithInvalidInitiation(String initiation) { + @Test(dataProvider = "AccountInitiationDataProvider", dataProviderClass = DataProviders.class) + public void testHandlePostWithInvalidInitiation(String initiation, String errorMessage) { - setConsentManageBuilder(); - JSONObject payload = new JSONObject(initiation); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(ConsentExtensionConstants.ACCOUNT_CONSENT_PATH).when(consentManageDataMock) - .getRequestPath(); - - defaultConsentManageHandler.handlePost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(initiation); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(ConsentExtensionConstants.ACCOUNT_CONSENT_PATH).when(consentManageDataMock) + .getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handlePost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + errorMessage); + } } @Test public void testHandlePostForAccounts() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); doReturn(headers).when(consentManageDataMock).getHeaders(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); doReturn(ConsentExtensionConstants.ACCOUNT_CONSENT_PATH).when(consentManageDataMock) @@ -232,12 +282,18 @@ public void testHandlePostForAccounts() { doReturn(payload).when(consentManageDataMock).getPayload(); defaultConsentManageHandler.handlePost(consentManageDataMock); + + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.CREATED); } @Test public void testHandlePostForAccountsWithExtensionEnabled() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock).getRequestPath(); @@ -259,6 +315,10 @@ public void testHandlePostForAccountsWithExtensionEnabled() { .thenReturn(postConsentGenerateResponseDTO); externalServiceConsentManageHandler.handlePost(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(3)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.CREATED); } } @@ -266,6 +326,7 @@ public void testHandlePostForAccountsWithExtensionEnabled() { public void testHandlePostForCOF() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); doReturn(headers).when(consentManageDataMock).getHeaders(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); doReturn(ConsentExtensionConstants.COF_CONSENT_PATH).when(consentManageDataMock) @@ -274,12 +335,17 @@ public void testHandlePostForCOF() { doReturn(payload).when(consentManageDataMock).getPayload(); defaultConsentManageHandler.handlePost(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.CREATED); } @Test public void testHandlePostForPayments() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); doReturn(headers).when(consentManageDataMock).getHeaders(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); doReturn(ConsentExtensionConstants.PAYMENT_CONSENT_PATH).when(consentManageDataMock) @@ -288,68 +354,101 @@ public void testHandlePostForPayments() { doReturn(payload).when(consentManageDataMock).getPayload(); defaultConsentManageHandler.handlePost(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.CREATED); } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFilePostWithoutClientId() { - setConsentManageBuilder(); - consentManageDataMock = mock(ConsentManageData.class); - doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); - defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); + + defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFilePostWithoutRequestPath() { - setConsentManageBuilder(); - consentManageDataMock = mock(ConsentManageData.class); - doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource path not found in the request"); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFilePostWithInvalidRequestPath() { - setConsentManageBuilder(); - consentManageDataMock = mock(ConsentManageData.class); - doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(headers).when(consentManageDataMock).getHeaders(); - - defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(headers).when(consentManageDataMock).getHeaders(); + + defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFilePostWithNullPayload() { - setConsentManageBuilder(); - consentManageDataMock = mock(ConsentManageData.class); - doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(null).when(consentManageDataMock).getPayload(); - defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getPayload(); + + defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid file content found in the request."); + } } @Test public void testHandlePostForFilePayments() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); doReturn(headers).when(consentManageDataMock).getHeaders(); doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); defaultConsentManageHandler.handleFileUploadPost(consentManageDataMock); + + Mockito.verify(consentManageDataMock).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(2)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } @Test public void testHandlePostForFilePaymentsWithExtensionEnabled() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); doReturn(TestConstants.SAMPLE_CONSENT_FILE).when(consentManageDataMock).getPayload(); doReturn(TestConstants.PAYMENTS_FILE_UPLOAD_PATH).when(consentManageDataMock).getRequestPath(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); @@ -362,82 +461,147 @@ public void testHandlePostForFilePaymentsWithExtensionEnabled() { ExternalAPIPreFileUploadRequestDTO.class))).thenReturn(preFileUploadResponseDTO); mockedStatic.when(() -> ExternalAPIConsentManageUtils.callExternalService(any( ExternalAPIPostFileUploadRequestDTO.class))).thenReturn(modifiedResponseDTO); + externalServiceConsentManageHandler.handleFileUploadPost(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(2)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleGetWithoutClientId() { - setConsentManageBuilder(); - doReturn(null).when(consentManageDataMock).getClientId(); - defaultConsentManageHandler.handleGet(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(null).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleGetWithInvalidHeaders() { - setConsentManageBuilderForErrorScenario(); + try { + setConsentManageBuilderForErrorScenario(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid headers"); + } + } - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + @Test + public void testHandleGetWithoutRequestPath() { - defaultConsentManageHandler.handleGet(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getRequestPath(); + + defaultConsentManageHandler.handleGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource path not found in the request"); + } } - @Test(expectedExceptions = ConsentException.class) - public void testHandleGetWithoutRequestPath() { + @Test + public void testHandleGetWithInvalidRequestPath() { - setConsentManageBuilder(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(null).when(consentManageDataMock).getRequestPath(); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(headers).when(consentManageDataMock).getHeaders(); + + defaultConsentManageHandler.handleGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } + } - defaultConsentManageHandler.handleGet(consentManageDataMock); + @Test + public void testHandleGetWithInvalidConsentId() { + + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.REQUEST_PATH_WITH_INVALID_CONSENT_ID).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) + .getPayload(); + + defaultConsentManageHandler.handleGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } } - @Test(expectedExceptions = ConsentException.class) - public void testHandleGetWithInvalidRequestPath() { + @Test + public void testHandleGetForInternalRequests() { setConsentManageBuilder(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock) + .getRequestPath(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + Map headers = new HashMap<>(); + headers.put(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER, "true"); doReturn(headers).when(consentManageDataMock).getHeaders(); defaultConsentManageHandler.handleGet(consentManageDataMock); - } - - @Test(expectedExceptions = ConsentException.class) - public void testHandleGetWithInvalidConsentId() { - setConsentManageBuilder(); - doReturn(TestConstants.REQUEST_PATH_WITH_INVALID_CONSENT_ID).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) - .getPayload(); - defaultConsentManageHandler.handleGet(consentManageDataMock); + Mockito.verify(consentManageDataMock).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(2)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } @Test public void testHandleGet() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock) .getRequestPath(); doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(new HashMap<>()).when(consentManageDataMock).getHeaders(); defaultConsentManageHandler.handleGet(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } @Test public void testHandleGetWithExtensionEnabled() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock).getRequestPath(); @@ -451,31 +615,49 @@ public void testHandleGetWithExtensionEnabled() { ).thenReturn(mockResponse); externalServiceConsentManageHandler.handleGet(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFileGetWithoutClientId() { - setConsentManageBuilder(); - doReturn(null).when(consentManageDataMock).getClientId(); - defaultConsentManageHandler.handleFileGet(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(null).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleFileGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleFileGetWithoutRequestPath() { - setConsentManageBuilder(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(null).when(consentManageDataMock).getRequestPath(); - - defaultConsentManageHandler.handleFileGet(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getRequestPath(); + + defaultConsentManageHandler.handleFileGet(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource path not found in the request"); + } } @Test public void testHandleFileGet() throws ConsentManagementException { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock) @@ -485,12 +667,17 @@ public void testHandleFileGet() throws ConsentManagementException { .getConsentFile(anyString()); defaultConsentManageHandler.handleFileGet(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } @Test public void testHandleFileGetWithExtensionEnabled() throws ConsentManagementException { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock).getRequestPath(); @@ -506,66 +693,103 @@ public void testHandleFileGetWithExtensionEnabled() throws ConsentManagementExce ).thenReturn(mockResponse); externalServiceConsentManageHandler.handleFileGet(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleDeleteWithoutClientId() { - setConsentManageBuilder(); - doReturn(null).when(consentManageDataMock).getClientId(); - defaultConsentManageHandler.handleDelete(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(null).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleDelete(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleDeleteWithInvalidHeaders() { - setConsentManageBuilderForErrorScenario(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - - defaultConsentManageHandler.handleDelete(consentManageDataMock); + try { + setConsentManageBuilderForErrorScenario(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + defaultConsentManageHandler.handleDelete(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid headers"); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleDeleteWithoutRequestPath() { - setConsentManageBuilder(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(null).when(consentManageDataMock).getRequestPath(); - - defaultConsentManageHandler.handleDelete(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getRequestPath(); + + defaultConsentManageHandler.handleDelete(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource Path Not Found"); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleDeleteWithInvalidRequestPath() { - setConsentManageBuilder(); - JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); - doReturn(payload).when(consentManageDataMock).getPayload(); - doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(headers).when(consentManageDataMock).getHeaders(); - - defaultConsentManageHandler.handleDelete(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(headers).when(consentManageDataMock).getHeaders(); + + defaultConsentManageHandler.handleDelete(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } } - @Test(expectedExceptions = ConsentException.class) + @Test public void testHandleDeleteWithInvalidConsentId() { - setConsentManageBuilder(); - doReturn(TestConstants.REQUEST_PATH_WITH_INVALID_CONSENT_ID).when(consentManageDataMock).getRequestPath(); - doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); - doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) - .getPayload(); - defaultConsentManageHandler.handleDelete(consentManageDataMock); + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.REQUEST_PATH_WITH_INVALID_CONSENT_ID).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) + .getPayload(); + + defaultConsentManageHandler.handleDelete(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } } @Test public void testHandleDelete() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock) @@ -573,12 +797,17 @@ public void testHandleDelete() { doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); defaultConsentManageHandler.handleDelete(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(2)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.NO_CONTENT); } @Test public void testHandleDeleteWithExtensionEnabled() { setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); JSONObject payload = new JSONObject(); doReturn(payload).when(consentManageDataMock).getPayload(); doReturn(TestConstants.ACCOUNT_CONSENT_GET_PATH).when(consentManageDataMock).getRequestPath(); @@ -593,19 +822,224 @@ public void testHandleDeleteWithExtensionEnabled() { ).thenReturn(mockResponse); externalServiceConsentManageHandler.handleDelete(consentManageDataMock); + + Mockito.verify(consentManageDataMock).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.NO_CONTENT); + } + } + + @Test + public void testHandlePutWithoutClientId() { + + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(null).when(consentManageDataMock).getClientId(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Client ID missing in the request."); + } + } + + @Test + public void testHandlePutWithoutRequestPath() { + + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(null).when(consentManageDataMock).getRequestPath(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Resource Path Not Found"); + } + } + + @Test + public void testHandlePutWithInvalidConsentId() { + + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + setConsentManageBuilder(); + doReturn(TestConstants.INVALID_REQUEST_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) + .getPayload(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid Request Path. Valid consent id not found."); + } + } + + @Test + public void testHandlePutWithUnMatchingClientID() { + + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn("test123").when(consentManageDataMock).getClientId(); + doReturn(TestConstants.ACCOUNT_CONSENT_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + ConsentManageConstants.CLIENT_ID_MISMATCH_ERROR); + } + } + + @Test + public void testHandlePutWithInvalidRequestPath() { + + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.AUTHORISATION_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(headers).when(consentManageDataMock).getHeaders(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Invalid request path found."); } } - @Test(expectedExceptions = ConsentException.class) + @Test + public void testHandlePutWithInvalidRequestPayload() { + + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.ACCOUNT_CONSENT_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + doReturn(TestConstants.INVALID_INITIATION_OBJECT).when(consentManageDataMock) + .getPayload(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Payload is not in the correct format"); + } + } + + @Test public void testHandlePut() { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + doReturn(TestConstants.ACCOUNT_CONSENT_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(3)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(4)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); } - @Test(expectedExceptions = ConsentException.class) + @Test + public void testHandlePutWithExtensionEnabled() { + + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + JSONObject payload = new JSONObject(TestConstants.VALID_INITIATION); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(TestConstants.ACCOUNT_CONSENT_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + + try (MockedStatic mockedStatic = mockStatic( + ExternalAPIConsentManageUtils.class)) { + ExternalAPIPreConsentUpdateResponseDTO preConsentGenerateResponseDTO = + TestUtil.getSampleExternalAPIPreConsentUpdateResponseDTO(); + ExternalAPIModifiedResponseDTO postConsentGenerateResponseDTO = + TestUtil.getSampleExternalAPIModifiedResponseDTO(); + + mockedStatic.when(() -> ExternalAPIConsentManageUtils.callExternalService(any( + ExternalAPIPreConsentUpdateRequestDTO.class))).thenReturn(preConsentGenerateResponseDTO); + + mockedStatic.when(() -> + ExternalAPIConsentManageUtils.callExternalService( + any(ExternalAPIPostConsentUpdateRequestDTO.class))) + .thenReturn(postConsentGenerateResponseDTO); + + externalServiceConsentManageHandler.handlePut(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(4)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(3)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); + } + } + + @Test + public void testHandlePutAuthorisationWithoutAuthID() { + + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + Map internalHeaders = Map.of(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER, "true"); + doReturn(TestConstants.AUTHORISATION_UPDATE_PATH_WITHOUT_AUTH_ID).when(consentManageDataMock) + .getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + JSONObject payload = new JSONObject(TestConstants.CONSENT_AUTHORISATION_UPDATE_PAYLOAD); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(internalHeaders).when(consentManageDataMock).getHeaders(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + doReturn(headers).when(consentManageDataMock).getHeaders(); + } catch (ConsentException e) { + Assert.assertTrue(e.getPayload().getJSONObject("error").getString("description") + .contains("Invalid request path: Auth id is missing.")); + } + } + + @Test + public void testHandlePutAuthorisationWithInvalidAuthID() { + + try { + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + Map internalHeaders = Map.of(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER, "true"); + doReturn(TestConstants.AUTHORISATION_UPDATE_PATH_WITH_INVALID_AUTH_ID).when(consentManageDataMock) + .getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + JSONObject payload = new JSONObject(TestConstants.CONSENT_AUTHORISATION_UPDATE_PAYLOAD); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(internalHeaders).when(consentManageDataMock).getHeaders(); + defaultConsentManageHandler.handlePut(consentManageDataMock); + doReturn(headers).when(consentManageDataMock).getHeaders(); + } catch (ConsentException e) { + Assert.assertTrue(e.getPayload().getJSONObject("error").getString("description") + .contains("No matching authorization resource found for consent id")); + } + } + + @Test + public void testHandlePutAuthorisation() { + + setConsentManageBuilder(); + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + Map internalHeaders = Map.of(ConsentManageConstants.INTERNAL_API_REQUEST_HEADER, "true"); + doReturn(TestConstants.AUTHORISATION_UPDATE_PATH).when(consentManageDataMock).getRequestPath(); + doReturn(TestConstants.SAMPLE_CLIENT_ID).when(consentManageDataMock).getClientId(); + JSONObject payload = new JSONObject(TestConstants.CONSENT_AUTHORISATION_UPDATE_PAYLOAD); + doReturn(payload).when(consentManageDataMock).getPayload(); + doReturn(internalHeaders).when(consentManageDataMock).getHeaders(); + + defaultConsentManageHandler.handlePut(consentManageDataMock); + + Mockito.verify(consentManageDataMock, Mockito.times(4)).getRequestPath(); + Mockito.verify(consentManageDataMock, Mockito.times(2)).getClientId(); + Mockito.verify(consentManageDataMock).setResponseStatus(ResponseStatus.OK); + } + + @Test public void testHandlePatch() { - defaultConsentManageHandler.handlePatch(consentManageDataMock); + try { + ConsentManageData consentManageDataMock = mock(ConsentManageData.class); + defaultConsentManageHandler.handlePatch(consentManageDataMock); + } catch (ConsentException e) { + Assert.assertEquals(e.getPayload().getJSONObject("error").getString("description"), + "Method PATCH is not supported"); + } } private static void setConsentManageBuilder() { diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/DataProviders.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/DataProviders.java index b8cc89d75..581e09e10 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/DataProviders.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/DataProviders.java @@ -29,18 +29,18 @@ public class DataProviders { Object[][] getAccountInitiationDataProvider() { return new Object[][]{ - {"{}"}, - {INITIATION_WITHOUT_ACCOUNT_PERMISSION}, - {INITIATION_WITH_UNACCEPTABLE_PERMISSION}, - {INITIATION_WITHOUT_EXPIRATION_DATES}, - {INITIATION_NON_STRING_EXPIRATION_DATES}, - {INITIATION_WITH_INVALID_EXPIRATION_DATES}, - {INITIATION_WITH_PAST_EXPIRATION_DATES}, - {INITIATION_NON_STRING_TRANS_FROM_DATES}, - {INITIATION_WITH_INVALID_TRANS_FROM_DATES}, - {INITIATION_NON_STRING_TRANS_TO_DATES}, - {INITIATION_WITH_INVALID_TRANS_TO_DATES}, - {INITIATION_WITH_PAST_TRANS_TO_DATES} + {"{}", "Invalid request payload"}, + {INITIATION_WITHOUT_ACCOUNT_PERMISSION, "Permissions are invalid"}, + {INITIATION_WITH_UNACCEPTABLE_PERMISSION, "Permissions are invalid"}, + {INITIATION_WITHOUT_EXPIRATION_DATES, "ExpirationDateTime is invalid"}, + {INITIATION_NON_STRING_EXPIRATION_DATES, "ExpirationDateTime is invalid"}, + {INITIATION_WITH_INVALID_EXPIRATION_DATES, "ExpirationDateTime is invalid"}, + {INITIATION_WITH_PAST_EXPIRATION_DATES, "ExpirationDateTime should be a future date"}, + {INITIATION_NON_STRING_TRANS_FROM_DATES, "TransactionFromDateTime is invalid"}, + {INITIATION_WITH_INVALID_TRANS_FROM_DATES, "TransactionFromDateTime is invalid"}, + {INITIATION_NON_STRING_TRANS_TO_DATES, "TransactionToDateTime is invalid"}, + {INITIATION_WITH_INVALID_TRANS_TO_DATES, "TransactionToDateTime is invalid"}, + {INITIATION_WITH_PAST_TRANS_TO_DATES, "TransactionToDateTime should be after TransactionFromDateTime"} }; } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestConstants.java index f5d431269..680807bc1 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestConstants.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestConstants.java @@ -39,6 +39,7 @@ public class TestConstants { public static final String INVALID_REQUEST_PATH = "accounts"; public static final String REQUEST_PATH_WITH_INVALID_CONSENT_ID = "accounts/1234"; public static final String ACCOUNT_CONSENT_GET_PATH = "account-access-consents/" + SAMPLE_CONSENT_ID; + public static final String ACCOUNT_CONSENT_UPDATE_PATH = "account-access-consents/" + SAMPLE_CONSENT_ID; public static final String PAYMENTS_FILE_UPLOAD_PATH = "fileUpload/" + SAMPLE_CONSENT_ID; public static final String INVALID_INITIATION_OBJECT = "Invalid Object"; public static final int SAMPLE_CONSENT_FREQUENCY = 1; @@ -70,6 +71,12 @@ public class TestConstants { public static final String SAMPLE_PREVIOUS_STATUS = "Received"; public static final String SAMPLE_CONSENT_FILE = "sample file content"; public static final long CREATED_TIME = Instant.now().toEpochMilli(); + public static final String AUTHORISATION_UPDATE_PATH = "/consent/" + SAMPLE_CONSENT_ID + "/authorisations/" + + SAMPLE_AUTH_ID; + public static final String AUTHORISATION_UPDATE_PATH_WITHOUT_AUTH_ID = "/consent/" + SAMPLE_CONSENT_ID + + "/authorisations/"; + public static final String AUTHORISATION_UPDATE_PATH_WITH_INVALID_AUTH_ID = "/consent/" + SAMPLE_CONSENT_ID + + "/authorisations/" + SAMPLE_CONSENT_ID; public static final Map SAMPLE_CONSENT_ATTRIBUTES_MAP = new HashMap() { { put("x-request-id", UUID.randomUUID().toString()); @@ -617,4 +624,17 @@ public class TestConstants { " ]\n" + "}"; + public static final String CONSENT_AUTHORISATION_UPDATE_PAYLOAD = "{\n" + + " \"userID\": \"test@wso2.com\",\n" + + " \"authorizationType\": \"authorisation\",\n" + + " \"authorizationStatus\": \"Created\",\n" + + " \"mappingResources\": [\n" + + " {\n" + + " \"accountID\": \"1234\",\n" + + " \"permission\": \"auth\",\n" + + " \"mappingStatus\": \"active\"\n" + + " }\n" + + " ]\n" + + " }"; + } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestUtil.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestUtil.java index 7c542a802..b0f7995a8 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestUtil.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.extensions/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/extensions/util/TestUtil.java @@ -18,6 +18,7 @@ package org.wso2.financial.services.accelerator.consent.mgt.extensions.util; +import com.google.gson.JsonParser; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.AuthorizationResource; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentAttributes; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentFile; @@ -26,8 +27,11 @@ import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentResource; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.ConsentStatusAuditRecord; import org.wso2.financial.services.accelerator.consent.mgt.dao.models.DetailedConsentResource; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIBasicConsentResourceResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.common.model.ExternalAPIConsentResourceResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIModifiedResponseDTO; import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentGenerateResponseDTO; +import org.wso2.financial.services.accelerator.consent.mgt.extensions.manage.model.ExternalAPIPreConsentUpdateResponseDTO; import java.util.ArrayList; import java.util.Collections; @@ -257,4 +261,37 @@ public static ExternalAPIPreConsentGenerateResponseDTO getSampleExternalAPIPreCo responseDTO.setConsentResource(resource); return responseDTO; } + + public static ExternalAPIPreConsentUpdateResponseDTO getSampleExternalAPIPreConsentUpdateResponseDTO() { + + ExternalAPIPreConsentUpdateResponseDTO responseDTO = new ExternalAPIPreConsentUpdateResponseDTO(); + + ExternalAPIBasicConsentResourceResponseDTO resource = new ExternalAPIBasicConsentResourceResponseDTO(); + resource.setType("ACCOUNT"); + resource.setStatus("AWAITING_AUTHORIZATION"); + resource.setFrequency(3); + resource.setValidityTime(3600L); + resource.setRecurringIndicator(true); + + // Sample receipt + Map receipt = new HashMap<>(); + receipt.put("confirmationCode", "ABC123"); + resource.setReceipt(receipt); + + // Sample attributes + Map attributes = new HashMap<>(); + attributes.put("channel", "MOBILE"); + resource.setAttributes(attributes); + + responseDTO.setConsentResource(resource); + return responseDTO; + } + + public static ExternalAPIModifiedResponseDTO getSampleExternalAPIModifiedResponseDTO() { + + ExternalAPIModifiedResponseDTO responseDTO = new ExternalAPIModifiedResponseDTO(); + responseDTO.setModifiedResponse(JsonParser.parseString("{}")); + responseDTO.setResponseHeaders(new HashMap<>()); + return responseDTO; + } } diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/ConsentCoreService.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/ConsentCoreService.java index 6496d28b1..bef704615 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/ConsentCoreService.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/ConsentCoreService.java @@ -120,6 +120,17 @@ ConsentResource getConsent(String consentID, boolean withConsentAttributes) DetailedConsentResource storeDetailedConsentResource(DetailedConsentResource detailedConsentResource) throws ConsentManagementException; + /** + * This method is used to update an existing consent (excluding client ID and CreatedTime) + * based on the given consent. + * An audit record will be created. + * + * @param consentResource the consent resource with updated values and new associations + * @return the updated consent resource + * @throws ConsentManagementException thrown if an error occurs during the operation + */ + ConsentResource updateConsent(ConsentResource consentResource) throws ConsentManagementException; + /** * This method is used to update an existing consent (excluding client ID and CreatedTime) and create * new authorization and mapping records based on the given detailed consent. diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/constants/ConsentCoreServiceConstants.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/constants/ConsentCoreServiceConstants.java index 4a4ce7d84..9ed195c57 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/constants/ConsentCoreServiceConstants.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/constants/ConsentCoreServiceConstants.java @@ -45,6 +45,7 @@ public class ConsentCoreServiceConstants { public static final String USER_ACCOUNTS_BINDING_REASON = "Bind user accounts to consent"; public static final String CONSENT_REVOKE_REASON = "Revoke the consent"; public static final String CONSENT_REAUTHORIZE_REASON = "Reauthorize consent"; + public static final String BASIC_CONSENT_UPDATE_REASON = "Update Basic consent"; public static final String CONSENT_AMEND_REASON = "Amend consent"; public static final String DEFAULT_PERMISSION_VALUE = "n/a"; public static final String ADDITIONAL_AUTHORIZATION_RESOURCES = "AdditionalAuthorizationResources"; diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentCoreServiceImpl.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentCoreServiceImpl.java index 1e28ca041..e471a809f 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentCoreServiceImpl.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/main/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentCoreServiceImpl.java @@ -280,6 +280,49 @@ public DetailedConsentResource storeDetailedConsentResource(DetailedConsentResou } } + @Override + public ConsentResource updateConsent(ConsentResource consentResource) throws ConsentManagementException { + + if (consentResource == null || StringUtils.isBlank(consentResource.getConsentID())) { + log.error("Consent or consentId is missing"); + throw new ConsentManagementException("Consent or consentId is missing"); + } + + Connection connection = DatabaseUtils.getDBConnection(); + try { + ConsentCoreDAO consentCoreDAO = ConsentStoreInitializer.getInitializedConsentCoreDAOImpl(); + + try { + ConsentResource previousConsent = getConsent(consentResource.getConsentID(), false); + /* Update the base consent using updated values from ConsentResource. + Immutable parameters are ignored in the update (i.e. clientId, createdTime) at DAO level.*/ + consentCoreDAO.updateConsentResource(connection, consentResource); + + // Add audit record + HashMap consentDataMap = new HashMap<>(); + consentDataMap.put(ConsentCoreServiceConstants.CONSENT_RESOURCE, consentResource); + + ConsentCoreServiceUtil.postStateChange(connection, consentCoreDAO, consentResource.getConsentID(), + null, consentResource.getCurrentStatus(), previousConsent.getCurrentStatus(), + ConsentCoreServiceConstants.BASIC_CONSENT_UPDATE_REASON, consentResource.getClientID(), + consentDataMap + ); + + DatabaseUtils.commitTransaction(connection); + log.debug("Updated consent successfully."); + return consentResource; + + } catch (ConsentDataInsertionException | ConsentDataUpdationException e) { + log.error("Error during updating consent, rolling back", e); + DatabaseUtils.rollbackTransaction(connection); + throw new ConsentManagementException("Failed to update consent and create related records", e); + } + } finally { + log.debug(ConsentCoreServiceConstants.DATABASE_CONNECTION_CLOSE_LOG_MSG); + DatabaseUtils.closeConnection(connection); + } + } + @Override public DetailedConsentResource updateConsentAndCreateAuthResources(DetailedConsentResource detailedConsentResource, String primaryUserId) throws ConsentManagementException { diff --git a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentMgtCoreServiceTests.java b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentMgtCoreServiceTests.java index 781425cea..d9d72c39a 100644 --- a/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentMgtCoreServiceTests.java +++ b/financial-services-accelerator/components/org.wso2.financial.services.accelerator.consent.mgt.service/src/test/java/org/wso2/financial/services/accelerator/consent/mgt/service/impl/ConsentMgtCoreServiceTests.java @@ -536,6 +536,40 @@ public void testStoreDetailedConsentResourceWithMissingConsentID() throws Consen consentCoreServiceImpl.storeDetailedConsentResource(detailedConsentResource); } + @Test + public void testUpdateConsent() throws ConsentManagementException, + ConsentDataRetrievalException, ConsentDataUpdationException { + + doReturn(ConsentMgtServiceTestData.getSampleStoredConsentResource()).when(mockedConsentCoreDAO) + .getConsentResource(any(), anyString()); + doNothing().when(mockedConsentCoreDAO).updateConsentResource(any(), any()); + + ConsentResource result = consentCoreServiceImpl.updateConsent( + ConsentMgtServiceTestData.getSampleStoredConsentResource()); + Assert.assertNotNull(result); + } + + @Test(expectedExceptions = ConsentManagementException.class) + public void testUpdateConsentWithoutConsentId() throws ConsentManagementException { + + ConsentResource consentResource = new ConsentResource(); + consentResource.setConsentID(null); + + consentCoreServiceImpl.updateConsent(consentResource); + } + + @Test(expectedExceptions = ConsentManagementException.class) + public void testUpdateConsentWithConsentUpdateError() throws ConsentManagementException, + ConsentDataUpdationException, ConsentDataRetrievalException { + + doReturn(ConsentMgtServiceTestData.getSampleStoredConsentResource()).when(mockedConsentCoreDAO) + .getConsentResource(any(), anyString()); + doThrow(ConsentDataUpdationException.class).when(mockedConsentCoreDAO).updateConsentResource(any(), any()); + + ConsentResource result = consentCoreServiceImpl.updateConsent( + ConsentMgtServiceTestData.getSampleStoredConsentResource()); + } + @Test public void testUpdateConsentAndCreateAuthResources() throws ConsentManagementException, ConsentDataRetrievalException, ConsentDataInsertionException {