Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion obp-api/src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import code.endpointMapping.EndpointMapping
import code.endpointTag.EndpointTag
import code.entitlement.{Entitlement, MappedEntitlement}
import code.entitlementrequest.MappedEntitlementRequest
import code.accountaccessrequest.AccountAccessRequest
import code.etag.MappedETag
import code.fx.{MappedCurrency, MappedFXRate}
import code.group.Group
Expand Down Expand Up @@ -1187,7 +1188,8 @@ object ToSchemify {
TransactionIdMapping,
RegulatedEntityAttribute,
BankAccountBalance,
Group
Group,
AccountAccessRequest
)

// start grpc server
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package code.accountaccessrequest

import java.util.Date
import code.util.{MappedUUID, UUIDString}
import com.openbankproject.commons.model.enums.AccountAccessRequestStatus
import net.liftweb.common.{Box, Full}
import net.liftweb.mapper._
import net.liftweb.util.Helpers.tryo

object MappedAccountAccessRequestProvider extends AccountAccessRequestProvider {

override def createAccountAccessRequest(
bankId: String,
accountId: String,
viewId: String,
isSystemView: Boolean,
requestorUserId: String,
targetUserId: String,
businessJustification: String
): Box[AccountAccessRequestTrait] = {
tryo {
AccountAccessRequest.create
.BankId(bankId)
.AccountId(accountId)
.ViewId(viewId)
.IsSystemView(isSystemView)
.RequestorUserId(requestorUserId)
.TargetUserId(targetUserId)
.BusinessJustification(businessJustification)
.Status(AccountAccessRequestStatus.INITIATED.toString)
.CheckerUserId("")
.CheckerComment("")
.saveMe()
}
}

override def getById(accountAccessRequestId: String): Box[AccountAccessRequestTrait] = {
AccountAccessRequest.find(By(AccountAccessRequest.AccountAccessRequestId, accountAccessRequestId))
}

override def getByAccount(bankId: String, accountId: String): Box[List[AccountAccessRequestTrait]] = {
tryo {
AccountAccessRequest.findAll(
By(AccountAccessRequest.BankId, bankId),
By(AccountAccessRequest.AccountId, accountId),
OrderBy(AccountAccessRequest.id, Descending)
)
}
}

override def getByAccountAndStatus(bankId: String, accountId: String, status: String): Box[List[AccountAccessRequestTrait]] = {
tryo {
AccountAccessRequest.findAll(
By(AccountAccessRequest.BankId, bankId),
By(AccountAccessRequest.AccountId, accountId),
By(AccountAccessRequest.Status, status),
OrderBy(AccountAccessRequest.id, Descending)
)
}
}

override def getByRequestorUserId(requestorUserId: String): Box[List[AccountAccessRequestTrait]] = {
tryo {
AccountAccessRequest.findAll(
By(AccountAccessRequest.RequestorUserId, requestorUserId),
OrderBy(AccountAccessRequest.id, Descending)
)
}
}

override def getByUserAccountView(
targetUserId: String,
bankId: String,
accountId: String,
viewId: String
): Box[AccountAccessRequestTrait] = {
AccountAccessRequest.find(
By(AccountAccessRequest.TargetUserId, targetUserId),
By(AccountAccessRequest.BankId, bankId),
By(AccountAccessRequest.AccountId, accountId),
By(AccountAccessRequest.ViewId, viewId),
By(AccountAccessRequest.Status, AccountAccessRequestStatus.INITIATED.toString)
)
}

override def updateStatus(
accountAccessRequestId: String,
status: String,
checkerUserId: String,
checkerComment: String
): Box[AccountAccessRequestTrait] = {
AccountAccessRequest.find(By(AccountAccessRequest.AccountAccessRequestId, accountAccessRequestId)).flatMap { request =>
tryo {
request
.Status(status)
.CheckerUserId(checkerUserId)
.CheckerComment(checkerComment)
.saveMe()
}
}
}
}

