|
| 1 | +package code.views |
| 2 | + |
| 3 | +import code.api.Constant |
| 4 | +import code.api.Constant.ALL_CONSUMERS |
| 5 | +import code.setup.{DefaultUsers, ServerSetup} |
| 6 | +import code.views.system.{AccountAccess, ViewDefinition} |
| 7 | +import com.openbankproject.commons.model.{AccountId, BankId, BankIdAccountId} |
| 8 | +import net.liftweb.common.Full |
| 9 | + |
| 10 | +class PrivateViewsUserCanAccessTest extends ServerSetup with DefaultUsers { |
| 11 | + |
| 12 | + override def beforeAll(): Unit = { |
| 13 | + super.beforeAll() |
| 14 | + } |
| 15 | + |
| 16 | + override def afterEach(): Unit = { |
| 17 | + super.afterEach() |
| 18 | + AccountAccess.bulkDelete_!!() |
| 19 | + ViewDefinition.bulkDelete_!!() |
| 20 | + } |
| 21 | + |
| 22 | + val bankId1 = BankId("test-bank-1") |
| 23 | + val bankId2 = BankId("test-bank-2") |
| 24 | + val accountId1 = AccountId("test-account-1") |
| 25 | + val accountId2 = AccountId("test-account-2") |
| 26 | + val accountId3 = AccountId("test-account-3") |
| 27 | + |
| 28 | + private def createSystemViewAndGrantAccess(bankId: BankId, accountId: AccountId, viewId: String, user: code.model.dataAccess.ResourceUser): Unit = { |
| 29 | + // Ensure the system view exists |
| 30 | + MapperViews.getOrCreateSystemViewFromCbs(viewId) |
| 31 | + // Grant access |
| 32 | + val view = ViewDefinition.findSystemView(viewId).openOrThrowException(s"System view $viewId not found") |
| 33 | + MapperViews.grantAccessToSystemView(bankId, accountId, view, user) |
| 34 | + } |
| 35 | + |
| 36 | + feature("privateViewsUserCanAccess") { |
| 37 | + |
| 38 | + scenario("User with no account access returns empty lists") { |
| 39 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 40 | + views should be(empty) |
| 41 | + accountAccess should be(empty) |
| 42 | + } |
| 43 | + |
| 44 | + scenario("User with one system view access returns that view") { |
| 45 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 46 | + |
| 47 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 48 | + views.size should be(1) |
| 49 | + accountAccess.size should be(1) |
| 50 | + views.head.viewId.value should equal(Constant.SYSTEM_OWNER_VIEW_ID.toLowerCase()) |
| 51 | + accountAccess.head.bank_id.get should equal(bankId1.value) |
| 52 | + accountAccess.head.account_id.get should equal(accountId1.value) |
| 53 | + } |
| 54 | + |
| 55 | + scenario("User with access to multiple accounts returns all views") { |
| 56 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 57 | + createSystemViewAndGrantAccess(bankId1, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 58 | + createSystemViewAndGrantAccess(bankId2, accountId3, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 59 | + |
| 60 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 61 | + accountAccess.size should be(3) |
| 62 | + // All three account access records should be for the owner view |
| 63 | + accountAccess.map(_.view_id.get).distinct should equal(List(Constant.SYSTEM_OWNER_VIEW_ID.toLowerCase())) |
| 64 | + // Check all bank/account combinations are present |
| 65 | + val bankAccountPairs = accountAccess.map(a => (a.bank_id.get, a.account_id.get)).toSet |
| 66 | + bankAccountPairs should contain((bankId1.value, accountId1.value)) |
| 67 | + bankAccountPairs should contain((bankId1.value, accountId2.value)) |
| 68 | + bankAccountPairs should contain((bankId2.value, accountId3.value)) |
| 69 | + } |
| 70 | + |
| 71 | + scenario("User with multiple view types on the same account") { |
| 72 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 73 | + createSystemViewAndGrantAccess(bankId1, accountId1, "accountant", resourceUser1) |
| 74 | + |
| 75 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 76 | + accountAccess.size should be(2) |
| 77 | + views.map(_.viewId.value).toSet should contain(Constant.SYSTEM_OWNER_VIEW_ID.toLowerCase()) |
| 78 | + views.map(_.viewId.value).toSet should contain("accountant") |
| 79 | + } |
| 80 | + |
| 81 | + scenario("Different users have independent access") { |
| 82 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 83 | + createSystemViewAndGrantAccess(bankId1, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser2) |
| 84 | + |
| 85 | + val (views1, access1) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 86 | + val (views2, access2) = MapperViews.privateViewsUserCanAccess(resourceUser2) |
| 87 | + |
| 88 | + access1.size should be(1) |
| 89 | + access1.head.account_id.get should equal(accountId1.value) |
| 90 | + |
| 91 | + access2.size should be(1) |
| 92 | + access2.head.account_id.get should equal(accountId2.value) |
| 93 | + } |
| 94 | + |
| 95 | + scenario("Views are distinct even when user has access to same view type across accounts") { |
| 96 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 97 | + createSystemViewAndGrantAccess(bankId1, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 98 | + |
| 99 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 100 | + accountAccess.size should be(2) |
| 101 | + // The underlying ViewDefinition for system views is the same, so distinct views should be |
| 102 | + // based on the view definition, but with bank_id/account_id set per account access |
| 103 | + views.size should be >= 1 |
| 104 | + } |
| 105 | + |
| 106 | + scenario("Returned accountAccess entries match returned views") { |
| 107 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 108 | + createSystemViewAndGrantAccess(bankId1, accountId1, "accountant", resourceUser1) |
| 109 | + createSystemViewAndGrantAccess(bankId2, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 110 | + |
| 111 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccess(resourceUser1) |
| 112 | + accountAccess.size should be(3) |
| 113 | + |
| 114 | + // Every accountAccess view_id should correspond to a returned view |
| 115 | + val viewIds = views.map(_.viewId.value).toSet |
| 116 | + accountAccess.foreach { aa => |
| 117 | + viewIds should contain(aa.view_id.get) |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + feature("privateViewsUserCanAccessAtBank") { |
| 123 | + |
| 124 | + scenario("Filters to only the requested bank") { |
| 125 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 126 | + createSystemViewAndGrantAccess(bankId2, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 127 | + |
| 128 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccessAtBank(resourceUser1, bankId1) |
| 129 | + accountAccess.size should be(1) |
| 130 | + accountAccess.head.bank_id.get should equal(bankId1.value) |
| 131 | + } |
| 132 | + |
| 133 | + scenario("Returns empty for bank with no access") { |
| 134 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 135 | + |
| 136 | + val (views, accountAccess) = MapperViews.privateViewsUserCanAccessAtBank(resourceUser1, bankId2) |
| 137 | + views should be(empty) |
| 138 | + accountAccess should be(empty) |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + feature("privateViewsUserCanAccessForAccount") { |
| 143 | + |
| 144 | + scenario("Returns views for the specific account only") { |
| 145 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 146 | + createSystemViewAndGrantAccess(bankId1, accountId1, "accountant", resourceUser1) |
| 147 | + createSystemViewAndGrantAccess(bankId1, accountId2, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 148 | + |
| 149 | + val views = MapperViews.privateViewsUserCanAccessForAccount(resourceUser1, BankIdAccountId(bankId1, accountId1)) |
| 150 | + views.size should be(2) |
| 151 | + views.map(_.viewId.value).toSet should equal(Set(Constant.SYSTEM_OWNER_VIEW_ID.toLowerCase(), "accountant")) |
| 152 | + } |
| 153 | + |
| 154 | + scenario("Returns empty for account with no access") { |
| 155 | + createSystemViewAndGrantAccess(bankId1, accountId1, Constant.SYSTEM_OWNER_VIEW_ID, resourceUser1) |
| 156 | + |
| 157 | + val views = MapperViews.privateViewsUserCanAccessForAccount(resourceUser1, BankIdAccountId(bankId1, accountId2)) |
| 158 | + views should be(empty) |
| 159 | + } |
| 160 | + } |
| 161 | +} |
0 commit comments