refactor: separate manual vs apikey logic in group service

This commit is contained in:
Jeeiii
2024-03-28 18:54:57 +01:00
parent 011f7b9fd5
commit a1f1f3d131
5 changed files with 814 additions and 113 deletions

View File

@@ -2,11 +2,13 @@ import {
Column,
CreateDateColumn,
Entity,
Index,
PrimaryColumn,
UpdateDateColumn
} from "typeorm"
@Entity("admins")
@Index(["apiKey"], { unique: true })
export class Admin {
@PrimaryColumn({ unique: true })
id: string

View File

@@ -416,7 +416,6 @@ describe("GroupsService", () => {
it("Should create a group via API", async () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
@@ -432,15 +431,10 @@ describe("GroupsService", () => {
it("Should remove a group via API", async () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
await groupsService.removeGroupWithAPIKey(
group.id,
admin.id,
apiKey
)
await groupsService.removeGroupWithAPIKey(group.id, apiKey)
const fun = groupsService.getGroup(group.id)
@@ -450,69 +444,51 @@ describe("GroupsService", () => {
})
it("Should not create a group if the admin does not exist", async () => {
const fun = groupsService.createGroupWithAPIKey(
groupDto,
"wrong",
apiKey
)
const fun = groupsService.createGroupWithAPIKey(groupDto, "wrong")
await expect(fun).rejects.toThrow(`Invalid admin for the groups`)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not remove a group if the admin does not exist", async () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
const fun = groupsService.removeGroupWithAPIKey(
group.id,
"wrong",
apiKey
)
const fun = groupsService.removeGroupWithAPIKey(group.id, "wrong")
await expect(fun).rejects.toThrow(`Invalid admin for the groups`)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not create a group if the API key is invalid", async () => {
const fun = groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
"apiKey"
)
const fun = groupsService.createGroupWithAPIKey(groupDto, "apiKey")
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
`Invalid API key or invalid admin for the groups`
)
})
it("Should not remove a group if the API key is invalid", async () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
const fun = groupsService.removeGroupWithAPIKey(
group.id,
admin.id,
"wrong"
)
const fun = groupsService.removeGroupWithAPIKey(group.id, "wrong")
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
`Invalid API key or invalid admin for the groups`
)
})
it("Should not create a group if the API key is disabled for the admin", async () => {
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)
const fun = groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
const fun = groupsService.createGroupWithAPIKey(groupDto, apiKey)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
@@ -524,17 +500,12 @@ describe("GroupsService", () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)
const fun = groupsService.removeGroupWithAPIKey(
group.id,
admin.id,
apiKey
)
const fun = groupsService.removeGroupWithAPIKey(group.id, apiKey)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
@@ -546,7 +517,6 @@ describe("GroupsService", () => {
const group = await groupsService.createGroupWithAPIKey(
groupDto,
admin.id,
apiKey
)
@@ -564,7 +534,6 @@ describe("GroupsService", () => {
const fun = groupsService.removeGroupWithAPIKey(
group.id,
anotherAdmin.id,
anotherApiKey
)
@@ -619,7 +588,6 @@ describe("GroupsService", () => {
it("Should create the groups via API", async () => {
const groups = await groupsService.createGroupsWithAPIKey(
groupsDtos,
admin.id,
apiKey
)
@@ -644,11 +612,7 @@ describe("GroupsService", () => {
expect(groups).toHaveLength(4)
await groupsService.removeGroupsWithAPIKey(
[ids[0], ids[1]],
admin.id,
apiKey
)
await groupsService.removeGroupsWithAPIKey([ids[0], ids[1]], apiKey)
groups = await groupsService.getGroups({
adminId: admin.id
@@ -677,55 +641,45 @@ describe("GroupsService", () => {
it("Should not create the groups if the admin does not exist", async () => {
const fun = groupsService.createGroupsWithAPIKey(
groupsDtos,
"wrong",
apiKey
"wrong"
)
await expect(fun).rejects.toThrow(`Invalid admin for the groups`)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not remove the groups if the admin does not exist", async () => {
const fun = groupsService.removeGroupsWithAPIKey(
ids,
"wrong",
apiKey
)
const fun = groupsService.removeGroupsWithAPIKey(ids, "wrong")
await expect(fun).rejects.toThrow(`Invalid admin for the groups`)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not create the groups if the API key is invalid", async () => {
const fun = groupsService.createGroupsWithAPIKey(
groupsDtos,
admin.id,
"apiKey"
"wrong"
)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
`Invalid API key or invalid admin for the groups`
)
})
it("Should not remove the groups if the API key is invalid", async () => {
const fun = groupsService.removeGroupsWithAPIKey(
ids,
admin.id,
"apiKey"
)
const fun = groupsService.removeGroupsWithAPIKey(ids, "wrong")
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
`Invalid API key or invalid admin for the groups`
)
})
it("Should not create the groups if the API key is disabled for the admin", async () => {
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)
const fun = groupsService.createGroupsWithAPIKey(
groupsDtos,
admin.id,
apiKey
)
const fun = groupsService.createGroupsWithAPIKey(groupsDtos, apiKey)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
@@ -733,11 +687,7 @@ describe("GroupsService", () => {
})
it("Should not remove the groups if the API key is disabled for the admin", async () => {
const fun = groupsService.removeGroupsWithAPIKey(
ids,
admin.id,
apiKey
)
const fun = groupsService.removeGroupsWithAPIKey(ids, apiKey)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
@@ -759,7 +709,6 @@ describe("GroupsService", () => {
const fun = groupsService.removeGroupsWithAPIKey(
[ids[2]],
anotherAdmin.id,
anotherApiKey
)
@@ -805,7 +754,6 @@ describe("GroupsService", () => {
const updatedGroup = await groupsService.updateGroupWithApiKey(
group.id,
updateDto,
admin.id,
apiKey
)
@@ -825,25 +773,23 @@ describe("GroupsService", () => {
const fun = groupsService.updateGroupWithApiKey(
groupId,
groupDto,
"wrong-admin",
apiKey
)
await expect(fun).rejects.toThrow(
`Invalid admin for the group '${groupId}'`
`You are not the admin of the group '${groupId}'`
)
})
it("Should not update a group if the group does not exist", async () => {
const fun = groupsService.updateGroupWithApiKey(
"wrong-group",
"wrong",
groupDto,
admin.id,
apiKey
)
await expect(fun).rejects.toThrow(
`Group with id 'wrong-group' does not exist`
`Group with id 'wrong' does not exist`
)
})
@@ -851,12 +797,11 @@ describe("GroupsService", () => {
const fun = groupsService.updateGroupWithApiKey(
groupId,
groupDto,
admin.id,
"invalid-apikey"
)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
`Invalid API key or invalid admin for the groups`
)
})
@@ -866,7 +811,132 @@ describe("GroupsService", () => {
const fun = groupsService.updateGroupWithApiKey(
groupId,
groupDto,
apiKey
)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
})
})
describe("# Update groups via API", () => {
const groupsDtos: Array<CreateGroupDto> = [
{
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
},
{
id: "2",
name: "Group2",
description: "This is a new group2",
treeDepth: 32,
fingerprintDuration: 7200
}
]
const updateDtos: Array<UpdateGroupDto> = [
{
description: "This is a new new group1",
treeDepth: 32,
fingerprintDuration: 7200
},
{
description: "This is a new new group2",
treeDepth: 32,
fingerprintDuration: 14400
}
]
let admin: Admin
let apiKey: string
let groups: Array<Group>
let groupId1: string
let groupId2: string
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
apiKey = await adminsService.updateApiKey(
admin.id,
ApiKeyActions.Generate
)
groups = await groupsService.createGroupsManually(
groupsDtos,
admin.id
)
groupId1 = groups[0].id
groupId2 = groups[1].id
})
it("Should update the groups via API", async () => {
const updatedGroups = await groupsService.updateGroupsWithApiKey(
[groupId1, groupId2],
updateDtos,
apiKey
)
updatedGroups.forEach((updatedGroup: Group, i: number) => {
expect(updatedGroup.id).toBe(groupsDtos[i].id)
expect(updatedGroup.adminId).toBe(admin.id)
expect(updatedGroup.description).toBe(updateDtos[i].description)
expect(updatedGroup.name).toBe(groupsDtos[i].name)
expect(updatedGroup.treeDepth).toBe(updateDtos[i].treeDepth)
expect(updatedGroup.fingerprintDuration).toBe(
updateDtos[i].fingerprintDuration
)
expect(updatedGroup.members).toHaveLength(0)
})
})
it("Should not update the groups if the admin is the wrong one", async () => {
const fun = groupsService.updateGroupsWithApiKey(
[groupId1, groupId2],
groupsDtos,
"wrong"
)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not update the groups if the group does not exist", async () => {
const fun = groupsService.updateGroupsWithApiKey(
["wrong"],
groupsDtos,
apiKey
)
await expect(fun).rejects.toThrow(
`Group with id 'wrong' does not exist`
)
})
it("Should not update the groups if the API key is invalid", async () => {
const fun = groupsService.updateGroupsWithApiKey(
[groupId1, groupId2],
groupsDtos,
"invalid-apikey"
)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should not update the groups if the API key is disabled", async () => {
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)
const fun = groupsService.updateGroupsWithApiKey(
[groupId1, groupId2],
groupsDtos,
apiKey
)
@@ -1283,6 +1353,402 @@ describe("GroupsService", () => {
})
})
describe("# createGroupManually", () => {
const groupDto: CreateGroupDto = {
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
}
let admin: Admin
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
})
it("Should create a group manually", async () => {
const group = await groupsService.createGroupManually(
groupDto,
admin.id
)
expect(group.id).toBe(groupDto.id)
expect(group.adminId).toBe(admin.id)
expect(group.description).toBe(groupDto.description)
expect(group.name).toBe(groupDto.name)
expect(group.treeDepth).toBe(groupDto.treeDepth)
expect(group.fingerprintDuration).toBe(groupDto.fingerprintDuration)
expect(group.members).toHaveLength(0)
})
it("Should not create a group manually if the admin doesn't exist", async () => {
const fun = groupsService.createGroupManually(groupDto, "wrong")
await expect(fun).rejects.toThrow(`You are not an admin`)
})
})
describe("# createGroupsManually", () => {
const groupsDtos: Array<CreateGroupDto> = [
{
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
},
{
id: "2",
name: "Group2",
description: "This is a new group2",
treeDepth: 32,
fingerprintDuration: 7200
}
]
let admin: Admin
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
})
it("Should create a group manually", async () => {
const groups = await groupsService.createGroupsManually(
groupsDtos,
admin.id
)
groups.forEach((group: Group, i: number) => {
expect(group.id).toBe(groupsDtos[i].id)
expect(group.adminId).toBe(admin.id)
expect(group.description).toBe(groupsDtos[i].description)
expect(group.name).toBe(groupsDtos[i].name)
expect(group.treeDepth).toBe(groupsDtos[i].treeDepth)
expect(group.fingerprintDuration).toBe(
groupsDtos[i].fingerprintDuration
)
expect(group.members).toHaveLength(0)
})
})
it("Should not create a group manually if the admin doesn't exist", async () => {
const fun = groupsService.createGroupsManually(groupsDtos, "wrong")
await expect(fun).rejects.toThrow(`You are not an admin`)
})
})
describe("# removeGroupManually", () => {
const groupDto: CreateGroupDto = {
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
}
let admin: Admin
let group: Group
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
group = await groupsService.createGroupManually(groupDto, admin.id)
})
it("Should remove a group manually", async () => {
await groupsService.removeGroupManually(group.id, admin.id)
const fun = groupsService.getGroup(group.id)
await expect(fun).rejects.toThrow(
`Group with id '${group.id}' does not exist`
)
})
it("Should not remove a group manually if the group doesn't exist", async () => {
const fun = groupsService.removeGroupManually(group.id, admin.id)
await expect(fun).rejects.toThrow(
`Group with id '${group.id}' does not exist`
)
})
it("Should not remove a group manually if the admin doesn't exist or is not the admin of the group", async () => {
group = await groupsService.createGroupManually(groupDto, admin.id)
const fun = groupsService.removeGroupManually(group.id, "wrong")
await expect(fun).rejects.toThrow(
`You are not the admin of the group '${group.id}'`
)
})
})
describe("# removeGroupsManually", () => {
const groupsDtos: Array<CreateGroupDto> = [
{
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
},
{
id: "2",
name: "Group2",
description: "This is a new group2",
treeDepth: 32,
fingerprintDuration: 7200
}
]
let admin: Admin
let groups: Array<Group>
let groupId1: string
let groupId2: string
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
groups = await groupsService.createGroupsManually(
groupsDtos,
admin.id
)
groupId1 = groups[0].id
groupId2 = groups[1].id
})
it("Should remove the groups manually", async () => {
await groupsService.removeGroupsManually(
[groupId1, groupId2],
admin.id
)
const fun1 = groupsService.getGroup(groupId1)
const fun2 = groupsService.getGroup(groupId2)
await expect(fun1).rejects.toThrow(
`Group with id '${groupId1}' does not exist`
)
await expect(fun2).rejects.toThrow(
`Group with id '${groupId2}' does not exist`
)
})
it("Should not remove the groups manually if one or more group doesn't exist", async () => {
const fun = groupsService.removeGroupsManually(
[groupId1, groupId2],
admin.id
)
await expect(fun).rejects.toThrow(
`Group with id '${groupId1}' does not exist`
)
})
it("Should not remove the groups manually if the admin doesn't exist or is not the admin of the group", async () => {
groups = await groupsService.createGroupsManually(
groupsDtos,
admin.id
)
const fun = groupsService.removeGroupsManually(
[groupId1, groupId2],
"wrong"
)
await expect(fun).rejects.toThrow(
`You are not the admin of the group '${groupId1}'`
)
})
})
describe("# updateGroupManually", () => {
const groupDto: CreateGroupDto = {
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
}
const updateDto: UpdateGroupDto = {
description: "This is a new new group1",
treeDepth: 32,
fingerprintDuration: 7200
}
let admin: Admin
let group: Group
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
group = await groupsService.createGroupManually(groupDto, admin.id)
})
it("Should update a group manually", async () => {
const updatedGroup = await groupsService.updateGroupManually(
group.id,
updateDto,
admin.id
)
expect(updatedGroup.id).toBe(groupDto.id)
expect(updatedGroup.adminId).toBe(admin.id)
expect(updatedGroup.description).toBe(updateDto.description)
expect(updatedGroup.name).toBe(groupDto.name)
expect(updatedGroup.treeDepth).toBe(updateDto.treeDepth)
expect(updatedGroup.fingerprintDuration).toBe(
updateDto.fingerprintDuration
)
expect(updatedGroup.members).toHaveLength(0)
expect(updatedGroup.credentials).toBeNull()
})
it("Should not update a group manually if the group doesn't exist", async () => {
const fun = groupsService.updateGroupManually(
"wrong",
updateDto,
admin.id
)
await expect(fun).rejects.toThrow(
`Group with id 'wrong' does not exist`
)
})
it("Should not update a group manually if the admin doesn't exist or is not the admin of the group", async () => {
const fun = groupsService.updateGroupManually(
group.id,
updateDto,
"wrong"
)
await expect(fun).rejects.toThrow(
`You are not the admin of the group '${group.id}'`
)
})
})
describe("# updateGroupsManually", () => {
const groupsDtos: Array<CreateGroupDto> = [
{
id: "1",
name: "Group1",
description: "This is a new group1",
treeDepth: 16,
fingerprintDuration: 3600
},
{
id: "2",
name: "Group2",
description: "This is a new group2",
treeDepth: 32,
fingerprintDuration: 7200
}
]
const updateDtos: Array<UpdateGroupDto> = [
{
description: "This is a new new group1",
treeDepth: 32,
fingerprintDuration: 7200
},
{
description: "This is a new new group2",
treeDepth: 32,
fingerprintDuration: 14400
}
]
let admin: Admin
let groups: Array<Group>
let groupId1: string
let groupId2: string
beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})
admin = await adminsService.findOne({ id: admin.id })
groups = await groupsService.createGroupsManually(
groupsDtos,
admin.id
)
groupId1 = groups[0].id
groupId2 = groups[1].id
})
it("Should update the groups manually", async () => {
const updatedGroups = await groupsService.updateGroupsManually(
[groupId1, groupId2],
updateDtos,
admin.id
)
updatedGroups.forEach((updatedGroup: Group, i: number) => {
expect(updatedGroup.id).toBe(groupsDtos[i].id)
expect(updatedGroup.adminId).toBe(admin.id)
expect(updatedGroup.description).toBe(updateDtos[i].description)
expect(updatedGroup.name).toBe(groupsDtos[i].name)
expect(updatedGroup.treeDepth).toBe(updateDtos[i].treeDepth)
expect(updatedGroup.fingerprintDuration).toBe(
updateDtos[i].fingerprintDuration
)
expect(updatedGroup.members).toHaveLength(0)
})
})
it("Should not update the groups manually if one or more groups doesn't exist", async () => {
const fun = groupsService.updateGroupsManually(
["wrong"],
updateDtos,
admin.id
)
await expect(fun).rejects.toThrow(
`Group with id 'wrong' does not exist`
)
})
it("Should not update the groups manually if the admin doesn't exist or is not the admin of the groups", async () => {
const fun = groupsService.updateGroupsManually(
[groupId1, groupId2],
updateDtos,
"wrong"
)
await expect(fun).rejects.toThrow(
`You are not the admin of the group '${groupId1}'`
)
})
})
describe("# initialize", () => {
it("Should initialize the cached groups", async () => {
const currentCachedGroups = await groupsService.getGroups()

View File

@@ -19,7 +19,7 @@ import { UpdateGroupDto } from "./dto/update-group.dto"
import { Group } from "./entities/group.entity"
import { Member } from "./entities/member.entity"
import { MerkleProof } from "./types"
import { adminApiKeyCheck } from "./groups.utils"
import { getAndCheckAdmin } from "./groups.utils"
@Injectable()
export class GroupsService {
@@ -61,16 +61,14 @@ export class GroupsService {
/**
* Create a group using API Key.
* @param dto External parameters used to create a new group.
* @param adminId Admin id.
* @param apiKey The API Key.
* @returns Created group.
*/
async createGroupWithAPIKey(
dto: CreateGroupDto,
adminId: string,
apiKey: string
): Promise<Group> {
const groups = await this.createGroupsWithAPIKey([dto], adminId, apiKey)
const groups = await this.createGroupsWithAPIKey([dto], apiKey)
return groups.at(0)
}
@@ -78,20 +76,58 @@ export class GroupsService {
/**
* Create groups using API Key.
* @param dtos External parameters used to create new groups.
* @param adminId Admin id.
* @param apiKey The API Key.
* @returns Created groups.
*/
async createGroupsWithAPIKey(
dtos: Array<CreateGroupDto>,
adminId: string,
apiKey: string
): Promise<Array<Group>> {
const newGroups: Array<Group> = []
const admin = await getAndCheckAdmin(this.adminsService, apiKey)
for await (const dto of dtos) {
const group = await this.createGroup(dto, admin.id)
newGroups.push(group)
}
return newGroups
}
/**
* Create group manually without using API Key.
* @param dto External parameters used to create a new group.
* @param adminId Admin id.
* @returns Created group.
*/
async createGroupManually(
dto: CreateGroupDto,
adminId: string
): Promise<Group> {
const admin = await this.adminsService.findOne({ id: adminId })
await adminApiKeyCheck(admin, apiKey)
if (!admin) throw new BadRequestException(`You are not an admin`)
return this.createGroup(dto, adminId)
}
/**
* Create groups manually without using API Key.
* @param dtos External parameters used to create new groups.
* @param adminId Admin id.
* @returns Created groups.
*/
async createGroupsManually(
dtos: Array<CreateGroupDto>,
adminId: string
): Promise<Array<Group>> {
const admin = await this.adminsService.findOne({ id: adminId })
if (!admin) throw new BadRequestException(`You are not an admin`)
const newGroups: Array<Group> = []
for await (const dto of dtos) {
const group = await this.createGroup(dto, adminId)
@@ -160,27 +196,45 @@ export class GroupsService {
*/
async removeGroupWithAPIKey(
groupId: string,
adminId: string,
apiKey: string
): Promise<void> {
return this.removeGroupsWithAPIKey([groupId], adminId, apiKey)
return this.removeGroupsWithAPIKey([groupId], apiKey)
}
/**
* Remove groups using API Key.
* @param groupsIds Groups identifiers.
* @param adminId Admin id.
* @param apiKey the api key.
*/
async removeGroupsWithAPIKey(
groupsIds: Array<string>,
adminId: string,
apiKey: string
): Promise<void> {
const admin = await this.adminsService.findOne({ id: adminId })
const admin = await getAndCheckAdmin(this.adminsService, apiKey)
await adminApiKeyCheck(admin, apiKey)
for await (const groupId of groupsIds) {
await this.removeGroup(groupId, admin.id)
}
}
/**
* Remove a group manually without using API Key.
* @param groupId Group id.
* @param adminId Admin id.
*/
async removeGroupManually(groupId: string, adminId: string): Promise<void> {
return this.removeGroup(groupId, adminId)
}
/**
* Remove groups manually without using API Key.
* @param groupsIds Groups identifiers.
* @param adminId Admin id.
*/
async removeGroupsManually(
groupsIds: Array<string>,
adminId: string
): Promise<void> {
for await (const groupId of groupsIds) {
await this.removeGroup(groupId, adminId)
}
@@ -194,6 +248,11 @@ export class GroupsService {
async removeGroup(groupId: string, adminId: string): Promise<void> {
const group = await this.getGroup(groupId)
if (!group)
throw new BadRequestException(
`The group '${groupId}' does not exists`
)
if (group.adminId !== adminId) {
throw new UnauthorizedException(
`You are not the admin of the group '${groupId}'`
@@ -211,23 +270,82 @@ export class GroupsService {
* Update a group using API Key.
* @param groupId Group id.
* @param dto External parameters used to update a group.
* @param adminId Group admin id.
* @param apiKey the API Key.
* @returns Updated group.
*/
async updateGroupWithApiKey(
groupId: string,
dto: UpdateGroupDto,
adminId: string,
apiKey: string
): Promise<Group> {
const admin = await this.adminsService.findOne({ id: adminId })
const admin = await getAndCheckAdmin(this.adminsService, apiKey)
await adminApiKeyCheck(admin, apiKey, groupId)
return this.updateGroup(groupId, dto, admin.id)
}
/**
* Update groups using API Key.
* @param groupsIds Groups ids.
* @param dtos External parameters used to update groups.
* @param apiKey the API Key.
* @returns Updated group.
*/
async updateGroupsWithApiKey(
groupsIds: Array<string>,
dtos: Array<UpdateGroupDto>,
apiKey: string
): Promise<Array<Group>> {
const updatedGroups: Array<Group> = []
const admin = await getAndCheckAdmin(this.adminsService, apiKey)
for await (const [index, groupId] of groupsIds.entries()) {
const dto = dtos[index]
const group = await this.updateGroup(groupId, dto, admin.id)
updatedGroups.push(group)
}
return updatedGroups
}
/**
* Update a group manually without using API Key.
* @param groupId Group id.
* @param dto External parameters used to update a group.
* @param adminId Group admin id.
* @returns Updated group.
*/
async updateGroupManually(
groupId: string,
dto: UpdateGroupDto,
adminId: string
): Promise<Group> {
return this.updateGroup(groupId, dto, adminId)
}
/**
* Update groups manually without using API Key.
* @param groupsIds Groups ids.
* @param dtos External parameters used to update groups.
* @param adminId Group admin id.
* @returns Updated groups.
*/
async updateGroupsManually(
groupsIds: Array<string>,
dtos: Array<UpdateGroupDto>,
adminId: string
): Promise<Array<Group>> {
const updatedGroups: Array<Group> = []
for await (const [index, groupId] of groupsIds.entries()) {
const dto = dtos[index]
const group = await this.updateGroup(groupId, dto, adminId)
updatedGroups.push(group)
}
return updatedGroups
}
/**
* Updates some parameters of the group.
* @param groupId Group id.
@@ -247,6 +365,11 @@ export class GroupsService {
): Promise<Group> {
const group = await this.getGroup(groupId)
if (!group)
throw new BadRequestException(
`The group '${groupId}' does not exists`
)
if (group.adminId !== adminId) {
throw new UnauthorizedException(
`You are not the admin of the group '${groupId}'`

View File

@@ -1,6 +1,47 @@
import { mapGroupToResponseDTO } from "./groups.utils"
import { ScheduleModule } from "@nestjs/schedule"
import { Test } from "@nestjs/testing"
import { TypeOrmModule } from "@nestjs/typeorm"
import { ApiKeyActions } from "@bandada/utils"
import { Invite } from "../invites/entities/invite.entity"
import { InvitesService } from "../invites/invites.service"
import { OAuthAccount } from "../credentials/entities/credentials-account.entity"
import { Group } from "./entities/group.entity"
import { Member } from "./entities/member.entity"
import { GroupsService } from "./groups.service"
import { AdminsService } from "../admins/admins.service"
import { AdminsModule } from "../admins/admins.module"
import { Admin } from "../admins/entities/admin.entity"
import { mapGroupToResponseDTO, getAndCheckAdmin } from "./groups.utils"
describe("Groups utils", () => {
let groupsService: GroupsService
let adminsService: AdminsService
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: "sqlite",
database: ":memory:",
dropSchema: true,
entities: [Group, Invite, Member, OAuthAccount, Admin],
synchronize: true
})
}),
TypeOrmModule.forFeature([Group, Invite, Member, Admin]),
ScheduleModule.forRoot(),
AdminsModule
],
providers: [GroupsService, InvitesService, AdminsService]
}).compile()
groupsService = await module.resolve(GroupsService)
adminsService = await module.resolve(AdminsService)
await groupsService.initialize()
})
describe("# mapGroupToResponseDTO", () => {
it("Should map the group data", async () => {
const group = {
@@ -29,4 +70,68 @@ describe("Groups utils", () => {
expect(fingerprint).toBe("12345")
})
})
describe("# getAndCheckAdmin", () => {
const groupId = "1"
let apiKey = ""
let admin: Admin = {} as any
beforeAll(async () => {
admin = await adminsService.create({
id: groupId,
address: "0x00"
})
apiKey = await adminsService.updateApiKey(
admin.id,
ApiKeyActions.Generate
)
admin = await adminsService.findOne({ id: admin.id })
})
it("Should successfully check and return the admin", async () => {
const checkedAdmin = await getAndCheckAdmin(adminsService, apiKey)
expect(checkedAdmin.id).toBe(admin.id)
expect(checkedAdmin.address).toBe(admin.address)
expect(checkedAdmin.apiKey).toBe(admin.apiKey)
expect(checkedAdmin.apiEnabled).toBe(admin.apiEnabled)
expect(checkedAdmin.username).toBe(admin.username)
})
it("Should throw if the API Key or admin is invalid", async () => {
const fun = getAndCheckAdmin(adminsService, "wrong")
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the groups`
)
})
it("Should throw if the API Key or admin is invalid (w/ group identifier)", async () => {
const fun = getAndCheckAdmin(adminsService, "wrong", groupId)
await expect(fun).rejects.toThrow(
`Invalid API key or invalid admin for the group '${groupId}'`
)
})
it("Should throw if the API Key is invalid or API access is disabled", async () => {
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)
const fun = getAndCheckAdmin(adminsService, apiKey)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
})
it("Should throw if the API Key is invalid or API access is disabled (w/ group identifier)", async () => {
const fun = getAndCheckAdmin(adminsService, apiKey, groupId)
await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
})
})
})

View File

@@ -1,6 +1,7 @@
import { BadRequestException } from "@nestjs/common"
import { Group } from "./entities/group.entity"
import { Admin } from "../admins/entities/admin.entity"
import { AdminsService } from "../admins/admins.service"
export function mapGroupToResponseDTO(group: Group, fingerprint: string = "") {
const dto = {
@@ -19,16 +20,18 @@ export function mapGroupToResponseDTO(group: Group, fingerprint: string = "") {
return dto
}
export async function adminApiKeyCheck(
admin: Admin,
export async function getAndCheckAdmin(
adminService: AdminsService,
apiKey: string,
groupId?: string
) {
if (!admin) {
): Promise<Admin> {
const admin = await adminService.findOne({ apiKey })
if (!apiKey || !admin) {
throw new BadRequestException(
groupId
? `Invalid admin for the group '${groupId}'`
: `Invalid admin for the groups`
? `Invalid API key or invalid admin for the group '${groupId}'`
: `Invalid API key or invalid admin for the groups`
)
}
@@ -37,4 +40,6 @@ export async function adminApiKeyCheck(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
}
return admin
}