refactor/seperate the checkSystemViewIdOrName and checkCustomViewIdOrName methods

This commit is contained in:
hongwei 2024-04-02 15:45:40 +02:00
parent dd257c12d4
commit ef3528bb1d
12 changed files with 78 additions and 62 deletions

View File

@ -4059,28 +4059,34 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
//we need set guard to easily distinguish the system view and custom view,
// customer view must start with '_', system can not
// viewName and viewId are the same value, just with different format, eg: createViewIdByName(view.name)
def checkSystemViewIdOrName(viewId: String): Boolean = !checkCustomViewIdOrName(viewId: String)
// viewId is created by viewName, please check method : createViewIdByName(view.name)
// so here we can use isSystemViewName method to check viewId
def isValidatedSystemViewId(viewId: String): Boolean = isValidatedSystemViewName(viewId: String)
def isValidatedSystemViewName(viewName: String): Boolean = !isValidatedCustomViewName(viewName: String)
// viewId is created by viewName, please check method : createViewIdByName(view.name)
// so here we can use isCustomViewName method to check viewId
def isValidatedCustomViewId(viewId: String): Boolean = isValidatedCustomViewName(viewId)
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
// viewName and viewId are the same value, just with different format, eg: createViewIdByName(view.name)
def checkCustomViewIdOrName(name: String): Boolean = name match {
def isValidatedCustomViewName(name: String): Boolean = name match {
case x if x.startsWith("_") => true // Allowed case
case _ => false
}
def canGrantAccessToView(bankId: BankId, accountId: AccountId, targetViewId : ViewId, user: User, callContext: Option[CallContext]): Boolean = {
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 targetViewId is systemView. just compare all the permissions
if(checkSystemViewIdOrName(targetViewId.value)){
//1. if viewIdTobeGranted is systemView. just compare all the permissions
if(isValidatedSystemViewId(viewIdTobeGranted.value)){
val allCanGrantAccessToViewsPermissions: List[String] = permission
.map(_.views.map(_.canGrantAccessToViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct
.map(_.views.map(_.canGrantAccessToSystemViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct
allCanGrantAccessToViewsPermissions.contains(targetViewId.value)
allCanGrantAccessToViewsPermissions.contains(viewIdTobeGranted.value)
} else{
//2. if targetViewId is customView, we only need to check the `canGrantAccessToCustomViews`.
//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)
@ -4092,10 +4098,10 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
//1st: get the view
val view: Box[View] = Views.views.vend.getViewByBankIdAccountIdViewIdUserPrimaryKey(bankIdAccountIdViewId, user.userPrimaryKey)
//2rd: f targetViewId is systemView. we need to check `view.canGrantAccessToViews` field.
if(checkSystemViewIdOrName(targetViewId.value)){
val canGrantAccessToView: Box[List[String]] = view.map(_.canGrantAccessToViews.getOrElse(Nil))
canGrantAccessToView.getOrElse(Nil).contains(targetViewId.value)
//2rd: f targetViewId is systemView. we need to check `view.canGrantAccessToSystemViews` field.
if(isValidatedSystemViewId(targetViewId.value)){
val canGrantAccessToSystemViews: Box[List[String]] = view.map(_.canGrantAccessToSystemViews.getOrElse(Nil))
canGrantAccessToSystemViews.getOrElse(Nil).contains(targetViewId.value)
} else{
//3rd. if targetViewId is customView, we need to check `view.canGrantAccessToCustomViews` field.
view.map(_.canGrantAccessToCustomViews).getOrElse(false)
@ -4106,19 +4112,22 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
//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)
//Retrieve all views from the 'canRevokeAccessToViews' list within each view from the permission views.
val allCanGrantAccessToSystemViews: List[String] = permissionBox.map(_.views.map(_.canGrantAccessToSystemViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct
val allSystemViewsIdsTobeGranted: List[String] = viewIdsTobeGranted.map(_.value).distinct.filter(isValidatedSystemViewId)
val canGrantAllSystemViewsIdsTobeGranted = allSystemViewsIdsTobeGranted.forall(allCanGrantAccessToSystemViews.contains)
if (viewIdsTobeGranted.map(_.value).distinct.find(checkCustomViewIdOrName).isDefined){
//check if we can grant all customViews Access
//if the viewIdsTobeGranted contains custom view ids, we need to check the both canGrantAccessToCustomViews and canGrantAccessToSystemViews
if (viewIdsTobeGranted.map(_.value).distinct.find(isValidatedCustomViewId).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
canGrantAllSystemViewsIdsTobeGranted && canGrantAccessToAllCustomViews
} else {// if viewIdsTobeGranted only contains system view ids, we only need to check `canGrantAccessToSystemViews`
canGrantAllSystemViewsIdsTobeGranted
}
}
@ -4127,11 +4136,11 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
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
if (isValidatedSystemViewId(viewIdToBeRevoked.value)) {
val allCanRevokeAccessToSystemViews: List[String] = permission
.map(_.views.map(_.canRevokeAccessToSystemViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct
allCanRevokeAccessToViewsPermissions.contains(viewIdToBeRevoked.value)
allCanRevokeAccessToSystemViews.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)
@ -4144,25 +4153,27 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
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)
//Retrieve all views from the 'canRevokeAccessToViews' list within each view from the permission views.
val allCanRevokeAccessToViews: List[String] = permissionBox.map(_.views.map(_.canRevokeAccessToSystemViews.getOrElse(Nil)).flatten).getOrElse(Nil).distinct
//All targetViewIds:
val allTargetViewIds: List[String] = permissionBox.map(_.views.map(_.viewId.value)).getOrElse(Nil).distinct
val allSystemTargetViewIs: List[String] = allTargetViewIds.filter(isValidatedSystemViewId)
val canRevokeAccessToAllSystemTargetViews = allSystemTargetViewIs.forall(allCanRevokeAccessToViews.contains)
if (allAccountAccessSystemViews.find(checkCustomViewIdOrName).isDefined){
//if allTargetViewIds contains customViewId,we need to check both `canRevokeAccessToCustomViews` and `canRevokeAccessToSystemViews` fields
if (allTargetViewIds.find(isValidatedCustomViewId).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
canRevokeAccessToAllSystemTargetViews && canRevokeAccessToAllCustomViews
}else
canRevokeAccessToAllSystemTargetViews
}
}
def getJValueFromJsonFile(path: String) = {
val stream = getClass().getClassLoader().getResourceAsStream(path)
try {

View File

@ -595,7 +595,7 @@ trait APIMethods121 {
u <- cc.user ?~ UserNotLoggedIn
createViewJsonV121 <- tryo{json.extract[CreateViewJsonV121]} ?~ InvalidJsonFormat
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_<- booleanToBox(checkCustomViewIdOrName(createViewJsonV121.name), InvalidCustomViewFormat+s"Current view_name (${createViewJsonV121.name})")
_<- booleanToBox(isValidatedCustomViewName(createViewJsonV121.name), InvalidCustomViewFormat+s"Current view_name (${createViewJsonV121.name})")
account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound
createViewJson = CreateViewJson(
createViewJsonV121.name,

View File

@ -191,7 +191,7 @@ trait APIMethods220 {
for {
createViewJsonV121 <- tryo{json.extract[CreateViewJsonV121]} ?~!InvalidJsonFormat
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_<- booleanToBox(checkCustomViewIdOrName(createViewJsonV121.name), InvalidCustomViewFormat+s"Current view_name (${createViewJsonV121.name})")
_<- booleanToBox(isValidatedCustomViewName(createViewJsonV121.name), InvalidCustomViewFormat+s"Current view_name (${createViewJsonV121.name})")
u <- cc.user ?~!UserNotLoggedIn
account <- BankAccountX(bankId, accountId) ?~! BankAccountNotFound
createViewJson = CreateViewJson(

View File

@ -210,7 +210,7 @@ trait APIMethods300 {
}
//customer views are started ith `_`,eg _life, _work, and System views startWith letter, eg: owner
_ <- Helper.booleanToFuture(failMsg = InvalidCustomViewFormat+s"Current view_name (${createViewJson.name})", cc=callContext) {
checkCustomViewIdOrName(createViewJson.name)
isValidatedCustomViewName(createViewJson.name)
}
(account, callContext) <- NewStyle.function.getBankAccount(bankId, accountId, callContext)

View File

@ -3985,7 +3985,7 @@ trait APIMethods310 {
}
//System views can not startwith '_'
_ <- Helper.booleanToFuture(failMsg = InvalidSystemViewFormat+s"Current view_name (${createViewJson.name})", cc = callContext) {
checkSystemViewIdOrName(createViewJson.name)
isValidatedSystemViewName(createViewJson.name)
}
_ <- Helper.booleanToFuture(SystemViewCannotBePublicError, failCode=400, cc=callContext) {
createViewJson.is_public == false

View File

@ -1889,7 +1889,7 @@ trait APIMethods500 {
}
// custom views are started with `_`,eg _ life, _ work, and System views can not, eg: owner.
_ <- Helper.booleanToFuture(failMsg = InvalidSystemViewFormat +s"Current view_name (${createViewJson.name})", cc = cc.callContext) {
checkSystemViewIdOrName(createViewJson.name)
isValidatedSystemViewName(createViewJson.name)
}
view <- NewStyle.function.createSystemView(createViewJson.toCreateViewJson, cc.callContext)
} yield {

View File

@ -888,8 +888,8 @@ object JSONFactory500 {
can_create_direct_debit = view.canCreateDirectDebit,
can_create_standing_order = view.canCreateStandingOrder,
// Version 5.0.0
can_grant_access_to_views = view.canGrantAccessToViews.getOrElse(Nil),
can_revoke_access_to_views = view.canRevokeAccessToViews.getOrElse(Nil),
can_grant_access_to_views = view.canGrantAccessToSystemViews.getOrElse(Nil),
can_revoke_access_to_views = view.canRevokeAccessToSystemViews.getOrElse(Nil),
)
}
def createViewsJsonV500(views : List[View]) : ViewsJsonV500 = {

View File

@ -47,7 +47,7 @@ object MapperViews extends Views with MdcLoggable {
}
private def getViewFromAccountAccess(accountAccess: AccountAccess) = {
if (checkSystemViewIdOrName(accountAccess.view_id.get)) {
if (isValidatedSystemViewId(accountAccess.view_id.get)) {
ViewDefinition.findSystemView(accountAccess.view_id.get)
.map(v => v.bank_id(accountAccess.bank_id.get).account_id(accountAccess.account_id.get)) // in case system view do not contains the bankId, and accountId.
} else {
@ -381,7 +381,7 @@ object MapperViews extends Views with MdcLoggable {
def createSystemView(view: CreateViewJson) : Future[Box[View]] = Future {
if(view.is_public) {
Failure(SystemViewCannotBePublicError)
}else if (!checkSystemViewIdOrName(view.name)) {
}else if (!isValidatedSystemViewName(view.name)) {
Failure(InvalidSystemViewFormat+s"Current view_name (${view.name})")
} else {
view.name.contentEquals("") match {
@ -415,7 +415,7 @@ object MapperViews extends Views with MdcLoggable {
* */
def createCustomView(bankAccountId: BankIdAccountId, view: CreateViewJson): Box[View] = {
if(!checkCustomViewIdOrName(view.name)) {
if(!isValidatedCustomViewName(view.name)) {
return Failure(InvalidCustomViewFormat)
}

View File

@ -1,12 +1,12 @@
package code.views.system
import code.api.util.APIUtil.{checkCustomViewIdOrName, checkSystemViewIdOrName}
import code.api.util.APIUtil.{isValidatedCustomViewId, isValidatedCustomViewName, isValidatedSystemViewId}
import code.api.util.ErrorMessages.{CreateSystemViewError, InvalidCustomViewFormat, InvalidSystemViewFormat}
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.{MappedBoolean, _}
import net.liftweb.mapper._
import scala.collection.immutable.List
@ -51,12 +51,17 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
object hideOtherAccountMetadataIfAlias_ extends MappedBoolean(this){
override def defaultValue = false
}
//This is the system views list, custom views please check `canGrantAccessToCustomViews_` field
object canGrantAccessToViews_ extends MappedText(this){
override def defaultValue = ""
}
//This is the system views list.custom views please check `canRevokeAccessToCustomViews_` field
object canRevokeAccessToViews_ extends MappedText(this){
override def defaultValue = ""
}
object canRevokeAccessToCustomViews_ extends MappedBoolean(this){
override def defaultValue = false
}
@ -467,7 +472,7 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
def hideOtherAccountMetadataIfAlias: Boolean = hideOtherAccountMetadataIfAlias_.get
//This current view can grant access to other views.
override def canGrantAccessToViews : Option[List[String]] = {
override def canGrantAccessToSystemViews : Option[List[String]] = {
canGrantAccessToViews_.get == null || canGrantAccessToViews_.get.isEmpty() match {
case true => None
case _ => Some(canGrantAccessToViews_.get.split(",").toList.map(_.trim))
@ -477,7 +482,7 @@ class ViewDefinition extends View with LongKeyedMapper[ViewDefinition] with Many
def canGrantAccessToCustomViews : Boolean = canGrantAccessToCustomViews_.get
//the current view can revoke access to other views.
override def canRevokeAccessToViews : Option[List[String]] = {
override def canRevokeAccessToSystemViews : Option[List[String]] = {
canRevokeAccessToViews_.get == null || canRevokeAccessToViews_.get.isEmpty() match {
case true => None
case _ => Some(canRevokeAccessToViews_.get.split(",").toList.map(_.trim))
@ -599,10 +604,10 @@ object ViewDefinition extends ViewDefinition with LongKeyedMetaMapper[ViewDefini
t.composite_unique_key(compositeUniqueKey)
}
if (t.isSystem && !checkSystemViewIdOrName(t.view_id.get)) {
if (t.isSystem && !isValidatedSystemViewId(t.view_id.get)) {
throw new RuntimeException(InvalidSystemViewFormat+s"Current view_id (${t.view_id.get})")
}
if (!t.isSystem && !checkCustomViewIdOrName(t.view_id.get)) {
if (!t.isSystem && !isValidatedCustomViewId(t.view_id.get)) {
throw new RuntimeException(InvalidCustomViewFormat+s"Current view_id (${t.view_id.get})")
}

View File

@ -31,7 +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.api.util.APIUtil.isValidatedSystemViewId
import code.bankconnectors.Connector
import code.setup.{APIResponse, DefaultUsers, PrivateUser2AccountsAndSetUpWithTestData, ServerSetupWithTestData}
import code.views.Views
@ -165,7 +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(view=> checkSystemViewIdOrName(view.id))
.filterNot(view=> isValidatedSystemViewId(view.id))
val randomPosition = nextInt(possibleViewsPermalinks.size)
possibleViewsPermalinks(randomPosition).id
}

View File

@ -3,7 +3,7 @@ package code.setup
import bootstrap.liftweb.ToSchemify
import code.accountholders.AccountHolders
import code.api.Constant.{CUSTOM_PUBLIC_VIEW_ID, SYSTEM_OWNER_VIEW_ID}
import code.api.util.APIUtil.checkCustomViewIdOrName
import code.api.util.APIUtil.isValidatedCustomViewName
import code.api.util.ErrorMessages._
import code.model._
import code.model.dataAccess._
@ -40,7 +40,7 @@ trait TestConnectorSetupWithStandardPermissions extends TestConnectorSetup {
val viewId = MapperViews.createViewIdByName(viewName)
val description = randomString(40)
if (!checkCustomViewIdOrName(viewName)) {
if (!isValidatedCustomViewName(viewName)) {
throw new RuntimeException(InvalidCustomViewFormat)
}

View File

@ -250,10 +250,10 @@ trait View {
def usePrivateAliasIfOneExists: Boolean
def hideOtherAccountMetadataIfAlias: Boolean
//TODO, in progress, we only make the system view work, the custom views are VIP.
def canGrantAccessToViews : Option[List[String]] = None
def canGrantAccessToSystemViews : 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 canRevokeAccessToSystemViews : 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