diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 49c1f1947..4742c74a2 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -4189,6 +4189,15 @@ object SwaggerDefinitionsJSON { valid_from = Some(new Date()), time_to_live = Some(3600) ) + + val postConsentImplicitJsonV310 = PostConsentImplicitJsonV310( + everything = false, + views = List(PostConsentViewJsonV310(bankIdExample.value, accountIdExample.value, viewIdExample.value)), + entitlements = List(PostConsentEntitlementJsonV310(bankIdExample.value, "CanGetCustomer")), + consumer_id = Some(consumerIdExample.value), + valid_from = Some(new Date()), + time_to_live = Some(3600) + ) val postConsentRequestJsonV310 = postConsentPhoneJsonV310.copy(consumer_id = None) val consentsJsonV310 = ConsentsJsonV310(List(consentJsonV310)) diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index a00dcd329..1326ac1bf 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -444,6 +444,7 @@ object ErrorMessages { val GetCustomerAccountLinksError = "OBP-30226: Could not get the customer account links." val UpdateCustomerAccountLinkError = "OBP-30227: Could not update the customer account link." val DeleteCustomerAccountLinkError = "OBP-30228: Could not delete the customer account link." + val GetConsentImplicitSCAError = "OBP-30229: Could not get the consent implicit SCA." val CreateSystemViewError = "OBP-30250: Could not create the system view" val DeleteSystemViewError = "OBP-30251: Could not delete the system view" @@ -489,7 +490,7 @@ object ErrorMessages { val ConsentCheckExpiredIssue = "OBP-35006: Cannot check is Consent-Id expired. " val ConsentDisabled = "OBP-35007: Consents are not allowed at this instance. " val ConsentHeaderNotFound = "OBP-35008: Cannot get Consent-Id. " - val ConsentAllowedScaMethods = "OBP-35009: Only SMS and EMAIL are supported as SCA methods. " + val ConsentAllowedScaMethods = "OBP-35009: Only SMS, EMAIL and IMPLICIT are supported as SCA methods. " val SmsServerNotResponding = "OBP-35010: SMS server is not working or SMS server can not send the message to the phone number:" val AuthorizationNotFound = "OBP-35011: Resource identification of the related Consent authorisation sub-resource not found by AUTHORIZATION_ID. " val ConsentAlreadyRevoked = "OBP-35012: Consent is already revoked. " @@ -514,6 +515,7 @@ object ErrorMessages { val ConsumerKeyIsToLong = "OBP-35031: The Consumer Key max length <= 512" val ConsentHeaderValueInvalid = "OBP-35032: The Consent's Request Header value is not formatted as UUID or JWT." val RolesForbiddenInConsent = s"OBP-35033: Consents cannot contain the following Roles: ${canCreateEntitlementAtOneBank} and ${canCreateEntitlementAtAnyBank}." + val UserAuthContextUpdateRequestAllowedScaMethods = "OBP-35034: Only SMS and EMAIL are supported as SCA methods. " //Authorisations val AuthorisationNotFound = "OBP-36001: Authorisation not found. Please specify valid values for PAYMENT_ID and AUTHORISATION_ID. " diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index f41002514..467689599 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3942,6 +3942,11 @@ object NewStyle extends MdcLoggable{ i => (unboxFullOrFail(i._1, callContext, UpdateCustomerAccountLinkError), i._2) } + def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[ConsentImplicitSCAT] = + Connector.connector.vend.getConsentImplicitSCA(user: User, callContext: Option[CallContext]) map { + i => (unboxFullOrFail(i._1, callContext, GetConsentImplicitSCAError), i._2) + } + def getAtmsByBankId(bankId: BankId, offset: Box[String], limit: Box[String], callContext: Option[CallContext]): OBPReturnType[List[AtmT]] = Connector.connector.vend.getAtms(bankId, callContext) map { case Empty => diff --git a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala index 0a3aa24ff..4f261b888 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/APIMethods310.scala @@ -3235,8 +3235,9 @@ trait APIMethods310 { | |The Consent is created in an ${ConsentStatus.INITIATED} state. | - |A One Time Password (OTP) (AKA security challenge) is sent Out of band (OOB) to the User via the transport defined in SCA_METHOD - |SCA_METHOD is typically "SMS" or "EMAIL". "EMAIL" is used for testing purposes. + |A One Time Password (OTP) (AKA security challenge) is sent Out of Band (OOB) to the User via the transport defined in SCA_METHOD + |SCA_METHOD is typically "SMS","EMAIL" or "IMPLICIT". "EMAIL" is used for testing purposes. OBP mapped mode "IMPLICIT" is "EMAIL". + |Other mode, bank can decide it in the connector method 'getConsentImplicitSCA'. | |When the Consent is created, OBP (or a backend system) stores the challenge so it can be checked later against the value supplied by the User with the Answer Consent Challenge endpoint. | @@ -3250,7 +3251,7 @@ trait APIMethods310 { | "views": [], | "entitlements": [], | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", - | "email": "eveline@example.com" + | "phone_number": "+49 170 1234567" |} | |Please note that consumer_id is optional field @@ -3259,7 +3260,7 @@ trait APIMethods310 { | "everything": true, | "views": [], | "entitlements": [], - | "email": "eveline@example.com" + | "phone_number": "+49 170 1234567" |} | |Please note if everything=false you need to explicitly specify views and entitlements @@ -3280,7 +3281,7 @@ trait APIMethods310 { | } | ], | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", - | "email": "eveline@example.com" + | "phone_number": "+49 170 1234567" |} | |""", @@ -3314,7 +3315,8 @@ trait APIMethods310 { |The Consent is created in an ${ConsentStatus.INITIATED} state. | |A One Time Password (OTP) (AKA security challenge) is sent Out of Band (OOB) to the User via the transport defined in SCA_METHOD - |SCA_METHOD is typically "SMS" or "EMAIL". "EMAIL" is used for testing purposes. + |SCA_METHOD is typically "SMS","EMAIL" or "IMPLICIT". "EMAIL" is used for testing purposes. OBP mapped mode "IMPLICIT" is "EMAIL". + |Other mode, bank can decide it in the connector method 'getConsentImplicitSCA'. | |When the Consent is created, OBP (or a backend system) stores the challenge so it can be checked later against the value supplied by the User with the Answer Consent Challenge endpoint. | @@ -3380,8 +3382,87 @@ trait APIMethods310 { ), apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil) + resourceDocs += ResourceDoc( + createConsentImplicit, + implementedInApiVersion, + nameOf(createConsentImplicit), + "POST", + "/banks/BANK_ID/my/consents/IMPLICIT", + "Create Consent (IMPLICIT)", + s""" + | + |This endpoint starts the process of creating a Consent. + | + |The Consent is created in an ${ConsentStatus.INITIATED} state. + | + |A One Time Password (OTP) (AKA security challenge) is sent Out of Band (OOB) to the User via the transport defined in SCA_METHOD + |SCA_METHOD is typically "SMS","EMAIL" or "IMPLICIT". "EMAIL" is used for testing purposes. OBP mapped mode "IMPLICIT" is "EMAIL". + |Other mode, bank can decide it in the connector method 'getConsentImplicitSCA'. + | + |When the Consent is created, OBP (or a backend system) stores the challenge so it can be checked later against the value supplied by the User with the Answer Consent Challenge endpoint. + | + |$generalObpConsentText + | + |${authenticationRequiredMessage(true)} + | + |Example 1: + |{ + | "everything": true, + | "views": [], + | "entitlements": [], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + |} + | + |Please note that consumer_id is optional field + |Example 2: + |{ + | "everything": true, + | "views": [], + | "entitlements": [], + |} + | + |Please note if everything=false you need to explicitly specify views and entitlements + |Example 3: + |{ + | "everything": false, + | "views": [ + | { + | "bank_id": "GENODEM1GLS", + | "account_id": "8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", + | "view_id": "owner" + | } + | ], + | "entitlements": [ + | { + | "bank_id": "GENODEM1GLS", + | "role_name": "CanGetCustomer" + | } + | ], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + |} + | + |""", + postConsentImplicitJsonV310, + consentJsonV310, + List( + UserNotLoggedIn, + BankNotFound, + InvalidJsonFormat, + ConsentAllowedScaMethods, + RolesAllowedInConsent, + ViewsAllowedInConsent, + ConsumerNotFoundByConsumerId, + ConsumerIsDisabled, + MissingPropsValueAtThisInstance, + SmsServerNotResponding, + InvalidConnectorResponse, + UnknownError + ), + apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil) + lazy val createConsentEmail = createConsent lazy val createConsentSms = createConsent + lazy val createConsentImplicit = createConsent lazy val createConsent : OBPEndpoint = { case "banks" :: BankId(bankId) :: "my" :: "consents" :: scaMethod :: Nil JsonPost json -> _ => { @@ -3390,7 +3471,7 @@ trait APIMethods310 { (Full(user), callContext) <- authenticatedAccess(cc) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ - List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString()).exists(_ == scaMethod) + List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString(), StrongCustomerAuthentication.IMPLICIT.toString()).exists(_ == scaMethod) } failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentBodyCommonJson " consentJson <- NewStyle.function.tryons(failMsg, 400, callContext) { @@ -3485,6 +3566,32 @@ trait APIMethods310 { callContext ) } yield Future{status} + case v if v == StrongCustomerAuthentication.IMPLICIT.toString => // Not implemented + for { + (consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext) + status <- consentImplicitSCA.scaMethod match { + case v if v == StrongCustomerAuthentication.EMAIL => // Send the email + Connector.connector.vend.sendCustomerNotification ( + StrongCustomerAuthentication.EMAIL, + consentImplicitSCA.recipient, + Some ("OBP Consent Challenge"), + challengeText, + callContext + ) + case v if v == StrongCustomerAuthentication.SMS => // Not implemented + Connector.connector.vend.sendCustomerNotification( + StrongCustomerAuthentication.SMS, + consentImplicitSCA.recipient, + None, + challengeText, + callContext + ) + case _ => Future { + "Success" + } + }} yield { + status + } case _ =>Future{"Success"} } } yield { @@ -3671,7 +3778,7 @@ trait APIMethods310 { checkScope(bankId.value, getConsumerPrimaryKey(callContext), ApiRole.canCreateUserAuthContextUpdate) } (_, callContext) <- NewStyle.function.getBank(bankId, callContext) - _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ + _ <- Helper.booleanToFuture(UserAuthContextUpdateRequestAllowedScaMethods, cc=callContext){ List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString()).exists(_ == scaMethod) } failMsg = s"$InvalidJsonFormat The Json body should be the $PostUserAuthContextJson " diff --git a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala index f4ceeda39..c7036700a 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/JSONFactory3.1.0.scala @@ -556,6 +556,15 @@ case class PostConsentPhoneJsonV310( time_to_live: Option[Long] ) extends PostConsentCommonBody +case class PostConsentImplicitJsonV310( + everything: Boolean, + views: List[PostConsentViewJsonV310], + entitlements: List[PostConsentEntitlementJsonV310], + consumer_id: Option[String], + valid_from: Option[Date], + time_to_live: Option[Long] +) extends PostConsentCommonBody + case class ConsentJsonV310(consent_id: String, jwt: String, status: String) case class ConsentsJsonV310(consents: List[ConsentJsonV310]) diff --git a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala index d5ba4e446..2649a2018 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/APIMethods500.scala @@ -512,7 +512,7 @@ trait APIMethods500 { _ <- Helper.booleanToFuture(failMsg = ConsumerHasMissingRoles + CanCreateUserAuthContextUpdate, cc=callContext) { checkScope(bankId.value, getConsumerPrimaryKey(callContext), ApiRole.canCreateUserAuthContextUpdate) } - _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ + _ <- Helper.booleanToFuture(UserAuthContextUpdateRequestAllowedScaMethods, cc=callContext){ List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString()).exists(_ == scaMethod) } failMsg = s"$InvalidJsonFormat The Json body should be the $PostUserAuthContextJson " @@ -798,12 +798,83 @@ trait APIMethods500 { UnknownError ), apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil) + staticResourceDocs += ResourceDoc( + createConsentByConsentRequestIdImplicit, + implementedInApiVersion, + nameOf(createConsentByConsentRequestIdImplicit), + "POST", + "/consumer/consent-requests/CONSENT_REQUEST_ID/IMPLICIT/consents", + "Create Consent By CONSENT_REQUEST_ID (IMPLICIT)", + s""" + | + |This endpoint continues the process of creating a Consent. It starts the SCA flow which changes the status of the consent from INITIATED to ACCEPTED or REJECTED. + |Please note that the Consent cannot elevate the privileges logged in user already have. + | + |""", + EmptyBody, + consentJsonV500, + List( + UserNotLoggedIn, + $BankNotFound, + InvalidJsonFormat, + ConsentRequestIsInvalid, + ConsentAllowedScaMethods, + RolesAllowedInConsent, + ViewsAllowedInConsent, + ConsumerNotFoundByConsumerId, + ConsumerIsDisabled, + MissingPropsValueAtThisInstance, + SmsServerNotResponding, + InvalidConnectorResponse, + UnknownError + ), + apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil) lazy val createConsentByConsentRequestIdEmail = createConsentByConsentRequestId lazy val createConsentByConsentRequestIdSms = createConsentByConsentRequestId + lazy val createConsentByConsentRequestIdImplicit = createConsentByConsentRequestId lazy val createConsentByConsentRequestId : OBPEndpoint = { + case "consumer" :: "consent-requests":: consentRequestId :: scaMethod :: "consents" :: Nil JsonPost _ -> _ => { + def sendEmailNotification(callContext: Option[CallContext], consentRequestJson: PostConsentRequestJsonV500, challengeText: String) = { + for { + failMsg <- Future { + s"$InvalidJsonFormat The Json body must contain the field email" + } + consentScaEmail <- NewStyle.function.tryons(failMsg, 400, callContext) { + consentRequestJson.email.head + } + (Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification( + StrongCustomerAuthentication.EMAIL, + consentScaEmail, + Some("OBP Consent Challenge"), + challengeText, + callContext + ) + } yield { + status + } + } + def sendSmsNotification(callContext: Option[CallContext], consentRequestJson: PostConsentRequestJsonV500, challengeText: String) = { + for { + failMsg <- Future { + s"$InvalidJsonFormat The Json body must contain the field phone_number" + } + consentScaPhoneNumber <- NewStyle.function.tryons(failMsg, 400, callContext) { + consentRequestJson.phone_number.head + } + (Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification( + StrongCustomerAuthentication.SMS, + consentScaPhoneNumber, + None, + challengeText, + callContext + ) + } yield { + status + } + } cc => for { (Full(user), callContext) <- authenticatedAccess(cc) @@ -816,7 +887,7 @@ trait APIMethods500 { Consents.consentProvider.vend.getConsentByConsentRequestId(consentRequestId).isEmpty } _ <- Helper.booleanToFuture(ConsentAllowedScaMethods, cc=callContext){ - List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString()).exists(_ == scaMethod) + List(StrongCustomerAuthentication.SMS.toString(), StrongCustomerAuthentication.EMAIL.toString(), StrongCustomerAuthentication.IMPLICIT.toString()).exists(_ == scaMethod) } failMsg = s"$InvalidJsonFormat The Json body should be the $PostConsentBodyCommonJson " consentRequestJson <- NewStyle.function.tryons(failMsg, 400, callContext) { @@ -912,35 +983,23 @@ trait APIMethods500 { challengeText = s"Your consent challenge : ${challengeAnswer}, Application: $applicationText" _ <- scaMethod match { case v if v == StrongCustomerAuthentication.EMAIL.toString => // Send the email - for{ - failMsg <- Future {s"$InvalidJsonFormat The Json body must contain the field email"} - consentScaEmail <- NewStyle.function.tryons(failMsg, 400, callContext) { - consentRequestJson.email.head - } - (Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification( - StrongCustomerAuthentication.EMAIL, - consentScaEmail, - Some("OBP Consent Challenge"), - challengeText, - callContext - ) - } yield Future{status} + sendEmailNotification(callContext, consentRequestJson, challengeText) case v if v == StrongCustomerAuthentication.SMS.toString => // Not implemented + sendSmsNotification(callContext, consentRequestJson, challengeText) + case v if v == StrongCustomerAuthentication.IMPLICIT.toString => for { - failMsg <- Future { - s"$InvalidJsonFormat The Json body must contain the field phone_number" - } - consentScaPhoneNumber <- NewStyle.function.tryons(failMsg, 400, callContext) { - consentRequestJson.phone_number.head - } - (Full(status), callContext) <- Connector.connector.vend.sendCustomerNotification( - StrongCustomerAuthentication.SMS, - consentScaPhoneNumber, - None, - challengeText, - callContext - ) - } yield Future{status} + (consentImplicitSCA, callContext) <- NewStyle.function.getConsentImplicitSCA(user, callContext) + status <- consentImplicitSCA.scaMethod match { + case v if v == StrongCustomerAuthentication.EMAIL => // Send the email + sendEmailNotification(callContext, consentRequestJson.copy(email=Some(consentImplicitSCA.recipient)), challengeText) + case v if v == StrongCustomerAuthentication.SMS => // Not implemented + sendSmsNotification(callContext, consentRequestJson.copy(phone_number=Some(consentImplicitSCA.recipient)), challengeText) + case _ => Future { + "Success" + } + }} yield { + status + } case _ =>Future{"Success"} } } yield { diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index d38abc3d0..a61efb294 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -2683,4 +2683,6 @@ trait Connector extends MdcLoggable { def updateCustomerAccountLinkById(customerAccountLinkId: String, relationshipType: String, callContext: Option[CallContext]): OBPReturnType[Box[CustomerAccountLinkTrait]] = Future{(Failure(setUnimplementedError), callContext)} + def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[Box[ConsentImplicitSCAT]] = Future{(Failure(setUnimplementedError), callContext)} + } diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index 9e9131fd5..7cbf40e07 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -2,7 +2,6 @@ package code.bankconnectors import java.util.Date import java.util.UUID.randomUUID - import _root_.akka.http.scaladsl.model.HttpMethod import code.DynamicData.DynamicDataProvider import code.DynamicEndpoint.{DynamicEndpointProvider, DynamicEndpointT} @@ -83,7 +82,7 @@ import com.openbankproject.commons.model.enums.DynamicEntityOperation._ import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.SCAStatus import com.openbankproject.commons.model.enums.{TransactionRequestStatus, _} -import com.openbankproject.commons.model.{AccountApplication, AccountAttribute, DirectDebitTrait, FXRate, Product, ProductAttribute, ProductCollectionItem, TaxResidence, TransactionRequestCommonBodyJSON, _} +import com.openbankproject.commons.model.{AccountApplication, AccountAttribute, ConsentImplicitSCAT, DirectDebitTrait, FXRate, Product, ProductAttribute, ProductCollectionItem, TaxResidence, TransactionRequestCommonBodyJSON, _} import com.tesobe.CacheKeyFromArguments import com.tesobe.model.UpdateBankAccount import com.twilio.Twilio @@ -5857,4 +5856,13 @@ object LocalMappedConnector extends Connector with MdcLoggable { CustomerAccountLinkTrait.customerAccountLink.vend.createCustomerAccountLink(customerId: String, bankId, accountId: String, relationshipType: String) map { ( _, callContext) } } + override def getConsentImplicitSCA(user: User, callContext: Option[CallContext]): OBPReturnType[Box[ConsentImplicitSCAT]] = Future { + //find the email from the user, and the OBP Implicit SCA is email + (Full(ConsentImplicitSCA( + scaMethod = StrongCustomerAuthentication.EMAIL, + recipient = user.emailAddress + )), callContext) + } + + } diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala index e8999f00d..12e155d93 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViews.scala @@ -144,10 +144,6 @@ object RemotedataViews extends ObpActorInit with Views { (actor ? cc.getOrCreatePublicPublicView(bankId, accountId, description)).mapTo[Box[View]] ) - def createCustomRandomView(bankId: BankId, accountId: AccountId) : Box[View] = getValueFromFuture( - (actor ? cc.createRandomView(bankId, accountId)).mapTo[Box[View]] - ) - // For tests def bulkDeleteAllPermissionsAndViews(): Boolean = getValueFromFuture( (actor ? cc.bulkDeleteAllPermissionsAndViews()).mapTo[Boolean] diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala b/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala index a731b5d74..da5a0562e 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataViewsActor.scala @@ -134,10 +134,6 @@ class RemotedataViewsActor extends Actor with ObpActorHelper with MdcLoggable { logger.debug("getOrCreatePublicPublicView(" + bankId +", "+ accountId +", "+ description +")") sender ! (mapper.getOrCreateCustomPublicView(bankId, accountId, description)) - case cc.createRandomView(bankId, accountId) => - logger.debug("createRandomView(" + bankId +", "+ accountId +")") - sender ! (mapper.createCustomRandomView(bankId, accountId)) - case cc.getOwners(view) => logger.debug("getOwners(" + view +")") sender ! (mapper.getOwners(view)) diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index c665393f8..06f209507 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -662,113 +662,6 @@ object MapperViews extends Views with MdcLoggable { } } - /** - * This is only for scala tests. - */ - def createCustomRandomView(bankId: BankId, accountId: AccountId) : Box[View] = { - //we set the length is to 40, try to be difficult for scala tests create the same viewName. - val viewName = "_" + randomString(40) - val viewId = MapperViews.createViewIdByName(viewName) - val description = randomString(40) - - if (!checkCustomViewIdOrName(viewName)) { - return Failure(InvalidCustomViewFormat) - } - - getExistingCustomView(bankId, accountId, viewId) match { - case Empty => { - tryo{ViewDefinition.create. - isSystem_(false). - isFirehose_(false). - name_(viewName). - metadataView_(SYSTEM_OWNER_VIEW_ID). - description_(description). - view_id(viewId). - isPublic_(false). - bank_id(bankId.value). - account_id(accountId.value). - usePrivateAliasIfOneExists_(false). - usePublicAliasIfOneExists_(false). - hideOtherAccountMetadataIfAlias_(false). - canSeeTransactionThisBankAccount_(true). - canSeeTransactionOtherBankAccount_(true). - canSeeTransactionMetadata_(true). - canSeeTransactionDescription_(true). - canSeeTransactionAmount_(true). - canSeeTransactionType_(true). - canSeeTransactionCurrency_(true). - canSeeTransactionStartDate_(true). - canSeeTransactionFinishDate_(true). - canSeeTransactionBalance_(true). - canSeeComments_(true). - canSeeOwnerComment_(true). - canSeeTags_(true). - canSeeImages_(true). - canSeeBankAccountOwners_(true). - canSeeBankAccountType_(true). - canSeeBankAccountBalance_(true). - canSeeBankAccountCurrency_(true). - canSeeBankAccountLabel_(true). - canSeeBankAccountNationalIdentifier_(true). - canSeeBankAccountSwift_bic_(true). - canSeeBankAccountIban_(true). - canSeeBankAccountNumber_(true). - canSeeBankAccountBankName_(true). - canSeeBankAccountBankPermalink_(true). - canSeeOtherAccountNationalIdentifier_(true). - canSeeOtherAccountSWIFT_BIC_(true). - canSeeOtherAccountIBAN_(true). - canSeeOtherAccountBankName_(true). - canSeeOtherAccountNumber_(true). - canSeeOtherAccountMetadata_(true). - canSeeOtherAccountKind_(true). - canSeeMoreInfo_(true). - canSeeUrl_(true). - canSeeImageUrl_(true). - canSeeOpenCorporatesUrl_(true). - canSeeCorporateLocation_(true). - canSeePhysicalLocation_(true). - canSeePublicAlias_(true). - canSeePrivateAlias_(true). - canAddMoreInfo_(true). - canAddURL_(true). - canAddImageURL_(true). - canAddOpenCorporatesUrl_(true). - canAddCorporateLocation_(true). - canAddPhysicalLocation_(true). - canAddPublicAlias_(true). - canAddPrivateAlias_(true). - canDeleteCorporateLocation_(true). - canDeletePhysicalLocation_(true). - canEditOwnerComment_(true). - canAddComment_(true). - canDeleteComment_(true). - canAddTag_(true). - canDeleteTag_(true). - canAddImage_(true). - canDeleteImage_(true). - canAddWhereTag_(true). - canSeeWhereTag_(true). - canDeleteWhereTag_(true). - canSeeBankRoutingScheme_(true). //added following in V300 - canSeeBankRoutingAddress_(true). - canSeeBankAccountRoutingScheme_(true). - canSeeBankAccountRoutingAddress_(true). - canSeeOtherBankRoutingScheme_(true). - canSeeOtherBankRoutingAddress_(true). - canSeeOtherAccountRoutingScheme_(true). - canSeeOtherAccountRoutingAddress_(true). - canAddTransactionRequestToOwnAccount_(false). //added following two for payments - canAddTransactionRequestToAnyAccount_(false). - canSeeBankAccountCreditLimit_(true). - saveMe} - } - case Full(v) => Full(v) - case Failure(msg, t, c) => Failure(msg, t, c) - case ParamFailure(x, y, z, q) => ParamFailure(x, y, z, q) - } - } - def createDefaultSystemView(name: String): Box[View] = { createAndSaveSystemView(name) } diff --git a/obp-api/src/main/scala/code/views/Views.scala b/obp-api/src/main/scala/code/views/Views.scala index 8049ec776..4abd2c425 100644 --- a/obp-api/src/main/scala/code/views/Views.scala +++ b/obp-api/src/main/scala/code/views/Views.scala @@ -113,11 +113,6 @@ trait Views { def getOrCreateSystemView(viewId: String) : Box[View] def getOrCreateCustomPublicView(bankId: BankId, accountId: AccountId, description: String) : Box[View] - /** - * this is only used for the scala test - */ - def createCustomRandomView(bankId: BankId, accountId: AccountId) : Box[View] - def getOwners(view: View): Set[User] def removeAllPermissions(bankId: BankId, accountId: AccountId) : Boolean @@ -167,7 +162,6 @@ class RemotedataViewsCaseClasses { case class getOrCreateSystemViewFromCbs(viewId: String) case class getOrCreateSystemView(viewId: String) case class getOrCreatePublicPublicView(bankId: BankId, accountId: AccountId, description: String) - case class createRandomView(bankId: BankId, accountId: AccountId) case class getOwners(view: View) diff --git a/obp-api/src/test/resources/frozen_type_meta_data b/obp-api/src/test/resources/frozen_type_meta_data index 16e25bbbf..1916ba779 100644 Binary files a/obp-api/src/test/resources/frozen_type_meta_data and b/obp-api/src/test/resources/frozen_type_meta_data differ diff --git a/obp-api/src/test/scala/code/api/v2_2_0/CreateCounterpartyTest.scala b/obp-api/src/test/scala/code/api/v2_2_0/CreateCounterpartyTest.scala index 46fab18eb..0ed14088e 100644 --- a/obp-api/src/test/scala/code/api/v2_2_0/CreateCounterpartyTest.scala +++ b/obp-api/src/test/scala/code/api/v2_2_0/CreateCounterpartyTest.scala @@ -90,7 +90,6 @@ class CreateCounterpartyTest extends V220ServerSetup with DefaultUsers { val bankId = testBank.bankId val accountId = AccountId("notExistingAccountId") val viewId =ViewId(SYSTEM_OWNER_VIEW_ID) - val ownerView = createOwnerView(bankId, accountId) Views.views.vend.grantAccessToCustomView(ViewIdBankIdAccountId(viewId, bankId, accountId), resourceUser1) val counterpartyPostJSON = SwaggerDefinitionsJSON.postCounterpartyJSON.copy(other_bank_routing_address=bankId.value,other_account_routing_address=accountId.value) diff --git a/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala index f9ebfa20e..ca2de8bb7 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/ConsentTest.scala @@ -64,6 +64,10 @@ class ConsentTest extends V310ServerSetup { .copy(entitlements=entitlements) .copy(consumer_id=Some(testConsumer.consumerId.get)) .copy(views=views) + lazy val postConsentImplicitJsonV310 = SwaggerDefinitionsJSON.postConsentImplicitJsonV310 + .copy(entitlements=entitlements) + .copy(consumer_id=Some(testConsumer.consumerId.get)) + .copy(views=views) val maxTimeToLive = APIUtil.getPropsAsIntValue(nameOfProperty="consents.max_time_to_live", defaultValue=3600) val timeToLive: Option[Long] = Some(maxTimeToLive + 10) @@ -78,6 +82,15 @@ class ConsentTest extends V310ServerSetup { response400.code should equal(401) response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) } + + scenario("We will call the endpoint without user credentials-IMPLICIT", ApiEndpoint1, VersionOfApi) { + When("We make a request") + val request400 = (v3_1_0_Request / "banks" / bankId / "my" / "consents" / "IMPLICIT" ).POST + val response400 = makePostRequest(request400, write(postConsentImplicitJsonV310)) + Then("We should get a 401") + response400.code should equal(401) + response400.body.extract[ErrorMessage].message should equal(UserNotLoggedIn) + } scenario("We will call the endpoint with user credentials but wrong SCA method", ApiEndpoint1, VersionOfApi) { When("We make a request") @@ -95,6 +108,14 @@ class ConsentTest extends V310ServerSetup { scenario("We will call the endpoint with user credentials and deprecated header name", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { wholeFunctionality(RequestHeader.`Consent-Id`) } + + scenario("We will call the endpoint with user credentials-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + wholeFunctionalityImplicit(RequestHeader.`Consent-JWT`) + } + + scenario("We will call the endpoint with user credentials and deprecated header name-Implicit", ApiEndpoint1, ApiEndpoint3, VersionOfApi, VersionOfApi2) { + wholeFunctionalityImplicit(RequestHeader.`Consent-Id`) + } } private def wholeFunctionality(nameOfRequestHeader: String) = { @@ -175,4 +196,83 @@ class ConsentTest extends V310ServerSetup { responseGetUserByUserId400.body.extract[ErrorMessage].message should include(ConsentDisabled) } } + + private def wholeFunctionalityImplicit(nameOfRequestHeader: String) = { + When("We make a request") + // Create a consent as the user1. + // Must fail because we try to set time_to_live=4500 + val requestWrongTimeToLive400 = (v3_1_0_Request / "banks" / bankId / "my" / "consents" / "IMPLICIT").POST <@ (user1) + val responseWrongTimeToLive400 = makePostRequest(requestWrongTimeToLive400, write(postConsentImplicitJsonV310.copy(time_to_live = timeToLive))) + Then("We should get a 400") + responseWrongTimeToLive400.code should equal(400) + responseWrongTimeToLive400.body.extract[ErrorMessage].message should include(ConsentMaxTTL) + + // Create a consent as the user1. + // Must fail because we try to assign a role other that user already have access to the request + val request400 = (v3_1_0_Request / "banks" / bankId / "my" / "consents" / "IMPLICIT").POST <@ (user1) + val response400 = makePostRequest(request400, write(postConsentImplicitJsonV310)) + Then("We should get a 400") + response400.code should equal(400) + response400.body.extract[ErrorMessage].message should equal(RolesAllowedInConsent) + + Then("We grant the role and test it again") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetAnyUser.toString) + // Create a consent as the user1. The consent is in status INITIATED + val secondResponse400 = makePostRequest(request400, write(postConsentImplicitJsonV310)) + Then("We should get a 201") + secondResponse400.code should equal(201) + + val consentId = secondResponse400.body.extract[ConsentJsonV310].consent_id + val jwt = secondResponse400.body.extract[ConsentJsonV310].jwt + val header = List((nameOfRequestHeader, jwt)) + + // Make a request with the consent which is NOT in status ACCEPTED + val requestGetUserByUserId400 = (v3_1_0_Request / "users" / "current").GET + val responseGetUserByUserId400 = makeGetRequest(requestGetUserByUserId400, header) + APIUtil.getPropsAsBoolValue(nameOfProperty = "consents.allowed", defaultValue = false) match { + case true => + // Due to the wrong status of the consent the request must fail + responseGetUserByUserId400.body.extract[ErrorMessage].message should include(ConsentStatusIssue) + + // Answer security challenge i.e. SCA + val answerConsentChallengeRequest = (v3_1_0_Request / "banks" / bankId / "consents" / consentId / "challenge").POST <@ (user1) + val challenge = Consent.challengeAnswerAtTestEnvironment + val post = PostConsentChallengeJsonV310(answer = challenge) + val response400 = makePostRequest(answerConsentChallengeRequest, write(post)) + Then("We should get a 201") + response400.code should equal(201) + + // Make a request WITHOUT the request header "Consumer-Key: SOME_VALUE" + // Due to missing value the request must fail + makeGetRequest(requestGetUserByUserId400, header) + .body.extract[ErrorMessage].message should include(ConsumerKeyHeaderMissing) + + // Make a request WITH the request header "Consumer-Key: NON_EXISTING_VALUE" + // Due to non existing value the request must fail + val headerConsumerKey = List((RequestHeader.`Consumer-Key`, "NON_EXISTING_VALUE")) + makeGetRequest(requestGetUserByUserId400, header ::: headerConsumerKey) + .body.extract[ErrorMessage].message should include(ConsentDoesNotMatchConsumer) + + // Make a request WITH the request header "Consumer-Key: EXISTING_VALUE" + val validHeaderConsumerKey = List((RequestHeader.`Consumer-Key`, user1.map(_._1.key).getOrElse("SHOULD_NOT_HAPPEN"))) + val response = makeGetRequest((v3_1_0_Request / "users" / "current").GET, header ::: validHeaderConsumerKey) + val user = response.body.extract[UserJsonV300] + val assignedEntitlements: Seq[PostConsentEntitlementJsonV310] = user.entitlements.list.flatMap( + e => entitlements.find(_ == PostConsentEntitlementJsonV310(e.bank_id, e.role_name)) + ) + // Check we have all entitlements from the consent + assignedEntitlements should equal(entitlements) + + // Every consent implies a brand new user is created + user.user_id should not equal (resourceUser1.userId) + + // Check we have all views from the consent + val assignedViews = user.views.map(_.list).toSeq.flatten + assignedViews.map(e => PostConsentViewJsonV310(e.bank_id, e.account_id, e.view_id)).distinct should equal(views) + + case false => + // Due to missing props at the instance the request must fail + responseGetUserByUserId400.body.extract[ErrorMessage].message should include(ConsentDisabled) + } + } } diff --git a/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala b/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala index 8317587e8..8cf489562 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/SystemViewsTests.scala @@ -71,7 +71,7 @@ class SystemViewsTests extends V310ServerSetup { // Custom view, name starts from `_` // System view, owner - val randomSystemViewId = APIUtil.generateUUID() + val randomSystemViewId = "a"+APIUtil.generateUUID() val postBodySystemViewJson = createSystemViewJsonV300.copy(name=randomSystemViewId).copy(metadata_view = randomSystemViewId).toCreateViewJson def getSystemView(viewId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { @@ -273,13 +273,19 @@ class SystemViewsTests extends V310ServerSetup { } feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access with proper Role in order to delete owner view") { scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemView.toString) + val responseCreate400 = postSystemView(postBodySystemViewJson, user1) + Then("We should get a 201") + responseCreate400.code should equal(201) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteSystemView.toString) When(s"We make a request $ApiEndpoint4") AccountAccess.findAll( - By(AccountAccess.view_id, SYSTEM_OWNER_VIEW_ID), + By(AccountAccess.view_id, randomSystemViewId), By(AccountAccess.user_fk, resourceUser1.id.get) - ).forall(_.delete_!) // Remove all rows assigned to the system owner view in order to delete it - val response400 = deleteSystemView(SYSTEM_OWNER_VIEW_ID, user1) + ).forall(_.delete_!) // Remove all rows assigned to the system view in order to delete it + val response400 = deleteSystemView(randomSystemViewId, user1) Then("We should get a 200") response400.code should equal(200) } diff --git a/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala b/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala index 9c1ebfbb8..a96b0c765 100644 --- a/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala +++ b/obp-api/src/test/scala/code/api/v5_0_0/ConsentRequestTest.scala @@ -77,18 +77,93 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{ val createConsentRequestUrl = (v5_0_0_Request / "consumer"/ "consent-requests").POST<@(user1) def getConsentRequestUrl(requestId:String) = (v5_0_0_Request / "consumer"/ "consent-requests"/requestId).GET<@(user1) def createConsentByConsentRequestIdEmail(requestId:String) = (v5_0_0_Request / "consumer"/ "consent-requests"/requestId/"EMAIL"/"consents").POST<@(user1) + def createConsentByConsentRequestIdImplicit(requestId:String) = (v5_0_0_Request / "consumer"/ "consent-requests"/requestId/"IMPLICIT"/"consents").POST<@(user1) def getConsentByRequestIdUrl(requestId:String) = (v5_0_0_Request / "consumer"/ "consent-requests"/requestId/"consents").GET<@(user1) feature("Create/Get Consent Request v5.0.0") { - scenario("We will call the Create endpoint without a user credentials", ApiEndpoint1, VersionOfApi) { - When("We make a request v5.0.0") - val response500 = makePostRequest(createConsentRequestWithoutLoginUrl, write(postConsentRequestJsonV310)) - Then("We should get a 401") - response500.code should equal(401) - response500.body.extract[ErrorMessage].message should equal (ApplicationNotIdentified) - } +// scenario("We will call the Create endpoint without a user credentials", ApiEndpoint1, VersionOfApi) { +// When("We make a request v5.0.0") +// val response500 = makePostRequest(createConsentRequestWithoutLoginUrl, write(postConsentRequestJsonV310)) +// Then("We should get a 401") +// response500.code should equal(401) +// response500.body.extract[ErrorMessage].message should equal (ApplicationNotIdentified) +// } +// +// scenario("We will call the Create, Get and Delete endpoints with user credentials ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { +// When(s"We try $ApiEndpoint1 v5.0.0") +// val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postConsentRequestJsonV310)) +// Then("We should get a 201") +// createConsentResponse.code should equal(201) +// val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] +// val consentRequestId = createConsentRequestResponseJson.consent_request_id +// +// When("We try to make the GET request v5.0.0") +// val successGetRes = makeGetRequest(getConsentRequestUrl(consentRequestId)) +// Then("We should get a 200") +// successGetRes.code should equal(200) +// val getConsentRequestResponseJson = successGetRes.body.extract[ConsentRequestResponseJson] +// getConsentRequestResponseJson.payload should not be("") +// +// When("We try to make the GET request v5.0.0") +// Then("We grant the role and test it again") +// Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetAnyUser.toString) +// val createConsentByRequestResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) +// Then("We should get a 200") +// createConsentByRequestResponse.code should equal(201) +// val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV500].consent_id +// val consentJwt = createConsentByRequestResponse.body.extract[ConsentJsonV500].jwt +// +// setPropsValues("consumer_validation_method_for_consent"->"NONE") +// val requestWhichFails = (v5_0_0_Request / "users").GET +// val responseWhichFails = makeGetRequest(requestWhichFails, List((s"Consent-JWT", consentJwt))) +// Then("We get successful response") +// responseWhichFails.code should equal(401) +// +// +// val answerConsentChallengeRequest = (v5_0_0_Request / "banks" / testBankId1.value / "consents" / consentId / "challenge").POST <@ (user1) +// val challenge = Consent.challengeAnswerAtTestEnvironment +// val post = PostConsentChallengeJsonV310(answer = challenge) +// val answerConsentChallengeResponse = makePostRequest(answerConsentChallengeRequest, write(post)) +// Then("We should get a 201") +// answerConsentChallengeResponse.code should equal(201) +// +// When("We try to make the GET request v5.0.0") +// val getConsentByRequestResponse = makeGetRequest(getConsentByRequestIdUrl(consentRequestId)) +// Then("We should get a 200") +// getConsentByRequestResponse.code should equal(200) +// val getConsentByRequestResponseJson = getConsentByRequestResponse.body.extract[ConsentJsonV500] +// getConsentByRequestResponseJson.consent_request_id.head should be(consentRequestId) +// getConsentByRequestResponseJson.status should be(ConsentStatus.ACCEPTED.toString) +// +// +// val requestGetUsers = (v5_0_0_Request / "users").GET +// +// // Test Request Header "Consent-JWT:SOME_VALUE" +// val consentRequestHeader = (s"Consent-JWT", getConsentByRequestResponseJson.jwt) +// val responseGetUsers = makeGetRequest(requestGetUsers, List(consentRequestHeader)) +// Then("We get successful response") +// responseGetUsers.code should equal(200) +// val users = responseGetUsers.body.extract[UsersJsonV400].users +// users.size should be > 0 +// +// // Test Request Header "Consent-Id:SOME_VALUE" +// val consentIdRequestHeader = (s"Consent-Id", getConsentByRequestResponseJson.consent_id) +// val responseGetUsersSecond = makeGetRequest(requestGetUsers, List(consentIdRequestHeader)) +// Then("We get successful response") +// responseGetUsersSecond.code should equal(200) +// val usersSecond = responseGetUsersSecond.body.extract[UsersJsonV400].users +// usersSecond.size should be > 0 +// users.size should equal(usersSecond.size) +// +// // Test Request Header "Consent-JWT:INVALID_JWT_VALUE" +// val wrongRequestHeader = (s"Consent-JWT", "INVALID_JWT_VALUE") +// val responseGetUsersWrong = makeGetRequest(requestGetUsers, List(wrongRequestHeader)) +// Then("We get successful response") +// responseGetUsersWrong.code should equal(401) +// responseGetUsersWrong.body.extract[ErrorMessage].message contains (ConsentHeaderValueInvalid) should be (true) +// } - scenario("We will call the Create, Get and Delete endpoints with user credentials ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { + scenario("We will call the Create (IMPLICIT), Get and Delete endpoints with user credentials ", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { When(s"We try $ApiEndpoint1 v5.0.0") val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postConsentRequestJsonV310)) Then("We should get a 201") @@ -106,7 +181,7 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{ When("We try to make the GET request v5.0.0") Then("We grant the role and test it again") Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanGetAnyUser.toString) - val createConsentByRequestResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) + val createConsentByRequestResponse = makePostRequest(createConsentByConsentRequestIdImplicit(consentRequestId), write("")) Then("We should get a 200") createConsentByRequestResponse.code should equal(201) val consentId = createConsentByRequestResponse.body.extract[ConsentJsonV500].consent_id @@ -162,39 +237,39 @@ class ConsentRequestTest extends V500ServerSetupAsync with PropsReset{ responseGetUsersWrong.body.extract[ErrorMessage].message contains (ConsentHeaderValueInvalid) should be (true) } - scenario(s"Check the forbidden roles ${CanCreateEntitlementAtAnyBank.toString()}", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { - When(s"We try $ApiEndpoint1 v5.0.0") - val postJsonForbiddenEntitlementAtAnyBank = postConsentRequestJsonV310.copy(entitlements = Some(forbiddenEntitlementAnyBank)) - val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postJsonForbiddenEntitlementAtAnyBank)) - Then("We should get a 201") - createConsentResponse.code should equal(201) - val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] - val consentRequestId = createConsentRequestResponseJson.consent_request_id - - // Role CanCreateEntitlementAtAnyBank MUST be forbidden - val forbiddenRoleResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) - Then("We should get a 400") - forbiddenRoleResponse.code should equal(400) - forbiddenRoleResponse.code should equal(400) - forbiddenRoleResponse.body.extract[ErrorMessage].message should equal (RolesForbiddenInConsent) - } - - scenario(s"Check the forbidden roles ${CanCreateEntitlementAtOneBank.toString()}", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { - When(s"We try $ApiEndpoint1 v5.0.0") - val postJsonForbiddenEntitlementAtOneBank = postConsentRequestJsonV310.copy(entitlements = Some(forbiddenEntitlementOneBank)) - val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postJsonForbiddenEntitlementAtOneBank)) - Then("We should get a 201") - createConsentResponse.code should equal(201) - val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] - val consentRequestId = createConsentRequestResponseJson.consent_request_id - - // Role CanCreateEntitlementAtOneBank MUST be forbidden - val forbiddenRoleResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) - Then("We should get a 400") - forbiddenRoleResponse.code should equal(400) - forbiddenRoleResponse.code should equal(400) - forbiddenRoleResponse.body.extract[ErrorMessage].message should equal (RolesForbiddenInConsent) - } +// scenario(s"Check the forbidden roles ${CanCreateEntitlementAtAnyBank.toString()}", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { +// When(s"We try $ApiEndpoint1 v5.0.0") +// val postJsonForbiddenEntitlementAtAnyBank = postConsentRequestJsonV310.copy(entitlements = Some(forbiddenEntitlementAnyBank)) +// val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postJsonForbiddenEntitlementAtAnyBank)) +// Then("We should get a 201") +// createConsentResponse.code should equal(201) +// val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] +// val consentRequestId = createConsentRequestResponseJson.consent_request_id +// +// // Role CanCreateEntitlementAtAnyBank MUST be forbidden +// val forbiddenRoleResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) +// Then("We should get a 400") +// forbiddenRoleResponse.code should equal(400) +// forbiddenRoleResponse.code should equal(400) +// forbiddenRoleResponse.body.extract[ErrorMessage].message should equal (RolesForbiddenInConsent) +// } +// +// scenario(s"Check the forbidden roles ${CanCreateEntitlementAtOneBank.toString()}", ApiEndpoint1, ApiEndpoint2, ApiEndpoint3, ApiEndpoint4, ApiEndpoint5, VersionOfApi) { +// When(s"We try $ApiEndpoint1 v5.0.0") +// val postJsonForbiddenEntitlementAtOneBank = postConsentRequestJsonV310.copy(entitlements = Some(forbiddenEntitlementOneBank)) +// val createConsentResponse = makePostRequest(createConsentRequestUrl, write(postJsonForbiddenEntitlementAtOneBank)) +// Then("We should get a 201") +// createConsentResponse.code should equal(201) +// val createConsentRequestResponseJson = createConsentResponse.body.extract[ConsentRequestResponseJson] +// val consentRequestId = createConsentRequestResponseJson.consent_request_id +// +// // Role CanCreateEntitlementAtOneBank MUST be forbidden +// val forbiddenRoleResponse = makePostRequest(createConsentByConsentRequestIdEmail(consentRequestId), write("")) +// Then("We should get a 400") +// forbiddenRoleResponse.code should equal(400) +// forbiddenRoleResponse.code should equal(400) +// forbiddenRoleResponse.body.extract[ErrorMessage].message should equal (RolesForbiddenInConsent) +// } } diff --git a/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala b/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala index d8c7d7ef1..a1789e121 100644 --- a/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala +++ b/obp-api/src/test/scala/code/api/v5_0_0/SystemViewsTests.scala @@ -71,11 +71,10 @@ class SystemViewsTests extends V500ServerSetup { // Custom view, name starts from `_` // System view, owner - val randomSystemViewId = APIUtil.generateUUID() + val randomSystemViewId = "a"+APIUtil.generateUUID() val postBodySystemViewJson = createSystemViewJsonV500 .copy(name=randomSystemViewId) .copy(metadata_view = randomSystemViewId).toCreateViewJson - val systemViewId = MapperViews.createViewIdByName(postBodySystemViewJson.name) def getSystemView(viewId : String, consumerAndToken: Option[(Consumer, Token)]): APIResponse = { val request = v5_0_0_Request / "system-views" / viewId <@(consumerAndToken) @@ -266,15 +265,21 @@ class SystemViewsTests extends V500ServerSetup { response400.code should equal(200) } } - feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access with proper Role in order to delete owner view") { + feature(s"test $ApiEndpoint4 version $VersionOfApi - Authorized access with proper Role in order to delete system view") { scenario("We will call the endpoint without user credentials", ApiEndpoint4, VersionOfApi) { + When(s"We make a request $ApiEndpoint2") + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanCreateSystemView.toString) + val responseCreate400 = postSystemView(postBodySystemViewJson, user1) + Then("We should get a 201") + responseCreate400.code should equal(201) + Entitlement.entitlement.vend.addEntitlement("", resourceUser1.userId, CanDeleteSystemView.toString) When(s"We make a request $ApiEndpoint4") AccountAccess.findAll( - By(AccountAccess.view_id, SYSTEM_OWNER_VIEW_ID), + By(AccountAccess.view_id, randomSystemViewId), By(AccountAccess.user_fk, resourceUser1.id.get) - ).forall(_.delete_!) // Remove all rows assigned to the system owner view in order to delete it - val response400 = deleteSystemView(SYSTEM_OWNER_VIEW_ID, user1) + ).forall(_.delete_!) // Remove all rows assigned to the system view in order to delete it + val response400 = deleteSystemView(randomSystemViewId, user1) Then("We should get a 200") response400.code should equal(200) } diff --git a/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala b/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala index a3805d5f4..03f96341a 100644 --- a/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala +++ b/obp-api/src/test/scala/code/setup/TestConnectorSetupWithStandardPermissions.scala @@ -3,11 +3,15 @@ package code.setup import bootstrap.liftweb.ToSchemify import code.accountholders.AccountHolders import code.api.Constant.{CUSTOM_PUBLIC_VIEW_ID, SYSTEM_OWNER_VIEW_ID} +import code.api.util.APIUtil.checkCustomViewIdOrName import code.api.util.ErrorMessages._ import code.model._ import code.model.dataAccess._ -import code.views.Views +import code.views.MapperViews.getExistingCustomView +import code.views.system.ViewDefinition +import code.views.{MapperViews, Views} import com.openbankproject.commons.model._ +import net.liftweb.common.{Failure, Full, ParamFailure} import net.liftweb.mapper.MetaMapper import net.liftweb.util.Helpers._ @@ -24,16 +28,117 @@ trait TestConnectorSetupWithStandardPermissions extends TestConnectorSetup { protected def getOrCreateSystemView(name: String) : View = { Views.views.vend.getOrCreateSystemView(name).openOrThrowException(attemptedToOpenAnEmptyBox) } - protected def createOwnerView(bankId: BankId, accountId: AccountId ) : View = { - Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID).openOrThrowException(attemptedToOpenAnEmptyBox) - } - + protected def createPublicView(bankId: BankId, accountId: AccountId) : View = { Views.views.vend.getOrCreateCustomPublicView(bankId: BankId, accountId: AccountId, CUSTOM_PUBLIC_VIEW_ID).openOrThrowException(attemptedToOpenAnEmptyBox) } - protected def createCustomRandomView(bankId: BankId, accountId: AccountId) : View = { - Views.views.vend.createCustomRandomView(bankId, accountId).openOrThrowException(attemptedToOpenAnEmptyBox) + def createCustomRandomView(bankId: BankId, accountId: AccountId) : View = { + { + //we set the length is to 40, try to be difficult for scala tests create the same viewName. + val viewName = "_" + randomString(40) + val viewId = MapperViews.createViewIdByName(viewName) + val description = randomString(40) + + if (!checkCustomViewIdOrName(viewName)) { + throw new RuntimeException(InvalidCustomViewFormat) + } + + getExistingCustomView(bankId, accountId, viewId) match { + case net.liftweb.common.Empty => { + tryo { + ViewDefinition.create. + isSystem_(false). + isFirehose_(false). + name_(viewName). + metadataView_(SYSTEM_OWNER_VIEW_ID). + description_(description). + view_id(viewId). + isPublic_(false). + bank_id(bankId.value). + account_id(accountId.value). + usePrivateAliasIfOneExists_(false). + usePublicAliasIfOneExists_(false). + hideOtherAccountMetadataIfAlias_(false). + canSeeTransactionThisBankAccount_(true). + canSeeTransactionOtherBankAccount_(true). + canSeeTransactionMetadata_(true). + canSeeTransactionDescription_(true). + canSeeTransactionAmount_(true). + canSeeTransactionType_(true). + canSeeTransactionCurrency_(true). + canSeeTransactionStartDate_(true). + canSeeTransactionFinishDate_(true). + canSeeTransactionBalance_(true). + canSeeComments_(true). + canSeeOwnerComment_(true). + canSeeTags_(true). + canSeeImages_(true). + canSeeBankAccountOwners_(true). + canSeeBankAccountType_(true). + canSeeBankAccountBalance_(true). + canSeeBankAccountCurrency_(true). + canSeeBankAccountLabel_(true). + canSeeBankAccountNationalIdentifier_(true). + canSeeBankAccountSwift_bic_(true). + canSeeBankAccountIban_(true). + canSeeBankAccountNumber_(true). + canSeeBankAccountBankName_(true). + canSeeBankAccountBankPermalink_(true). + canSeeOtherAccountNationalIdentifier_(true). + canSeeOtherAccountSWIFT_BIC_(true). + canSeeOtherAccountIBAN_(true). + canSeeOtherAccountBankName_(true). + canSeeOtherAccountNumber_(true). + canSeeOtherAccountMetadata_(true). + canSeeOtherAccountKind_(true). + canSeeMoreInfo_(true). + canSeeUrl_(true). + canSeeImageUrl_(true). + canSeeOpenCorporatesUrl_(true). + canSeeCorporateLocation_(true). + canSeePhysicalLocation_(true). + canSeePublicAlias_(true). + canSeePrivateAlias_(true). + canAddMoreInfo_(true). + canAddURL_(true). + canAddImageURL_(true). + canAddOpenCorporatesUrl_(true). + canAddCorporateLocation_(true). + canAddPhysicalLocation_(true). + canAddPublicAlias_(true). + canAddPrivateAlias_(true). + canDeleteCorporateLocation_(true). + canDeletePhysicalLocation_(true). + canEditOwnerComment_(true). + canAddComment_(true). + canDeleteComment_(true). + canAddTag_(true). + canDeleteTag_(true). + canAddImage_(true). + canDeleteImage_(true). + canAddWhereTag_(true). + canSeeWhereTag_(true). + canDeleteWhereTag_(true). + canSeeBankRoutingScheme_(true). //added following in V300 + canSeeBankRoutingAddress_(true). + canSeeBankAccountRoutingScheme_(true). + canSeeBankAccountRoutingAddress_(true). + canSeeOtherBankRoutingScheme_(true). + canSeeOtherBankRoutingAddress_(true). + canSeeOtherAccountRoutingScheme_(true). + canSeeOtherAccountRoutingAddress_(true). + canAddTransactionRequestToOwnAccount_(false). //added following two for payments + canAddTransactionRequestToAnyAccount_(false). + canSeeBankAccountCreditLimit_(true). + saveMe + } + } + case Full(v) => Full(v) + case Failure(msg, t, c) => Failure(msg, t, c) + case ParamFailure(x, y, z, q) => ParamFailure(x, y, z, q) + } + }.openOrThrowException(attemptedToOpenAnEmptyBox) } diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala index b95706620..62b8b6e63 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala @@ -582,7 +582,16 @@ trait ChallengeTrait { } +trait ConsentImplicitSCAT { + def scaMethod: SCA + def recipient: String +} + //---------------------------------------- trait dependents of case class +case class ConsentImplicitSCA( + scaMethod: SCA, + recipient: String +) extends ConsentImplicitSCAT @deprecated("Use Lobby instead which contains detailed fields, not this string","24 July 2017") case class LobbyString (hours : String) extends LobbyStringT diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala index 73e9251e7..8623e99a0 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala @@ -84,6 +84,7 @@ object StrongCustomerAuthentication extends OBPEnumeration[StrongCustomerAuthent type SCA = Value object SMS extends Value object EMAIL extends Value + object IMPLICIT extends Value object DUMMY extends Value object UNDEFINED extends Value