refactor/remove hasOwnerViewAccess replace with specific view permissions -- added canCreateCustomView_, canDeleteCustomView_ and canUpdateCustomView_ permissions

This commit is contained in:
hongwei 2023-06-23 15:39:45 +08:00
parent 5d6e395e1e
commit 7330e5434a
11 changed files with 135 additions and 72 deletions

View File

@ -455,6 +455,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. "

View File

@ -529,11 +529,6 @@ object NewStyle extends MdcLoggable{
} 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(_)
@ -3956,6 +3951,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", 404), 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", 404), callContext)
}
def removeCustomView(viewId: ViewId, bankAccountId: BankIdAccountId, callContext: Option[CallContext]) =
Future {
Views.views.vend.removeCustomView(viewId, bankAccountId)
} map { i =>
(unboxFullOrFail(i, callContext, s"$DeleteCustomViewError", 404), callContext)
}
}
}

View File

@ -24,6 +24,9 @@ object MigrationOfViewDefinitionPermissions {
.canSeeTransactionRequests_(true)
.canSeeAvailableViewsForBankAccount_(true)
.canUpdateBankAccountLabel_(true)
.canCreateCustomView_(true)
.canDeleteCustomView_(true)
.canUpdateCustomView_(true)
.save
).head
@ -37,6 +40,9 @@ object MigrationOfViewDefinitionPermissions {
|${ViewDefinition.canSeeTransactionRequests_.dbColumnName}
|${ViewDefinition.canSeeAvailableViewsForBankAccount_.dbColumnName}
|${ViewDefinition.canUpdateBankAccountLabel_.dbColumnName}
|${ViewDefinition.canCreateCustomView_.dbColumnName}
|${ViewDefinition.canDeleteCustomView_.dbColumnName}
|${ViewDefinition.canUpdateCustomView_.dbColumnName}
|Duration: ${endDate - startDate} ms;
""".stripMargin
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)

View File

@ -622,7 +622,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 `${ViewDefinition.canCreateCustomView_.dbColumnName}` 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)
@ -677,7 +683,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))
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 `${ViewDefinition.canUpdateCustomView_.dbColumnName}` 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)
@ -716,7 +728,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 `${ViewDefinition.canDeleteCustomView_.dbColumnName}` permission on any your views",
cc = callContext
) {
anyViewContainsCanDeleteCustomViewPermission
}
deleted <- NewStyle.function.removeCustomView(viewId, BankIdAccountId(bankId, accountId),callContext)
} yield {
(Full(deleted), HttpCode.`204`(callContext))
}

View File

@ -170,8 +170,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 `${ViewDefinition.canCreateCustomView_.dbColumnName}` 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)
@ -224,7 +230,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 `${ViewDefinition.canUpdateCustomView_.dbColumnName}` 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)

View File

@ -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._
@ -167,7 +168,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 {
@ -179,14 +179,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 `${ViewDefinition.canCreateCustomView_.dbColumnName}` 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.`200`(callContext))
}
res map { fullBoxOrException(_) } map { unboxFull(_) }
}
}
@ -253,7 +257,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 {
@ -273,14 +276,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 `${ViewDefinition.canUpdateCustomView_.dbColumnName}` 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(_) }
}
}

View File

@ -338,51 +338,6 @@ case class BankAccountExtended(val bankAccount: BankAccount) extends MdcLoggable
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
}
}
final def moderatedTransaction(transactionId: TransactionId, view: View, bankIdAccountId: BankIdAccountId, user: Box[User], callContext: Option[CallContext] = None) : Box[(ModeratedTransaction, Option[CallContext])] = {
if(APIUtil.hasAccountAccess(view, bankIdAccountId, user, callContext))
for{

View File

@ -439,6 +439,15 @@ 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
}
def id: Long = id_.get
def isSystem: Boolean = isSystem_.get
@ -555,6 +564,10 @@ 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
}
object ViewImpl extends ViewImpl with LongKeyedMetaMapper[ViewImpl]{

View File

@ -791,9 +791,12 @@ object MapperViews extends Views with MdcLoggable {
.canAddTransactionRequestToOwnAccount_(true) //added following two for payments
.canAddTransactionRequestToAnyAccount_(true)
.canSeeAvailableViewsForBankAccount_(false)
.canSeeTransactionRequests_(true)
.canSeeTransactionRequestTypes_(true)
.canUpdateBankAccountLabel_(true)
.canSeeTransactionRequests_(false)
.canSeeTransactionRequestTypes_(false)
.canUpdateBankAccountLabel_(false)
.canCreateCustomView_(false)
.canDeleteCustomView_(false)
.canUpdateCustomView_(false)
viewId match {
case SYSTEM_OWNER_VIEW_ID =>
@ -801,6 +804,10 @@ object MapperViews extends Views with MdcLoggable {
.canSeeAvailableViewsForBankAccount_(true)
.canSeeTransactionRequests_(true)
.canSeeTransactionRequestTypes_(true)
.canUpdateBankAccountLabel_(true)
.canCreateCustomView_(true)
.canDeleteCustomView_(true)
.canUpdateCustomView_(true)
case SYSTEM_STAGE_ONE_VIEW_ID =>
entity
.canSeeTransactionDescription_(false)

View File

@ -304,6 +304,16 @@ 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
}
//Important! If you add a field, be sure to handle it here in this function
def setFromViewData(viewData : ViewSpecification) = {
@ -410,6 +420,9 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
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"))
canCreateCustomView_(actions.exists(_ == "can_create_custom_view"))
canDeleteCustomView_(actions.exists(_ == "can_delete_custom_view"))
canUpdateCustomView_(actions.exists(_ == "can_update_custom_view"))
}
@ -544,6 +557,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)
}

View File

@ -421,4 +421,8 @@ trait View {
def canCreateDirectDebit: Boolean
def canCreateStandingOrder: Boolean
def canCreateCustomView: Boolean
def canDeleteCustomView: Boolean
def canUpdateCustomView: Boolean
}