chore: vault member permissions (#2509)

This commit is contained in:
Aman Harwara
2023-09-18 19:53:34 +05:30
committed by GitHub
parent 2af610c7bf
commit 48e7820100
32 changed files with 331 additions and 94 deletions

View File

@@ -191,7 +191,7 @@ export class VaultInviteService
public async getInvitableContactsForSharedVault(
sharedVault: SharedVaultListingInterface,
): Promise<TrustedContactInterface[]> {
const users = await this.vaultUsers.getSharedVaultUsers(sharedVault)
const users = await this.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
if (!users) {
return []
}

View File

@@ -0,0 +1,27 @@
import { UserServiceInterface } from '../../User/UserServiceInterface'
import { Result, SharedVaultUserPermission, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { SharedVaultListingInterface } from '@standardnotes/models'
import { VaultUserCache } from '../VaultUserCache'
export class IsReadonlyVaultMember implements SyncUseCaseInterface<boolean> {
constructor(
private users: UserServiceInterface,
private cache: VaultUserCache,
) {}
execute(vault: SharedVaultListingInterface): Result<boolean> {
if (!vault.sharing) {
return Result.ok(true)
}
if (!vault.sharing.ownerUserUuid) {
throw new Error(`Shared vault ${vault.sharing.sharedVaultUuid} does not have an owner user uuid`)
}
const user = this.users.sureUser
const vaultUsers = this.cache.get(vault.sharing.sharedVaultUuid)
const userPermission = vaultUsers?.find((vaultUser) => vaultUser.user_uuid === user.uuid)?.permission
return Result.ok(userPermission === SharedVaultUserPermission.PERMISSIONS.Read)
}
}

View File

@@ -0,0 +1,29 @@
import { UserServiceInterface } from '../../User/UserServiceInterface'
import { Result, SharedVaultUserPermission, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { SharedVaultListingInterface } from '@standardnotes/models'
import { VaultUserCache } from '../VaultUserCache'
export class IsVaultAdmin implements SyncUseCaseInterface<boolean> {
constructor(
private users: UserServiceInterface,
private cache: VaultUserCache,
) {}
execute(vault: SharedVaultListingInterface): Result<boolean> {
if (!vault.sharing) {
return Result.ok(true)
}
if (!vault.sharing.ownerUserUuid) {
throw new Error(`Shared vault ${vault.sharing.sharedVaultUuid} does not have an owner user uuid`)
}
const user = this.users.sureUser
const vaultUsers = this.cache.get(vault.sharing.sharedVaultUuid)
const userPermission = vaultUsers?.find((vaultUser) => vaultUser.user_uuid === user.uuid)?.permission
return Result.ok(
userPermission === SharedVaultUserPermission.PERMISSIONS.Admin || vault.sharing.ownerUserUuid === user.uuid,
)
}
}

View File

@@ -5,21 +5,31 @@ import { InternalEventBusInterface } from './../Internal/InternalEventBusInterfa
import { RemoveVaultMember } from './UseCase/RemoveSharedVaultMember'
import { VaultServiceInterface } from '../Vault/VaultServiceInterface'
import { GetVaultUsers } from './UseCase/GetVaultUsers'
import { SharedVaultListingInterface } from '@standardnotes/models'
import { SharedVaultListingInterface, VaultListingInterface } from '@standardnotes/models'
import { VaultUserServiceInterface } from './VaultUserServiceInterface'
import { ClientDisplayableError, SharedVaultUserServerHash, isClientDisplayableError } from '@standardnotes/responses'
import { AbstractService } from './../Service/AbstractService'
import { VaultUserServiceEvent } from './VaultUserServiceEvent'
import { Result } from '@standardnotes/domain-core'
import { IsVaultOwner } from './UseCase/IsVaultOwner'
import { InternalEventInterface } from '../Internal/InternalEventInterface'
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
import { IsReadonlyVaultMember } from './UseCase/IsReadonlyVaultMember'
import { IsVaultAdmin } from './UseCase/IsVaultAdmin'
export class VaultUserService extends AbstractService<VaultUserServiceEvent> implements VaultUserServiceInterface {
export class VaultUserService
extends AbstractService<VaultUserServiceEvent>
implements VaultUserServiceInterface, InternalEventHandlerInterface
{
constructor(
private vaults: VaultServiceInterface,
private vaultLocks: VaultLockServiceInterface,
private _getVaultUsers: GetVaultUsers,
private _removeVaultMember: RemoveVaultMember,
private _isVaultOwner: IsVaultOwner,
private _isVaultAdmin: IsVaultAdmin,
private _isReadonlyVaultMember: IsReadonlyVaultMember,
private _getVault: GetVault,
private _leaveVault: LeaveVault,
eventBus: InternalEventBusInterface,
@@ -37,7 +47,23 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
;(this._leaveVault as unknown) = undefined
}
public async getSharedVaultUsers(
async handleEvent(event: InternalEventInterface): Promise<void> {
if (event.type === ApplicationEvent.CompletedFullSync) {
this.vaults.getVaults().forEach((vault) => {
if (!vault.isSharedVaultListing()) {
return
}
this._getVaultUsers
.execute({
sharedVaultUuid: vault.sharing.sharedVaultUuid,
readFromCache: false,
})
.catch(console.error)
})
}
}
public async getSharedVaultUsersFromServer(
sharedVault: SharedVaultListingInterface,
): Promise<SharedVaultUserServerHash[] | undefined> {
const result = await this._getVaultUsers.execute({
@@ -55,6 +81,17 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
return this._isVaultOwner.execute(sharedVault).getValue()
}
public isCurrentUserSharedVaultAdmin(sharedVault: SharedVaultListingInterface): boolean {
return this._isVaultAdmin.execute(sharedVault).getValue()
}
public isCurrentUserReadonlyVaultMember(vault: VaultListingInterface): boolean {
if (!vault.isSharedVaultListing()) {
return false
}
return this._isReadonlyVaultMember.execute(vault).getValue()
}
async removeUserFromSharedVault(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>> {
if (!this.isCurrentUserSharedVaultOwner(sharedVault)) {
throw new Error('Only vault admins can remove users')
@@ -101,4 +138,17 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
void this.notifyEvent(VaultUserServiceEvent.UsersChanged)
}
getFormattedMemberPermission(permission: string): string {
switch (permission) {
case 'admin':
return 'Admin'
case 'write':
return 'Read / Write'
case 'read':
return 'Read-only'
default:
return 'Unknown'
}
}
}

View File

@@ -1,13 +1,18 @@
import { ApplicationServiceInterface } from './../Service/ApplicationServiceInterface'
import { SharedVaultListingInterface } from '@standardnotes/models'
import { SharedVaultListingInterface, VaultListingInterface } from '@standardnotes/models'
import { ClientDisplayableError, SharedVaultUserServerHash } from '@standardnotes/responses'
import { VaultUserServiceEvent } from './VaultUserServiceEvent'
import { Result } from '@standardnotes/domain-core'
export interface VaultUserServiceInterface extends ApplicationServiceInterface<VaultUserServiceEvent, unknown> {
getSharedVaultUsers(sharedVault: SharedVaultListingInterface): Promise<SharedVaultUserServerHash[] | undefined>
getSharedVaultUsersFromServer(
sharedVault: SharedVaultListingInterface,
): Promise<SharedVaultUserServerHash[] | undefined>
isCurrentUserSharedVaultOwner(sharedVault: SharedVaultListingInterface): boolean
isCurrentUserSharedVaultAdmin(sharedVault: SharedVaultListingInterface): boolean
isCurrentUserReadonlyVaultMember(vault: VaultListingInterface): boolean
removeUserFromSharedVault(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>>
leaveSharedVault(sharedVault: SharedVaultListingInterface): Promise<ClientDisplayableError | void>
isVaultUserOwner(user: SharedVaultUserServerHash): boolean
getFormattedMemberPermission(permission: string): string
}