class AccountAccessRequest extends AccountAccessRequestTrait with LongKeyedMapper[AccountAccessRequest] with IdPK with CreatedUpdated {

def getSingleton = AccountAccessRequest

object AccountAccessRequestId extends MappedUUID(this)
object BankId extends UUIDString(this)
object AccountId extends UUIDString(this)
object ViewId extends MappedString(this, 255)
object IsSystemView extends MappedBoolean(this)
object RequestorUserId extends UUIDString(this)
object TargetUserId extends UUIDString(this)
object BusinessJustification extends MappedText(this)
object Status extends MappedString(this, 64)
object CheckerUserId extends MappedString(this, 255)
object CheckerComment extends MappedText(this)

override def accountAccessRequestId: String = AccountAccessRequestId.get.toString
override def bankId: String = BankId.get
override def accountId: String = AccountId.get
override def viewId: String = ViewId.get
override def isSystemView: Boolean = IsSystemView.get
override def requestorUserId: String = RequestorUserId.get
override def targetUserId: String = TargetUserId.get
override def businessJustification: String = BusinessJustification.get
override def status: String = Status.get
override def checkerUserId: String = CheckerUserId.get
override def checkerComment: String = CheckerComment.get
override def created: Date = createdAt.get
override def updated: Date = updatedAt.get
}

