wip: add group invites model

This commit is contained in:
Mo
2023-05-19 15:42:48 -05:00
parent 1841c800e6
commit 0bbefbf899
30 changed files with 561 additions and 42 deletions
+1
View File
@@ -7,6 +7,7 @@ import '../src/Controller/ItemsController'
import '../src/Controller/LinksController'
import '../src/Controller/GroupsController'
import '../src/Controller/GroupUsersController'
import '../src/Controller/GroupInvitesController'
import '../src/Controller/ContactsController'
import helmet from 'helmet'
@@ -25,12 +25,6 @@ export class CreateGroupUser1684173023883 implements MigrationInterface {
length: '36',
isNullable: false,
}),
new TableColumn({
name: 'inviter_uuid',
type: 'varchar',
length: '36',
isNullable: false,
}),
new TableColumn({
name: 'permissions',
type: 'varchar',
@@ -0,0 +1,68 @@
import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm'
export class CreateGroupInvites1684528646896 implements MigrationInterface {
name = 'createGroupInvites1684528646896'
public async up(queryRunner: QueryRunner): Promise<void> {
const table = new Table({
name: 'group_invites',
columns: [
new TableColumn({
name: 'uuid',
type: 'varchar',
length: '36',
isPrimary: true,
}),
new TableColumn({
name: 'group_uuid',
type: 'varchar',
length: '36',
isNullable: false,
}),
new TableColumn({
name: 'user_uuid',
type: 'varchar',
length: '36',
isNullable: false,
}),
new TableColumn({
name: 'inviter_uuid',
type: 'varchar',
length: '255',
isNullable: false,
}),
new TableColumn({
name: 'invite_type',
type: 'varchar',
length: '255',
isNullable: false,
}),
new TableColumn({
name: 'inviter_public_key',
type: 'varchar',
length: '255',
isNullable: false,
}),
new TableColumn({
name: 'encrypted_group_key',
type: 'text',
isNullable: false,
}),
new TableColumn({
name: 'created_at_timestamp',
type: 'bigint',
}),
new TableColumn({
name: 'updated_at_timestamp',
type: 'bigint',
}),
],
})
await queryRunner.createTable(table)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('group_invites')
}
}
@@ -56,6 +56,9 @@ import {
SQSNewRelicEventMessageHandler,
} from '@standardnotes/domain-events-infra'
import { UserCredentialsChangedEventHandler } from '../Domain/Handler/UserCredentialsChangedEventHandler'
import { GroupInviteServiceInterface } from '../Domain/GroupInvite/Service/GroupInviteServiceInterface'
import { GroupInviteService } from '../Domain/GroupInvite/Service/GroupInviteService'
import { GroupInviteFactory } from '../Domain/GroupInvite/Factory/GroupInviteFactory'
export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -182,6 +185,17 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
context.container.get(TYPES.Timer),
)
})
container
.bind<GroupInviteServiceInterface>(TYPES.GroupInviteService)
.toDynamicValue((context: interfaces.Context) => {
return new GroupInviteService(
context.container.get(TYPES.GroupRepository),
context.container.get(TYPES.GroupInviteRepository),
context.container.get(TYPES.GroupInviteFactory),
context.container.get(TYPES.Timer),
)
})
container
.bind<SyncResponseFactory20161215>(TYPES.SyncResponseFactory20161215)
.toDynamicValue((context: interfaces.Context) => {
@@ -219,6 +233,9 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
container.bind<GroupUserFactory>(TYPES.GroupUserFactory).toDynamicValue((context: interfaces.Context) => {
return new GroupUserFactory(context.container.get(TYPES.Timer))
})
container.bind<GroupInviteFactory>(TYPES.GroupInviteFactory).toDynamicValue((context: interfaces.Context) => {
return new GroupInviteFactory(context.container.get(TYPES.Timer))
})
container
.bind<UserCredentialsChangedEventHandler>(TYPES.UserCredentialsChangedEventHandler)
+11 -6
View File
@@ -13,6 +13,7 @@ const TYPES = {
GroupRepository: Symbol.for('GroupRepository'),
GroupUserRepository: Symbol.for('GroupUserRepository'),
ContactRepository: Symbol.for('ContactRepository'),
GroupInviteRepository: Symbol.for('GroupInviteRepository'),
// ORM
ORMItemRepository: Symbol.for('ORMItemRepository'),
@@ -20,6 +21,7 @@ const TYPES = {
ORMGroupRepository: Symbol.for('ORMGroupRepository'),
ORMGroupUserRepository: Symbol.for('ORMGroupUserRepository'),
ORMContactRepository: Symbol.for('ORMContactRepository'),
ORMGroupInviteRepository: Symbol.for('ORMGroupInviteRepository'),
// Middleware
AuthMiddleware: Symbol.for('AuthMiddleware'),
@@ -31,13 +33,21 @@ const TYPES = {
GroupProjector: Symbol.for('GroupProjector'),
GroupUserProjector: Symbol.for('GroupUserProjector'),
ContactProjector: Symbol.for('ContactProjector'),
GroupInviteProjector: Symbol.for('GroupInviteProjector'),
// factories
// Services
GroupService: Symbol.for('GroupService'),
GroupUserService: Symbol.for('GroupUserService'),
ContactService: Symbol.for('ContactService'),
GroupInviteService: Symbol.for('GroupInviteService'),
// Factories
ItemFactory: Symbol.for('ItemFactory'),
ItemLinkFactory: Symbol.for('ItemLinkFactory'),
GroupFactory: Symbol.for('GroupFactory'),
GroupUserFactory: Symbol.for('GroupUserFactory'),
ContactFactory: Symbol.for('ContactFactory'),
GroupInviteFactory: Symbol.for('GroupInviteFactory'),
// env vars
REDIS_URL: Symbol.for('REDIS_URL'),
@@ -74,11 +84,6 @@ const TYPES = {
EmailBackupRequestedEventHandler: Symbol.for('EmailBackupRequestedEventHandler'),
ItemRevisionCreationRequestedEventHandler: Symbol.for('ItemRevisionCreationRequestedEventHandler'),
// Services
GroupService: Symbol.for('GroupService'),
GroupUserService: Symbol.for('GroupUserService'),
ContactService: Symbol.for('ContactService'),
ContentDecoder: Symbol.for('ContentDecoder'),
DomainEventPublisher: Symbol.for('DomainEventPublisher'),
DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'),
@@ -0,0 +1,84 @@
import { Request, Response } from 'express'
import { BaseHttpController, controller, httpPost, results, httpDelete, httpGet } from 'inversify-express-utils'
import TYPES from '../Bootstrap/Types'
import { inject } from 'inversify'
import { GroupInviteServiceInterface } from '../Domain/GroupInvite/Service/GroupInviteServiceInterface'
import { GroupInviteProjector } from '../Projection/GroupInviteProjector'
@controller('/groups/invites')
export class GroupInvitesController extends BaseHttpController {
constructor(
@inject(TYPES.GroupInviteService) private groupInviteService: GroupInviteServiceInterface,
@inject(TYPES.GroupInviteProjector) private groupInviteProjector: GroupInviteProjector,
) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
public async addUserToGroup(
request: Request,
response: Response,
): Promise<results.NotFoundResult | results.JsonResult> {
const result = await this.groupInviteService.createGroupInvite({
originatorUuid: response.locals.user.uuid,
groupUuid: request.params.groupUuid,
userUuid: request.body.invitee_uuid,
inviterUuid: response.locals.user.uuid,
permissions: request.body.permissions,
})
if (!result) {
return this.errorResponse(500, 'Could not add user to group')
}
return this.json({ groupInvite: await this.groupInviteProjector.projectFull(result) })
}
@httpGet('/', TYPES.AuthMiddleware)
public async getGroupInvites(
request: Request,
response: Response,
): Promise<results.NotFoundResult | results.JsonResult> {
const result = await this.groupInviteService.getGroupInvites({
originatorUuid: response.locals.user.uuid,
groupUuid: request.params.groupUuid,
})
if (!result) {
return this.errorResponse(500, 'Could not get group users')
}
const { users } = result
const projected = users.map((groupInvite) => this.groupInviteProjector.projectFull(groupInvite))
return this.json({ users: projected })
}
@httpDelete('/:userUuid', TYPES.AuthMiddleware)
public async deleteGroupInvite(
request: Request,
response: Response,
): Promise<results.NotFoundResult | results.JsonResult> {
const result = await this.groupInviteService.deleteGroupInvite({
groupUuid: request.params.groupUuid,
userUuid: request.params.userUuid,
originatorUuid: response.locals.user.uuid,
})
if (!result) {
return this.errorResponse(500, 'Could not delete user')
}
return this.json({ success: true })
}
private errorResponse(status: number, message?: string, tag?: string) {
return this.json(
{
error: { message, tag },
},
status,
)
}
}
@@ -23,7 +23,6 @@ export class GroupUsersController extends BaseHttpController {
originatorUuid: response.locals.user.uuid,
groupUuid: request.params.groupUuid,
userUuid: request.body.invitee_uuid,
inviterUuid: response.locals.user.uuid,
permissions: request.body.permissions,
})
@@ -38,7 +38,6 @@ export class GroupsController extends BaseHttpController {
groupUuid: result.uuid,
originatorUuid: response.locals.user.uuid,
userUuid: response.locals.user.uuid,
inviterUuid: response.locals.user.uuid,
permissions: 'write',
})
@@ -0,0 +1,31 @@
import { TimerInterface } from '@standardnotes/time'
import { GroupInviteFactoryInterface } from './GroupInviteFactoryInterface'
import { GroupInviteHash } from './GroupInviteHash'
import { GroupInvite } from '../Model/GroupInvite'
export class GroupInviteFactory implements GroupInviteFactoryInterface {
constructor(private timer: TimerInterface) {}
create(dto: GroupInviteHash): GroupInvite {
const newGroupInvite = new GroupInvite()
newGroupInvite.uuid = dto.uuid
newGroupInvite.userUuid = dto.user_uuid
newGroupInvite.groupUuid = dto.group_uuid
newGroupInvite.inviterUuid = dto.inviter_uuid
const now = this.timer.getTimestampInMicroseconds()
newGroupInvite.updatedAtTimestamp = now
newGroupInvite.createdAtTimestamp = now
if (dto.created_at_timestamp) {
newGroupInvite.createdAtTimestamp = dto.created_at_timestamp
}
return newGroupInvite
}
createStub(dto: GroupInviteHash): GroupInvite {
const item = this.create(dto)
return item
}
}
@@ -0,0 +1,7 @@
import { GroupInvite } from '../Model/GroupInvite'
import { GroupInviteHash } from './GroupInviteHash'
export interface GroupInviteFactoryInterface {
create(dto: GroupInviteHash): GroupInvite
createStub(dto: GroupInviteHash): GroupInvite
}
@@ -0,0 +1,9 @@
export type GroupInviteHash = {
uuid: string
user_uuid: string
group_uuid: string
inviter_uuid: string
permissions: string
created_at_timestamp?: number
updated_at_timestamp?: number
}
@@ -0,0 +1,51 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@Entity({ name: 'group_invites' })
export class GroupInvite {
@PrimaryGeneratedColumn('uuid')
declare uuid: string
@Column({
name: 'group_uuid',
length: 36,
})
declare groupUuid: string
@Column({
name: 'user_uuid',
length: 36,
})
declare userUuid: string
@Column({
name: 'inviter_uuid',
})
declare inviterUuid: string
@Column({
name: 'invite_type',
})
declare inviteType: 'join' | 'key-change'
@Column({
name: 'inviter_public_key',
})
declare inviterPublicKey: string
@Column({
name: 'encrypted_group_key',
})
declare encryptedGroupKey: string
@Column({
name: 'created_at_timestamp',
type: 'bigint',
})
declare createdAtTimestamp: number
@Column({
name: 'updated_at_timestamp',
type: 'bigint',
})
declare updatedAtTimestamp: number
}
@@ -0,0 +1,26 @@
import { GroupInvite } from '../Model/GroupInvite'
export type GroupInviteQuery = {
userUuid: string
}
export type GroupInviteFindAllForUserQuery = {
userUuid: string
lastSyncTime?: number
inviterUuid?: string
includeSentAndReceived?: boolean
}
export type GroupInviteFindAllForGroup = {
groupUuid: string
}
export interface GroupInviteRepositoryInterface {
findByUuid(groupInviteUuid: string): Promise<GroupInvite | null>
create(group: GroupInvite): Promise<GroupInvite>
save(groupInvite: GroupInvite): Promise<GroupInvite>
remove(group: GroupInvite): Promise<GroupInvite>
findAllForUser(query: GroupInviteFindAllForUserQuery): Promise<GroupInvite[]>
findAllForGroup(query: GroupInviteFindAllForGroup): Promise<GroupInvite[]>
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupInvite | null>
}
@@ -0,0 +1,75 @@
import { GroupInvite } from '../Model/GroupInvite'
import { Repository, SelectQueryBuilder } from 'typeorm'
import {
GroupInviteFindAllForGroup,
GroupInviteFindAllForUserQuery,
GroupInviteRepositoryInterface,
} from './GroupInviteRepositoryInterface'
export class TypeORMGroupInviteRepository implements GroupInviteRepositoryInterface {
constructor(private ormRepository: Repository<GroupInvite>) {}
async create(groupInvite: GroupInvite): Promise<GroupInvite> {
return this.ormRepository.save(groupInvite)
}
async save(groupInvite: GroupInvite): Promise<GroupInvite> {
return this.ormRepository.save(groupInvite)
}
findByUuid(uuid: string): Promise<GroupInvite | null> {
return this.ormRepository
.createQueryBuilder('group_invite')
.where('group_invite.uuid = :uuid', {
uuid,
})
.getOne()
}
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupInvite | null> {
return this.ormRepository
.createQueryBuilder('group_invite')
.where('group_invite.user_uuid = :userUuid', { userUuid })
.andWhere('group_invite.group_uuid = :groupUuid', { groupUuid })
.getOne()
}
async remove(groupInvite: GroupInvite): Promise<GroupInvite> {
return this.ormRepository.remove(groupInvite)
}
findAllForGroup(query: GroupInviteFindAllForGroup): Promise<GroupInvite[]> {
return this.ormRepository
.createQueryBuilder('group_invite')
.where('group_invite.group_uuid = :groupUuid', { groupUuid: query.groupUuid })
.getMany()
}
async findAllForUser(query: GroupInviteFindAllForUserQuery): Promise<GroupInvite[]> {
return this.createFindAllQueryBuilder(query).getMany()
}
private createFindAllQueryBuilder(query: GroupInviteFindAllForUserQuery): SelectQueryBuilder<GroupInvite> {
const queryBuilder = this.ormRepository.createQueryBuilder('group_invite')
queryBuilder.where('group_invite.user_uuid = :userUuid', { userUuid: query.userUuid })
if (query.includeSentAndReceived) {
queryBuilder.orWhere('group_invite.sender_uuid = :userUuid', { userUuid: query.userUuid })
}
if (query.lastSyncTime) {
queryBuilder.andWhere('group_invite.updated_at_timestamp > :lastSyncTime', {
lastSyncTime: query.lastSyncTime,
})
}
if (query.inviterUuid) {
queryBuilder.andWhere('group_invite.inviter_uuid = :inviterUuid', {
inviterUuid: query.inviterUuid,
})
}
return queryBuilder
}
}
@@ -0,0 +1,4 @@
export interface GetUserGroupKeysDTO {
userUuid: string
lastSyncTime?: number
}
@@ -0,0 +1,103 @@
import { TimerInterface } from '@standardnotes/time'
import { GroupInvite } from '../Model/GroupInvite'
import { GroupInviteFactoryInterface } from '../Factory/GroupInviteFactoryInterface'
import { v4 as uuidv4 } from 'uuid'
import { GroupInviteRepositoryInterface } from '../Repository/GroupInviteRepositoryInterface'
import { GroupInviteServiceInterface } from './GroupInviteServiceInterface'
import { GetUserGroupKeysDTO } from './GetUserGroupInvitesDTO'
import { GroupsRepositoryInterface } from '../../Group/Repository/GroupRepositoryInterface'
export class GroupInviteService implements GroupInviteServiceInterface {
constructor(
private groupRepository: GroupsRepositoryInterface,
private groupInviteRepository: GroupInviteRepositoryInterface,
private groupInviteFactory: GroupInviteFactoryInterface,
private timer: TimerInterface,
) {}
async createGroupInvite(dto: {
originatorUuid: string
groupUuid: string
userUuid: string
inviterUuid: string
permissions: string
}): Promise<GroupInvite | null> {
const group = await this.groupRepository.findByUuid(dto.groupUuid)
if (!group || group.userUuid !== dto.originatorUuid) {
return null
}
const groupInvite = this.groupInviteFactory.create({
uuid: uuidv4(),
user_uuid: dto.userUuid,
group_uuid: dto.groupUuid,
inviter_uuid: dto.inviterUuid,
permissions: dto.permissions,
created_at_timestamp: this.timer.getTimestampInSeconds(),
updated_at_timestamp: this.timer.getTimestampInSeconds(),
})
return this.groupInviteRepository.create(groupInvite)
}
getGroupInvitesForUser(dto: GetUserGroupKeysDTO): Promise<GroupInvite[]> {
return this.groupInviteRepository.findAllForUser(dto)
}
async getGroupInvites(dto: {
groupUuid: string
originatorUuid: string
}): Promise<{ users: GroupInvite[]; isAdmin: boolean } | undefined> {
const group = await this.groupRepository.findByUuid(dto.groupUuid)
if (!group) {
return undefined
}
const isUserGroupAdmin = group && group.userUuid === dto.originatorUuid
const doesUserBelongToGroup = await this.groupInviteRepository.findByUserUuidAndGroupUuid(
dto.originatorUuid,
dto.groupUuid,
)
if (!isUserGroupAdmin && !doesUserBelongToGroup) {
return undefined
}
const users = await this.groupInviteRepository.findAllForGroup({ groupUuid: dto.groupUuid })
return {
users,
isAdmin: isUserGroupAdmin,
}
}
async deleteGroupInvite(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise<boolean> {
const groupInvite = await this.groupInviteRepository.findByUserUuidAndGroupUuid(dto.userUuid, dto.groupUuid)
if (!groupInvite) {
return false
}
const isAuthorized = groupInvite.inviterUuid !== dto.originatorUuid && groupInvite.userUuid !== dto.originatorUuid
if (isAuthorized) {
return false
}
await this.groupInviteRepository.remove(groupInvite)
return true
}
async deleteAllGroupInvitesForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise<boolean> {
const group = await this.groupRepository.findByUuid(dto.groupUuid)
if (!group || group.userUuid !== dto.originatorUuid) {
return false
}
const groupInvites = await this.groupInviteRepository.findAllForGroup({ groupUuid: dto.groupUuid })
for (const groupInvite of groupInvites) {
await this.groupInviteRepository.remove(groupInvite)
}
return true
}
}
@@ -0,0 +1,23 @@
import { GroupInvite } from '../Model/GroupInvite'
import { GetUserGroupKeysDTO } from './GetUserGroupInvitesDTO'
export interface GroupInviteServiceInterface {
createGroupInvite(dto: {
originatorUuid: string
groupUuid: string
userUuid: string
inviterUuid: string
permissions: string
}): Promise<GroupInvite | null>
getGroupInvitesForUser(dto: GetUserGroupKeysDTO): Promise<GroupInvite[]>
getGroupInvites(dto: {
groupUuid: string
originatorUuid: string
}): Promise<{ users: GroupInvite[]; isAdmin: boolean } | undefined>
deleteGroupInvite(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise<boolean>
deleteAllGroupInvitesForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise<boolean>
}
@@ -10,7 +10,6 @@ export class GroupUserFactory implements GroupUserFactoryInterface {
const newGroupUser = new GroupUser()
newGroupUser.uuid = dto.uuid
newGroupUser.userUuid = dto.user_uuid
newGroupUser.inviterUuid = dto.inviter_uuid
newGroupUser.groupUuid = dto.group_uuid
newGroupUser.permissions = dto.permissions
@@ -2,7 +2,6 @@ export type GroupUserHash = {
uuid: string
user_uuid: string
group_uuid: string
inviter_uuid: string
permissions: string
created_at_timestamp?: number
updated_at_timestamp?: number
@@ -17,11 +17,6 @@ export class GroupUser {
})
declare userUuid: string
@Column({
name: 'inviter_uuid',
})
declare inviterUuid: string
@Column({
name: 'permissions',
})
@@ -1,4 +1,4 @@
export enum GroupPermission {
export enum GroupUserPermission {
Read = 'read',
Write = 'write',
Admin = 'admin',
@@ -7,7 +7,6 @@ export type GroupUserQuery = {
export type GroupUserFindAllForUserQuery = {
userUuid: string
lastSyncTime?: number
inviterUuid?: string
includeSentAndReceived?: boolean
}
@@ -64,12 +64,6 @@ export class TypeORMGroupUserRepository implements GroupUserRepositoryInterface
})
}
if (query.inviterUuid) {
queryBuilder.andWhere('group_user.inviter_uuid = :inviterUuid', {
inviterUuid: query.inviterUuid,
})
}
return queryBuilder
}
}
@@ -19,7 +19,6 @@ export class GroupUserService implements GroupUserServiceInterface {
originatorUuid: string
groupUuid: string
userUuid: string
inviterUuid: string
permissions: string
}): Promise<GroupUser | null> {
const group = await this.groupRepository.findByUuid(dto.groupUuid)
@@ -31,7 +30,6 @@ export class GroupUserService implements GroupUserServiceInterface {
uuid: uuidv4(),
user_uuid: dto.userUuid,
group_uuid: dto.groupUuid,
inviter_uuid: dto.inviterUuid,
permissions: dto.permissions,
created_at_timestamp: this.timer.getTimestampInSeconds(),
updated_at_timestamp: this.timer.getTimestampInSeconds(),
@@ -72,13 +70,18 @@ export class GroupUserService implements GroupUserServiceInterface {
}
async deleteGroupUser(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise<boolean> {
const groupUser = await this.groupUserRepository.findByUserUuidAndGroupUuid(dto.userUuid, dto.groupUuid)
if (!groupUser) {
const group = await this.groupRepository.findByUuid(dto.groupUuid)
if (!group) {
return false
}
const isAuthorized = groupUser.inviterUuid !== dto.originatorUuid && groupUser.userUuid !== dto.originatorUuid
if (isAuthorized) {
const isAuthorized = group.userUuid === dto.originatorUuid
if (!isAuthorized) {
return false
}
const groupUser = await this.groupUserRepository.findByUserUuidAndGroupUuid(dto.userUuid, dto.groupUuid)
if (!groupUser) {
return false
}
@@ -6,7 +6,6 @@ export interface GroupUserServiceInterface {
originatorUuid: string
groupUuid: string
userUuid: string
inviterUuid: string
permissions: string
}): Promise<GroupUser | null>
@@ -4,7 +4,7 @@ import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserServiceInterface'
import { ItemHash } from '../ItemHash'
import { GroupPermission } from '../../GroupUser/Model/GroupPermission'
import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission'
export class OwnershipFilter implements ItemSaveRuleInterface {
constructor(private groupUserService: GroupUserServiceInterface) {}
@@ -19,7 +19,7 @@ export class OwnershipFilter implements ItemSaveRuleInterface {
const groupAuthorization = await this.itemBelongsToAuthorizedSharedGroup(dto.userUuid, dto.itemHash)
if (groupAuthorization) {
if (isItemBeingRemovedFromGroup) {
if (groupAuthorization === GroupPermission.Admin) {
if (groupAuthorization === GroupUserPermission.Admin) {
return {
passed: true,
}
@@ -48,12 +48,12 @@ export class OwnershipFilter implements ItemSaveRuleInterface {
private async itemBelongsToAuthorizedSharedGroup(
userUuid: string,
itemHash: ItemHash,
): Promise<GroupPermission | undefined> {
): Promise<GroupUserPermission | undefined> {
const groupUsers = await this.groupUserService.getGroupUsersForUser({ userUuid })
for (const groupUser of groupUsers) {
if (itemHash.group_uuid === groupUser.groupUuid) {
return groupUser.permissions as GroupPermission
return groupUser.permissions as GroupUserPermission
}
}
@@ -0,0 +1,8 @@
export type GroupInviteProjection = {
uuid: string
group_uuid: string
user_uuid: string
inviter_uuid: string
created_at_timestamp: number
updated_at_timestamp: number
}
@@ -0,0 +1,29 @@
import { GroupInvite } from '../Domain/GroupInvite/Model/GroupInvite'
import { ProjectorInterface } from './ProjectorInterface'
import { GroupInviteProjection } from './GroupInviteProjection'
export class GroupInviteProjector implements ProjectorInterface<GroupInvite, GroupInviteProjection> {
projectSimple(_groupInvite: GroupInvite): GroupInviteProjection {
throw Error('not implemented')
}
projectCustom(_projectionType: string, groupInvite: GroupInvite): GroupInviteProjection {
const fullProjection = this.projectFull(groupInvite)
return {
...fullProjection,
user_uuid: groupInvite.userUuid,
}
}
projectFull(groupInvite: GroupInvite): GroupInviteProjection {
return {
uuid: groupInvite.uuid,
group_uuid: groupInvite.groupUuid,
user_uuid: groupInvite.userUuid,
inviter_uuid: groupInvite.inviterUuid,
created_at_timestamp: groupInvite.createdAtTimestamp,
updated_at_timestamp: groupInvite.updatedAtTimestamp,
}
}
}
@@ -2,7 +2,6 @@ export type GroupUserProjection = {
uuid: string
group_uuid: string
user_uuid: string
inviter_uuid: string
permissions: string
created_at_timestamp: number
updated_at_timestamp: number
@@ -35,7 +35,6 @@ export class GroupUserProjector implements ProjectorInterface<GroupUser, GroupUs
uuid: groupUser.uuid,
group_uuid: groupUser.groupUuid,
user_uuid: groupUser.userUuid,
inviter_uuid: groupUser.inviterUuid,
permissions: groupUser.permissions,
created_at_timestamp: groupUser.createdAtTimestamp,
updated_at_timestamp: groupUser.updatedAtTimestamp,