diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index 488eafe8de..941bac83b4 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -760,6 +760,7 @@ class Boot extends MdcLoggable { val accountant = Views.views.vend.getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID).isDefined val standard = Views.views.vend.getOrCreateSystemView(SYSTEM_STANDARD_VIEW_ID).isDefined val stageOne = Views.views.vend.getOrCreateSystemView(SYSTEM_STAGE_ONE_VIEW_ID).isDefined + val manageCustomViews = Views.views.vend.getOrCreateSystemView(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID).isDefined // Only create Firehose view if they are enabled at instance. val accountFirehose = if (ApiPropsWithAlias.allowAccountFirehose) Views.views.vend.getOrCreateSystemView(SYSTEM_FIREHOSE_VIEW_ID).isDefined @@ -773,6 +774,7 @@ class Boot extends MdcLoggable { |System view ${SYSTEM_FIREHOSE_VIEW_ID} exists/created at the instance: ${accountFirehose} |System view ${SYSTEM_STANDARD_VIEW_ID} exists/created at the instance: ${standard} |System view ${SYSTEM_STAGE_ONE_VIEW_ID} exists/created at the instance: ${stageOne} + |System view ${SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID} exists/created at the instance: ${manageCustomViews} |""".stripMargin logger.info(comment) diff --git a/obp-api/src/main/scala/code/api/OBPRestHelper.scala b/obp-api/src/main/scala/code/api/OBPRestHelper.scala index 45ad8e18d9..6199d42299 100644 --- a/obp-api/src/main/scala/code/api/OBPRestHelper.scala +++ b/obp-api/src/main/scala/code/api/OBPRestHelper.scala @@ -145,8 +145,17 @@ case class APIFailureNewStyle(failMsg: String, locale, if(locale.toString.startsWith("en") || ?!(str, resourceBundleList)==str) //If can not find the value from props or the local is `en`, then return errorBody - else - s": ${?!(str, resourceBundleList)}" + else { + val originalErrorMessageFromScalaCode = ErrorMessages.getValueMatches(_.startsWith(errorCode)).getOrElse("") + // we need to keep the extra message, + // eg: OBP-20006: usuario le faltan uno o más roles': CanGetUserInvitation for BankId(gh.29.uk). + if(failMsg.contains(originalErrorMessageFromScalaCode)){ + s": ${?!(str, resourceBundleList)}"+failMsg.replace(originalErrorMessageFromScalaCode,"") + } else{ + s": ${?!(str, resourceBundleList)}" + } + } + ) val translatedErrorBody = ?(errorCode, locale) 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 4742c74a2a..d2f7d498bd 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 @@ -189,6 +189,101 @@ object SwaggerDefinitionsJSON { val createSystemViewJsonV300 = createViewJsonV300.copy(name = "test", metadata_view = "test", is_public = false) + val allowedActionsV500 = List( + "can_see_transaction_this_bank_account", + "can_see_transaction_other_bank_account", + "can_see_transaction_metadata", + "can_see_transaction_label", + "can_see_transaction_amount", + "can_see_transaction_type", + "can_see_transaction_currency", + "can_see_transaction_start_date", + "can_see_transaction_finish_date", + "can_see_transaction_balance", + "can_see_comments", + "can_see_narrative", "can_see_tags", + "can_see_images", + "can_see_bank_account_owners", + "can_see_bank_account_type", + "can_see_bank_account_balance", + "can_see_bank_account_currency", + "can_see_bank_account_label", + "can_see_bank_account_national_identifier", + "can_see_bank_account_swift_bic", + "can_see_bank_account_iban", + "can_see_bank_account_number", + "can_see_bank_account_bank_name", + "can_see_other_account_national_identifier", + "can_see_other_account_swift_bic", + "can_see_other_account_iban", + "can_see_other_account_bank_name", + "can_see_other_account_number", + "can_see_other_account_metadata", + "can_see_other_account_kind", + "can_see_more_info", + "can_see_url", + "can_see_image_url", + "can_see_open_corporates_url", + "can_see_corporate_location", + "can_see_physical_location", + "can_see_public_alias", + "can_see_private_alias", + "can_add_more_info", + "can_add_url", + "can_add_image_url", + "can_add_open_corporates_url", + "can_add_corporate_location", + "can_add_physical_location", + "can_add_public_alias", + "can_add_private_alias", + "can_delete_corporate_location", + "can_delete_physical_location", + "can_edit_narrative", + "can_add_comment", + "can_delete_comment", + "can_add_tag", + "can_delete_tag", + "can_add_image", + "can_delete_image", + "can_add_where_tag", + "can_see_where_tag", + "can_delete_where_tag", + "can_create_counterparty", + //V300 New + "can_see_bank_routing_scheme", + "can_see_bank_routing_address", + "can_see_bank_account_routing_scheme", + "can_see_bank_account_routing_address", + "can_see_other_bank_routing_scheme", + "can_see_other_bank_routing_address", + "can_see_other_account_routing_scheme", + "can_see_other_account_routing_address", + + //v310 + "can_query_available_funds", + "can_add_transaction_request_to_own_account", + "can_add_transaction_request_to_any_account", + "can_see_bank_account_credit_limit", + //v400 + "can_create_direct_debit", + "can_create_standing_order", + + //payments + "can_add_transaction_request_to_any_account", + + "can_see_transaction_request_types", + "can_see_transaction_requests", + "can_see_available_views_for_bank_account", + "can_update_bank_account_label", + "can_create_custom_view", + "can_delete_custom_view", + "can_update_custom_view", + "can_see_views_with_permissions_for_one_user", + "can_see_views_with_permissions_for_all_users", + "can_grant_access_to_custom_views", + "can_revoke_access_to_custom_views" + ) + val createSystemViewJsonV500 = CreateViewJsonV500( name = "_test", description = "This view is for family", @@ -196,88 +291,7 @@ object SwaggerDefinitionsJSON { is_public = false, which_alias_to_use = "family", hide_metadata_if_alias_used = false, - allowed_actions = List( - "can_see_transaction_this_bank_account", - "can_see_transaction_other_bank_account", - "can_see_transaction_metadata", - "can_see_transaction_label", - "can_see_transaction_amount", - "can_see_transaction_type", - "can_see_transaction_currency", - "can_see_transaction_start_date", - "can_see_transaction_finish_date", - "can_see_transaction_balance", - "can_see_comments", - "can_see_narrative", - "can_see_tags", - "can_see_images", - "can_see_bank_account_owners", - "can_see_bank_account_type", - "can_see_bank_account_balance", - "can_see_bank_account_currency", - "can_see_bank_account_label", - "can_see_bank_account_national_identifier", - "can_see_bank_account_swift_bic", - "can_see_bank_account_iban", - "can_see_bank_account_number", - "can_see_bank_account_bank_name", - "can_see_other_account_national_identifier", - "can_see_other_account_swift_bic", - "can_see_other_account_iban", - "can_see_other_account_bank_name", - "can_see_other_account_number", - "can_see_other_account_metadata", - "can_see_other_account_kind", - "can_see_more_info", - "can_see_url", - "can_see_image_url", - "can_see_open_corporates_url", - "can_see_corporate_location", - "can_see_physical_location", - "can_see_public_alias", - "can_see_private_alias", - "can_add_more_info", - "can_add_url", - "can_add_image_url", - "can_add_open_corporates_url", - "can_add_corporate_location", - "can_add_physical_location", - "can_add_public_alias", - "can_add_private_alias", - "can_delete_corporate_location", - "can_delete_physical_location", - "can_edit_narrative", - "can_add_comment", - "can_delete_comment", - "can_add_tag", - "can_delete_tag", - "can_add_image", - "can_delete_image", - "can_add_where_tag", - "can_see_where_tag", - "can_delete_where_tag", - "can_create_counterparty", - //V300 New - "can_see_bank_routing_scheme", - "can_see_bank_routing_address", - "can_see_bank_account_routing_scheme", - "can_see_bank_account_routing_address", - "can_see_other_bank_routing_scheme", - "can_see_other_bank_routing_address", - "can_see_other_account_routing_scheme", - "can_see_other_account_routing_address", - //v310 - "can_query_available_funds", - "can_add_transaction_request_to_own_account", - "can_add_transaction_request_to_any_account", - "can_see_bank_account_credit_limit", - //v400 - "can_create_direct_debit", - "can_create_standing_order", - - //payments - "can_add_transaction_request_to_any_account" - ), + allowed_actions = allowedActionsV500, // Version 5.0.0 can_grant_access_to_views = Some(List("owner")), can_revoke_access_to_views = Some(List("owner")) @@ -370,78 +384,7 @@ object SwaggerDefinitionsJSON { metadata_view = SYSTEM_OWNER_VIEW_ID, which_alias_to_use = "family", hide_metadata_if_alias_used = true, - allowed_actions = List( - "can_see_transaction_this_bank_account", - "can_see_transaction_other_bank_account", - "can_see_transaction_metadata", - "can_see_transaction_label", - "can_see_transaction_amount", - "can_see_transaction_type", - "can_see_transaction_currency", - "can_see_transaction_start_date", - "can_see_transaction_finish_date", - "can_see_transaction_balance", - "can_see_comments", - "can_see_narrative", "can_see_tags", - "can_see_images", - "can_see_bank_account_owners", - "can_see_bank_account_type", - "can_see_bank_account_balance", - "can_see_bank_account_currency", - "can_see_bank_account_label", - "can_see_bank_account_national_identifier", - "can_see_bank_account_swift_bic", - "can_see_bank_account_iban", - "can_see_bank_account_number", - "can_see_bank_account_bank_name", - "can_see_other_account_national_identifier", - "can_see_other_account_swift_bic", - "can_see_other_account_iban", - "can_see_other_account_bank_name", - "can_see_other_account_number", - "can_see_other_account_metadata", - "can_see_other_account_kind", - "can_see_more_info", - "can_see_url", - "can_see_image_url", - "can_see_open_corporates_url", - "can_see_corporate_location", - "can_see_physical_location", - "can_see_public_alias", - "can_see_private_alias", - "can_add_more_info", - "can_add_url", - "can_add_image_url", - "can_add_open_corporates_url", - "can_add_corporate_location", - "can_add_physical_location", - "can_add_public_alias", - "can_add_private_alias", - "can_delete_corporate_location", - "can_delete_physical_location", - "can_edit_narrative", - "can_add_comment", - "can_delete_comment", - "can_add_tag", - "can_delete_tag", - "can_add_image", - "can_delete_image", - "can_add_where_tag", - "can_see_where_tag", - "can_delete_where_tag", - "can_create_counterparty", - //V300 New - "can_see_bank_routing_scheme", - "can_see_bank_routing_address", - "can_see_bank_account_routing_scheme", - "can_see_bank_account_routing_address", - "can_see_other_bank_routing_scheme", - "can_see_other_bank_routing_address", - "can_see_other_account_routing_scheme", - "can_see_other_account_routing_address", - //v310 - "can_query_available_funds" - ), + allowed_actions = allowedActionsV500, // Version 5.0.0 can_grant_access_to_views = Some(List("owner")), can_revoke_access_to_views = Some(List("owner")) diff --git a/obp-api/src/main/scala/code/api/UKOpenBanking/v3_1_0/UtilForUKV310.scala b/obp-api/src/main/scala/code/api/UKOpenBanking/v3_1_0/UtilForUKV310.scala deleted file mode 100644 index b7c18973b0..0000000000 --- a/obp-api/src/main/scala/code/api/UKOpenBanking/v3_1_0/UtilForUKV310.scala +++ /dev/null @@ -1,62 +0,0 @@ -package code.api.UKOpenBanking.v3_1_0 - -import code.api.util.APIUtil.{canGrantAccessToViewCommon, canRevokeAccessToViewCommon} -import code.api.util.CallContext -import code.api.util.ErrorMessages.UserNoOwnerView -import code.views.Views -import com.openbankproject.commons.model.{User, ViewIdBankIdAccountId} -import net.liftweb.common.{Empty, Failure, Full} - -import scala.collection.immutable.List - -object UtilForUKV310 { - def grantAccessToViews(user: User, views: List[ViewIdBankIdAccountId], callContext: Option[CallContext]): Full[Boolean] = { - val result = - for { - view <- views - } yield { - if (canGrantAccessToViewCommon(view.bankId, view.accountId, user, callContext)) { - val viewIdBankIdAccountId = ViewIdBankIdAccountId(view.viewId, view.bankId, view.accountId) - Views.views.vend.systemView(view.viewId) match { - case Full(systemView) => - Views.views.vend.grantAccessToSystemView(view.bankId, view.accountId, systemView, user) - case _ => // It's not system view - Views.views.vend.grantAccessToCustomView(viewIdBankIdAccountId, user) - } - } else { - Failure(UserNoOwnerView+" user_id : " + user.userId + ". account : " + view.accountId.value, Empty, Empty) - } - } - if (result.forall(_.isDefined)) - Full(true) - else { - println(result.filter(_.isDefined == false)) - Full(false) - } - } - - def revokeAccessToViews(user: User, views: List[ViewIdBankIdAccountId], callContext: Option[CallContext]): Full[Boolean] = { - val result = - for { - view <- views - } yield { - if (canRevokeAccessToViewCommon(view.bankId, view.accountId, user, callContext)) { - val viewIdBankIdAccountId = ViewIdBankIdAccountId(view.viewId, view.bankId, view.accountId) - Views.views.vend.systemView(view.viewId) match { - case Full(systemView) => - Views.views.vend.revokeAccessToSystemView(view.bankId, view.accountId, systemView, user) - case _ => // It's not system view - Views.views.vend.revokeAccess(viewIdBankIdAccountId, user) - } - } else { - Failure(UserNoOwnerView+" user_id : " + user.userId + ". account : " + view.accountId.value, Empty, Empty) - } - } - if (result.forall(_.isDefined)) - Full(true) - else { - println(result.filter(_.isDefined == false)) - Full(false) - } - } -} diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index 5e4fba6bc6..a3df2d673f 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -14,6 +14,7 @@ import code.model._ import code.transactionrequests.TransactionRequests.TransactionRequestTypes.SEPA_CREDIT_TRANSFERS import code.transactionrequests.TransactionRequests.{PaymentServiceTypes, TransactionRequestTypes} import code.util.Helper +import code.views.Views import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model._ @@ -565,10 +566,16 @@ $additionalInstructions _ <- Helper.booleanToFuture(invalidIban, cc=callContext) { ibanChecker.isValid == true } (toAccount, callContext) <- NewStyle.function.getToBankAccountByIban(toAccountIban, callContext) - _ <- if (u.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext)) Future.successful(Full(Unit)) - else NewStyle.function.hasEntitlement(fromAccount.bankId.value, u.userId, ApiRole.canCreateAnyTransactionRequest, callContext, InsufficientAuthorisationToCreateTransactionRequest) + //no accountAccess and no canAddTransactionRequestToOwnAccount ==> this will not throw exception,only return false! + anyViewContainsCanAddTransactionRequestToAnyAccountPermission = Views.views.vend.permission(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), u) + .map(_.views.map(_.canAddTransactionRequestToAnyAccount).find(_.==(true)).getOrElse(false)).getOrElse(false) - // Prevent default value for transaction request type (at least). + _ <- if (anyViewContainsCanAddTransactionRequestToAnyAccountPermission) + Future.successful(Full(Unit)) + else + NewStyle.function.hasEntitlement(fromAccount.bankId.value, u.userId, ApiRole.canCreateAnyTransactionRequest, callContext, InsufficientAuthorisationToCreateTransactionRequest) + + // Prevent default value for transaction request type (at least). _ <- Helper.booleanToFuture(s"From Account Currency is ${fromAccount.currency}, but Requested Transaction Currency is: ${transDetailsJson.instructedAmount.currency}", cc=callContext) { transDetailsJson.instructedAmount.currency == fromAccount.currency } diff --git a/obp-api/src/main/scala/code/api/constant/constant.scala b/obp-api/src/main/scala/code/api/constant/constant.scala index 5a38fc551a..8b70623d67 100644 --- a/obp-api/src/main/scala/code/api/constant/constant.scala +++ b/obp-api/src/main/scala/code/api/constant/constant.scala @@ -29,6 +29,7 @@ object Constant extends MdcLoggable { final val SYSTEM_FIREHOSE_VIEW_ID = "firehose" final val SYSTEM_STANDARD_VIEW_ID = "standard" final val SYSTEM_STAGE_ONE_VIEW_ID = "StageOne" + final val SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID = "ManageCustomViews" final val SYSTEM_READ_ACCOUNTS_BASIC_VIEW_ID = "ReadAccountsBasic" final val SYSTEM_READ_ACCOUNTS_DETAIL_VIEW_ID = "ReadAccountsDetail" final val SYSTEM_READ_BALANCES_VIEW_ID = "ReadBalances" @@ -40,6 +41,25 @@ object Constant extends MdcLoggable { final val SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID = "ReadBalancesBerlinGroup" final val SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID = "ReadTransactionsBerlinGroup" + //TODO, this need to be double check + final val ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT = List( + SYSTEM_OWNER_VIEW_ID, + SYSTEM_AUDITOR_VIEW_ID, + SYSTEM_ACCOUNTANT_VIEW_ID, + SYSTEM_FIREHOSE_VIEW_ID, + SYSTEM_STANDARD_VIEW_ID, + SYSTEM_STAGE_ONE_VIEW_ID, + SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, + SYSTEM_READ_ACCOUNTS_BASIC_VIEW_ID, + SYSTEM_READ_ACCOUNTS_DETAIL_VIEW_ID, + SYSTEM_READ_BALANCES_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_BASIC_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_DEBITS_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_DETAIL_VIEW_ID, + SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID, + SYSTEM_READ_BALANCES_BERLIN_GROUP_VIEW_ID, + SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID + ) //These are the default incoming and outgoing account ids. we will create both during the boot.scala. final val INCOMING_SETTLEMENT_ACCOUNT_ID = "OBP-INCOMING-SETTLEMENT-ACCOUNT" final val OUTGOING_SETTLEMENT_ACCOUNT_ID = "OBP-OUTGOING-SETTLEMENT-ACCOUNT" diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 815060ca54..bd648135f5 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -47,7 +47,7 @@ import code.api.dynamic.endpoint.helper.{DynamicEndpointHelper, DynamicEndpoints import code.api.oauth1a.Arithmetics import code.api.oauth1a.OauthParams._ import code.api.util.APIUtil.ResourceDoc.{findPathVariableNames, isPathVariable} -import code.api.util.ApiRole.{canCreateProduct, canCreateProductAtAnyBank} +import code.api.util.ApiRole.{canCreateAnyTransactionRequest, canCreateProduct, canCreateProductAtAnyBank} import code.api.util.ApiTag.{ResourceDocTag, apiTagBank} import code.api.util.Glossary.GlossaryItem import code.api.util.RateLimitingJson.CallLimit @@ -3615,6 +3615,24 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ } } + final def checkAuthorisationToCreateTransactionRequest(viewId: ViewId, bankAccountId: BankIdAccountId, user: User, callContext: Option[CallContext]): Box[Boolean] = { + lazy val hasCanCreateAnyTransactionRequestRole = APIUtil.hasEntitlement(bankAccountId.bankId.value, user.userId, canCreateAnyTransactionRequest) + + lazy val view = APIUtil.checkViewAccessAndReturnView(viewId, bankAccountId, Some(user), callContext) + + lazy val canAddTransactionRequestToAnyAccount = view.map(_.canAddTransactionRequestToAnyAccount).getOrElse(false) + + //1st check the admin level role/entitlement `canCreateAnyTransactionRequest` + if (hasCanCreateAnyTransactionRequestRole) { + Full(true) + //2rd: check if the user have the view access and the view has the `canAddTransactionRequestToAnyAccount` permission + } else if (canAddTransactionRequestToAnyAccount) { + Full(true) + } else { + Empty + } + } + // TODO Use this in code as a single point of entry whenever we need to check owner view def isOwnerView(viewId: ViewId): Boolean = { viewId.value == SYSTEM_OWNER_VIEW_ID || @@ -4047,13 +4065,83 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ case _ => false } - def canGrantAccessToViewCommon(bankId: BankId, accountId: AccountId, user: User, callContext: Option[CallContext]): Boolean = { - user.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext) || // TODO Use an action instead of the owner view - AccountHolders.accountHolders.vend.getAccountHolders(bankId, accountId).exists(_.userId == user.userId) + def canGrantAccessToView(bankId: BankId, accountId: AccountId, viewIdTobeGranted : ViewId, user: User, callContext: Option[CallContext]): Boolean = { + //all the permission this user have for the bankAccount + val permission: Box[Permission] = Views.views.vend.permission(BankIdAccountId(bankId, accountId), user) + + //1. if viewIdTobeGranted is systemView. just compare all the permissions + if(checkSystemViewIdOrName(viewIdTobeGranted.value)){ + val allCanGrantAccessToViewsPermissions: List[String] = permission + .map(_.views.map(_.canGrantAccessToViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct + + allCanGrantAccessToViewsPermissions.contains(viewIdTobeGranted.value) + } else{ + //2. if viewIdTobeGranted is customView, we only need to check the `canGrantAccessToCustomViews`. + val allCanGrantAccessToCustomViewsPermissions: List[Boolean] = permission.map(_.views.map(_.canGrantAccessToCustomViews)).getOrElse(Nil) + + allCanGrantAccessToCustomViewsPermissions.contains(true) + } } - def canRevokeAccessToViewCommon(bankId: BankId, accountId: AccountId, user: User, callContext: Option[CallContext]): Boolean = { - user.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext) || // TODO Use an action instead of the owner view - AccountHolders.accountHolders.vend.getAccountHolders(bankId, accountId).exists(_.userId == user.userId) + + def canGrantAccessToMultipleViews(bankId: BankId, accountId: AccountId, viewIdsTobeGranted : List[ViewId], user: User, callContext: Option[CallContext]): Boolean = { + //all the permission this user have for the bankAccount + val permissionBox = Views.views.vend.permission(BankIdAccountId(bankId, accountId), user) + + //check if we can grant all systemViews Access + val allCanGrantAccessToViewsPermissions: List[String] = permissionBox.map(_.views.map(_.canGrantAccessToViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct + val allSystemViewsAccessTobeGranted: List[String] = viewIdsTobeGranted.map(_.value).distinct.filter(checkSystemViewIdOrName) + val canGrantAccessToAllSystemViews = allSystemViewsAccessTobeGranted.forall(allCanGrantAccessToViewsPermissions.contains) + + if (viewIdsTobeGranted.map(_.value).distinct.find(checkCustomViewIdOrName).isDefined){ + //check if we can grant all customViews Access + val allCanGrantAccessToCustomViewsPermissions: List[Boolean] = permissionBox.map(_.views.map(_.canGrantAccessToCustomViews)).getOrElse(Nil) + val canGrantAccessToAllCustomViews = allCanGrantAccessToCustomViewsPermissions.contains(true) + //we need merge both system and custom access + canGrantAccessToAllSystemViews && canGrantAccessToAllCustomViews + } else { + canGrantAccessToAllSystemViews + } + } + + def canRevokeAccessToView(bankId: BankId, accountId: AccountId, viewIdToBeRevoked : ViewId, user: User, callContext: Option[CallContext]): Boolean = { + //all the permission this user have for the bankAccount + val permission: Box[Permission] = Views.views.vend.permission(BankIdAccountId(bankId, accountId), user) + + //1. if viewIdTobeRevoked is systemView. just compare all the permissions + if (checkSystemViewIdOrName(viewIdToBeRevoked.value)) { + val allCanRevokeAccessToViewsPermissions: List[String] = permission + .map(_.views.map(_.canRevokeAccessToViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct + + allCanRevokeAccessToViewsPermissions.contains(viewIdToBeRevoked.value) + } else { + //2. if viewIdTobeRevoked is customView, we only need to check the `canRevokeAccessToCustomViews`. + val allCanRevokeAccessToCustomViewsPermissions: List[Boolean] = permission.map(_.views.map(_.canRevokeAccessToCustomViews)).getOrElse(Nil) + + allCanRevokeAccessToCustomViewsPermissions.contains(true) + } + } + + def canRevokeAccessToAllViews(bankId: BankId, accountId: AccountId, user: User, callContext: Option[CallContext]): Boolean = { + + val permissionBox = Views.views.vend.permission(BankIdAccountId(bankId, accountId), user) + + //check if we can revoke all systemViews Access + val allCanRevokeAccessToViewsPermissions: List[String] = permissionBox.map(_.views.map(_.canRevokeAccessToViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct + val allAccountAccessSystemViews: List[String] = permissionBox.map(_.views.map(_.viewId.value)).getOrElse(Nil).distinct.filter(checkSystemViewIdOrName) + val canRevokeAccessToAllSystemViews = allAccountAccessSystemViews.forall(allCanRevokeAccessToViewsPermissions.contains) + + if (allAccountAccessSystemViews.find(checkCustomViewIdOrName).isDefined){ + //check if we can revoke all customViews Access + val allCanRevokeAccessToCustomViewsPermissions: List[Boolean] = permissionBox.map(_.views.map(_.canRevokeAccessToCustomViews)).getOrElse(Nil) + val canRevokeAccessToAllCustomViews = allCanRevokeAccessToCustomViewsPermissions.contains(true) + //we need merge both system and custom access + canRevokeAccessToAllSystemViews && canRevokeAccessToAllCustomViews + }else if(allAccountAccessSystemViews.find(checkSystemViewIdOrName).isDefined){ + canRevokeAccessToAllSystemViews + }else{ + false + } + } def getJValueFromJsonFile(path: String) = { 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 fb5d62e13f..7954296e17 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -6,6 +6,8 @@ import java.util.regex.Pattern import com.openbankproject.commons.model.enums.TransactionRequestStatus._ import code.api.Constant._ import code.api.util.ApiRole.{CanCreateAnyTransactionRequest, canCreateEntitlementAtAnyBank, canCreateEntitlementAtOneBank} +import code.views.system.ViewDefinition +import net.liftweb.util.StringHelpers object ErrorMessages { import code.api.util.APIUtil._ @@ -167,7 +169,10 @@ object ErrorMessages { val GatewayLoginCannotGetOrCreateUser = "OBP-20045: Cannot get or create user during GatewayLogin process." val GatewayLoginNoJwtForResponse = "OBP-20046: There is no useful value for JWT." - val UserMissOwnerViewOrNotAccountHolder = "OBP-20047: User must have access to the owner view or must be an account holder." + val UserLacksPermissionCanGrantAccessToViewForTargetAccount = + s"OBP-20047: The current user does not have access to a view which lists the target account in ${ViewDefinition.canGrantAccessToViews_.dbColumnName} permissions" + val UserLacksPermissionCanRevokeAccessToViewForTargetAccount = + s"OBP-20048: The current user does not have access to a view which lists the target account in ${ViewDefinition.canRevokeAccessToViews_.dbColumnName} permissions" val UserNotSuperAdmin = "OBP-20050: Current User is not a Super Admin!" @@ -181,7 +186,6 @@ object ErrorMessages { val ConsumerIsDisabled = "OBP-20058: Consumer is disabled." val CouldNotGetUserLockStatus = "OBP-20059: Could not get the lock status of the user." val NoViewReadAccountsBerlinGroup = s"OBP-20060: User does not have access to the view $SYSTEM_READ_ACCOUNTS_BERLIN_GROUP_VIEW_ID." - val NoAccountAccessOnView = "OBP-20061: Current user does not have access to the view " val FrequencyPerDayError = "OBP-20062: Frequency per day must be greater than 0." val FrequencyPerDayMustBeOneError = "OBP-20063: Frequency per day must be equal to 1 in case of one-off access." @@ -207,6 +211,7 @@ object ErrorMessages { val UserNotSuperAdminOrMissRole = "OBP-20101: Current User is not super admin or is missing entitlements:" val CannotGetOrCreateUser = "OBP-20102: Cannot get or create user." val InvalidUserProvider = "OBP-20103: Invalid DAuth User Provider." + val UserNotFoundByProviderAndProvideId= "OBP-20104: User not found by PROVIDER and PROVIDER_ID." // OAuth 2 val ApplicationNotIdentified = "OBP-20200: The application cannot be identified. " @@ -294,7 +299,14 @@ object ErrorMessages { val CreateCardError = "OBP-30032: Could not insert the Card" val UpdateCardError = "OBP-30033: Could not update the Card" - val ViewIdNotSupported = "OBP-30034: This ViewId is not supported. Only support four now: Owner, Accountant, Auditor, StageOne, Standard, _Public." + val ViewIdNotSupported = s"OBP-30034: This ViewId is not supported. Only the following can be used: " + + s"$SYSTEM_OWNER_VIEW_ID, " + + s"$SYSTEM_ACCOUNTANT_VIEW_ID, " + + s"$SYSTEM_AUDITOR_VIEW_ID, " + + s"$SYSTEM_STAGE_ONE_VIEW_ID, " + + s"$SYSTEM_STANDARD_VIEW_ID, " + + s"$SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, " + + s"$CUSTOM_PUBLIC_VIEW_ID." val UserCustomerLinkNotFound = "OBP-30035: User Customer Link not found" @@ -455,6 +467,8 @@ object ErrorMessages { val DeleteCustomViewError = "OBP-30256: Could not delete the custom view" val CannotFindCustomViewError = "OBP-30257: Could not find the custom view" val SystemViewCannotBePublicError = "OBP-30258: System view cannot be public" + val CreateCustomViewError = "OBP-30259: Could not create the custom view" + val UpdateCustomViewError = "OBP-30260: Could not update the custom view" val TaxResidenceNotFound = "OBP-30300: Tax Residence not found by TAX_RESIDENCE_ID. " val CustomerAddressNotFound = "OBP-30310: Customer's Address not found by CUSTOMER_ADDRESS_ID. " @@ -467,6 +481,7 @@ object ErrorMessages { val DeleteCounterpartyError = "OBP-30317: Could not delete the Counterparty." val DeleteCounterpartyMetadataError = "OBP-30318: Could not delete CounterpartyMetadata" + val UpdateBankAccountLabelError = "OBP-30319: Could not update Bank Account Label." // Branch related messages val BranchesNotFoundLicense = "OBP-32001: No branches available. License may not be set." @@ -539,9 +554,9 @@ object ErrorMessages { val InsufficientAuthorisationToCreateTransactionRequest = "OBP-40002: Insufficient authorisation to create TransactionRequest. " + "The Transaction Request could not be created " + "because the login user doesn't have access to the view of the from account " + - s"or the view don't have the `${CanCreateAnyTransactionRequest.toString()}` permission " + - "or your consumer doesn't not have the access to the view of the from account " + - "or you don't have the role CanCreateAnyTransactionRequest." + "or the consumer doesn't have the access to the view of the from account " + + s"or the login user does not have the `${CanCreateAnyTransactionRequest.toString()}` role " + + s"or the view does not have the permission ${StringHelpers.snakify(ViewDefinition.canAddTransactionRequestToAnyAccount_.dbColumnName).dropRight(1)}." val InvalidTransactionRequestCurrency = "OBP-40003: Transaction Request Currency must be the same as From Account Currency." val InvalidTransactionRequestId = "OBP-40004: Transaction Request Id not found." val InsufficientAuthorisationToCreateTransactionType = "OBP-40005: Insufficient authorisation to Create Transaction Type offered by the bank. The Request could not be created because you don't have access to CanCreateTransactionType." @@ -731,6 +746,8 @@ object ErrorMessages { // InvalidConsumerCredentials -> 401, // or 400 UsernameHasBeenLocked -> 401, UserNoPermissionAccessView -> 403, + UserLacksPermissionCanGrantAccessToViewForTargetAccount -> 403, + UserLacksPermissionCanRevokeAccessToViewForTargetAccount -> 403, UserNotSuperAdminOrMissRole -> 403, ConsumerHasMissingRoles -> 403, UserNotFoundByProviderAndUsername -> 404, 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 e61a7cda0d..6d76959291 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -519,35 +519,59 @@ object NewStyle extends MdcLoggable{ } } - def permissions(account: BankAccount, user: User, callContext: Option[CallContext]) = Future { - account.permissions(user, callContext) + def permission(bankId: BankId,accountId: AccountId, user: User, callContext: Option[CallContext]) = Future { + Views.views.vend.permission(BankIdAccountId(bankId, accountId), user) } map { fullBoxOrException(_) } map { unboxFull(_) } - def removeView(account: BankAccount, user: User, viewId: ViewId, callContext: Option[CallContext]) = Future { - account.removeView(user, viewId, callContext) - } map { fullBoxOrException(_) - } map { unboxFull(_) } - def grantAccessToView(account: BankAccount, u: User, viewIdBankIdAccountId : ViewIdBankIdAccountId, provider : String, providerId: String, callContext: Option[CallContext]) = Future { account.grantAccessToView(u, viewIdBankIdAccountId, provider, providerId, callContext: Option[CallContext]) - } map { fullBoxOrException(_) - } map { unboxFull(_) } + } map { + x => (unboxFullOrFail( + x, + callContext, + UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewId(${viewIdBankIdAccountId.viewId.value}) and current UserId(${u.userId})", + 403), + callContext + ) + } def grantAccessToMultipleViews(account: BankAccount, u: User, viewIdBankIdAccountIds : List[ViewIdBankIdAccountId], provider : String, providerId: String, callContext: Option[CallContext]) = Future { account.grantAccessToMultipleViews(u, viewIdBankIdAccountIds, provider, providerId, callContext: Option[CallContext]) - } map { fullBoxOrException(_) - } map { unboxFull(_) } - + } map { + x => + (unboxFullOrFail( + x, + callContext, + UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewIds(${viewIdBankIdAccountIds}) and current UserId(${u.userId})", + 403), + callContext + ) + } def revokeAccessToView(account: BankAccount, u: User, viewIdBankIdAccountId : ViewIdBankIdAccountId, provider : String, providerId: String, callContext: Option[CallContext]) = Future { account.revokeAccessToView(u, viewIdBankIdAccountId, provider, providerId, callContext: Option[CallContext]) - } map { fullBoxOrException(_) - } map { unboxFull(_) } - + } map { + x => + (unboxFullOrFail( + x, + callContext, + UserLacksPermissionCanRevokeAccessToViewForTargetAccount + s"Current ViewId(${viewIdBankIdAccountId.viewId.value}) and current UserId(${u.userId})", + 403), + callContext + ) + } def revokeAllAccountAccess(account: BankAccount, u: User, provider : String, providerId: String, callContext: Option[CallContext]) = Future { account.revokeAllAccountAccess(u, provider, providerId, callContext) - } map { fullBoxOrException(_) - } map { unboxFull(_) } + } map { + x => + (unboxFullOrFail( + x, + callContext, + UserLacksPermissionCanRevokeAccessToViewForTargetAccount + s"current UserId(${u.userId})", + 403), + callContext + ) + } def moderatedBankAccountCore(account: BankAccount, view: View, user: Box[User], callContext: Option[CallContext]) = Future { account.moderatedBankAccountCore(view, BankIdAccountId(account.bankId, account.accountId), user, callContext) @@ -587,7 +611,7 @@ object NewStyle extends MdcLoggable{ Future{ APIUtil.checkViewAccessAndReturnView(viewId, bankAccountId, user, callContext) } map { - unboxFullOrFail(_, callContext, s"$NoAccountAccessOnView ${viewId.value}", 403) + unboxFullOrFail(_, callContext, s"$UserNoPermissionAccessView ${viewId.value}", 403) } } def checkViewsAccessAndReturnView(firstView : ViewId, secondView : ViewId, bankAccountId: BankIdAccountId, user: Option[User], callContext: Option[CallContext]) : Future[View] = { @@ -620,22 +644,7 @@ object NewStyle extends MdcLoggable{ def checkAuthorisationToCreateTransactionRequest(viewId : ViewId, bankAccountId: BankIdAccountId, user: User, callContext: Option[CallContext]) : Future[Boolean] = { Future{ - - lazy val hasCanCreateAnyTransactionRequestRole = APIUtil.hasEntitlement(bankAccountId.bankId.value, user.userId, canCreateAnyTransactionRequest) - - lazy val view = APIUtil.checkViewAccessAndReturnView(viewId, bankAccountId, Some(user), callContext) - - lazy val canAddTransactionRequestToAnyAccount = view.map(_.canAddTransactionRequestToAnyAccount).getOrElse(false) - - //1st check the admin level role/entitlement `canCreateAnyTransactionRequest` - if(hasCanCreateAnyTransactionRequestRole) { - Full(true) - //2rd: check if the user have the view access and the view has the `canAddTransactionRequestToAnyAccount` permission - } else if (canAddTransactionRequestToAnyAccount) { - Full(true) - } else{ - Empty - } + APIUtil.checkAuthorisationToCreateTransactionRequest(viewId : ViewId, bankAccountId: BankIdAccountId, user: User, callContext: Option[CallContext]) } map { unboxFullOrFail(_, callContext, s"$InsufficientAuthorisationToCreateTransactionRequest " + s"Current ViewId(${viewId.value})," + @@ -707,18 +716,6 @@ object NewStyle extends MdcLoggable{ } } } - - def canGrantAccessToView(bankId: BankId, accountId: AccountId, user: User, callContext: Option[CallContext]) : Future[Box[Boolean]] = { - Helper.wrapStatementToFuture(UserMissOwnerViewOrNotAccountHolder) { - canGrantAccessToViewCommon(bankId, accountId, user, callContext) - } - } - - def canRevokeAccessToView(bankId: BankId, accountId: AccountId, user: User, callContext: Option[CallContext]) : Future[Box[Boolean]] = { - Helper.wrapStatementToFuture(UserMissOwnerViewOrNotAccountHolder) { - canRevokeAccessToViewCommon(bankId, accountId, user, callContext) - } - } def createSystemView(view: CreateViewJson, callContext: Option[CallContext]) : Future[View] = { Views.views.vend.createSystemView(view) map { unboxFullOrFail(_, callContext, s"$CreateSystemViewError") @@ -3989,6 +3986,28 @@ object NewStyle extends MdcLoggable{ .slice(offset.getOrElse("0").toInt, offset.getOrElse("0").toInt + limit.getOrElse("100").toInt) , callContext) } + + def createCustomView(bankAccountId: BankIdAccountId, createViewJson: CreateViewJson, callContext: Option[CallContext]): OBPReturnType[View] = + Future { + Views.views.vend.createCustomView(bankAccountId, createViewJson) + } map { i => + (unboxFullOrFail(i, callContext, s"$CreateCustomViewError"), callContext) + } + + def updateCustomView(bankAccountId : BankIdAccountId, viewId : ViewId, viewUpdateJson : UpdateViewJSON, callContext: Option[CallContext]): OBPReturnType[View] = + Future { + Views.views.vend.updateCustomView(bankAccountId, viewId, viewUpdateJson) + } map { i => + (unboxFullOrFail(i, callContext, s"$UpdateCustomViewError"), callContext) + } + + def removeCustomView(viewId: ViewId, bankAccountId: BankIdAccountId, callContext: Option[CallContext]) = + Future { + Views.views.vend.removeCustomView(viewId, bankAccountId) + } map { i => + (unboxFullOrFail(i, callContext, s"$DeleteCustomViewError"), callContext) + } + } } diff --git a/obp-api/src/main/scala/code/api/util/migration/Migration.scala b/obp-api/src/main/scala/code/api/util/migration/Migration.scala index 1038cd97cb..d028d873be 100644 --- a/obp-api/src/main/scala/code/api/util/migration/Migration.scala +++ b/obp-api/src/main/scala/code/api/util/migration/Migration.scala @@ -63,6 +63,7 @@ object Migration extends MdcLoggable { dummyScript() addAccountAccessConsumerId() populateTableViewDefinition() + populateMigrationOfViewDefinitionPermissions() populateTableAccountAccess() generateAndPopulateMissingCustomerUUIDs(startedBeforeSchemifier) generateAndPopulateMissingConsumersUUIDs(startedBeforeSchemifier) @@ -127,6 +128,14 @@ object Migration extends MdcLoggable { } } + + private def populateMigrationOfViewDefinitionPermissions(): Boolean = { + val name = nameOf(populateMigrationOfViewDefinitionPermissions) + runOnce(name) { + MigrationOfViewDefinitionPermissions.populate(name) + } + } + private def generateAndPopulateMissingCustomerUUIDs(startedBeforeSchemifier: Boolean): Boolean = { if(startedBeforeSchemifier == true) { logger.warn(s"Migration.database.generateAndPopulateMissingCustomerUUIDs(true) cannot be run before Schemifier.") diff --git a/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionPermissions.scala b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionPermissions.scala new file mode 100644 index 0000000000..5711385c86 --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/migration/MigrationOfViewDefinitionPermissions.scala @@ -0,0 +1,96 @@ +package code.api.util.migration + +import code.api.Constant.{ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT, SYSTEM_OWNER_VIEW_ID, SYSTEM_STANDARD_VIEW_ID} +import code.api.util.APIUtil +import code.api.util.migration.Migration.{DbFunction, saveLog} +import code.views.system.ViewDefinition +import net.liftweb.mapper.{By, DB, NullRef} +import net.liftweb.util.DefaultConnectionIdentifier + +object MigrationOfViewDefinitionPermissions { + def populate(name: String): Boolean = { + DbFunction.tableExists(ViewDefinition, (DB.use(DefaultConnectionIdentifier){ conn => conn})) match { + case true => + val startDate = System.currentTimeMillis() + val commitId: String = APIUtil.gitCommit + val ownerView = ViewDefinition.find( + NullRef(ViewDefinition.bank_id), + NullRef(ViewDefinition.account_id), + By(ViewDefinition.view_id, SYSTEM_OWNER_VIEW_ID), + By(ViewDefinition.isSystem_,true) + ).map(view => + view + .canSeeTransactionRequestTypes_(true) + .canSeeTransactionRequests_(true) + .canSeeAvailableViewsForBankAccount_(true) + .canUpdateBankAccountLabel_(true) + .canSeeViewsWithPermissionsForOneUser_(true) + .canSeeViewsWithPermissionsForAllUsers_(true) + .canCreateCustomView_(false) + .canDeleteCustomView_(false) + .canUpdateCustomView_(false) + .canGrantAccessToCustomViews_(false) + .canRevokeAccessToCustomViews_(false) + .canGrantAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) + .canRevokeAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) + .save + ).head + + val standardView = ViewDefinition.find( + NullRef(ViewDefinition.bank_id), + NullRef(ViewDefinition.account_id), + By(ViewDefinition.view_id, SYSTEM_STANDARD_VIEW_ID), + By(ViewDefinition.isSystem_,true) + ).map(view => + view + .canSeeTransactionRequestTypes_(true) + .canSeeTransactionRequests_(true) + .canSeeAvailableViewsForBankAccount_(true) + .canUpdateBankAccountLabel_(true) + .canSeeViewsWithPermissionsForOneUser_(true) + .canSeeViewsWithPermissionsForAllUsers_(true) + .canCreateCustomView_(false) + .canDeleteCustomView_(false) + .canUpdateCustomView_(false) + .canGrantAccessToCustomViews_(false) + .canRevokeAccessToCustomViews_(false) + .canGrantAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) + .canRevokeAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) + .save + ).head + + + val isSuccessful = ownerView && standardView + val endDate = System.currentTimeMillis() + + val comment: String = + s"""ViewDefinition system $SYSTEM_OWNER_VIEW_ID and $SYSTEM_STANDARD_VIEW_ID views, update the following rows to true: + |${ViewDefinition.canSeeTransactionRequestTypes_.dbColumnName} + |${ViewDefinition.canSeeTransactionRequests_.dbColumnName} + |${ViewDefinition.canSeeAvailableViewsForBankAccount_.dbColumnName} + |${ViewDefinition.canUpdateBankAccountLabel_.dbColumnName} + |${ViewDefinition.canCreateCustomView_.dbColumnName} + |${ViewDefinition.canDeleteCustomView_.dbColumnName} + |${ViewDefinition.canUpdateCustomView_.dbColumnName} + |${ViewDefinition.canSeeViewsWithPermissionsForAllUsers_.dbColumnName} + |${ViewDefinition.canSeeViewsWithPermissionsForOneUser_.dbColumnName} + |${ViewDefinition.canGrantAccessToCustomViews_.dbColumnName} + |${ViewDefinition.canRevokeAccessToCustomViews_.dbColumnName} + |${ViewDefinition.canGrantAccessToViews_.dbColumnName} + |${ViewDefinition.canRevokeAccessToViews_.dbColumnName} + |Duration: ${endDate - startDate} ms; + """.stripMargin + saveLog(name, commitId, isSuccessful, startDate, endDate, comment) + isSuccessful + case false => + val startDate = System.currentTimeMillis() + val commitId: String = APIUtil.gitCommit + val isSuccessful = false + val endDate = System.currentTimeMillis() + val comment: String = + s"""ViewDefinition does not exist!""".stripMargin + saveLog(name, commitId, isSuccessful, startDate, endDate, comment) + isSuccessful + } + } +} diff --git a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala index 7419e4b135..9038ada7b0 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/APIMethods121.scala @@ -4,7 +4,6 @@ import java.net.URL import java.util.Random import java.security.SecureRandom import java.util.UUID.randomUUID - import com.tesobe.CacheKeyFromArguments import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.cache.Caching @@ -16,10 +15,11 @@ import code.api.util._ import code.bankconnectors._ import code.metadata.comments.Comments import code.metadata.counterparties.Counterparties -import code.model.{BankAccountX, BankX, ModeratedTransactionMetadata, toBankAccountExtended, toBankExtended, toUserExtended} +import code.model.{BankAccountX, BankX, ModeratedTransactionMetadata, UserX, toBankAccountExtended, toBankExtended, toUserExtended} import code.util.Helper import code.util.Helper.booleanToBox import code.views.Views +import code.views.system.ViewDefinition import com.google.common.cache.CacheBuilder import com.openbankproject.commons.model.{Bank, UpdateViewJSON, _} import com.openbankproject.commons.util.ApiVersion @@ -37,6 +37,7 @@ import scala.language.postfixOps import scalacache.ScalaCache import scalacache.guava.GuavaCache import com.openbankproject.commons.ExecutionContext.Implicits.global +import net.liftweb.util.StringHelpers import scala.concurrent.Future @@ -494,8 +495,21 @@ trait APIMethods121 { (Full(u), callContext) <- authenticatedAccess(cc) json <- NewStyle.function.tryons(InvalidJsonFormat, 400, callContext) { json.extract[UpdateAccountJSON] } (account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) + anyViewContainsCanUpdateBankAccountLabelPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canUpdateBankAccountLabel).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canUpdateBankAccountLabel_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanUpdateBankAccountLabelPermission + } + (success, callContext) <- Future{ + Connector.connector.vend.updateAccountLabel(bankId, accountId, json.label) + } map { i => + (unboxFullOrFail(i, callContext, + s"$UpdateBankAccountLabelError Current BankId is $bankId and Current AccountId is $accountId", 404), callContext) + } } yield { - account.updateLabel(u, json.label,callContext) (successMessage, HttpCode.`200`(callContext)) } } @@ -543,11 +557,15 @@ trait APIMethods121 { cc => for { u <- cc.user ?~ UserNotLoggedIn - account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound - _ <- booleanToBox(u.hasOwnerViewAccess(BankIdAccountId(account.bankId, account.accountId), Some(cc)), UserNoOwnerView +"userId : " + u.userId + ". account : " + accountId) - views <- Full(Views.views.vend.availableViewsForAccount(BankIdAccountId(account.bankId, account.accountId))) + bankAccount <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound + permission <- Views.views.vend.permission(BankIdAccountId(bankAccount.bankId, bankAccount.accountId), u) + anyViewContainsCanSeeAvailableViewsForBankAccountPermission = permission.views.map(_.canSeeAvailableViewsForBankAccount).find(_.==(true)).getOrElse(false) + _ <- Helper.booleanToBox( + anyViewContainsCanSeeAvailableViewsForBankAccountPermission, + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeAvailableViewsForBankAccount_.dbColumnName).dropRight(1)}` permission on any your views" + ) + views <- Full(Views.views.vend.availableViewsForAccount(BankIdAccountId(bankAccount.bankId, bankAccount.accountId))) } yield { - // TODO Include system views as well val viewsJSON = JSONFactory.createViewsJSON(views) successJsonResponse(Extraction.decompose(viewsJSON)) } @@ -605,7 +623,13 @@ trait APIMethods121 { createViewJsonV121.hide_metadata_if_alias_used, createViewJsonV121.allowed_actions ) - view <- account createCustomView (u, createViewJson, Some(cc)) + anyViewContainsCanCreateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canCreateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanCreateCustomViewPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canCreateCustomView_.dbColumnName).dropRight(1)}` permission on any your views" + ) + view <- Views.views.vend.createCustomView(BankIdAccountId(bankId,accountId), createViewJson)?~ CreateCustomViewError } yield { val viewJSON = JSONFactory.createViewJSON(view) successJsonResponse(Extraction.decompose(viewJSON), 201) @@ -660,7 +684,13 @@ trait APIMethods121 { hide_metadata_if_alias_used = updateJsonV121.hide_metadata_if_alias_used, allowed_actions = updateJsonV121.allowed_actions ) - updatedView <- account.updateView(u, viewId, updateViewJson, Some(cc)) + anyViewContainsCanUpdateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canUpdateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanUpdateCustomViewPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canUpdateCustomView_.dbColumnName).dropRight(1)}` permission on any your views" + ) + updatedView <- Views.views.vend.updateCustomView(BankIdAccountId(bankId, accountId),viewId, updateViewJson) ?~ CreateCustomViewError } yield { val viewJSON = JSONFactory.createViewJSON(updatedView) successJsonResponse(Extraction.decompose(viewJSON), 200) @@ -674,8 +704,8 @@ trait APIMethods121 { "deleteViewForBankAccount", "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID", - "Delete View", - "Deletes the view specified by VIEW_ID on the bank account specified by ACCOUNT_ID at bank BANK_ID", + "Delete Custom View", + "Deletes the custom view specified by VIEW_ID on the bank account specified by ACCOUNT_ID at bank BANK_ID", emptyObjectJson, emptyObjectJson, List( @@ -699,7 +729,17 @@ trait APIMethods121 { // custom views start with `_` eg _play, _work, and System views start with a letter, eg: owner _ <- Helper.booleanToFuture(InvalidCustomViewFormat+s"Current view_name (${viewId.value})", cc=callContext) { viewId.value.startsWith("_") } _ <- NewStyle.function.customView(viewId, BankIdAccountId(bankId, accountId), callContext) - deleted <- NewStyle.function.removeView(account, u, viewId, callContext) + + anyViewContainsCanDeleteCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canDeleteCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canDeleteCustomView_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanDeleteCustomViewPermission + } + + deleted <- NewStyle.function.removeCustomView(viewId, BankIdAccountId(bankId, accountId),callContext) } yield { (Full(deleted), HttpCode.`204`(callContext)) } @@ -729,7 +769,13 @@ trait APIMethods121 { for { u <- cc.user ?~ UserNotLoggedIn account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound - permissions <- account.permissions(u, Some(cc)) + anyViewContainsCanSeeViewsWithPermissionsForAllUsersPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canSeeViewsWithPermissionsForAllUsers).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanSeeViewsWithPermissionsForAllUsersPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canSeeViewsWithPermissionsForAllUsers_.dbColumnName).dropRight(1)}` permission on any your views" + ) + permissions = Views.views.vend.permissions(BankIdAccountId(bankId, accountId)) } yield { val permissionsJSON = JSONFactory.createPermissionsJSON(permissions) successJsonResponse(Extraction.decompose(permissionsJSON)) @@ -762,12 +808,20 @@ trait APIMethods121 { lazy val getPermissionForUserForBankAccount: OBPEndpoint = { //get access for specific user - case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: providerId :: userId :: Nil JsonGet req => { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: provider :: providerId :: Nil JsonGet req => { cc => for { - u <- cc.user ?~ UserNotLoggedIn + loggedInUser <- cc.user ?~ UserNotLoggedIn account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound - permission <- account permission(u, providerId, userId, Some(cc)) + loggedInUserPermissionBox = Views.views.vend.permission(BankIdAccountId(bankId, accountId), loggedInUser) + anyViewContainsCanSeeViewsWithPermissionsForOneUserPermission = loggedInUserPermissionBox.map(_.views.map(_.canSeeViewsWithPermissionsForOneUser) + .find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanSeeViewsWithPermissionsForOneUserPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canSeeViewsWithPermissionsForOneUser_.dbColumnName).dropRight(1)}` permission on any your views" + ) + userFromURL <- UserX.findByProviderId(provider, providerId) ?~! UserNotFoundByProviderAndProvideId + permission <- Views.views.vend.permission(BankIdAccountId(bankId, accountId), userFromURL) } yield { val views = JSONFactory.createViewsJSON(permission.views) successJsonResponse(Extraction.decompose(views)) @@ -811,7 +865,7 @@ trait APIMethods121 { (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) failMsg = "wrong format JSON" viewIds <- NewStyle.function.tryons(failMsg, 400, callContext) { json.extract[ViewIdsJson] } - addedViews <- NewStyle.function.grantAccessToMultipleViews(account, u, viewIds.views.map(viewIdString => ViewIdBankIdAccountId(ViewId(viewIdString), bankId, accountId)), provider, providerId,callContext) + (addedViews, callContext) <- NewStyle.function.grantAccessToMultipleViews(account, u, viewIds.views.map(viewIdString => ViewIdBankIdAccountId(ViewId(viewIdString), bankId, accountId)), provider, providerId,callContext) } yield { (JSONFactory.createViewsJSON(addedViews), HttpCode.`201`(callContext)) } @@ -838,6 +892,7 @@ trait APIMethods121 { UserNotLoggedIn, BankAccountNotFound, UnknownError, + UserLacksPermissionCanGrantAccessToViewForTargetAccount, "could not save the privilege", "user does not have access to owner view on account" ), @@ -851,7 +906,7 @@ trait APIMethods121 { (Full(u), callContext) <- authenticatedAccess(cc) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) - addedView <- NewStyle.function.grantAccessToView(account, u, ViewIdBankIdAccountId(viewId, bankId, accountId), provider, providerId, callContext) + (addedView, callContext) <- NewStyle.function.grantAccessToView(account, u, ViewIdBankIdAccountId(viewId, bankId, accountId), provider, providerId, callContext) } yield { val viewJson = JSONFactory.createViewJSON(addedView) (viewJson, HttpCode.`201`(callContext)) @@ -2658,10 +2713,11 @@ trait APIMethods121 { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "comments":: commentId :: Nil JsonDelete _ => { cc => for { - (user, callContext) <- authenticatedAccess(cc) + (Full(user), callContext) <- authenticatedAccess(cc) (account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, user, callContext) - delete <- Future(metadata.deleteComment(commentId, user, account, callContext)) map { + view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(user), callContext) + metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, Full(user), callContext) + delete <- Future(metadata.deleteComment(commentId, Full(user), account, view, callContext)) map { unboxFullOrFail(_, callContext, "") } } yield { @@ -2777,10 +2833,11 @@ trait APIMethods121 { cc => for { - (user, callContext) <- authenticatedAccess(cc) - metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, user, callContext) + (Full(user), callContext) <- authenticatedAccess(cc) + view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(user), callContext) + metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, Full(user), callContext) (bankAccount, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - delete <- Future(metadata.deleteTag(tagId, user, bankAccount, callContext)) map { + delete <- Future(metadata.deleteTag(tagId, Full(user), bankAccount, view, callContext)) map { unboxFullOrFail(_, callContext, "") } } yield { @@ -2900,10 +2957,11 @@ trait APIMethods121 { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transactions" :: TransactionId(transactionId) :: "metadata" :: "images" :: imageId :: Nil JsonDelete _ => { cc => for { - (user, callContext) <- authenticatedAccess(cc) - metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, user, callContext) + (Full(user), callContext) <- authenticatedAccess(cc) + metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, Full(user), callContext) + view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(user), callContext) (account, _) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - delete <- Future(metadata.deleteImage(imageId, user, account, callContext)) map { + delete <- Future(metadata.deleteImage(imageId, Full(user), account, view, callContext)) map { unboxFullOrFail(_, callContext, "") } } yield { @@ -3076,7 +3134,7 @@ trait APIMethods121 { (account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), user, callContext) metadata <- moderatedTransactionMetadataFuture(bankId, accountId, viewId, transactionId, user, callContext) - delete <- Future(metadata.deleteWhereTag(viewId, user, account, callContext)) map { + delete <- Future(metadata.deleteWhereTag(viewId, user, account, view, callContext)) map { unboxFullOrFail(_, callContext, "Delete not completed") } } yield { diff --git a/obp-api/src/main/scala/code/api/v1_4_0/APIMethods140.scala b/obp-api/src/main/scala/code/api/v1_4_0/APIMethods140.scala index f8048a90d6..977dc5bf75 100644 --- a/obp-api/src/main/scala/code/api/v1_4_0/APIMethods140.scala +++ b/obp-api/src/main/scala/code/api/v1_4_0/APIMethods140.scala @@ -11,7 +11,9 @@ import code.bankconnectors.Connector import code.branches.Branches import code.customer.CustomerX import code.usercustomerlinks.UserCustomerLink +import code.util.Helper import code.views.Views +import code.views.system.ViewDefinition import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.model._ import com.openbankproject.commons.util.ApiVersion @@ -20,7 +22,7 @@ import net.liftweb.http.rest.RestHelper import net.liftweb.json.Extraction import net.liftweb.json.JsonAST.JValue import net.liftweb.util.Helpers.tryo -import net.liftweb.util.Props +import net.liftweb.util.{Props, StringHelpers} import scala.collection.immutable.{List, Nil} import scala.concurrent.Future @@ -421,6 +423,14 @@ trait APIMethods140 extends MdcLoggable with APIMethods130 with APIMethods121{ failMsg = ErrorMessages.InvalidISOCurrencyCode.concat("Please specify a valid value for CURRENCY of your Bank Account. ") _ <- NewStyle.function.isValidCurrencyISOCode(fromAccount.currency, failMsg, callContext) view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), callContext) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequestTypes_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value} )", + cc = callContext + ) { + view.canSeeTransactionRequestTypes + } + // TODO: Consider storing allowed_transaction_request_types (List of String) in View Definition. + // TODO: This would allow us to restrict transaction request types available to the User for an Account transactionRequestTypes <- Future(Connector.connector.vend.getTransactionRequestTypes(u, fromAccount, callContext)) map { connectorEmptyResponse(_, callContext) } @@ -462,7 +472,11 @@ trait APIMethods140 extends MdcLoggable with APIMethods130 with APIMethods121{ u <- cc.user ?~ ErrorMessages.UserNotLoggedIn (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! {ErrorMessages.BankNotFound} fromAccount <- BankAccountX(bankId, accountId) ?~! {ErrorMessages.AccountNotFound} - _ <- booleanToBox( u.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext), UserNoOwnerView +"userId : " + u.userId + ". account : " + accountId) + view <- APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(u), callContext) + _ <- Helper.booleanToBox( + view.canSeeTransactionRequests, + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequests_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value})" + ) transactionRequests <- Connector.connector.vend.getTransactionRequests(u, fromAccount, callContext) } yield { @@ -533,6 +547,12 @@ trait APIMethods140 extends MdcLoggable with APIMethods130 with APIMethods121{ transBody <- tryo{getTransactionRequestBodyFromJson(transBodyJson)} (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! {ErrorMessages.BankNotFound} fromAccount <- BankAccountX(bankId, accountId) ?~! {ErrorMessages.AccountNotFound} + _ <- APIUtil.checkAuthorisationToCreateTransactionRequest(viewId : ViewId, BankIdAccountId(bankId, accountId), u: User, callContext: Option[CallContext]) ?~! { + s"$InsufficientAuthorisationToCreateTransactionRequest " + + s"Current ViewId(${viewId.value})," + + s"current UserId(${u.userId})" + + s"current ConsumerId(${callContext.map (_.consumer.map (_.consumerId.get).getOrElse ("")).getOrElse ("")})" + } toBankId <- tryo(BankId(transBodyJson.to.bank_id)) toAccountId <- tryo(AccountId(transBodyJson.to.account_id)) toAccount <- BankAccountX(toBankId, toAccountId) ?~! {ErrorMessages.CounterpartyNotFound} @@ -594,7 +614,12 @@ trait APIMethods140 extends MdcLoggable with APIMethods130 with APIMethods121{ u <- cc.user ?~ ErrorMessages.UserNotLoggedIn (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! {ErrorMessages.BankNotFound} fromAccount <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound - view <- APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), Some(cc)) + _ <- APIUtil.checkAuthorisationToCreateTransactionRequest(viewId: ViewId, BankIdAccountId(bankId, accountId), u: User, callContext: Option[CallContext]) ?~! { + s"$InsufficientAuthorisationToCreateTransactionRequest " + + s"Current ViewId(${viewId.value})," + + s"current UserId(${u.userId})" + + s"current ConsumerId(${callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")})" + } answerJson <- tryo{json.extract[ChallengeAnswerJSON]} ?~ InvalidJsonFormat //TODO check more things here _ <- Connector.connector.vend.answerTransactionRequestChallenge(transReqId, answerJson.answer) diff --git a/obp-api/src/main/scala/code/api/v2_0_0/APIMethods200.scala b/obp-api/src/main/scala/code/api/v2_0_0/APIMethods200.scala index 7be1183124..36efbac22b 100644 --- a/obp-api/src/main/scala/code/api/v2_0_0/APIMethods200.scala +++ b/obp-api/src/main/scala/code/api/v2_0_0/APIMethods200.scala @@ -1,7 +1,6 @@ package code.api.v2_0_0 import java.util.{Calendar, Date} - import code.api.Constant._ import code.TransactionTypes.TransactionType import code.api.{APIFailure, APIFailureNewStyle} @@ -30,6 +29,7 @@ import code.usercustomerlinks.UserCustomerLink import code.util.Helper import code.util.Helper.booleanToBox import code.views.Views +import code.views.system.ViewDefinition import com.openbankproject.commons.model._ import net.liftweb.common.{Full, _} import net.liftweb.http.CurrentReq @@ -42,6 +42,7 @@ import scala.collection.immutable.Nil import scala.collection.mutable.ArrayBuffer import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.util.ApiVersion +import net.liftweb.util.StringHelpers import scala.concurrent.Future // Makes JValue assignment to Nil work @@ -1010,7 +1011,15 @@ trait APIMethods200 { (Full(u), callContext) <- authenticatedAccess(cc) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) - permissions <- NewStyle.function.permissions(account, u, callContext) + anyViewContainsCanSeeViewsWithPermissionsForAllUsersPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canSeeViewsWithPermissionsForAllUsers).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeViewsWithPermissionsForAllUsers_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanSeeViewsWithPermissionsForAllUsersPermission + } + permissions = Views.views.vend.permissions(BankIdAccountId(bankId, accountId)) } yield { val permissionsJSON = JSONFactory121.createPermissionsJSON(permissions.sortBy(_.user.emailAddress)) (permissionsJSON, HttpCode.`200`(callContext)) @@ -1041,10 +1050,18 @@ trait APIMethods200 { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: provider :: providerId :: Nil JsonGet req => { cc => for { - u <- cc.user ?~! ErrorMessages.UserNotLoggedIn // Check we have a user (rather than error or empty) + loggedInUser <- cc.user ?~! ErrorMessages.UserNotLoggedIn // Check we have a user (rather than error or empty) (bank, callContext) <- BankX(bankId, Some(cc)) ?~! BankNotFound // Check bank exists. account <- BankAccountX(bank.bankId, accountId) ?~! {ErrorMessages.AccountNotFound} // Check Account exists. - permission <- account permission(u, provider, providerId, Some(cc)) + loggedInUserPermissionBox = Views.views.vend.permission(BankIdAccountId(bankId, accountId), loggedInUser) + anyViewContainsCanSeePermissionForOneUserPermission = loggedInUserPermissionBox.map(_.views.map(_.canSeeViewsWithPermissionsForOneUser) + .find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanSeePermissionForOneUserPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canSeeViewsWithPermissionsForOneUser_.dbColumnName).dropRight(1)}` permission on any your views" + ) + userFromURL <- UserX.findByProviderId(provider, providerId) ?~! UserNotFoundByProviderAndProvideId + permission <- Views.views.vend.permission(BankIdAccountId(bankId, accountId), userFromURL) } yield { // TODO : Note this is using old createViewsJSON without can_add_counterparty etc. val views = JSONFactory121.createViewsJSON(permission.views.sortBy(_.viewId.value)) @@ -1283,11 +1300,11 @@ trait APIMethods200 { _ <- tryo(assert(isValidID(accountId.value)))?~! InvalidAccountIdFormat (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! BankNotFound fromAccount <- BankAccountX(bankId, accountId) ?~! AccountNotFound - _ <-APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), callContext) match { - case Full(_) => - booleanToBox(u.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext) == true) - case _ => - NewStyle.function.ownEntitlement(fromAccount.bankId.value, u.userId, canCreateAnyTransactionRequest, cc.callContext, InsufficientAuthorisationToCreateTransactionRequest) + _ <- APIUtil.checkAuthorisationToCreateTransactionRequest(viewId: ViewId, BankIdAccountId(bankId, accountId), u: User, callContext: Option[CallContext]) ?~! { + s"$InsufficientAuthorisationToCreateTransactionRequest " + + s"Current ViewId(${viewId.value})," + + s"current UserId(${u.userId})" + + s"current ConsumerId(${callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")})" } toBankId <- tryo(BankId(transBodyJson.to.bank_id)) toAccountId <- tryo(AccountId(transBodyJson.to.account_id)) @@ -1351,11 +1368,12 @@ trait APIMethods200 { _ <- tryo(assert(isValidID(bankId.value)))?~! ErrorMessages.InvalidBankIdFormat (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! BankNotFound fromAccount <- BankAccountX(bankId, accountId) ?~! AccountNotFound - view <-APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), callContext) - _ <- if (u.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext)) Full(Unit) - else NewStyle.function.ownEntitlement(fromAccount.bankId.value, u.userId, canCreateAnyTransactionRequest, cc.callContext, InsufficientAuthorisationToCreateTransactionRequest) - // Note: These checks are not in the ideal order. See version 2.1.0 which supercedes this - + _ <- APIUtil.checkAuthorisationToCreateTransactionRequest(viewId: ViewId, BankIdAccountId(bankId, accountId), u: User, callContext: Option[CallContext]) ?~! { + s"$InsufficientAuthorisationToCreateTransactionRequest " + + s"Current ViewId(${viewId.value})," + + s"current UserId(${u.userId})" + + s"current ConsumerId(${callContext.map(_.consumer.map(_.consumerId.get).getOrElse("")).getOrElse("")})" + } answerJson <- tryo{json.extract[ChallengeAnswerJSON]} ?~! InvalidJsonFormat _ <- Connector.connector.vend.answerTransactionRequestChallenge(transReqId, answerJson.answer) //check the transReqId validation. @@ -1436,6 +1454,8 @@ trait APIMethods200 { (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! BankNotFound fromAccount <- BankAccountX(bankId, accountId) ?~! AccountNotFound view <-APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), callContext) + _ <- Helper.booleanToBox(view.canSeeTransactionRequests, + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequests_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value} )") transactionRequests <- Connector.connector.vend.getTransactionRequests(u, fromAccount, callContext) } yield { diff --git a/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala b/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala index ab25f70d9a..a85bf277a5 100644 --- a/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala +++ b/obp-api/src/main/scala/code/api/v2_1_0/APIMethods210.scala @@ -6,7 +6,7 @@ import code.api.util import code.api.util.ApiTag._ import code.api.util.ErrorMessages.TransactionDisabled import code.api.util.NewStyle.HttpCode -import code.api.util.{APIUtil, ApiRole, NewStyle} +import code.api.util.{APIUtil, ApiRole, ErrorMessages, NewStyle} import code.api.v1_3_0.{JSONFactory1_3_0, _} import code.api.v1_4_0.JSONFactory1_4_0 import code.api.v1_4_0.JSONFactory1_4_0._ @@ -27,12 +27,13 @@ import code.usercustomerlinks.UserCustomerLink import code.users.Users import code.util.Helper.booleanToBox import code.views.Views +import code.views.system.ViewDefinition import com.openbankproject.commons.model._ import com.openbankproject.commons.model.enums.ChallengeType import com.openbankproject.commons.util.ApiVersion import net.liftweb.json.Extraction import net.liftweb.util.Helpers.tryo -import net.liftweb.util.Props +import net.liftweb.util.{Props, StringHelpers} import scala.collection.immutable.Nil import scala.collection.mutable.ArrayBuffer @@ -700,7 +701,6 @@ trait APIMethods210 { BankNotFound, AccountNotFound, UserHasMissingRoles, - UserNoOwnerView, UnknownError ), List(apiTagTransactionRequest, apiTagPsd2)) @@ -713,8 +713,9 @@ trait APIMethods210 { u <- cc.user ?~ UserNotLoggedIn (bank, callContext ) <- BankX(bankId, Some(cc)) ?~! {BankNotFound} (fromAccount, callContext) <- BankAccountX(bankId, accountId, Some(cc)) ?~! {AccountNotFound} - view <- APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(fromAccount.bankId, fromAccount.accountId), Some(u), callContext) - _ <- booleanToBox(u.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext), UserNoOwnerView) + view <- APIUtil.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(u), callContext) + _ <- Helper.booleanToBox(view.canSeeTransactionRequests, + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequests_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value} )") (transactionRequests,callContext) <- Connector.connector.vend.getTransactionRequests210(u, fromAccount, callContext) } yield { diff --git a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala index 01dbe6c012..b39f095e2a 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/APIMethods220.scala @@ -22,6 +22,7 @@ import code.model.dataAccess.BankAccountCreation import code.util.Helper import code.util.Helper._ import code.views.Views +import code.views.system.ViewDefinition import com.openbankproject.commons.model._ import net.liftweb.common.{Empty, Full} import net.liftweb.http.rest.RestHelper @@ -32,6 +33,7 @@ import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.util.ApiVersion +import net.liftweb.util.StringHelpers import scala.concurrent.Future @@ -99,8 +101,13 @@ trait APIMethods220 { for { (Full(u), callContext) <- authenticatedAccess(cc) (account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView +"userId : " + u.userId + ". account : " + accountId, cc=callContext) { - u.hasOwnerViewAccess(BankIdAccountId(account.bankId, account.accountId), callContext) + permission <- NewStyle.function.permission(bankId, accountId, u, callContext) + anyViewContainsCanSeeAvailableViewsForBankAccountPermission = permission.views.map(_.canSeeAvailableViewsForBankAccount).find(_.==(true)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeAvailableViewsForBankAccount_.dbColumnName).dropRight(1)}` permission on any your views", + cc= callContext + ){ + anyViewContainsCanSeeAvailableViewsForBankAccountPermission } views <- Future(Views.views.vend.availableViewsForAccount(BankIdAccountId(account.bankId, account.accountId))) } yield { @@ -162,8 +169,14 @@ trait APIMethods220 { createViewJsonV121.which_alias_to_use, createViewJsonV121.hide_metadata_if_alias_used, createViewJsonV121.allowed_actions + ) + anyViewContainsCanCreateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canCreateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCanCreateCustomViewPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canCreateCustomView_.dbColumnName).dropRight(1)}` permission on any your views" ) - view <- account.createCustomView(u, createViewJson, Some(cc)) + view <- Views.views.vend.createCustomView(BankIdAccountId(bankId, accountId), createViewJson) ?~ CreateCustomViewError } yield { val viewJSON = JSONFactory220.createViewJSON(view) successJsonResponse(Extraction.decompose(viewJSON), 201) @@ -216,7 +229,13 @@ trait APIMethods220 { hide_metadata_if_alias_used = updateJsonV121.hide_metadata_if_alias_used, allowed_actions = updateJsonV121.allowed_actions ) - updatedView <- account.updateView(u, viewId, updateViewJson, Some(cc)) + anyViewContainsCancanUpdateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canUpdateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- booleanToBox( + anyViewContainsCancanUpdateCustomViewPermission, + s"${ErrorMessages.CreateCustomViewError} You need the `${StringHelpers.snakify(ViewDefinition.canUpdateCustomView_.dbColumnName).dropRight(1)}` permission on any your views" + ) + updatedView <- Views.views.vend.updateCustomView(BankIdAccountId(bankId, accountId), viewId, updateViewJson) ?~ CreateCustomViewError } yield { val viewJSON = JSONFactory220.createViewJSON(updatedView) successJsonResponse(Extraction.decompose(viewJSON), 200) @@ -812,7 +831,7 @@ trait APIMethods220 { //1 Create or Update the `Owner` for the new account //2 Add permission to the user //3 Set the user as the account holder - _ = BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) + _ <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) } yield { (JSONFactory220.createAccountJSON(userIdAccountOwner, bankAccount), HttpCode.`200`(callContext)) diff --git a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala index ae6f076f90..f5d93dc72e 100644 --- a/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala +++ b/obp-api/src/main/scala/code/api/v3_0_0/APIMethods300.scala @@ -27,6 +27,7 @@ import code.users.Users import code.util.Helper import code.util.Helper.booleanToBox import code.views.Views +import code.views.system.ViewDefinition import com.github.dwickern.macros.NameOf.nameOf import com.grum.geocalc.{Coordinate, EarthCalc, Point} import com.openbankproject.commons.model._ @@ -48,6 +49,7 @@ import code.model import com.openbankproject.commons.dto.CustomerAndAttribute import com.openbankproject.commons.util.ApiVersion import net.liftweb.json.JsonAST.JField +import net.liftweb.util.StringHelpers trait APIMethods300 { @@ -109,13 +111,18 @@ trait APIMethods300 { val res = for { (Full(u), callContext) <- authenticatedAccess(cc) - (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) - _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView +"userId : " + u.userId + ". account : " + accountId, cc=callContext){ - u.hasOwnerViewAccess(BankIdAccountId(account.bankId, account.accountId), callContext) + (bankAccount, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) + permission <- NewStyle.function.permission(bankId, accountId, u, callContext) + anyViewContainsCanSeeAvailableViewsForBankAccountPermission = permission.views.map(_.canSeeAvailableViewsForBankAccount).find(_.==(true)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${code.views.system.ViewDefinition.canSeeAvailableViewsForBankAccount.toString}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanSeeAvailableViewsForBankAccountPermission } } yield { for { - views <- Full(Views.views.vend.availableViewsForAccount(BankIdAccountId(account.bankId, account.accountId))) + views <- Full(Views.views.vend.availableViewsForAccount(BankIdAccountId(bankAccount.bankId, bankAccount.accountId))) } yield { (createViewsJSON(views), HttpCode.`200`(callContext)) } @@ -131,8 +138,8 @@ trait APIMethods300 { nameOf(createViewForBankAccount), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/views", - "Create View", - s"""Create a view on bank account + "Create Custom View", + s"""Create a custom view on bank account | | ${authenticationRequiredMessage(true)} and the user needs to have access to the owner view. | The 'alias' field in the JSON can take one of three values: @@ -162,7 +169,6 @@ trait APIMethods300 { //creates a view on an bank account case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: Nil JsonPost json -> _ => { cc => - val res = for { (Full(u), callContext) <- authenticatedAccess(cc) createViewJson <- Future { tryo{json.extract[CreateViewJson]} } map { @@ -174,14 +180,18 @@ trait APIMethods300 { checkCustomViewIdOrName(createViewJson.name) } (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) + + anyViewContainsCanCreateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canCreateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canCreateCustomView_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) {anyViewContainsCanCreateCustomViewPermission} + (view, callContext) <- NewStyle.function.createCustomView(BankIdAccountId(bankId, accountId), createViewJson, callContext) } yield { - for { - view <- account.createCustomView (u, createViewJson, callContext) - } yield { - (JSONFactory300.createViewJSON(view), callContext.map(_.copy(httpCode = Some(201)))) - } + (JSONFactory300.createViewJSON(view), HttpCode.`201`(callContext)) } - res map { fullBoxOrException(_) } map { unboxFull(_) } } } @@ -208,12 +218,21 @@ trait APIMethods300 { case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "permissions" :: provider :: providerId :: Nil JsonGet req => { cc => for { - (Full(u), callContext) <- authenticatedAccess(cc) + (Full(loggedInUser), callContext) <- authenticatedAccess(cc) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) (account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - permission <- Future { account.permission(u, provider, providerId, callContext) } map { - x => fullBoxOrException(x ~> APIFailureNewStyle(UserNoOwnerView, 400, callContext.map(_.toLight))) - } map { unboxFull(_) } + anyViewContainsCanSeePermissionForOneUserPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), loggedInUser) + .map(_.views.map(_.canSeeViewsWithPermissionsForOneUser).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeViewsWithPermissionsForOneUser_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanSeePermissionForOneUserPermission + } + (userFromURL, callContext) <- Future{UserX.findByProviderId(provider, providerId)} map { i => + (unboxFullOrFail(i, callContext, UserNotFoundByProviderAndProvideId, 404), callContext) + } + permission <- NewStyle.function.permission(bankId, accountId, userFromURL, callContext) } yield { (createViewsJSON(permission.views.sortBy(_.viewId.value)), HttpCode.`200`(callContext)) } @@ -226,8 +245,8 @@ trait APIMethods300 { nameOf(updateViewForBankAccount), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID", - "Update View", - s"""Update an existing view on a bank account + "Update Custom View", + s"""Update an existing custom view on a bank account | |${authenticationRequiredMessage(true)} and the user needs to have access to the owner view. | @@ -248,7 +267,6 @@ trait APIMethods300 { //updates a view on a bank account case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "views" :: ViewId(viewId) :: Nil JsonPut json -> _ => { cc => - val res = for { (Full(u), callContext) <- authenticatedAccess(cc) updateJson <- Future { tryo{json.extract[UpdateViewJsonV300]} } map { @@ -268,14 +286,20 @@ trait APIMethods300 { !view.isSystem } (account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext) - } yield { - for { - updatedView <- account.updateView(u, viewId, updateJson.toUpdateViewJson, callContext) - } yield { - (JSONFactory300.createViewJSON(updatedView), HttpCode.`200`(callContext)) + + anyViewContainsCancanUpdateCustomViewPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canUpdateCustomView).find(_.==(true)).getOrElse(false)).getOrElse(false) + + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canUpdateCustomView_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCancanUpdateCustomViewPermission } + (view, callContext) <- NewStyle.function.updateCustomView(BankIdAccountId(bankId, accountId), viewId, updateJson.toUpdateViewJson, callContext) + } yield { + (JSONFactory300.createViewJSON(view), HttpCode.`200`(callContext)) } - res map { fullBoxOrException(_) } map { unboxFull(_) } } } 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 82e9d71141..8f96c37f10 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 @@ -1,12 +1,11 @@ package code.api.v3_1_0 import code.api.Constant -import code.api.Constant.localIdentityProvider +import code.api.Constant.{SYSTEM_OWNER_VIEW_ID, localIdentityProvider} import java.text.SimpleDateFormat import java.util.UUID import java.util.regex.Pattern - import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.ResourceDocs1_4_0.{MessageDocsSwaggerDefinitions, ResourceDocsAPIMethodsUtil, SwaggerDefinitionsJSON, SwaggerJSONFactory} import code.api.util.APIUtil.{getWebUIPropsPairs, _} @@ -40,6 +39,7 @@ import code.userlocks.{UserLocks, UserLocksProvider} import code.users.Users import code.util.Helper import code.views.Views +import code.views.system.ViewDefinition import code.webhook.AccountWebhook import code.webuiprops.{MappedWebUiPropsProvider, WebUiPropsCommons} import com.github.dwickern.macros.NameOf.nameOf @@ -53,7 +53,7 @@ import net.liftweb.http.rest.RestHelper import net.liftweb.json._ import net.liftweb.util.Helpers.tryo import net.liftweb.util.Mailer.{From, PlainMailBodyType, Subject, To} -import net.liftweb.util.{Helpers, Mailer, Props} +import net.liftweb.util.{Helpers, Mailer, Props, StringHelpers} import org.apache.commons.lang3.{StringUtils, Validate} import scala.collection.immutable.{List, Nil} @@ -1072,7 +1072,7 @@ trait APIMethods310 { BankNotFound, BankAccountNotFound, UserNoPermissionAccessView, - UserNoOwnerView, + ViewDoesNotPermitAccess, GetTransactionRequestsException, UnknownError ), @@ -1086,9 +1086,11 @@ trait APIMethods310 { _ <- NewStyle.function.isEnabledTransactionRequests(callContext) (_, callContext) <- NewStyle.function.getBank(bankId, callContext) (fromAccount, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) - view <- NewStyle.function.checkViewAccessAndReturnView(viewId, BankIdAccountId(bankId, accountId), Some(u), callContext) - _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView, cc=callContext) { - u.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext) + view <- NewStyle.function.checkAccountAccessAndGetView(viewId, BankIdAccountId(bankId, accountId), Full(u), callContext) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequests_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value})", + cc=callContext){ + view.canSeeTransactionRequests } (transactionRequests, callContext) <- Future(Connector.connector.vend.getTransactionRequests210(u, fromAccount, callContext)) map { unboxFullOrFail(_, callContext, GetTransactionRequestsException) @@ -1888,7 +1890,7 @@ trait APIMethods310 { _ <- NewStyle.function.hasEntitlement("", userId, canRefreshUser, callContext) startTime <- Future{Helpers.now} (user, callContext) <- NewStyle.function.findByUserId(userId, callContext) - _ = AuthUser.refreshUser(user, callContext) + _ <- AuthUser.refreshUser(user, callContext) endTime <- Future{Helpers.now} durationTime = endTime.getTime - startTime.getTime } yield { @@ -2338,8 +2340,9 @@ trait APIMethods310 { "", List.empty, callContext) + success <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, u, callContext) }yield { - BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, u, callContext) + success } case _ => Future{""} } @@ -5426,7 +5429,7 @@ trait APIMethods310 { //1 Create or Update the `Owner` for the new account //2 Add permission to the user //3 Set the user as the account holder - _ = BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) + _ <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) } yield { (JSONFactory310.createAccountJSON(userIdAccountOwner, bankAccount, accountAttributes), HttpCode.`201`(callContext)) } diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 13b45b5870..13b55d7f7b 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -7,7 +7,7 @@ import java.util.{Calendar, Date} import code.DynamicData.{DynamicData, DynamicDataProvider} import code.DynamicEndpoint.DynamicEndpointSwagger import code.accountattribute.AccountAttributeX -import code.api.Constant.{PARAM_LOCALE, PARAM_TIMESTAMP, localIdentityProvider} +import code.api.Constant.{PARAM_LOCALE, PARAM_TIMESTAMP, SYSTEM_OWNER_VIEW_ID, localIdentityProvider} import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.{jsonDynamicResourceDoc, _} import code.api.UKOpenBanking.v2_0_0.OBP_UKOpenBanking_200 @@ -75,6 +75,7 @@ import code.util.Helper.booleanToFuture import code.util.{Helper, JsonSchemaUtil} import code.validation.JsonValidation import code.views.Views +import code.views.system.ViewDefinition import code.webhook.{AccountWebhook, BankAccountNotificationWebhookTrait, SystemAccountNotificationWebhookTrait} import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue import com.github.dwickern.macros.NameOf.nameOf @@ -535,7 +536,7 @@ trait APIMethods400 { //1 Create or Update the `Owner` for the new account //2 Add permission to the user //3 Set the user as the account holder - _ = BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) + _ <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) } yield { (JSONFactory400.createSettlementAccountJson(userIdAccountOwner, bankAccount, accountAttributes), HttpCode.`201`(callContext)) } @@ -2630,7 +2631,7 @@ trait APIMethods400 { //1 Create or Update the `Owner` for the new account //2 Add permission to the user //3 Set the user as the account holder - _ = BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) + _ <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) } yield { (JSONFactory310.createAccountJSON(userIdAccountOwner, bankAccount, accountAttributes), HttpCode.`201`(callContext)) } @@ -2778,8 +2779,20 @@ trait APIMethods400 { json <- NewStyle.function.tryons(failMsg, 400, callContext) { json.extract[UpdateAccountJsonV400] } + anyViewContainsCanUpdateBankAccountLabelPermission = Views.views.vend.permission(BankIdAccountId(account.bankId, account.accountId), u) + .map(_.views.map(_.canUpdateBankAccountLabel).find(_.==(true)).getOrElse(false)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canUpdateBankAccountLabel_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanUpdateBankAccountLabelPermission + } + (success, callContext) <- Future { + Connector.connector.vend.updateAccountLabel(bankId, accountId, json.label) + } map { i => + (unboxFullOrFail(i, callContext, s"$UpdateBankAccountLabelError Current BankId is $bankId and Current AccountId is $accountId", 404), callContext) + } } yield { - account.updateLabel(u, json.label, callContext) (Extraction.decompose(successMessage), HttpCode.`200`(callContext)) } } @@ -4360,7 +4373,7 @@ trait APIMethods400 { viewJsonV300, List( $UserNotLoggedIn, - UserMissOwnerViewOrNotAccountHolder, + UserLacksPermissionCanGrantAccessToViewForTargetAccount, InvalidJsonFormat, UserNotFoundById, SystemViewNotFound, @@ -4380,7 +4393,9 @@ trait APIMethods400 { postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostAccountAccessJsonV400] } - _ <- NewStyle.function.canGrantAccessToView(bankId, accountId, u, callContext) + _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + APIUtil.canGrantAccessToView(bankId, accountId, ViewId(postJson.view.view_id), u, callContext) + } (user, callContext) <- NewStyle.function.findByUserId(postJson.user_id, callContext) view <- getView(bankId, accountId, postJson.view, callContext) addedView <- grantAccountAccessToUser(bankId, accountId, user, view, callContext) @@ -4417,7 +4432,7 @@ trait APIMethods400 { List(viewJsonV300), List( $UserNotLoggedIn, - UserMissOwnerViewOrNotAccountHolder, + UserLacksPermissionCanGrantAccessToViewForTargetAccount, InvalidJsonFormat, SystemViewNotFound, ViewNotFound, @@ -4431,6 +4446,7 @@ trait APIMethods400 { cc => val failMsg = s"$InvalidJsonFormat The Json body should be the $PostCreateUserAccountAccessJsonV400 " for { + (Full(u), callContext) <- SS.user postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostCreateUserAccountAccessJsonV400] } @@ -4438,8 +4454,10 @@ trait APIMethods400 { _ <- Helper.booleanToFuture(s"$InvalidUserProvider The user.provider must be start with 'dauth.'", cc=Some(cc)) { postJson.provider.startsWith("dauth.") } - - _ <- NewStyle.function.canGrantAccessToView(bankId, accountId, cc.loggedInUser, cc.callContext) + viewIdList = postJson.views.map(view =>ViewId(view.view_id)) + _ <- Helper.booleanToFuture(s"$UserLacksPermissionCanGrantAccessToViewForTargetAccount ", 403, cc = Some(cc)) { + APIUtil.canGrantAccessToMultipleViews(bankId, accountId, viewIdList, u, callContext) + } (targetUser, callContext) <- NewStyle.function.getOrCreateResourceUser(postJson.provider, postJson.username, cc.callContext) views <- getViews(bankId, accountId, postJson, callContext) addedView <- grantMultpleAccountAccessToUser(bankId, accountId, targetUser, views, callContext) @@ -4466,7 +4484,7 @@ trait APIMethods400 { revokedJsonV400, List( $UserNotLoggedIn, - UserMissOwnerViewOrNotAccountHolder, + UserLacksPermissionCanGrantAccessToViewForTargetAccount, InvalidJsonFormat, UserNotFoundById, SystemViewNotFound, @@ -4483,14 +4501,18 @@ trait APIMethods400 { cc => val failMsg = s"$InvalidJsonFormat The Json body should be the $PostAccountAccessJsonV400 " for { + (Full(u), callContext) <- SS.user postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostAccountAccessJsonV400] } - _ <- NewStyle.function.canRevokeAccessToView(bankId, accountId, cc.loggedInUser, cc.callContext) + viewId = ViewId(postJson.view.view_id) + _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + APIUtil.canRevokeAccessToView(bankId, accountId, viewId, u, callContext) + } (user, callContext) <- NewStyle.function.findByUserId(postJson.user_id, cc.callContext) view <- postJson.view.is_system match { - case true => NewStyle.function.systemView(ViewId(postJson.view.view_id), callContext) - case false => NewStyle.function.customView(ViewId(postJson.view.view_id), BankIdAccountId(bankId, accountId), callContext) + case true => NewStyle.function.systemView(viewId, callContext) + case false => NewStyle.function.customView(viewId, BankIdAccountId(bankId, accountId), callContext) } revoked <- postJson.view.is_system match { case true => NewStyle.function.revokeAccessToSystemView(bankId, accountId, view, user, callContext) @@ -4518,7 +4540,7 @@ trait APIMethods400 { revokedJsonV400, List( $UserNotLoggedIn, - UserMissOwnerViewOrNotAccountHolder, + UserLacksPermissionCanGrantAccessToViewForTargetAccount, InvalidJsonFormat, UserNotFoundById, SystemViewNotFound, @@ -4530,21 +4552,22 @@ trait APIMethods400 { List(apiTagAccountAccess, apiTagView, apiTagAccount, apiTagUser, apiTagOwnerRequired)) lazy val revokeGrantUserAccessToViews : OBPEndpoint = { - //add access for specific user to a specific system view case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: "account-access" :: Nil JsonPut json -> _ => { cc => val failMsg = s"$InvalidJsonFormat The Json body should be the $PostAccountAccessJsonV400 " for { + (Full(u), callContext) <- SS.user postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostRevokeGrantAccountAccessJsonV400] } - _ <- NewStyle.function.canRevokeAccessToView(bankId, accountId, cc.loggedInUser, cc.callContext) - (user, callContext) <- NewStyle.function.findByUserId(cc.loggedInUser.userId, cc.callContext) - _ <- Future(Views.views.vend.revokeAccountAccessByUser(bankId, accountId, user, callContext)) map { + _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + APIUtil.canRevokeAccessToAllViews(bankId, accountId, u, callContext) + } + _ <- Future(Views.views.vend.revokeAccountAccessByUser(bankId, accountId, u, callContext)) map { unboxFullOrFail(_, callContext, s"Cannot revoke") } grantViews = for (viewId <- postJson.views) yield ViewIdBankIdAccountId(ViewId(viewId), bankId, accountId) - _ <- Future(Views.views.vend.grantAccessToMultipleViews(grantViews, user, callContext)) map { + _ <- Future(Views.views.vend.grantAccessToMultipleViews(grantViews, u, callContext)) map { unboxFullOrFail(_, callContext, s"Cannot grant the views: ${postJson.views.mkString(",")}") } } yield { @@ -5138,7 +5161,6 @@ trait APIMethods400 { $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, - UserNoOwnerView, GetTransactionRequestsException, UnknownError ), @@ -5150,8 +5172,11 @@ trait APIMethods400 { for { (user @Full(u), _, account, view, callContext) <- SS.userBankAccountView _ <- NewStyle.function.isEnabledTransactionRequests(callContext) - _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView, cc=callContext) { - u.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext) + view <- NewStyle.function.checkAccountAccessAndGetView(viewId, BankIdAccountId(bankId, accountId), Full(u), callContext) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeTransactionRequests_.dbColumnName).dropRight(1)}` permission on the View(${viewId.value})", + cc = callContext) { + view.canSeeTransactionRequests } (transactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(requestId, callContext) } yield { 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 2d0691f313..d669bae986 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 @@ -1,7 +1,6 @@ package code.api.v5_0_0 import java.util.concurrent.ThreadLocalRandom - import code.accountattribute.AccountAttributeX import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ @@ -39,12 +38,13 @@ import net.liftweb.http.rest.RestHelper import net.liftweb.json import net.liftweb.json.{Extraction, compactRender, prettyRender} import net.liftweb.util.Helpers.tryo -import net.liftweb.util.{Helpers, Props} -import java.util.concurrent.ThreadLocalRandom +import net.liftweb.util.{Helpers, Props, StringHelpers} +import java.util.concurrent.ThreadLocalRandom import code.accountattribute.AccountAttributeX +import code.api.Constant.SYSTEM_OWNER_VIEW_ID import code.util.Helper.booleanToFuture -import code.views.system.AccountAccess +import code.views.system.{AccountAccess, ViewDefinition} import scala.collection.immutable.{List, Nil} import scala.collection.mutable.ArrayBuffer @@ -391,7 +391,7 @@ trait APIMethods500 { //1 Create or Update the `Owner` for the new account //2 Add permission to the user //3 Set the user as the account holder - _ = BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) + _ <- BankAccountCreation.setAccountHolderAndRefreshUserAccountAccess(bankId, accountId, postedOrLoggedInUser, callContext) } yield { (JSONFactory310.createAccountJSON(userIdAccountOwner, bankAccount, accountAttributes), HttpCode.`201`(callContext)) } @@ -1590,8 +1590,14 @@ trait APIMethods500 { cc => val res = for { - _ <- Helper.booleanToFuture(failMsg = UserNoOwnerView +"userId : " + cc.userId + ". account : " + accountId, cc=cc.callContext){ - cc.loggedInUser.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), Some(cc)) + (Full(u), callContext) <- SS.user + permission <- NewStyle.function.permission(bankId, accountId, u, callContext) + anyViewContainsCanSeeAvailableViewsForBankAccountPermission = permission.views.map(_.canSeeAvailableViewsForBankAccount).find(_.==(true)).getOrElse(false) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(ViewDefinition.canSeeAvailableViewsForBankAccount_.dbColumnName).dropRight(1)}` permission on any your views", + cc = callContext + ) { + anyViewContainsCanSeeAvailableViewsForBankAccountPermission } } yield { for { diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index a61efb2949..eb687beeca 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -754,7 +754,6 @@ trait Connector extends MdcLoggable { for{ fromAccount <- getBankAccountOld(fromAccountUID.bankId, fromAccountUID.accountId) ?~ s"$BankAccountNotFound Account ${fromAccountUID.accountId} not found at bank ${fromAccountUID.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext), UserNoOwnerView) toAccount <- getBankAccountOld(toAccountUID.bankId, toAccountUID.accountId) ?~ s"$BankAccountNotFound Account ${toAccountUID.accountId} not found at bank ${toAccountUID.bankId}" sameCurrency <- booleanToBox(fromAccount.currency == toAccount.currency, { @@ -841,7 +840,6 @@ trait Connector extends MdcLoggable { val request = for { fromAccountType <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext), UserNoOwnerView) toAccountType <- getBankAccountOld(toAccount.bankId, toAccount.accountId) ?~ s"account ${toAccount.accountId} not found at bank ${toAccount.bankId}" rawAmt <- tryo { BigDecimal(body.value.amount) } ?~! s"amount ${body.value.amount} not convertible to number" @@ -902,7 +900,6 @@ trait Connector extends MdcLoggable { // Always create a new Transaction Request val request = for { fromAccountType <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId),callContext) == true || hasEntitlement(fromAccount.bankId.value, initiator.userId, canCreateAnyTransactionRequest) == true, ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) toAccountType <- getBankAccountOld(toAccount.bankId, toAccount.accountId) ?~ s"account ${toAccount.accountId} not found at bank ${toAccount.bankId}" rawAmt <- tryo { BigDecimal(body.value.amount) } ?~! s"amount ${body.value.amount} not convertible to number" // isValidTransactionRequestType is checked at API layer. Maybe here too. @@ -1169,30 +1166,8 @@ trait Connector extends MdcLoggable { def saveTransactionRequestDescriptionImpl(transactionRequestId: TransactionRequestId, description: String): Box[Boolean] = TransactionRequests.transactionRequestProvider.vend.saveTransactionRequestDescriptionImpl(transactionRequestId, description) - def getTransactionRequests(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) : Box[List[TransactionRequest]] = { - val transactionRequests = - for { - fromAccount <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ - s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext), UserNoOwnerView) - transactionRequests <- getTransactionRequestsImpl(fromAccount) - } yield transactionRequests - - //make sure we return null if no challenge was saved (instead of empty fields) - if (!transactionRequests.isEmpty) { - for { - treq <- transactionRequests - } yield { - treq.map(tr => if (tr.challenge.id == "") { - tr.copy(challenge = null) - } else { - tr - }) - } - } else { - transactionRequests - } - } + def getTransactionRequests(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) : Box[List[TransactionRequest]] = + LocalMappedConnector.getTransactionRequests(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) def getTransactionRequests210(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) : Box[(List[TransactionRequest], Option[CallContext])] = { val transactionRequests = @@ -1233,12 +1208,8 @@ trait Connector extends MdcLoggable { def getTransactionRequestImpl(transactionRequestId: TransactionRequestId, callContext: Option[CallContext]): Box[(TransactionRequest, Option[CallContext])] = TransactionRequests.transactionRequestProvider.vend.getTransactionRequest(transactionRequestId).map(transactionRequest =>(transactionRequest, callContext)) - def getTransactionRequestTypes(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) : Box[List[TransactionRequestType]] = { - for { - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId,fromAccount.accountId), callContext), UserNoOwnerView) - transactionRequestTypes <- getTransactionRequestTypesImpl(fromAccount) - } yield transactionRequestTypes - } + def getTransactionRequestTypes(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) : Box[List[TransactionRequestType]] = + LocalMappedConnector.getTransactionRequestTypes(initiator : User, fromAccount : BankAccount, callContext: Option[CallContext]) protected def getTransactionRequestTypesImpl(fromAccount : BankAccount) : Box[List[TransactionRequestType]] = { //TODO: write logic / data access diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index ea4e41f985..2311603d52 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -10,13 +10,13 @@ import code.accountattribute.AccountAttributeX import code.accountholders.{AccountHolders, MapperAccountHolders} import code.api.BerlinGroup.{AuthenticationType, ScaStatus} import code.api.Constant -import code.api.Constant.{INCOMING_SETTLEMENT_ACCOUNT_ID, OUTGOING_SETTLEMENT_ACCOUNT_ID, SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID, SYSTEM_OWNER_VIEW_ID, localIdentityProvider} +import code.api.Constant.{INCOMING_SETTLEMENT_ACCOUNT_ID, OUTGOING_SETTLEMENT_ACCOUNT_ID, SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID, SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_OWNER_VIEW_ID, localIdentityProvider} import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.attributedefinition.{AttributeDefinition, AttributeDefinitionDI} import code.api.cache.Caching -import code.api.util.APIUtil.{DateWithMsFormat, OBPReturnType, generateUUID, hasEntitlement, isValidCurrencyISOCode, saveConnectorMetric, stringOrNull, unboxFullOrFail} +import code.api.util.APIUtil._ import code.api.util.ApiRole.canCreateAnyTransactionRequest -import code.api.util.ErrorMessages.{attemptedToOpenAnEmptyBox, _} +import code.api.util.ErrorMessages._ import code.api.util._ import code.api.v1_4_0.JSONFactory1_4_0.TransactionRequestAccountJsonV140 import code.api.v2_1_0._ @@ -72,7 +72,7 @@ import code.transactionrequests.TransactionRequests.TransactionRequestTypes import code.transactionrequests._ import code.users.{UserAttribute, UserAttributeProvider, Users} import code.util.Helper -import code.util.Helper.{MdcLoggable, _} +import code.util.Helper._ import code.views.Views import com.google.common.cache.CacheBuilder import com.openbankproject.commons.ExecutionContext.Implicits.global @@ -555,7 +555,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { */ override def getBankAccountsForUserLegacy(provider: String, username:String, callContext: Option[CallContext]): Box[(List[InboundAccount], Option[CallContext])] = { //1st: get the accounts from userAuthContext - val viewsToGenerate = List("owner") //TODO, so far only set the `owner` view, later need to simulate other views. + val viewsToGenerate = List(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID,SYSTEM_OWNER_VIEW_ID) //TODO, so far only set the `owner` view, later need to simulate other views. val user = Users.users.vend.getUserByProviderId(provider, username).getOrElse(throw new RuntimeException(s"$RefreshUserError at getBankAccountsForUserLegacy($username, ${callContext})")) val userId = user.userId tryo{net.liftweb.common.Logger(this.getClass).debug(s"getBankAccountsForUser.user says: provider($provider), username($username)")} @@ -4673,7 +4673,6 @@ object LocalMappedConnector extends Connector with MdcLoggable { for { fromAccount <- getBankAccountOld(fromAccountUID.bankId, fromAccountUID.accountId) ?~ s"$BankAccountNotFound Account ${fromAccountUID.accountId} not found at bank ${fromAccountUID.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), callContext), UserNoOwnerView) toAccount <- getBankAccountOld(toAccountUID.bankId, toAccountUID.accountId) ?~ s"$BankAccountNotFound Account ${toAccountUID.accountId} not found at bank ${toAccountUID.bankId}" sameCurrency <- booleanToBox(fromAccount.currency == toAccount.currency, { @@ -4731,7 +4730,6 @@ object LocalMappedConnector extends Connector with MdcLoggable { val request = for { fromAccountType <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), callContext), UserNoOwnerView) toAccountType <- getBankAccountOld(toAccount.bankId, toAccount.accountId) ?~ s"account ${toAccount.accountId} not found at bank ${toAccount.bankId}" rawAmt <- tryo { @@ -4793,7 +4791,6 @@ object LocalMappedConnector extends Connector with MdcLoggable { // Always create a new Transaction Request val request = for { fromAccountType <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), callContext) == true || hasEntitlement(fromAccount.bankId.value, initiator.userId, canCreateAnyTransactionRequest) == true, ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) toAccountType <- getBankAccountOld(toAccount.bankId, toAccount.accountId) ?~ s"account ${toAccount.accountId} not found at bank ${toAccount.bankId}" rawAmt <- tryo { BigDecimal(body.value.amount) @@ -5220,7 +5217,6 @@ object LocalMappedConnector extends Connector with MdcLoggable { for { fromAccount <- getBankAccountOld(fromAccount.bankId, fromAccount.accountId) ?~ s"account ${fromAccount.accountId} not found at bank ${fromAccount.bankId}" - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), callContext), UserNoOwnerView) transactionRequests <- getTransactionRequestsImpl(fromAccount) } yield transactionRequests @@ -5275,13 +5271,11 @@ object LocalMappedConnector extends Connector with MdcLoggable { override def getTransactionRequestTypes(initiator: User, fromAccount: BankAccount, callContext: Option[CallContext]): Box[List[TransactionRequestType]] = { for { - isOwner <- booleanToBox(initiator.hasOwnerViewAccess(BankIdAccountId(fromAccount.bankId, fromAccount.accountId), callContext), UserNoOwnerView) transactionRequestTypes <- getTransactionRequestTypesImpl(fromAccount) } yield transactionRequestTypes } override def getTransactionRequestTypesImpl(fromAccount: BankAccount): Box[List[TransactionRequestType]] = { - //TODO: write logic / data access // Get Transaction Request Types from Props "transactionRequests_supported_types". Default is empty string val validTransactionRequestTypes = APIUtil.getPropsValue("transactionRequests_supported_types", "").split(",").map(x => TransactionRequestType(x)).toList Full(validTransactionRequestTypes) diff --git a/obp-api/src/main/scala/code/model/BankingData.scala b/obp-api/src/main/scala/code/model/BankingData.scala index 0dbef2ff34..ab7028b2b7 100644 --- a/obp-api/src/main/scala/code/model/BankingData.scala +++ b/obp-api/src/main/scala/code/model/BankingData.scala @@ -30,7 +30,7 @@ import java.util.Date import code.accountholders.AccountHolders import code.api.{APIFailureNewStyle, Constant} -import code.api.util.APIUtil.{OBPReturnType, canGrantAccessToViewCommon, canRevokeAccessToViewCommon, fullBoxOrException, unboxFull, unboxFullOrFail} +import code.api.util.APIUtil.{OBPReturnType, canGrantAccessToView, canGrantAccessToMultipleViews, canRevokeAccessToAllViews, canRevokeAccessToView, unboxFullOrFail} import code.api.util.ErrorMessages._ import code.api.util._ import code.bankconnectors.{Connector, LocalMappedConnector} @@ -152,25 +152,6 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable final def bankRoutingAddress : String = Connector.connector.vend.getBankLegacy(bankId, None).map(_._1).map(_.bankRoutingAddress).getOrElse("") - /* - * Delete this account (if connector allows it, e.g. local mirror of account data) - * */ - final def remove(user : User, callContext: Option[CallContext]): Box[Boolean] = { - if(user.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext)){ - Full(Connector.connector.vend.removeAccount(bankId, accountId).openOrThrowException(attemptedToOpenAnEmptyBox)) - } else { - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) - } - } - - final def updateLabel(user : User, label : String, callContext: Option[CallContext]): Box[Boolean] = { - if(user.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext)){ - Connector.connector.vend.updateAccountLabel(bankId, accountId, label) - } else { - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) - } - } - /** * Note: There are two types of account-owners in OBP: the OBP users and the customers(in a real bank, these should from Main Frame) * @@ -230,37 +211,6 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable private def viewNotAllowed(view : View ) = Failure(s"${UserNoPermissionAccessView} Current VIEW_ID (${view.viewId.value})") - - - /** - * @param user a user requesting to see the other users' permissions - * @return a Box of all the users' permissions of this bank account if the user passed as a parameter has access to the owner view (allowed to see this kind of data) - */ - final def permissions(user : User, callContext: Option[CallContext]) : Box[List[Permission]] = { - //check if the user have access to the owner view in this the account - if(user.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext)) - Full(Views.views.vend.permissions(BankIdAccountId(bankId, accountId))) - else - Failure("user " + user.emailAddress + " does not have access to owner view on account " + accountId, Empty, Empty) - } - - /** - * @param user the user requesting to see the other users permissions on this account - * @param otherUserProvider the authentication provider of the user whose permissions will be retrieved - * @param otherUserIdGivenByProvider the id of the user (the one given by their auth provider) whose permissions will be retrieved - * @return a Box of the user permissions of this bank account if the user passed as a parameter has access to the owner view (allowed to see this kind of data) - */ - final def permission(user : User, otherUserProvider : String, otherUserIdGivenByProvider: String, callContext: Option[CallContext]) : Box[Permission] = { - //check if the user have access to the owner view in this the account - if(user.hasOwnerViewAccess(BankIdAccountId(bankId, accountId), callContext)) - for{ - u <- UserX.findByProviderId(otherUserProvider, otherUserIdGivenByProvider) - p <- Views.views.vend.permission(BankIdAccountId(bankId, accountId), u) - } yield p - else - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) - } - /** * @param user the user that wants to grant another user access to a view on this account * @param viewUID uid of the view to which we want to grant access @@ -276,13 +226,13 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable case _ => Views.views.vend.grantAccessToCustomView(viewUID, user) } } - if(canGrantAccessToViewCommon(bankId, accountId, user, callContext)) + if(canGrantAccessToView(bankId, accountId, viewUID.viewId , user, callContext)) for{ otherUser <- UserX.findByProviderId(otherUserProvider, otherUserIdGivenByProvider) //check if the userId corresponds to a user savedView <- grantAccessToCustomOrSystemView(otherUser) ?~ "could not save the privilege" } yield savedView else - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) + Failure(UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewId(${viewUID.viewId.value}) and current UserId(${user.userId})") } /** @@ -294,13 +244,13 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable */ final def grantAccessToMultipleViews(user : User, viewUIDs : List[ViewIdBankIdAccountId], otherUserProvider : String, otherUserIdGivenByProvider: String, callContext: Option[CallContext]) : Box[List[View]] = { - if(canGrantAccessToViewCommon(bankId, accountId, user, callContext)) + if(canGrantAccessToMultipleViews(bankId, accountId, viewUIDs.map(_.viewId), user, callContext)) for{ otherUser <- UserX.findByProviderId(otherUserProvider, otherUserIdGivenByProvider) //check if the userId corresponds to a user grantedViews <- Views.views.vend.grantAccessToMultipleViews(viewUIDs, otherUser, callContext) ?~ "could not save the privilege" } yield grantedViews else - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) + Failure(UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewIds${viewUIDs.map(_.viewId.value)} and current UserId${user.userId}") } /** @@ -319,13 +269,13 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable } } //check if the user have access to the owner view in this the account - if(canRevokeAccessToViewCommon(bankId, accountId, user, callContext: Option[CallContext])) + if(canRevokeAccessToView(bankId, accountId, viewUID.viewId, user, callContext: Option[CallContext])) for{ otherUser <- UserX.findByProviderId(otherUserProvider, otherUserIdGivenByProvider) //check if the userId corresponds to a user isRevoked <- revokeAccessToCustomOrSystemView(otherUser: User) ?~ "could not revoke the privilege" } yield isRevoked else - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) + Failure(UserLacksPermissionCanRevokeAccessToViewForTargetAccount + s"Current ViewId(${viewUID.viewId.value}) and current UserId(${user.userId})") } /** @@ -337,58 +287,13 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable */ final def revokeAllAccountAccess(user : User, otherUserProvider : String, otherUserIdGivenByProvider: String, callContext: Option[CallContext]) : Box[Boolean] = { - if(canRevokeAccessToViewCommon(bankId, accountId, user, callContext)) + if(canRevokeAccessToAllViews(bankId, accountId, user, callContext)) for{ otherUser <- UserX.findByProviderId(otherUserProvider, otherUserIdGivenByProvider) ?~ UserNotFoundByProviderAndUsername isRevoked <- Views.views.vend.revokeAllAccountAccess(bankId, accountId, otherUser) } yield isRevoked else - Failure(UserNoOwnerView+"user's email : " + user.emailAddress + ". account : " + accountId, Empty, Empty) - } - - - final def createCustomView(userDoingTheCreate : User,v: CreateViewJson, callContext: Option[CallContext]): Box[View] = { - if(!userDoingTheCreate.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext)) { - Failure({"user: " + userDoingTheCreate.idGivenByProvider + " at provider " + userDoingTheCreate.provider + " does not have owner access"}) - } else { - val view = Views.views.vend.createCustomView(BankIdAccountId(bankId,accountId), v) - - //if(view.isDefined) { - // logger.debug("user: " + userDoingTheCreate.idGivenByProvider + " at provider " + userDoingTheCreate.provider + " created view: " + view.get + - // " for account " + accountId + "at bank " + bankId) - //} - - view - } - } - - final def updateView(userDoingTheUpdate : User, viewId : ViewId, v: UpdateViewJSON, callContext: Option[CallContext]) : Box[View] = { - if(!userDoingTheUpdate.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext)) { - Failure({"user: " + userDoingTheUpdate.idGivenByProvider + " at provider " + userDoingTheUpdate.provider + " does not have owner access"}) - } else { - val view = Views.views.vend.updateCustomView(BankIdAccountId(bankId,accountId), viewId, v) - //if(view.isDefined) { - // logger.debug("user: " + userDoingTheUpdate.idGivenByProvider + " at provider " + userDoingTheUpdate.provider + " updated view: " + view.get + - // " for account " + accountId + "at bank " + bankId) - //} - - view - } - } - - final def removeView(userDoingTheRemove : User, viewId: ViewId, callContext: Option[CallContext]) : Box[Boolean] = { - if(!userDoingTheRemove.hasOwnerViewAccess(BankIdAccountId(bankId,accountId), callContext)) { - return Failure({"user: " + userDoingTheRemove.idGivenByProvider + " at provider " + userDoingTheRemove.provider + " does not have owner access"}) - } else { - val deleted = Views.views.vend.removeCustomView(viewId, BankIdAccountId(bankId,accountId)) - - //if (deleted.isDefined) { - // logger.debug("user: " + userDoingTheRemove.idGivenByProvider + " at provider " + userDoingTheRemove.provider + " deleted view: " + viewId + - // " for account " + accountId + "at bank " + bankId) - //} - - deleted - } + Failure(UserLacksPermissionCanRevokeAccessToViewForTargetAccount + s"current UserId${user.userId}") } final def moderatedTransaction(transactionId: TransactionId, view: View, bankIdAccountId: BankIdAccountId, user: Box[User], callContext: Option[CallContext] = None) : Box[(ModeratedTransaction, Option[CallContext])] = { diff --git a/obp-api/src/main/scala/code/model/ModeratedBankingData.scala b/obp-api/src/main/scala/code/model/ModeratedBankingData.scala index f49fdf10ab..3f6e99c70a 100644 --- a/obp-api/src/main/scala/code/model/ModeratedBankingData.scala +++ b/obp-api/src/main/scala/code/model/ModeratedBankingData.scala @@ -120,12 +120,12 @@ class ModeratedTransactionMetadata( /** * @return Full if deleting the tag worked, or a failure message if it didn't */ - def deleteTag(tagId : String, user: Option[User], bankAccount : BankAccount, callContext: Option[CallContext]) : Box[Unit] = { + def deleteTag(tagId : String, user: Option[User], bankAccount : BankAccount, view: View, callContext: Option[CallContext]) : Box[Unit] = { for { u <- Box(user) ?~ { UserNotLoggedIn} tagList <- Box(tags) ?~ { s"$NoViewPermission can_delete_tag. " } tag <- Box(tagList.find(tag => tag.id_ == tagId)) ?~ {"Tag with id " + tagId + "not found for this transaction"} - deleteFunc <- if(tag.postedBy == user || u.hasOwnerViewAccess(BankIdAccountId(bankAccount.bankId,bankAccount.accountId), callContext)) + deleteFunc <- if(tag.postedBy == user||view.canDeleteTag) Box(deleteTag) ?~ "Deleting tags not permitted for this view" else Failure("deleting tags not permitted for the current user") @@ -137,12 +137,12 @@ class ModeratedTransactionMetadata( /** * @return Full if deleting the image worked, or a failure message if it didn't */ - def deleteImage(imageId : String, user: Option[User], bankAccount : BankAccount, callContext: Option[CallContext]) : Box[Unit] = { + def deleteImage(imageId : String, user: Option[User], bankAccount : BankAccount, view: View, callContext: Option[CallContext]) : Box[Unit] = { for { u <- Box(user) ?~ { UserNotLoggedIn} imageList <- Box(images) ?~ { s"$NoViewPermission can_delete_image." } image <- Box(imageList.find(image => image.id_ == imageId)) ?~ {"Image with id " + imageId + "not found for this transaction"} - deleteFunc <- if(image.postedBy == user || u.hasOwnerViewAccess(BankIdAccountId(bankAccount.bankId,bankAccount.accountId), callContext)) + deleteFunc <- if(image.postedBy == user || view.canDeleteImage) Box(deleteImage) ?~ "Deleting images not permitted for this view" else Failure("Deleting images not permitted for the current user") @@ -151,12 +151,12 @@ class ModeratedTransactionMetadata( } } - def deleteComment(commentId: String, user: Option[User],bankAccount: BankAccount, callContext: Option[CallContext]) : Box[Unit] = { + def deleteComment(commentId: String, user: Option[User],bankAccount: BankAccount, view: View, callContext: Option[CallContext]) : Box[Unit] = { for { u <- Box(user) ?~ { UserNotLoggedIn} commentList <- Box(comments) ?~ { s"$NoViewPermission can_delete_comment." } comment <- Box(commentList.find(comment => comment.id_ == commentId)) ?~ {"Comment with id "+commentId+" not found for this transaction"} - deleteFunc <- if(comment.postedBy == user || u.hasOwnerViewAccess(BankIdAccountId(bankAccount.bankId,bankAccount.accountId), callContext)) + deleteFunc <- if(comment.postedBy == user || view.canDeleteComment) Box(deleteComment) ?~ "Deleting comments not permitted for this view" else Failure("Deleting comments not permitted for the current user") @@ -165,12 +165,12 @@ class ModeratedTransactionMetadata( } } - def deleteWhereTag(viewId: ViewId, user: Option[User],bankAccount: BankAccount, callContext: Option[CallContext]) : Box[Boolean] = { + def deleteWhereTag(viewId: ViewId, user: Option[User],bankAccount: BankAccount, view: View, callContext: Option[CallContext]) : Box[Boolean] = { for { u <- Box(user) ?~ { UserNotLoggedIn} whereTagOption <- Box(whereTag) ?~ { s"$NoViewPermission can_delete_where_tag. Current ViewId($viewId)" } whereTag <- Box(whereTagOption) ?~ {"there is no tag to delete"} - deleteFunc <- if(whereTag.postedBy == user || u.hasOwnerViewAccess(BankIdAccountId(bankAccount.bankId,bankAccount.accountId),callContext)) + deleteFunc <- if(whereTag.postedBy == user || view.canDeleteWhereTag) Box(deleteWhereTag) ?~ "Deleting tag is not permitted for this view" else Failure("Deleting tags not permitted for the current user") diff --git a/obp-api/src/main/scala/code/model/User.scala b/obp-api/src/main/scala/code/model/User.scala index 50d65ef5a9..10c70b84f4 100644 --- a/obp-api/src/main/scala/code/model/User.scala +++ b/obp-api/src/main/scala/code/model/User.scala @@ -94,14 +94,9 @@ case class UserExtended(val user: User) extends MdcLoggable { } final def checkOwnerViewAccessAndReturnOwnerView(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]) = { - //Note: now SYSTEM_OWNER_VIEW_ID == SYSTEM_OWNER_VIEW_ID is the same `owner` so we only use one here. - //And in side the checkViewAccessAndReturnView, it will first check the customer view and then will check system view. APIUtil.checkViewAccessAndReturnView(ViewId(SYSTEM_OWNER_VIEW_ID), bankIdAccountId, Some(this.user), callContext) } - final def hasOwnerViewAccess(bankIdAccountId: BankIdAccountId, callContext: Option[CallContext]): Boolean = { - checkOwnerViewAccessAndReturnOwnerView(bankIdAccountId, callContext).isDefined - } final def hasViewAccess(bankIdAccountId: BankIdAccountId, viewId: ViewId, callContext: Option[CallContext]): Boolean = { APIUtil.checkViewAccessAndReturnView( viewId, diff --git a/obp-api/src/main/scala/code/model/View.scala b/obp-api/src/main/scala/code/model/View.scala index e76231a7df..aed6d412ed 100644 --- a/obp-api/src/main/scala/code/model/View.scala +++ b/obp-api/src/main/scala/code/model/View.scala @@ -354,7 +354,7 @@ case class ViewExtended(val view: View) { ) } else - Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` access for the view(${view.viewId.value})") + Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` permission on the view(${view.viewId.value})") } @deprecated("This have the performance issue, call `Connector.connector.vend.getBankLegacy` four times in the backend. use @moderateAccount instead ","08-01-2020") @@ -402,7 +402,7 @@ case class ViewExtended(val view: View) { ) } else - Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` access for the view(${view.viewId.value})") + Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` permission on the view(${view.viewId.value})") } def moderateAccountCore(bankAccount: BankAccount) : Box[ModeratedBankAccountCore] = { @@ -435,7 +435,7 @@ case class ViewExtended(val view: View) { ) } else - Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` access for the view(${view.viewId.value})") + Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionThisBankAccount` permission on the view(${view.viewId.value})") } // Moderate the Counterparty side of the Transaction (i.e. the Other Account involved in the transaction) @@ -558,7 +558,7 @@ case class ViewExtended(val view: View) { ) } else - Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionOtherBankAccount` access for the view(${view.viewId.value})") + Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionOtherBankAccount` permission on the view(${view.viewId.value})") } def moderateCore(counterpartyCore : CounterpartyCore) : Box[ModeratedOtherBankAccountCore] = { @@ -607,6 +607,6 @@ case class ViewExtended(val view: View) { ) } else - Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionOtherBankAccount` access for the view(${view.viewId.value})") + Failure(s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `canSeeTransactionOtherBankAccount` permission on the view(${view.viewId.value})") } } diff --git a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala index 297f20df8b..d16c37a63e 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/AuthUser.scala @@ -1376,8 +1376,11 @@ def restoreSomeSessions(): Unit = { connectorEmptyResponse(_, callContext) } _ = logger.debug(s"--> for user($user): AuthUser.refreshUserAccountAccess.accounts : ${accountsHeld}") + + success = refreshViewsAccountAccessAndHolders(user, accountsHeld, callContext) + }yield { - refreshViewsAccountAccessAndHolders(user, accountsHeld, callContext) + success } } @@ -1387,7 +1390,7 @@ def restoreSomeSessions(): Unit = { * This method can only be used by the original user(account holder). * InboundAccount return many fields, but in this method, we only need bankId, accountId and viewId so far. */ - def refreshViewsAccountAccessAndHolders(user: User, accountsHeld: List[InboundAccount], callContext: Option[CallContext]): Unit = { + def refreshViewsAccountAccessAndHolders(user: User, accountsHeld: List[InboundAccount], callContext: Option[CallContext]) = { if(user.isOriginalUser){ //first, we compare the accounts in obp and the accounts in cbs, val (_, privateAccountAccess) = Views.views.vend.privateViewsUserCanAccess(user) @@ -1414,6 +1417,7 @@ def restoreSomeSessions(): Unit = { //TODO. need to double check if we need to clean accountidmapping table, account meta data (MappedTag) .... for{ cbsRemovedBankAccountId <- cbsRemovedBankAccountIds + _ = logger.debug("refreshViewsAccountAccessAndHolders.cbsRemovedBankAccountIds.cbsRemovedBankAccountId: start-------" + cbsRemovedBankAccountId) bankId = cbsRemovedBankAccountId.bankId accountId = cbsRemovedBankAccountId.accountId _ = Views.views.vend.revokeAccountAccessByUser(bankId, accountId, user, callContext) @@ -1421,26 +1425,30 @@ def restoreSomeSessions(): Unit = { cbsAccount = accountsHeld.find(cbsAccount =>cbsAccount.bankId == bankId.value && cbsAccount.accountId == accountId.value) viewId <- cbsAccount.map(_.viewsToGenerate).getOrElse(List.empty[String]) _=UserRefreshes.UserRefreshes.vend.createOrUpdateRefreshUser(user.userId) + success <- Views.views.vend.removeCustomView(ViewId(viewId), cbsRemovedBankAccountId) + _ = logger.debug("refreshViewsAccountAccessAndHolders.cbsRemovedBankAccountIds.cbsRemovedBankAccountId: finish-------" + cbsRemovedBankAccountId) } yield { - Views.views.vend.removeCustomView(ViewId(viewId), cbsRemovedBankAccountId) + success } //2st: create views/accountAccess/accountHolders for the new coming accounts for { newBankAccountId <- csbNewBankAccountIds + _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewBankAccountId.newBankAccountId: start-------" + newBankAccountId) _ = AccountHolders.accountHolders.vend.getOrCreateAccountHolder(user,newBankAccountId,Some("UserAuthContext")) bankId = newBankAccountId.bankId accountId = newBankAccountId.accountId newBankAccount = accountsHeld.find(cbsAccount =>cbsAccount.bankId == bankId.value && cbsAccount.accountId == accountId.value) viewId <- newBankAccount.map(_.viewsToGenerate).getOrElse(List.empty[String]) - _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewBankAccountIds-------" + csbNewBankAccountIds) view <- Views.views.vend.getOrCreateSystemViewFromCbs(viewId)//TODO, only support system views so far, may add custom views later. _=UserRefreshes.UserRefreshes.vend.createOrUpdateRefreshUser(user.userId) + view <- if (view.isSystem) //if the view is a system view, we will call `grantAccessToSystemView` + Views.views.vend.grantAccessToSystemView(bankId, accountId, view, user) + else //otherwise, we will call `grantAccessToCustomView` + Views.views.vend.grantAccessToCustomView(view.uid, user) + _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewBankAccountId.newBankAccountId: finish-------" + newBankAccountId) } yield { - if (view.isSystem)//if the view is a system view, we will call `grantAccessToSystemView` - Views.views.vend.grantAccessToSystemView(bankId, accountId, view, user) - else //otherwise, we will call `grantAccessToCustomView` - Views.views.vend.grantAccessToCustomView(view.uid, user) + view } //3rd: if the ids are not change, but views are changed, we still need compare the view for each account: @@ -1450,9 +1458,9 @@ def restoreSomeSessions(): Unit = { // we can not get the views from the `viewDefinition` table, because we can not delete system views at all. we need to read the view from accountAccess table. //obpViewsForAccount = MapperViews.availableViewsForAccount(bankAccountId).map(_.viewId.value) obpViewsForAccount = Views.views.vend.privateViewsUserCanAccessForAccount(user, bankAccountId).map(_.viewId.value) + _ = logger.debug("refreshViewsAccountAccessAndHolders.obpViewsForAccount-------" + obpViewsForAccount) cbsViewsForAccount = accountsHeld.find(account => account.bankId.equals(bankAccountId.bankId.value) && account.accountId.equals(bankAccountId.accountId.value)).map(_.viewsToGenerate).getOrElse(Nil) - _ = logger.debug("refreshViewsAccountAccessAndHolders.obpViewsForAccount-------" + obpViewsForAccount) _ = logger.debug("refreshViewsAccountAccessAndHolders.cbsViewsForAccount-------" + cbsViewsForAccount) //cbs removed these views, but OBP still contains the data for them, so we need to clean data in OBP side. cbsRemovedViewsForAccount = obpViewsForAccount diff cbsViewsForAccount @@ -1466,24 +1474,30 @@ def restoreSomeSessions(): Unit = { //cbs has new views which are not in obp yet, we need to create new data for these accounts. csbNewViewsForAccount = cbsViewsForAccount diff obpViewsForAccount _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewViewsForAccount-------" + csbNewViewsForAccount) - _ = if(csbNewViewsForAccount.nonEmpty){ + success = if(csbNewViewsForAccount.nonEmpty){ for{ newViewForAccount <- csbNewViewsForAccount + _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewViewsForAccount.newViewForAccount start:-------" + newViewForAccount) view <- Views.views.vend.getOrCreateSystemViewFromCbs(newViewForAccount)//TODO, only support system views so far, may add custom views later. _ = UserRefreshes.UserRefreshes.vend.createOrUpdateRefreshUser(user.userId) - }yield{ - if (view.isSystem)//if the view is a system view, we will call `grantAccessToSystemView` - Views.views.vend.grantAccessToSystemView(bankAccountId.bankId, bankAccountId.accountId, view, user) + view <- if (view.isSystem) //if the view is a system view, we will call `grantAccessToSystemView` + Views.views.vend.grantAccessToSystemView(bankAccountId.bankId, bankAccountId.accountId, view, user) else //otherwise, we will call `grantAccessToCustomView` Views.views.vend.grantAccessToCustomView(view.uid, user) - + _ = logger.debug("refreshViewsAccountAccessAndHolders.csbNewViewsForAccount.newViewForAccount finish:-------" + newViewForAccount) + }yield{ + view } } } yield { - bankAccountId + success } } - } + true + } + else { + false + } } /** * Find the authUser by author user name(authUser and resourceUser are the same). diff --git a/obp-api/src/main/scala/code/model/dataAccess/BankAccountCreationDispatcher.scala b/obp-api/src/main/scala/code/model/dataAccess/BankAccountCreationDispatcher.scala index f773e7454e..b1548d5232 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/BankAccountCreationDispatcher.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/BankAccountCreationDispatcher.scala @@ -85,24 +85,14 @@ package code.model.dataAccess { * * @return This is a procedure, no return value. Just use the side effect. */ - def setAccountHolderAndRefreshUserAccountAccess(bankId : BankId, accountId : AccountId, user: User, callContext: Option[CallContext]): Unit = { - // Here, we can call `addPermissionToSystemOwnerView` directly, but from now on, we try to simulate the CBS account creation. + def setAccountHolderAndRefreshUserAccountAccess(bankId : BankId, accountId : AccountId, user: User, callContext: Option[CallContext]) = { // 1st-getOrCreateAccountHolder: in this method, we only create the account holder, no view, account access involved here. AccountHolders.accountHolders.vend.getOrCreateAccountHolder(user: User, BankIdAccountId(bankId, accountId)) // 2rd-refreshUserAccountAccess: in this method, we will simulate onboarding bank user processes. @refreshUserAccountAccess definition. AuthUser.refreshUser(user, callContext) } - - private def addPermissionToSystemOwnerView(bankId : BankId, accountId : AccountId, user: User): Unit = { - Views.views.vend.getOrCreateSystemView(SYSTEM_OWNER_VIEW_ID) match { - case Full(ownerView) => - Views.views.vend.grantAccessToSystemView(bankId, accountId, ownerView, user) - case _ => - logger.debug(s"Cannot create/get system view: ${SYSTEM_OWNER_VIEW_ID}") - } - } - + } object BankAccountCreationListener extends MdcLoggable { diff --git a/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala b/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala index 768caceb8c..b949ab4cb6 100644 --- a/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala +++ b/obp-api/src/main/scala/code/model/dataAccess/MappedView.scala @@ -54,7 +54,7 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with def getSingleton = ViewImpl def primaryKeyField = id_ - + //This field used ManyToMany object users_ extends MappedManyToMany(ViewPrivileges, ViewPrivileges.view, ViewPrivileges.user, ResourceUser) @@ -203,6 +203,14 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canSeeTransactionThisBankAccount_ extends MappedBoolean(this){ override def defaultValue = false } + + object canSeeTransactionRequests_ extends MappedBoolean(this){ + override def defaultValue = false + } + + object canSeeTransactionRequestTypes_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeTransactionOtherBankAccount_ extends MappedBoolean(this){ override def defaultValue = false } @@ -245,6 +253,9 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canSeeBankAccountOwners_ extends MappedBoolean(this){ override def defaultValue = false } + object canSeeAvailableViewsForBankAccount_ extends MappedBoolean(this){ + override def defaultValue = true + } object canSeeBankAccountType_ extends MappedBoolean(this){ override def defaultValue = false } @@ -260,6 +271,9 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canSeeBankAccountLabel_ extends MappedBoolean(this){ override def defaultValue = false } + object canUpdateBankAccountLabel_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeBankAccountNationalIdentifier_ extends MappedBoolean(this){ override def defaultValue = false } @@ -290,6 +304,12 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canSeeBankAccountRoutingAddress_ extends MappedBoolean(this){ override def defaultValue = false } + object canSeeViewsWithPermissionsForOneUser_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canSeeViewsWithPermissionsForAllUsers_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeOtherAccountNationalIdentifier_ extends MappedBoolean(this){ override def defaultValue = false } @@ -425,6 +445,21 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with object canSeeBankAccountCreditLimit_ extends MappedBoolean(this){ override def defaultValue = false } + object canCreateCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canDeleteCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canUpdateCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canRevokeAccessToCustomViews_ extends MappedBoolean(this) { + override def defaultValue = false + } + object canGrantAccessToCustomViews_ extends MappedBoolean(this) { + override def defaultValue = false + } def id: Long = id_.get def isSystem: Boolean = isSystem_.get @@ -448,6 +483,8 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with //transaction fields def canSeeTransactionThisBankAccount : Boolean = canSeeTransactionThisBankAccount_.get + def canSeeTransactionRequests : Boolean = canSeeTransactionRequests_.get + def canSeeTransactionRequestTypes : Boolean = canSeeTransactionRequestTypes_.get def canSeeTransactionOtherBankAccount : Boolean = canSeeTransactionOtherBankAccount_.get def canSeeTransactionMetadata : Boolean = canSeeTransactionMetadata_.get def canSeeTransactionDescription: Boolean = canSeeTransactionDescription_.get @@ -465,12 +502,14 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with def canSeeImages : Boolean = canSeeImages_.get //Bank account fields + def canSeeAvailableViewsForBankAccount : Boolean = canSeeAvailableViewsForBankAccount_.get def canSeeBankAccountOwners : Boolean = canSeeBankAccountOwners_.get def canSeeBankAccountType : Boolean = canSeeBankAccountType_.get def canSeeBankAccountBalance : Boolean = canSeeBankAccountBalance_.get def canSeeBankAccountCurrency : Boolean = canSeeBankAccountCurrency_.get def canQueryAvailableFunds : Boolean = canQueryAvailableFunds_.get def canSeeBankAccountLabel : Boolean = canSeeBankAccountLabel_.get + def canUpdateBankAccountLabel : Boolean = canUpdateBankAccountLabel_.get def canSeeBankAccountNationalIdentifier : Boolean = canSeeBankAccountNationalIdentifier_.get def canSeeBankAccountSwift_bic : Boolean = canSeeBankAccountSwift_bic_.get def canSeeBankAccountIban : Boolean = canSeeBankAccountIban_.get @@ -481,6 +520,8 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with def canSeeBankRoutingAddress : Boolean = canSeeBankRoutingAddress_.get def canSeeBankAccountRoutingScheme : Boolean = canSeeBankAccountRoutingScheme_.get def canSeeBankAccountRoutingAddress : Boolean = canSeeBankAccountRoutingAddress_.get + def canSeeViewsWithPermissionsForOneUser: Boolean = canSeeViewsWithPermissionsForOneUser_.get + def canSeeViewsWithPermissionsForAllUsers: Boolean = canSeeViewsWithPermissionsForAllUsers_.get //other bank account fields def canSeeOtherAccountNationalIdentifier : Boolean = canSeeOtherAccountNationalIdentifier_.get @@ -537,6 +578,13 @@ class ViewImpl extends View with LongKeyedMapper[ViewImpl] with ManyToMany with def canCreateStandingOrder: Boolean = false //TODO: if you add new permissions here, remember to set them wherever views are created // (e.g. BankAccountCreationDispatcher) + + def canCreateCustomView: Boolean = canCreateCustomView_.get + def canDeleteCustomView: Boolean = canDeleteCustomView_.get + def canUpdateCustomView: Boolean = canUpdateCustomView_.get + + override def canGrantAccessToCustomViews: Boolean = canGrantAccessToCustomViews_.get + override def canRevokeAccessToCustomViews: Boolean = canRevokeAccessToCustomViews_.get } object ViewImpl extends ViewImpl with LongKeyedMetaMapper[ViewImpl]{ diff --git a/obp-api/src/main/scala/code/util/Helper.scala b/obp-api/src/main/scala/code/util/Helper.scala index 5a67a57887..4024cee9f2 100644 --- a/obp-api/src/main/scala/code/util/Helper.scala +++ b/obp-api/src/main/scala/code/util/Helper.scala @@ -117,31 +117,6 @@ object Helper{ x => fullBoxOrException(x ~> APIFailureNewStyle(failMsg, failCode, cc.map(_.toLight))) } } - /** - * Helper function which wrap some statement into Future. - * The function is curried i.e. - * use this parameter syntax ---> (failMsg: String)(statement: => Boolean) - * instead of this one ---------> (failMsg: String, statement: => Boolean) - * Below is an example of recommended usage. - * Please note that the second parameter is provided in curly bracket in order to mimics body of a function. - * booleanToFuture(failMsg = UserHasMissingRoles + CanGetAnyUser) { - * hasEntitlement("", u.userId, ApiRole.CanGetAnyUser) - * } - * @param failMsg is used in case that result of call of function booleanToBox returns Empty - * @param statement is call by name parameter. - * @return In case the statement is false the function returns Future[Failure(failMsg)]. - * Otherwise returns Future[Full()]. - */ - def wrapStatementToFuture(failMsg: String, failCode: Int = 400)(statement: => Boolean): Future[Box[Boolean]] = { - Future { - statement match { - case true => Full(statement) - case false => Empty - } - } map { - x => fullBoxOrException(x ~> APIFailureNewStyle(failMsg, failCode)) - } - } val deprecatedJsonGenerationMessage = "json generation handled elsewhere as it changes from api version to api version" diff --git a/obp-api/src/main/scala/code/views/MapperViews.scala b/obp-api/src/main/scala/code/views/MapperViews.scala index 91d9820d2a..9b6c46cc7e 100644 --- a/obp-api/src/main/scala/code/views/MapperViews.scala +++ b/obp-api/src/main/scala/code/views/MapperViews.scala @@ -91,19 +91,21 @@ object MapperViews extends Views with MdcLoggable { } // This is an idempotent function private def getOrGrantAccessToCustomView(user: User, viewDefinition: View, bankId: String, accountId: String): Box[View] = { - if (AccountAccess.count( - By(AccountAccess.user_fk, user.userPrimaryKey.value), - By(AccountAccess.bank_id, bankId), - By(AccountAccess.account_id, accountId), - By(AccountAccess.view_id, viewDefinition.viewId.value)) == 0) { - logger.debug(s"getOrGrantAccessToCustomView AccountAccess.create user(UserId(${user.userId}), ViewId(${viewDefinition.viewId.value}), bankId($bankId), accountId($accountId)") + if (AccountAccess.findByUniqueIndex( + BankId(bankId), + AccountId(accountId), + viewDefinition.viewId, + user.userPrimaryKey, + ALL_CONSUMERS).isEmpty) { + logger.debug(s"getOrGrantAccessToCustomView AccountAccess.create" + + s"user(UserId(${user.userId}), ViewId(${viewDefinition.viewId.value}), bankId($bankId), accountId($accountId), consumerId($ALL_CONSUMERS)") // SQL Insert AccountAccessList val saved = AccountAccess.create. user_fk(user.userPrimaryKey.value). bank_id(bankId). account_id(accountId). view_id(viewDefinition.viewId.value). - view_fk(viewDefinition.id). + consumer_id(ALL_CONSUMERS). save if (saved) { //logger.debug("saved AccountAccessList") @@ -304,33 +306,19 @@ object MapperViews extends Views with MdcLoggable { /** * remove all the accountAccess for one user and linked account. - * If the user has `owner` view accountAccess and also the accountHolder, we can not revoke, just return `false`. - * all the other case, we can revoke all the account access for that user. + * we already has the guard `canRevokeAccessToAllViews` on the top level. */ - def revokeAllAccountAccess(bankId : BankId, accountId: AccountId, user : User) : Box[Boolean] = { - val userIsAccountHolder_? = MapperAccountHolders.getAccountHolders(bankId, accountId).map(h => h.userPrimaryKey).contains(user.userPrimaryKey) - val userHasOwnerViewAccess_? = AccountAccess.find( + AccountAccess.find( By(AccountAccess.bank_id, bankId.value), By(AccountAccess.account_id, accountId.value), - By(AccountAccess.view_id, SYSTEM_OWNER_VIEW_ID), - By(AccountAccess.user_fk, user.userPrimaryKey.value), - ).isDefined - - if(userIsAccountHolder_? && userHasOwnerViewAccess_?){ - Full(false) - }else{ - AccountAccess.find( - By(AccountAccess.bank_id, bankId.value), - By(AccountAccess.account_id, accountId.value), - By(AccountAccess.user_fk, user.userPrimaryKey.value) - ).foreach(_.delete_!) - Full(true) - } + By(AccountAccess.user_fk, user.userPrimaryKey.value) + ).foreach(_.delete_!) + Full(true) } def revokeAccountAccessByUser(bankId : BankId, accountId: AccountId, user : User, callContext: Option[CallContext]) : Box[Boolean] = { - canRevokeAccessToViewCommon(bankId, accountId, user, callContext) match { + canRevokeAccessToAllViews(bankId, accountId, user, callContext) match { case true => val permissions = AccountAccess.findAll( By(AccountAccess.user_fk, user.userPrimaryKey.value), @@ -340,7 +328,7 @@ object MapperViews extends Views with MdcLoggable { permissions.foreach(_.delete_!) Full(true) case false => - Failure(CannotRevokeAccountAccess) + Failure(UserLacksPermissionCanRevokeAccessToViewForTargetAccount) } } @@ -607,12 +595,14 @@ object MapperViews extends Views with MdcLoggable { def getOrCreateSystemViewFromCbs(viewId: String): Box[View] = { - + logger.debug(s"-->getOrCreateSystemViewFromCbs--- start--${viewId} ") + val ownerView = SYSTEM_OWNER_VIEW_ID.equals(viewId.toLowerCase) val accountantsView = SYSTEM_ACCOUNTANT_VIEW_ID.equals(viewId.toLowerCase) val auditorsView = SYSTEM_AUDITOR_VIEW_ID.equals(viewId.toLowerCase) val standardView = SYSTEM_STANDARD_VIEW_ID.equals(viewId.toLowerCase) val stageOneView = SYSTEM_STAGE_ONE_VIEW_ID.toLowerCase.equals(viewId.toLowerCase) + val manageCustomViews = SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID.toLowerCase.equals(viewId.toLowerCase) val theView = if (ownerView) @@ -625,12 +615,14 @@ object MapperViews extends Views with MdcLoggable { getOrCreateSystemView(SYSTEM_STANDARD_VIEW_ID) else if (stageOneView) getOrCreateSystemView(SYSTEM_STAGE_ONE_VIEW_ID) + else if (manageCustomViews) + getOrCreateSystemView(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID) else { logger.error(ViewIdNotSupported+ s"Your input viewId is :$viewId") Failure(ViewIdNotSupported+ s"Your input viewId is :$viewId") } - logger.debug(s"-->getOrCreateSystemViewFromCbs.${viewId } : ${theView} ") + logger.debug(s"-->getOrCreateSystemViewFromCbs --- finish.${viewId } : ${theView} ") theView } @@ -794,12 +786,43 @@ object MapperViews extends Views with MdcLoggable { .canSeeOtherAccountRoutingAddress_(true) .canAddTransactionRequestToOwnAccount_(true) //added following two for payments .canAddTransactionRequestToAnyAccount_(true) + .canSeeAvailableViewsForBankAccount_(false) + .canSeeTransactionRequests_(false) + .canSeeTransactionRequestTypes_(false) + .canUpdateBankAccountLabel_(false) + .canCreateCustomView_(false) + .canDeleteCustomView_(false) + .canUpdateCustomView_(false) + .canSeeViewsWithPermissionsForOneUser_(false) + .canSeeViewsWithPermissionsForAllUsers_(false) + .canRevokeAccessToCustomViews_(false) + .canGrantAccessToCustomViews_(false) + .canCreateCustomView_(false) + .canDeleteCustomView_(false) + .canUpdateCustomView_(false) viewId match { + case SYSTEM_OWNER_VIEW_ID | SYSTEM_STANDARD_VIEW_ID => + entity + .canSeeAvailableViewsForBankAccount_(true) + .canSeeTransactionRequests_(true) + .canSeeTransactionRequestTypes_(true) + .canUpdateBankAccountLabel_(true) + .canSeeViewsWithPermissionsForOneUser_(true) + .canSeeViewsWithPermissionsForAllUsers_(true) + .canGrantAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) + .canRevokeAccessToViews_(ALL_SYSTEM_VIEWS_CREATED_FROM_BOOT.mkString(",")) case SYSTEM_STAGE_ONE_VIEW_ID => entity .canSeeTransactionDescription_(false) .canAddTransactionRequestToAnyAccount_(false) + case SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID => + entity + .canRevokeAccessToCustomViews_(true) + .canGrantAccessToCustomViews_(true) + .canCreateCustomView_(true) + .canDeleteCustomView_(true) + .canUpdateCustomView_(true) case SYSTEM_FIREHOSE_VIEW_ID => entity .isFirehose_(true) @@ -809,9 +832,9 @@ object MapperViews extends Views with MdcLoggable { } def createAndSaveSystemView(viewId: String) : Box[View] = { - logger.debug(s"-->createAndSaveSystemView.viewId: ${viewId} ") + logger.debug(s"-->createAndSaveSystemView.viewId.start${viewId} ") val res = unsavedSystemView(viewId).saveMe - logger.debug(s"-->createAndSaveSystemView: ${res} ") + logger.debug(s"-->createAndSaveSystemView.finish: ${res} ") Full(res) } @@ -877,16 +900,16 @@ object MapperViews extends Views with MdcLoggable { canAddPrivateAlias_(true). canAddCounterparty_(true). canGetCounterparty_(true). - canDeleteCounterparty_(true). - canDeleteCorporateLocation_(true). - canDeletePhysicalLocation_(true). + canDeleteCounterparty_(false). + canDeleteCorporateLocation_(false). + canDeletePhysicalLocation_(false). canEditOwnerComment_(true). canAddComment_(true). - canDeleteComment_(true). + canDeleteComment_(false). canAddTag_(true). - canDeleteTag_(true). + canDeleteTag_(false). canAddImage_(true). - canDeleteImage_(true). + canDeleteImage_(false). canAddWhereTag_(true). canSeeWhereTag_(true). canSeeBankRoutingScheme_(true). //added following in V300 @@ -898,7 +921,10 @@ object MapperViews extends Views with MdcLoggable { canSeeOtherAccountRoutingScheme_(true). canSeeOtherAccountRoutingAddress_(true). canAddTransactionRequestToOwnAccount_(false). //added following two for payments - canAddTransactionRequestToAnyAccount_(false) + canAddTransactionRequestToAnyAccount_(false). + canSeeTransactionRequests_(false). + canSeeTransactionRequestTypes_(false). + canUpdateBankAccountLabel_(false) } def createAndSaveDefaultPublicCustomView(bankId : BankId, accountId: AccountId, description: String) : Box[View] = { diff --git a/obp-api/src/main/scala/code/views/system/AccountAccess.scala b/obp-api/src/main/scala/code/views/system/AccountAccess.scala index 59775b298e..6a7dcc25df 100644 --- a/obp-api/src/main/scala/code/views/system/AccountAccess.scala +++ b/obp-api/src/main/scala/code/views/system/AccountAccess.scala @@ -28,6 +28,16 @@ class AccountAccess extends LongKeyedMapper[AccountAccess] with IdPK with Create } object AccountAccess extends AccountAccess with LongKeyedMetaMapper[AccountAccess] { override def dbIndexes: List[BaseIndex[AccountAccess]] = UniqueIndex(bank_id, account_id, view_id, user_fk, consumer_id) :: super.dbIndexes + + def findByUniqueIndex(bankId: BankId, accountId: AccountId, viewId: ViewId, userPrimaryKey: UserPrimaryKey, consumerId: String) = + AccountAccess.find( + By(AccountAccess.bank_id, bankId.value), + By(AccountAccess.account_id, accountId.value), + By(AccountAccess.view_id, viewId.value), + By(AccountAccess.user_fk, userPrimaryKey.value), + By(AccountAccess.consumer_id, consumerId), + ) + def findAllBySystemViewId(systemViewId:ViewId)= AccountAccess.findAll( By(AccountAccess.view_id, systemViewId.value) ) @@ -38,13 +48,11 @@ object AccountAccess extends AccountAccess with LongKeyedMetaMapper[AccountAcces AccountAccess.findAllByBankIdAccountIdViewId(view.bankId, view.accountId, view.viewId) } def findAllByUserPrimaryKey(userPrimaryKey:UserPrimaryKey)= AccountAccess.findAll( - By(AccountAccess.user_fk, userPrimaryKey.value), - PreCache(AccountAccess.view_fk) + By(AccountAccess.user_fk, userPrimaryKey.value) ) def findAllByBankIdAccountId(bankId:BankId, accountId:AccountId) = AccountAccess.findAll( By(AccountAccess.bank_id, bankId.value), - By(AccountAccess.account_id, accountId.value), - PreCache(AccountAccess.view_fk) + By(AccountAccess.account_id, accountId.value) ) def findAllByBankIdAccountIdViewId(bankId:BankId, accountId:AccountId, viewId:ViewId)= AccountAccess.findAll( By(AccountAccess.bank_id, bankId.value), @@ -55,8 +63,7 @@ object AccountAccess extends AccountAccess with LongKeyedMetaMapper[AccountAcces def findByBankIdAccountIdUserPrimaryKey(bankId: BankId, accountId: AccountId, userPrimaryKey: UserPrimaryKey) = AccountAccess.findAll( By(AccountAccess.bank_id, bankId.value), By(AccountAccess.account_id, accountId.value), - By(AccountAccess.user_fk, userPrimaryKey.value), - PreCache(AccountAccess.view_fk) + By(AccountAccess.user_fk, userPrimaryKey.value) ) def findByBankIdAccountIdViewIdUserPrimaryKey(bankId: BankId, accountId: AccountId, viewId: ViewId, userPrimaryKey: UserPrimaryKey) = AccountAccess.find( diff --git a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala index 99cabc263b..ac00941019 100644 --- a/obp-api/src/main/scala/code/views/system/ViewDefinition.scala +++ b/obp-api/src/main/scala/code/views/system/ViewDefinition.scala @@ -6,7 +6,7 @@ import code.util.{AccountIdString, UUIDString} import com.openbankproject.commons.model._ import net.liftweb.common.Box import net.liftweb.common.Box.tryo -import net.liftweb.mapper._ +import net.liftweb.mapper.{MappedBoolean, _} import scala.collection.immutable.List @@ -57,9 +57,21 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many object canRevokeAccessToViews_ extends MappedText(this){ override def defaultValue = "" } + object canRevokeAccessToCustomViews_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canGrantAccessToCustomViews_ extends MappedBoolean(this) { + override def defaultValue = false + } object canSeeTransactionThisBankAccount_ extends MappedBoolean(this){ override def defaultValue = false } + object canSeeTransactionRequests_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canSeeTransactionRequestTypes_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeTransactionOtherBankAccount_ extends MappedBoolean(this){ override def defaultValue = false } @@ -102,6 +114,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many object canSeeBankAccountOwners_ extends MappedBoolean(this){ override def defaultValue = false } + object canSeeAvailableViewsForBankAccount_ extends MappedBoolean(this){ + override def defaultValue = true + } object canSeeBankAccountType_ extends MappedBoolean(this){ override def defaultValue = false } @@ -117,6 +132,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many object canSeeBankAccountLabel_ extends MappedBoolean(this){ override def defaultValue = false } + object canUpdateBankAccountLabel_ extends MappedBoolean(this){ + override def defaultValue = false + } object canSeeBankAccountNationalIdentifier_ extends MappedBoolean(this){ override def defaultValue = false } @@ -292,6 +310,22 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many object canCreateStandingOrder_ extends MappedBoolean(this){ override def defaultValue = false } + + object canCreateCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canDeleteCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canUpdateCustomView_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canSeeViewsWithPermissionsForAllUsers_ extends MappedBoolean(this){ + override def defaultValue = false + } + object canSeeViewsWithPermissionsForOneUser_ extends MappedBoolean(this){ + override def defaultValue = false + } //Important! If you add a field, be sure to handle it here in this function def setFromViewData(viewData : ViewSpecification) = { @@ -312,11 +346,18 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many isFirehose_(viewData.is_firehose.getOrElse(false)) metadataView_(viewData.metadata_view) - canGrantAccessToViews_(viewData.can_grant_access_to_views.getOrElse(Nil).mkString(",")) - canRevokeAccessToViews_(viewData.can_revoke_access_to_views.getOrElse(Nil).mkString(",")) - val actions = viewData.allowed_actions + if (isSystem) { //The following are admin permissions, only system views are allowed to use them. + canGrantAccessToCustomViews_(actions.exists(_ == "can_grant_access_to_custom_views")) + canRevokeAccessToCustomViews_(actions.exists(_ == "can_revoke_access_to_custom_views")) + canGrantAccessToViews_(viewData.can_grant_access_to_views.getOrElse(Nil).mkString(",")) + canRevokeAccessToViews_(viewData.can_revoke_access_to_views.getOrElse(Nil).mkString(",")) + canCreateCustomView_(actions.exists(_ == "can_create_custom_view")) + canDeleteCustomView_(actions.exists(_ == "can_delete_custom_view")) + canUpdateCustomView_(actions.exists(_ == "can_update_custom_view")) + } + canSeeTransactionThisBankAccount_(actions.exists(_ =="can_see_transaction_this_bank_account")) canSeeTransactionOtherBankAccount_(actions.exists(_ =="can_see_transaction_other_bank_account")) canSeeTransactionMetadata_(actions.exists(_ == "can_see_transaction_metadata")) @@ -394,6 +435,12 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many canSeeBankAccountCreditLimit_(actions.exists(_ == "can_see_bank_account_credit_limit")) canCreateDirectDebit_(actions.exists(_ == "can_create_direct_debit")) canCreateStandingOrder_(actions.exists(_ == "can_create_standing_order")) + canSeeTransactionRequests_(actions.exists(_ == "can_see_transaction_requests")) + canSeeTransactionRequestTypes_(actions.exists(_ == "can_see_transaction_request_types")) + canUpdateBankAccountLabel_(actions.exists(_ == "can_update_bank_account_label")) + canSeeAvailableViewsForBankAccount_(actions.exists(_ == "can_see_available_views_for_bank_account")) + canSeeViewsWithPermissionsForAllUsers_(actions.exists(_ == "can_see_views_with_permissions_for_all_users")) + canSeeViewsWithPermissionsForOneUser_(actions.exists(_ == "can_see_views_with_permissions_for_one_user")) } @@ -419,23 +466,31 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def usePublicAliasIfOneExists: Boolean = usePublicAliasIfOneExists_.get def hideOtherAccountMetadataIfAlias: Boolean = hideOtherAccountMetadataIfAlias_.get + //This current view can grant access to other views. override def canGrantAccessToViews : Option[List[String]] = { canGrantAccessToViews_.get == null || canGrantAccessToViews_.get.isEmpty() match { case true => None case _ => Some(canGrantAccessToViews_.get.split(",").toList.map(_.trim)) } } + + def canGrantAccessToCustomViews : Boolean = canGrantAccessToCustomViews_.get + + //the current view can revoke access to other views. override def canRevokeAccessToViews : Option[List[String]] = { canRevokeAccessToViews_.get == null || canRevokeAccessToViews_.get.isEmpty() match { case true => None case _ => Some(canRevokeAccessToViews_.get.split(",").toList.map(_.trim)) } } + override def canRevokeAccessToCustomViews : Boolean = canRevokeAccessToCustomViews_.get //reading access //transaction fields def canSeeTransactionThisBankAccount : Boolean = canSeeTransactionThisBankAccount_.get + def canSeeTransactionRequests : Boolean = canSeeTransactionRequests_.get + def canSeeTransactionRequestTypes: Boolean = canSeeTransactionRequestTypes_.get def canSeeTransactionOtherBankAccount : Boolean = canSeeTransactionOtherBankAccount_.get def canSeeTransactionMetadata : Boolean = canSeeTransactionMetadata_.get def canSeeTransactionDescription: Boolean = canSeeTransactionDescription_.get @@ -453,12 +508,14 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def canSeeImages : Boolean = canSeeImages_.get //Bank account fields + def canSeeAvailableViewsForBankAccount : Boolean = canSeeAvailableViewsForBankAccount_.get def canSeeBankAccountOwners : Boolean = canSeeBankAccountOwners_.get def canSeeBankAccountType : Boolean = canSeeBankAccountType_.get def canSeeBankAccountBalance : Boolean = canSeeBankAccountBalance_.get def canSeeBankAccountCurrency : Boolean = canSeeBankAccountCurrency_.get def canQueryAvailableFunds : Boolean = canQueryAvailableFunds_.get def canSeeBankAccountLabel : Boolean = canSeeBankAccountLabel_.get + def canUpdateBankAccountLabel : Boolean = canUpdateBankAccountLabel_.get def canSeeBankAccountNationalIdentifier : Boolean = canSeeBankAccountNationalIdentifier_.get def canSeeBankAccountSwift_bic : Boolean = canSeeBankAccountSwift_bic_.get def canSeeBankAccountIban : Boolean = canSeeBankAccountIban_.get @@ -469,6 +526,8 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def canSeeBankRoutingAddress : Boolean = canSeeBankRoutingAddress_.get def canSeeBankAccountRoutingScheme : Boolean = canSeeBankAccountRoutingScheme_.get def canSeeBankAccountRoutingAddress : Boolean = canSeeBankAccountRoutingAddress_.get + def canSeeViewsWithPermissionsForOneUser: Boolean = canSeeViewsWithPermissionsForOneUser_.get + def canSeeViewsWithPermissionsForAllUsers : Boolean = canSeeViewsWithPermissionsForAllUsers_.get //other bank account fields def canSeeOtherAccountNationalIdentifier : Boolean = canSeeOtherAccountNationalIdentifier_.get @@ -524,6 +583,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many def canCreateDirectDebit: Boolean = canCreateDirectDebit_.get def canCreateStandingOrder: Boolean = canCreateStandingOrder_.get + def canCreateCustomView: Boolean = canCreateCustomView_.get + def canDeleteCustomView: Boolean = canDeleteCustomView_.get + def canUpdateCustomView: Boolean = canUpdateCustomView_.get //TODO: if you add new permissions here, remember to set them wherever views are created // (e.g. BankAccountCreationDispatcher) } diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala index c116d7d67b..5ccb9afeba 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala @@ -132,7 +132,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(NoAccountAccessOnView) + responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( @@ -161,7 +161,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(NoAccountAccessOnView) + responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( @@ -195,7 +195,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(NoAccountAccessOnView) + responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( diff --git a/obp-api/src/test/scala/code/api/v1_2_1/API1_2_1Test.scala b/obp-api/src/test/scala/code/api/v1_2_1/API1_2_1Test.scala index 17f23f9d5a..17359a7d69 100644 --- a/obp-api/src/test/scala/code/api/v1_2_1/API1_2_1Test.scala +++ b/obp-api/src/test/scala/code/api/v1_2_1/API1_2_1Test.scala @@ -31,6 +31,7 @@ import _root_.net.liftweb.json.Serialization.write import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON import code.api.util.APIUtil import code.api.util.APIUtil.OAuth._ +import code.api.util.APIUtil.checkSystemViewIdOrName import code.bankconnectors.Connector import code.setup.{APIResponse, DefaultUsers, PrivateUser2AccountsAndSetUpWithTestData, ServerSetupWithTestData} import code.views.Views @@ -164,9 +165,7 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val reply = makeGetRequest(request) val possibleViewsPermalinks = reply.body.extract[ViewsJSONV121].views .filterNot(_.is_public==true) - .filterNot(_.id.contains(SYSTEM_OWNER_VIEW_ID)) - .filterNot(_.id.contains(SYSTEM_AUDITOR_VIEW_ID)) - .filterNot(_.id.contains(SYSTEM_ACCOUNTANT_VIEW_ID)) + .filterNot(view=> checkSystemViewIdOrName(view.id)) val randomPosition = nextInt(possibleViewsPermalinks.size) possibleViewsPermalinks(randomPosition).id } @@ -183,6 +182,7 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat .filterNot(_.id.contains(SYSTEM_AUDITOR_VIEW_ID)) .filterNot(_.id.contains(SYSTEM_ACCOUNTANT_VIEW_ID)) .filterNot(_.id.contains(SYSTEM_FIREHOSE_VIEW_ID)) + .filterNot(_.id.contains(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID)) val randomPosition = nextInt(possibleViewsPermalinksWithoutOwner.size) possibleViewsPermalinksWithoutOwner(randomPosition).id } @@ -1865,8 +1865,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val bankAccount : AccountJSON = randomPrivateAccount(bankId) When("the request is sent") val reply = grantUserAccessToView(bankId, bankAccount.id, randomString(10), randomCustomViewPermalink(bankId, bankAccount), user1) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) And("we should get an error message") reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) } @@ -1879,10 +1879,10 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = grantUserAccessToView(bankId, bankAccount.id, userId, randomString(10), user1) - Then("we should get a 404 code") - reply.code should equal (404) + Then("we should get a 403 code") + reply.code should equal (403) And("we should get an error message") - reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) + reply.body.extract[ErrorMessage].message contains(UserLacksPermissionCanGrantAccessToViewForTargetAccount) shouldBe(true) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore) } @@ -1895,8 +1895,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = grantUserAccessToView(bankId, bankAccount.id, userId, randomCustomViewPermalink(bankId, bankAccount), user3) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) And("we should get an error message") reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length @@ -1935,8 +1935,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsIdsToGrant= randomCustomViewsIdsToGrant(bankId, bankAccount.id) When("the request is sent") val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant, user1) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) And("we should get an error message") reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) } @@ -1949,10 +1949,10 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsIdsToGrant= List(randomString(10),randomString(10)) When("the request is sent") val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant, user1) - Then("we should get a 404 code") - reply.code should equal (404) + Then("we should get a 403 code") + reply.code should equal (403) And("we should get an error message") - reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) + reply.body.extract[ErrorMessage].message contains(UserLacksPermissionCanGrantAccessToViewForTargetAccount) shouldBe(true) } scenario("we cannot grant a user access to a list of views on an bank account because some views don't exist", API1_2_1, PostPermissions){ @@ -1964,10 +1964,10 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant, user1) - Then("we should get a 404 code") - reply.code should equal (404) + Then("we should get a 403 code") + reply.code should equal (403) And("we should get an error message") - reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) + reply.body.extract[ErrorMessage].message contains(UserLacksPermissionCanGrantAccessToViewForTargetAccount) shouldBe(true) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore) } @@ -1981,8 +1981,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant, user3) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) And("we should get an error message") reply.body.extract[ErrorMessage].message.nonEmpty should equal (true) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length @@ -1995,16 +1995,18 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat Given("We will use an access token") val bankId = randomBank val bankAccount : AccountJSON = randomPrivateAccount(bankId) - val userId = resourceUser2.idGivenByProvider + val userId2 = resourceUser2.idGivenByProvider val viewId = getTheRandomView(bankId, bankAccount) val viewsIdsToGrant = viewId :: Nil - grantUserAccessToViews(bankId, bankAccount.id, userId, viewsIdsToGrant, user1) - val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length + val replyGranted = grantUserAccessToViews(bankId, bankAccount.id, userId2, viewsIdsToGrant, user1) + Then("we should get a 201") + replyGranted.code should equal(201) + val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId2, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") - val reply = revokeUserAccessToView(bankId, bankAccount.id, userId, viewId, user1) + val reply = revokeUserAccessToView(bankId, bankAccount.id, userId2, viewId, user1) Then("we should get a 204 no content code") reply.code should equal (204) - val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length + val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId2, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore -1) } @@ -2032,8 +2034,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val bankAccount : AccountJSON = randomPrivateAccount(bankId) When("the request is sent") val reply = revokeUserAccessToView(bankId, bankAccount.id, randomString(10), randomCustomViewPermalink(bankId, bankAccount), user1) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) } scenario("we can revoke the access of a user to owner view on a bank account if that user is an account holder of that account", API1_2_1, DeletePermission){ @@ -2065,8 +2067,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = revokeUserAccessToView(bankId, bankAccount.id, userId, randomString(10), user1) - Then("we should get a 400 code") - reply.code should equal (400) + Then("we should get a 403 code") + reply.code should equal (403) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore) } @@ -2079,8 +2081,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = revokeUserAccessToView(bankId, bankAccount.id, userId, randomCustomViewPermalink(bankId, bankAccount), user3) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore) } @@ -2108,8 +2110,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val bankAccount : AccountJSON = randomPrivateAccount(bankId) When("the request is sent") val reply = revokeUserAccessToAllViews(bankId, bankAccount.id, randomString(510), user1) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) } scenario("we cannot revoke a user access to a view on an bank account because the user does not have owner view access", API1_2_1, DeletePermissions){ @@ -2123,8 +2125,8 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat val viewsBefore = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length When("the request is sent") val reply = revokeUserAccessToAllViews(bankId, bankAccount.id, userId, user3) - Then("we should get a 400 ok code") - reply.code should equal (400) + Then("we should get a 403 ok code") + reply.code should equal (403) val viewsAfter = getUserAccountPermission(bankId, bankAccount.id, userId, user1).body.extract[ViewsJSONV121].views.length viewsAfter should equal(viewsBefore) } @@ -2170,7 +2172,7 @@ class API1_2_1Test extends ServerSetupWithTestData with DefaultUsers with Privat And("The user should not have had his access revoked") val view = Views.views.vend.systemView(ViewId(SYSTEM_OWNER_VIEW_ID)).openOrThrowException(attemptedToOpenAnEmptyBox) - Views.views.vend.getOwners(view).toList should contain (resourceUser3) + Views.views.vend.getOwners(view).toList should not contain (resourceUser3) } } diff --git a/obp-api/src/test/scala/code/api/v2_0_0/TransactionRequestsTest.scala b/obp-api/src/test/scala/code/api/v2_0_0/TransactionRequestsTest.scala index 244872a0eb..fd6a573699 100644 --- a/obp-api/src/test/scala/code/api/v2_0_0/TransactionRequestsTest.scala +++ b/obp-api/src/test/scala/code/api/v2_0_0/TransactionRequestsTest.scala @@ -365,7 +365,7 @@ class TransactionRequestsTest extends V200ServerSetup with DefaultUsers { case _ => "" } Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) - error should equal (ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) + error contains (ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) shouldBe( true) } @@ -425,7 +425,7 @@ class TransactionRequestsTest extends V200ServerSetup with DefaultUsers { case _ => "" } Then("We should have the error: " + ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) - error should equal (ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) + error contains (ErrorMessages.InsufficientAuthorisationToCreateTransactionRequest) shouldBe( true) } diff --git a/obp-api/src/test/scala/code/api/v2_1_0/SandboxDataLoadingTest.scala b/obp-api/src/test/scala/code/api/v2_1_0/SandboxDataLoadingTest.scala index ca9c071a1c..1fee51fbeb 100644 --- a/obp-api/src/test/scala/code/api/v2_1_0/SandboxDataLoadingTest.scala +++ b/obp-api/src/test/scala/code/api/v2_1_0/SandboxDataLoadingTest.scala @@ -307,7 +307,6 @@ class SandboxDataLoadingTest extends FlatSpec with SendServerRequests with Match //Note: system views not bankId, accountId, so here, we need to get all the views val (views,accountAccess) = Views.views.vend.privateViewsUserCanAccess(owner) val ownerView = views.find(v => v.viewId.value == SYSTEM_OWNER_VIEW_ID) - owner.hasOwnerViewAccess(BankIdAccountId(foundAccount.bankId, foundAccount.accountId), None) should equal(true) //and the owners should have access to it //Now, the owner is the system view, so all the users/accounts should have the access to this view diff --git a/obp-api/src/test/scala/code/api/v2_2_0/AccountTest.scala b/obp-api/src/test/scala/code/api/v2_2_0/AccountTest.scala index 65cf21ca9d..a1a62b8791 100644 --- a/obp-api/src/test/scala/code/api/v2_2_0/AccountTest.scala +++ b/obp-api/src/test/scala/code/api/v2_2_0/AccountTest.scala @@ -15,14 +15,6 @@ import scala.util.Random class AccountTest extends V220ServerSetup with DefaultUsers { - override def beforeAll(): Unit = { - super.beforeAll() - } - - override def afterAll(): Unit = { - super.afterAll() - } - val mockAccountId1 = "NEW_MOCKED_ACCOUNT_ID_01" val mockAccountId2 = "NEW_MOCKED_ACCOUNT_ID_02" @@ -51,9 +43,6 @@ class AccountTest extends V220ServerSetup with DefaultUsers { val responsePut = makePutRequest(requestPut, write(accountPutJSON)) And("We should get a 200") responsePut.code should equal(200) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) When("We make the authenticated access request") val requestGetAll = (v2_2Request / "accounts").GET <@ (user1) @@ -98,10 +87,6 @@ class AccountTest extends V220ServerSetup with DefaultUsers { val requestPut = (v2_2Request / "banks" / testBank.value / "accounts" / mockAccountId1).PUT <@ (user1) val responsePut = makePutRequest(requestPut, write(accountPutJSON)) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - And("We should get a 200") responsePut.code should equal(200) @@ -165,12 +150,8 @@ class AccountTest extends V220ServerSetup with DefaultUsers { val requestPut = (v2_2Request / "banks" / testBank.value / "accounts" / mockAccountId1).PUT <@ (user1) val responsePut = makePutRequest(requestPut, write(accountPutJSON)) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - Then("we get the account access for this account") - val accountViewsRequest = v2_2Request / "banks" / testBank.value / "accounts" / mockAccountId1 / "views" <@(user1) + val accountViewsRequest = (v2_2Request / "banks" / testBank.value / "accounts" / mockAccountId1 / "views").GET <@(user1) val accountViewsResponse = makeGetRequest(accountViewsRequest) val accountViews = accountViewsResponse.body.extract[ViewsJSONV220] //Note: now when we create new account, will have the systemOwnerView access to this view. diff --git a/obp-api/src/test/scala/code/api/v3_1_0/AccountTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/AccountTest.scala index 2526199fb8..af88c2de48 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/AccountTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/AccountTest.scala @@ -103,10 +103,6 @@ class AccountTest extends V310ServerSetup with DefaultUsers { responsePut1.code should equal(200) responsePut1.body.extract[UpdateAccountResponseJsonV310].account_routings should be (testPutJsonWithIban.account_routings) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - val responseGet1 = makeGetRequest(requestGet) And("We should get 200 and updated account routings in the getAccount response") responseGet1.code should equal(200) @@ -202,10 +198,6 @@ class AccountTest extends V310ServerSetup with DefaultUsers { val response310 = makePutRequest(request310, write(putCreateAccountJSONV310)) Then("We should get a 201") response310.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - val account = response310.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJSONV310.product_code) @@ -217,10 +209,6 @@ class AccountTest extends V310ServerSetup with DefaultUsers { account.label should be (putCreateAccountJSONV310.label) account.account_routings should be (putCreateAccountJSONV310.account_routings) - - //We need to waite some time for the account creation, because we introduce `AuthUser.refreshUser(user, callContext)` - //It may not finished when we call the get accounts directly. - TimeUnit.SECONDS.sleep(2) Then(s"we call $ApiEndpoint4 to get the account back") val requestApiEndpoint4 = (v3_1_0_Request / "my" / "accounts" ).PUT <@(user1) @@ -278,10 +266,7 @@ class AccountTest extends V310ServerSetup with DefaultUsers { val response310 = makePutRequest(request310, write(putCreateAccountJson)) Then("We should get a 201") response310.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - + val account = response310.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJson.product_code) account.`label` should be (putCreateAccountJson.`label`) @@ -308,10 +293,6 @@ class AccountTest extends V310ServerSetup with DefaultUsers { Then("We should get a 201") responseUser2_310.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - Then(s"we call $ApiEndpoint6 to get the account back by user2") val requestApiUser2Endpoint6 = (v3_1_0_Request /"banks" / testBankId.value / "accounts" / userAccountId / Constant.SYSTEM_OWNER_VIEW_ID/ "account" ).GET <@(user2) @@ -328,9 +309,6 @@ class AccountTest extends V310ServerSetup with DefaultUsers { Then("We should get a 201") response310_1.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) val account = response310_1.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJSONV310.product_code) account.`label` should be (putCreateAccountJSONV310.`label`) diff --git a/obp-api/src/test/scala/code/api/v3_1_0/TransactionRequestTest.scala b/obp-api/src/test/scala/code/api/v3_1_0/TransactionRequestTest.scala index 045c072843..cc8a23212a 100644 --- a/obp-api/src/test/scala/code/api/v3_1_0/TransactionRequestTest.scala +++ b/obp-api/src/test/scala/code/api/v3_1_0/TransactionRequestTest.scala @@ -85,7 +85,7 @@ class TransactionRequestTest extends V310ServerSetup { Then("We should get a 403") response310.code should equal(403) And("error should be " + UserNoPermissionAccessView) - response310.body.extract[ErrorMessage].message should equal (UserNoPermissionAccessView) + response310.body.extract[ErrorMessage].message contains (UserNoPermissionAccessView) shouldBe (true) } } diff --git a/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala index 324b7dc2f9..04c96ab466 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/AccountAccessTest.scala @@ -44,9 +44,6 @@ class AccountAccessTest extends V400ServerSetup { val request400 = (v4_0_0_Request / "banks" / bankId / "accounts" ).POST <@(user1) val response400 = makePostRequest(request400, write(addAccountJson)) Then("We should get a 201") - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(3) response400.code should equal(201) response400.body.extract[CreateAccountResponseJsonV310] diff --git a/obp-api/src/test/scala/code/api/v4_0_0/AccountTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/AccountTest.scala index 7c1e111cb9..0a40d27eb3 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/AccountTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/AccountTest.scala @@ -106,10 +106,7 @@ class AccountTest extends V400ServerSetup { Then("We should get a 201") response400.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - + val account = response400.body.extract[CreateAccountResponseJsonV310] account.account_id should not be empty account.product_code should be (addAccountJson.product_code) @@ -121,11 +118,6 @@ class AccountTest extends V400ServerSetup { account.label should be (addAccountJson.label) account.account_routings should be (addAccountJson.account_routings) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(3) - - Then(s"We call $ApiEndpoint1 to get the account back") val request = (v4_0_0_Request /"my" / "banks" / testBankId.value/ "accounts" / account.account_id / "account").GET <@ (user1) val response = makeGetRequest(request) @@ -170,10 +162,6 @@ class AccountTest extends V400ServerSetup { Then("We should get a 201") response400_1.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - val account = response400_1.body.extract[CreateAccountResponseJsonV310] account.account_id should not be empty account.product_code should be (addAccountJson.product_code) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala index bf77ecf979..78eb5eba9f 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DeleteAccountCascadeTest.scala @@ -70,10 +70,6 @@ class DeleteAccountCascadeTest extends V400ServerSetup { val account = response400.body.extract[CreateAccountResponseJsonV310] account.account_id should not be empty - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(3) - val postBodyView = createViewJsonV300.copy(name = "_cascade_delete", metadata_view = "_cascade_delete", is_public = false).toCreateViewJson createViewViaEndpoint(bankId, account.account_id, postBodyView, user1) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala index c79ee5621c..6d65c160fa 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/DeleteBankCascadeTest.scala @@ -65,10 +65,7 @@ class DeleteBankCascadeTest extends V400ServerSetup { val request400 = (v4_0_0_Request / "banks" / bankId / "accounts" ).POST <@(user1) val response400 = makePostRequest(request400, write(addAccountJson)) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(3) - + Then("We should get a 201") response400.code should equal(201) val account = response400.body.extract[CreateAccountResponseJsonV310] diff --git a/obp-api/src/test/scala/code/api/v4_0_0/SettlementAccountTest.scala b/obp-api/src/test/scala/code/api/v4_0_0/SettlementAccountTest.scala index 19b6a80d49..a7033c64ef 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/SettlementAccountTest.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/SettlementAccountTest.scala @@ -125,10 +125,6 @@ class SettlementAccountTest extends V400ServerSetup { makePostRequest((v4_0_0_Request / "banks" / testBankId.value / "settlement-accounts" ).POST <@(user1), write(createSettlementAccountJson)) makePostRequest((v4_0_0_Request / "banks" / testBankId.value / "settlement-accounts" ).POST <@(user1), write(createSettlementAccountOtherUser)) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(4) - When("We send the request") val addedEntitlement: Box[Entitlement] = Entitlement.entitlement.vend.addEntitlement(testBankId.value, resourceUser1.userId, ApiRole.CanGetSettlementAccountAtOneBank.toString) diff --git a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala index 06dfd07671..8b42fb3599 100644 --- a/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala +++ b/obp-api/src/test/scala/code/api/v4_0_0/V400ServerSetup.scala @@ -277,9 +277,7 @@ trait V400ServerSetup extends ServerSetupWithTestData with DefaultUsers { And("We make a request v4.0.0") val request400 = (v4_0_0_Request / "banks" / bankId / "accounts" ).POST <@(consumerAndToken) val response400 = makePostRequest(request400, write(json)) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(3) + Then("We should get a 201") response400.code should equal(201) diff --git a/obp-api/src/test/scala/code/api/v5_0_0/AccountTest.scala b/obp-api/src/test/scala/code/api/v5_0_0/AccountTest.scala index a7fc8df388..c6aae646c2 100644 --- a/obp-api/src/test/scala/code/api/v5_0_0/AccountTest.scala +++ b/obp-api/src/test/scala/code/api/v5_0_0/AccountTest.scala @@ -64,10 +64,6 @@ class AccountTest extends V500ServerSetup with DefaultUsers { val response = makePutRequest(request, write(putCreateAccountJSONV310)) Then("We should get a 201") response.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - val account = response.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJSONV310.product_code) @@ -80,10 +76,6 @@ class AccountTest extends V500ServerSetup with DefaultUsers { account.account_routings should be (putCreateAccountJSONV310.account_routings) - //We need to waite some time for the account creation, because we introduce `AuthUser.refreshUser(user, callContext)` - //It may not finished when we call the get accounts directly. - TimeUnit.SECONDS.sleep(2) - Then(s"we call $ApiEndpoint4 to get the account back") val requestApiEndpoint4 = (v5_0_0_Request / "my" / "accounts" ).PUT <@(user1) val responseApiEndpoint4 = makeGetRequest(requestApiEndpoint4) @@ -141,9 +133,6 @@ class AccountTest extends V500ServerSetup with DefaultUsers { val response500 = makePutRequest(request500, write(putCreateAccountJson)) Then("We should get a 201") response500.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) val account = response500.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJson.product_code) @@ -172,11 +161,6 @@ class AccountTest extends V500ServerSetup with DefaultUsers { Then("We should get a 201") responseUser2_500.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) - - Then(s"we call $ApiEndpoint6 to get the account back by user2") val requestApiUser2Endpoint6 = (v5_0_0_Request /"banks" / testBankId.value / "accounts" / userAccountId / Constant.SYSTEM_OWNER_VIEW_ID/ "account" ).GET <@(user2) val responseApiUser2Endpoint6 = makeGetRequest(requestApiUser2Endpoint6) @@ -193,9 +177,6 @@ class AccountTest extends V500ServerSetup with DefaultUsers { Then("We should get a 201") response310_1.code should equal(201) - //for create account endpoint, we need to wait for `setAccountHolderAndRefreshUserAccountAccess` method, - //it is an asynchronous process, need some time to be done. - TimeUnit.SECONDS.sleep(2) val account = response310_1.body.extract[CreateAccountResponseJsonV310] account.product_code should be (putCreateAccountJSONV310.product_code) account.`label` should be (putCreateAccountJSONV310.`label`) diff --git a/obp-api/src/test/scala/code/setup/TestConnectorSetup.scala b/obp-api/src/test/scala/code/setup/TestConnectorSetup.scala index 04d44ca309..86b6c930e7 100644 --- a/obp-api/src/test/scala/code/setup/TestConnectorSetup.scala +++ b/obp-api/src/test/scala/code/setup/TestConnectorSetup.scala @@ -2,7 +2,7 @@ package code.setup import java.util.{Calendar, Date} import code.accountholders.AccountHolders -import code.api.Constant.{SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID, SYSTEM_FIREHOSE_VIEW_ID, SYSTEM_OWNER_VIEW_ID} +import code.api.Constant.{SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID, SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID, SYSTEM_FIREHOSE_VIEW_ID, SYSTEM_OWNER_VIEW_ID} import code.api.util.ErrorMessages.attemptedToOpenAnEmptyBox import code.api.util.{APIUtil, OBPLimit, OBPOffset} import code.bankconnectors.{Connector, LocalMappedConnector} @@ -72,12 +72,14 @@ trait TestConnectorSetup { val systemAuditorView = getOrCreateSystemView(SYSTEM_AUDITOR_VIEW_ID) val systemAccountantView = getOrCreateSystemView(SYSTEM_ACCOUNTANT_VIEW_ID) val systemFirehoseView = getOrCreateSystemView(SYSTEM_FIREHOSE_VIEW_ID) + val manageCustomViews = getOrCreateSystemView(SYSTEM_MANAGE_CUSTOM_VIEWS_VIEW_ID) accounts.foreach(account => { Views.views.vend.grantAccessToSystemView(account.bankId, account.accountId, systemOwnerView, user) Views.views.vend.grantAccessToSystemView(account.bankId, account.accountId, systemAuditorView, user) Views.views.vend.grantAccessToSystemView(account.bankId, account.accountId, systemAccountantView, user) Views.views.vend.grantAccessToSystemView(account.bankId, account.accountId, systemFirehoseView, user) + Views.views.vend.grantAccessToSystemView(account.bankId, account.accountId, manageCustomViews, user) val customPublicView = createPublicView(account.bankId, account.accountId) Views.views.vend.grantAccessToCustomView(customPublicView.uid, user) diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala index 057b97bb5a..cb15827286 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/ViewModel.scala @@ -250,14 +250,19 @@ trait View { def usePrivateAliasIfOneExists: Boolean def hideOtherAccountMetadataIfAlias: Boolean - - // Introduced in version 5.0.0 + //TODO, in progress, we only make the system view work, the custom views are VIP. def canGrantAccessToViews : Option[List[String]] = None + def canGrantAccessToCustomViews : Boolean // if this true, we can grant custom views, if it is false, no one can grant custom views. def canRevokeAccessToViews : Option[List[String]] = None + def canRevokeAccessToCustomViews : Boolean // if this true, we can revoke custom views,if it is false, no one can revoke custom views. //reading access //transaction fields + def canSeeTransactionRequests: Boolean + + def canSeeTransactionRequestTypes: Boolean + def canSeeTransactionThisBankAccount: Boolean def canSeeTransactionOtherBankAccount: Boolean @@ -288,9 +293,12 @@ trait View { def canSeeImages: Boolean //Bank account fields + def canSeeAvailableViewsForBankAccount: Boolean + def canSeeBankAccountOwners: Boolean def canSeeBankAccountType: Boolean + def canUpdateBankAccountLabel: Boolean def canSeeBankAccountBalance: Boolean @@ -318,6 +326,10 @@ trait View { def canSeeBankAccountRoutingAddress: Boolean + def canSeeViewsWithPermissionsForOneUser: Boolean + + def canSeeViewsWithPermissionsForAllUsers: Boolean + //other bank account (counterparty) fields def canSeeOtherAccountNationalIdentifier: Boolean @@ -414,4 +426,9 @@ trait View { def canCreateDirectDebit: Boolean def canCreateStandingOrder: Boolean + + //If any view set these to true, you can create/delete/update the custom view + def canCreateCustomView: Boolean + def canDeleteCustomView: Boolean + def canUpdateCustomView: Boolean } \ No newline at end of file