From 904009c49fd3540ddd58a50981ec2e1e9fc4f0f5 Mon Sep 17 00:00:00 2001 From: hongwei Date: Wed, 28 May 2025 14:28:44 +0200 Subject: [PATCH] feature/add revoke consent endpoint for user --- .../scala/code/api/v4_0_0/OBPAPI4_0_0.scala | 5 +- .../scala/code/api/v5_1_0/APIMethods510.scala | 64 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala b/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala index 5121f0dd1..0d8c671fa 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/OBPAPI4_0_0.scala @@ -28,7 +28,7 @@ package code.api.v4_0_0 import code.api.OBPRestHelper import code.api.util.APIUtil.{OBPEndpoint, getAllowedEndpoints} -import code.api.util.{APIUtil, VersionedOBPApis} +import code.api.util.VersionedOBPApis import code.api.v1_3_0.APIMethods130 import code.api.v1_4_0.APIMethods140 import code.api.v2_0_0.APIMethods200 @@ -39,7 +39,7 @@ import code.api.v3_0_0.custom.CustomAPIMethods300 import code.api.v3_1_0.{APIMethods310, OBPAPI3_1_0} import code.util.Helper.MdcLoggable import com.github.dwickern.macros.NameOf.nameOf -import com.openbankproject.commons.util.{ApiVersion,ApiVersionStatus} +import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus} import net.liftweb.common.{Box, Full} import net.liftweb.http.{LiftResponse, PlainTextResponse} import org.apache.http.HttpStatus @@ -63,6 +63,7 @@ object OBPAPI4_0_0 extends OBPRestHelper with APIMethods130 with APIMethods140 w nameOf(Implementations1_2_1.addPermissionForUserForBankAccountForOneView) :: nameOf(Implementations1_2_1.removePermissionForUserForBankAccountForOneView) :: nameOf(Implementations3_1_0.createAccount) :: + nameOf(Implementations3_1_0.revokeConsent) :://this endpoint is not restful, we do not support it in V510. Nil // if old version ResourceDoc objects have the same name endpoint with new version, omit old version ResourceDoc. diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index 37c91d520..63cbc179e 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -14,17 +14,17 @@ import code.api.util.NewStyle.HttpCode import code.api.util.NewStyle.function.extractQueryParams import code.api.util.X509.{getCommonName, getEmailAddress, getOrganization} import code.api.util._ -import code.api.util.newstyle.{BalanceNewStyle, RegulatedEntityAttributeNewStyle} import code.api.util.newstyle.Consumer.createConsumerNewStyle import code.api.util.newstyle.RegulatedEntityNewStyle.{createRegulatedEntityNewStyle, deleteRegulatedEntityNewStyle, getRegulatedEntitiesNewStyle, getRegulatedEntityByEntityIdNewStyle} +import code.api.util.newstyle.{BalanceNewStyle, RegulatedEntityAttributeNewStyle} import code.api.v2_0_0.AccountsHelper.{accountTypeFilterText, getFilteredCoreAccounts} import code.api.v2_1_0.{ConsumerRedirectUrlJSON, JSONFactory210} import code.api.v3_0_0.JSONFactory300 import code.api.v3_0_0.JSONFactory300.createAggregateMetricJson -import code.api.v3_1_0.{ConsentChallengeJsonV310, ConsentJsonV310, PostConsentBodyCommonJson, PostConsentEmailJsonV310, PostConsentPhoneJsonV310} -import code.api.v3_1_0.JSONFactory310.{createBadLoginStatusJson, createConsumerJSON, createRefreshUserJson} +import code.api.v3_1_0.JSONFactory310.{createBadLoginStatusJson, createConsumerJSON} +import code.api.v3_1_0._ import code.api.v4_0_0.JSONFactory400.{createAccountBalancesJson, createBalancesJson, createNewCoreBankAccountJson} -import code.api.v4_0_0.{JSONFactory400, PostAccountAccessJsonV400, PostApiCollectionJson400, PutConsentStatusJsonV400, PutConsentUserJsonV400, RevokedJsonV400} +import code.api.v4_0_0._ import code.api.v5_0_0.JSONFactory500 import code.api.v5_1_0.JSONFactory510.{createConsentsInfoJsonV510, createConsentsJsonV510, createRegulatedEntitiesJson, createRegulatedEntityJson} import code.atmattribute.AtmAttribute @@ -34,9 +34,8 @@ import code.consumer.Consumers import code.entitlement.Entitlement import code.loginattempts.LoginAttempt import code.metrics.APIMetrics -import code.metrics.MappedMetric.userId -import code.model.{AppType, Consumer} import code.model.dataAccess.{AuthUser, MappedBankAccount} +import code.model.{AppType, Consumer} import code.regulatedentities.MappedRegulatedEntityProvider import code.userlocks.UserLocksProvider import code.users.Users @@ -47,7 +46,7 @@ import code.views.system.{AccountAccess, ViewDefinition} import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model._ -import com.openbankproject.commons.model.enums.{AtmAttributeType, ConsentType, RegulatedEntityAttributeType, StrongCustomerAuthentication, TransactionRequestStatus, UserAttributeType} +import com.openbankproject.commons.model.enums.{TransactionRequestStatus, _} import com.openbankproject.commons.util.{ApiVersion, ScannedApiVersion} import net.liftweb.common.Full import net.liftweb.http.rest.RestHelper @@ -57,7 +56,6 @@ import net.liftweb.mapper.By import net.liftweb.util.Helpers.tryo import net.liftweb.util.{Helpers, Props, StringHelpers} -import java.text.SimpleDateFormat import java.time.{LocalDate, ZoneId} import java.util.Date import scala.collection.immutable.{List, Nil} @@ -1883,7 +1881,57 @@ trait APIMethods510 { } } + resourceDocs += ResourceDoc( + revokeMyConsent, + implementedInApiVersion, + nameOf(revokeMyConsent), + "Delete", + "/my/consents/CONSENT_ID", + "Revoke My Consent", + s""" + |Revoke Consent for current user specified by CONSENT_ID + | + |There are a few reasons you might need to revoke an application’s access to a user’s account: + | - The user explicitly wishes to revoke the application’s access + | - You as the service provider have determined an application is compromised or malicious, and want to disable it + | - etc. + | + |Please note that this endpoint only supports the case:: "The user explicitly wishes to revoke the application’s access" + | + |OBP as a resource server stores access tokens in a database, then it is relatively easy to revoke some token that belongs to a particular user. + |The status of the token is changed to "REVOKED" so the next time the revoked client makes a request, their token will fail to validate. + | + |${userAuthenticationMessage(true)} + | + """.stripMargin, + EmptyBody, + revokedConsentJsonV310, + List( + $UserNotLoggedIn, + BankNotFound, + UnknownError + ), + List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2)) + lazy val revokeMyConsent: OBPEndpoint = { + case "my" :: "consents" :: consentId :: Nil JsonDelete _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- SS.user + consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map { + unboxFullOrFail(_, callContext, ConsentNotFound) + } + _ <- Helper.booleanToFuture(failMsg = ConsentNotFound, cc=callContext) { + consent.mUserId == u.userId + } + consent <- Future(Consents.consentProvider.vend.revoke(consentId)) map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (ConsentJsonV310(consent.consentId, consent.jsonWebToken, consent.status), HttpCode.`200`(callContext)) + } + } + } val generalObpConsentText: String = s""" |