chore(server): Check more permissions in bulk (#5315)

Modify Access repository, to evaluate `authDevice`, `library`, `partner`,
`person`, and `timeline` permissions in bulk.
Queries have been validated to match what they currently generate for
single ids.

As an extra performance improvement, we now use a custom QueryBuilder
for the Partners queries, to avoid the eager relationships that add
unneeded `LEFT JOIN` clauses. We only filter based on the ids present in
the `partners` table, so those joins can be avoided.

Queries:

* `library` owner access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "libraries" "LibraryEntity"
  WHERE
    "LibraryEntity"."id" = $1
    AND "LibraryEntity"."ownerId" = $2
    AND "LibraryEntity"."deletedAt" IS NULL
)
LIMIT 1

-- After
SELECT "LibraryEntity"."id" AS "LibraryEntity_id"
FROM "libraries" "LibraryEntity"
WHERE
  "LibraryEntity"."id" IN ($1, $2)
  AND "LibraryEntity"."ownerId" = $3
  AND "LibraryEntity"."deletedAt" IS NULL
```

* `library` partner access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "partners" "PartnerEntity"
    LEFT JOIN "users" "PartnerEntity__sharedBy"
      ON "PartnerEntity__sharedBy"."id"="PartnerEntity"."sharedById"
      AND "PartnerEntity__sharedBy"."deletedAt" IS NULL
    LEFT JOIN "users" "PartnerEntity__sharedWith"
      ON "PartnerEntity__sharedWith"."id"="PartnerEntity"."sharedWithId"
      AND "PartnerEntity__sharedWith"."deletedAt" IS NULL
  WHERE
    "PartnerEntity"."sharedWithId" = $1
    AND "PartnerEntity"."sharedById" = $2
)
LIMIT 1

-- After
SELECT
  "partner"."sharedById" AS "partner_sharedById",
  "partner"."sharedWithId" AS "partner_sharedWithId"
FROM "partners" "partner"
WHERE
  "partner"."sharedById" IN ($1, $2)
  AND "partner"."sharedWithId" = $3
```

* `authDevice` owner access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "user_token" "UserTokenEntity"
  WHERE
    "UserTokenEntity"."userId" = $1
    AND "UserTokenEntity"."id" = $2
)
LIMIT 1

-- After
SELECT "UserTokenEntity"."id" AS "UserTokenEntity_id"
FROM "user_token" "UserTokenEntity"
WHERE
  "UserTokenEntity"."userId" = $1
  AND "UserTokenEntity"."id" IN ($2, $3)
```

* `timeline` partner access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "partners" "PartnerEntity"
    LEFT JOIN "users" "PartnerEntity__sharedBy"
      ON "PartnerEntity__sharedBy"."id"="PartnerEntity"."sharedById"
      AND "PartnerEntity__sharedBy"."deletedAt" IS NULL
    LEFT JOIN "users" "PartnerEntity__sharedWith"
      ON "PartnerEntity__sharedWith"."id"="PartnerEntity"."sharedWithId"
      AND "PartnerEntity__sharedWith"."deletedAt" IS NULL
  WHERE
    "PartnerEntity"."sharedWithId" = $1
    AND "PartnerEntity"."sharedById" = $2
)
LIMIT 1

-- After
SELECT
  "partner"."sharedById" AS "partner_sharedById",
  "partner"."sharedWithId" AS "partner_sharedWithId"
FROM "partners" "partner"
WHERE
  "partner"."sharedById" IN ($1, $2)
  AND "partner"."sharedWithId" = $3
```

* `person` owner access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "person" "PersonEntity"
  WHERE
    "PersonEntity"."id" = $1
    AND "PersonEntity"."ownerId" = $2
)
LIMIT 1

-- After
SELECT "PersonEntity"."id" AS "PersonEntity_id"
FROM "person" "PersonEntity"
WHERE
  "PersonEntity"."id" IN ($1, $2)
  AND "PersonEntity"."ownerId" = $3
```

* `partner` update access:

```sql
-- Before
SELECT 1 AS "row_exists" FROM (SELECT 1 AS dummy_column) "dummy_table" WHERE EXISTS (
  SELECT 1
  FROM "partners" "PartnerEntity"
    LEFT JOIN "users" "PartnerEntity__sharedBy"
      ON "PartnerEntity__sharedBy"."id"="PartnerEntity"."sharedById"
      AND "PartnerEntity__sharedBy"."deletedAt" IS NULL
    LEFT JOIN "users" "PartnerEntity__sharedWith"
      ON "PartnerEntity__sharedWith"."id"="PartnerEntity"."sharedWithId"
      AND "PartnerEntity__sharedWith"."deletedAt" IS NULL
  WHERE
    "PartnerEntity"."sharedWithId" = $1
    AND "PartnerEntity"."sharedById" = $2
)
LIMIT 1

-- After
SELECT
  "partner"."sharedById" AS "partner_sharedById",
  "partner"."sharedWithId" AS "partner_sharedWithId"
FROM "partners" "partner"
WHERE
  "partner"."sharedById" IN ($1, $2)
  AND "partner"."sharedWithId" = $3
```
This commit is contained in:
Michael Manganiello
2023-11-26 07:50:41 -05:00
committed by GitHub
parent f97dca7707
commit c04340c63e
9 changed files with 190 additions and 151 deletions

View File

@@ -130,7 +130,7 @@ describe('AssetService', () => {
const dto = _getCreateAssetDto();
assetRepositoryMock.create.mockResolvedValue(assetEntity);
accessMock.library.hasOwnerAccess.mockResolvedValue(true);
accessMock.library.checkOwnerAccess.mockResolvedValue(new Set([dto.libraryId!]));
await expect(sut.uploadFile(authStub.user1, dto, file)).resolves.toEqual({ duplicate: false, id: 'id_1' });
@@ -150,7 +150,7 @@ describe('AssetService', () => {
assetRepositoryMock.create.mockRejectedValue(error);
assetRepositoryMock.getAssetsByChecksums.mockResolvedValue([_getAsset_1()]);
accessMock.library.hasOwnerAccess.mockResolvedValue(true);
accessMock.library.checkOwnerAccess.mockResolvedValue(new Set([dto.libraryId!]));
await expect(sut.uploadFile(authStub.user1, dto, file)).resolves.toEqual({ duplicate: true, id: 'id_1' });
@@ -167,7 +167,7 @@ describe('AssetService', () => {
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoMotionAsset);
assetRepositoryMock.create.mockResolvedValueOnce(assetStub.livePhotoStillAsset);
accessMock.library.hasOwnerAccess.mockResolvedValue(true);
accessMock.library.checkOwnerAccess.mockResolvedValue(new Set([dto.libraryId!]));
await expect(
sut.uploadFile(authStub.user1, dto, fileStub.livePhotoStill, fileStub.livePhotoMotion),