object AccountAccessRequest extends AccountAccessRequest with LongKeyedMetaMapper[AccountAccessRequest] {
override def dbTableName = "AccountAccessRequest"
override def dbIndexes = Index(AccountAccessRequestId) :: Index(BankId, AccountId) :: Index(RequestorUserId) :: Index(Status) :: super.dbIndexes
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package code.accountaccessrequest

import java.util.Date
import net.liftweb.common.Box
import net.liftweb.util.SimpleInjector

object AccountAccessRequestTrait extends SimpleInjector {
val accountAccessRequest = new Inject(buildOne _) {}

def buildOne: AccountAccessRequestProvider = MappedAccountAccessRequestProvider
}

trait AccountAccessRequestProvider {
def createAccountAccessRequest(
bankId: String,
accountId: String,
viewId: String,
isSystemView: Boolean,
requestorUserId: String,
targetUserId: String,
businessJustification: String
): Box[AccountAccessRequestTrait]

def getById(accountAccessRequestId: String): Box[AccountAccessRequestTrait]

def getByAccount(bankId: String, accountId: String): Box[List[AccountAccessRequestTrait]]

def getByAccountAndStatus(bankId: String, accountId: String, status: String): Box[List[AccountAccessRequestTrait]]

def getByRequestorUserId(requestorUserId: String): Box[List[AccountAccessRequestTrait]]

def getByUserAccountView(
targetUserId: String,
bankId: String,
accountId: String,
viewId: String
): Box[AccountAccessRequestTrait]

def updateStatus(
accountAccessRequestId: String,
status: String,
checkerUserId: String,
checkerComment: String
): Box[AccountAccessRequestTrait]
}

trait AccountAccessRequestTrait {
def accountAccessRequestId: String
def bankId: String
def accountId: String
def viewId: String
def isSystemView: Boolean
def requestorUserId: String
def targetUserId: String
def businessJustification: String
def status: String
def checkerUserId: String
def checkerComment: String
def created: Date
def updated: Date
}
Original file line number Diff line number Diff line change
Expand Up @@ -6153,8 +6153,44 @@ object SwaggerDefinitionsJSON {
description = descriptionExample.value
)

// Account Access Request samples (V600)
lazy val postAccountAccessRequestJsonV600 = JSONFactory600.PostAccountAccessRequestJsonV600(
target_user_id = userIdExample.value,
view_id = viewIdExample.value,
is_system_view = true,
business_justification = "Need access to review monthly account statements for audit purposes."
)

lazy val postApproveAccountAccessRequestJsonV600 = JSONFactory600.PostApproveAccountAccessRequestJsonV600(
comment = Some("Approved for Q1 audit.")
)

lazy val postRejectAccountAccessRequestJsonV600 = JSONFactory600.PostRejectAccountAccessRequestJsonV600(
comment = "Insufficient business justification provided."
)

lazy val accountAccessRequestJsonV600 = JSONFactory600.AccountAccessRequestJsonV600(
account_access_request_id = "b4e0352a-9a0f-4bfa-b30b-9003aa467f51",
bank_id = bankIdExample.value,
account_id = accountIdExample.value,
view_id = viewIdExample.value,
is_system_view = true,
requestor_user_id = userIdExample.value,
target_user_id = "9ca9a7e4-6d02-40e3-a129-0b2bf89de9b2",
business_justification = "Need access to review monthly account statements for audit purposes.",
status = "INITIATED",
checker_user_id = "",
checker_comment = "",
created = DateWithMsExampleObject,
updated = DateWithMsExampleObject
)

lazy val accountAccessRequestsJsonV600 = JSONFactory600.AccountAccessRequestsJsonV600(
account_access_requests = List(accountAccessRequestJsonV600)
)

//The common error or success format.
//Just some helper format to use in Json
//Just some helper format to use in Json
case class NotSupportedYet()

lazy val notSupportedYet = NotSupportedYet()
Expand Down
16 changes: 16 additions & 0 deletions obp-api/src/main/scala/code/api/util/ApiRole.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,22 @@ object ApiRole extends MdcLoggable{
case class CanGetUserGroupMembershipsAtOneBank(requiresBankId: Boolean = true) extends ApiRole
lazy val canGetUserGroupMembershipsAtOneBank = CanGetUserGroupMembershipsAtOneBank()

// Account Access Request roles
case class CanCreateAccountAccessRequestAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
lazy val canCreateAccountAccessRequestAtAnyBank = CanCreateAccountAccessRequestAtAnyBank()
case class CanCreateAccountAccessRequestAtOneBank(requiresBankId: Boolean = true) extends ApiRole
lazy val canCreateAccountAccessRequestAtOneBank = CanCreateAccountAccessRequestAtOneBank()

case class CanGetAccountAccessRequestsAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
lazy val canGetAccountAccessRequestsAtAnyBank = CanGetAccountAccessRequestsAtAnyBank()
case class CanGetAccountAccessRequestsAtOneBank(requiresBankId: Boolean = true) extends ApiRole
lazy val canGetAccountAccessRequestsAtOneBank = CanGetAccountAccessRequestsAtOneBank()

case class CanUpdateAccountAccessRequestAtAnyBank(requiresBankId: Boolean = false) extends ApiRole
lazy val canUpdateAccountAccessRequestAtAnyBank = CanUpdateAccountAccessRequestAtAnyBank()
case class CanUpdateAccountAccessRequestAtOneBank(requiresBankId: Boolean = true) extends ApiRole
lazy val canUpdateAccountAccessRequestAtOneBank = CanUpdateAccountAccessRequestAtOneBank()

private val dynamicApiRoles = new ConcurrentHashMap[String, ApiRole]

private case class DynamicApiRole(role: String, requiresBankId: Boolean = false) extends ApiRole{
Expand Down
1 change: 1 addition & 0 deletions obp-api/src/main/scala/code/api/util/ApiTag.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ object ApiTag {
val apiTagAccount = ResourceDocTag("Account")
val apiTagAccountAttribute = ResourceDocTag("Account-Attribute")
val apiTagAccountAccess = ResourceDocTag("Account-Access")
val apiTagAccountAccessRequest = ResourceDocTag("Account-Access-Request")
val apiTagDirectDebit = ResourceDocTag("Direct-Debit")
val apiTagStandingOrder = ResourceDocTag("Standing-Order")
val apiTagAccountMetadata = ResourceDocTag("Account-Metadata")
Expand Down
9 changes: 9 additions & 0 deletions obp-api/src/main/scala/code/api/util/ErrorMessages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,15 @@ object ErrorMessages {
val ParentCustomerNotFound = "OBP-30273: Parent customer not found. The parent_customer_id must reference an existing customer at the same bank."
val CustomerTypeMismatch = "OBP-30274: Customer type does not match the endpoint. Use the generic /customers endpoint or the correct type-specific endpoint."

val AccountAccessRequestNotFound = "OBP-30275: Account Access Request not found."
val AccountAccessRequestAlreadyExists = "OBP-30276: An Account Access Request already exists for this user, account, and view."
val AccountAccessRequestCannotBeCreated = "OBP-30277: Account Access Request could not be created."
val AccountAccessRequestStatusNotInitiated = "OBP-30278: Account Access Request status is not INITIATED. Only INITIATED requests can be approved or rejected."
val MakerCheckerSameUser = "OBP-30279: The checker (approver/rejecter) cannot be the same user as the maker (requestor). Maker/Checker separation is required."
val BusinessJustificationRequired = "OBP-30280: Business justification is required."
val CheckerCommentRequiredForRejection = "OBP-30281: A comment is required when rejecting an Account Access Request."
val AccountAccessRequestCannotBeUpdated = "OBP-30282: Account Access Request could not be updated."

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. "
val AccountApplicationNotFound = "OBP-30311: AccountApplication not found by ACCOUNT_APPLICATION_ID. "
Expand Down
Loading