Merge pull request #1794 from hedgedoc/addSpecialGroups

This commit is contained in:
Yannick Bungers 2021-11-08 21:11:07 +01:00 committed by GitHub
commit bb7561b9ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 139 additions and 17 deletions

View file

@ -9,7 +9,7 @@ import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import appConfigMock from '../config/mock/app.config.mock';
import { NotInDBError } from '../errors/errors';
import { AlreadyInDBError, NotInDBError } from '../errors/errors';
import { LoggerModule } from '../logger/logger.module';
import { Group } from './group.entity';
import { GroupsService } from './groups.service';
@ -46,6 +46,33 @@ describe('GroupsService', () => {
expect(service).toBeDefined();
});
describe('createGroup', () => {
const groupName = 'testGroup';
const displayname = 'Group Test';
beforeEach(() => {
jest
.spyOn(groupRepo, 'save')
.mockImplementationOnce(async (group: Group): Promise<Group> => group);
});
it('successfully creates a group', async () => {
const user = await service.createGroup(groupName, displayname);
expect(user.name).toEqual(groupName);
expect(user.displayName).toEqual(displayname);
});
it('fails if group name is already taken', async () => {
// add additional mock implementation for failure
jest.spyOn(groupRepo, 'save').mockImplementationOnce(() => {
throw new Error();
});
// create first group with group name
await service.createGroup(groupName, displayname);
// attempt to create second group with group name
await expect(service.createGroup(groupName, displayname)).rejects.toThrow(
AlreadyInDBError,
);
});
});
describe('getGroupByName', () => {
it('works', async () => {
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);

View file

@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { NotInDBError } from '../errors/errors';
import { AlreadyInDBError, NotInDBError } from '../errors/errors';
import { ConsoleLoggerService } from '../logger/console-logger.service';
import { GroupInfoDto } from './group-info.dto';
import { Group } from './group.entity';
@ -21,6 +21,35 @@ export class GroupsService {
this.logger.setContext(GroupsService.name);
}
/**
* @async
* Create a new group with a given name and displayName
* @param name - the group name the new group shall have
* @param displayName - the display name the new group shall have
* @param special - if the group is special or not
* @return {Group} the group
* @throws {AlreadyInDBError} the group name is already taken.
*/
async createGroup(
name: string,
displayName: string,
special = false,
): Promise<Group> {
const group = Group.create(name, displayName);
group.special = special;
try {
return await this.groupRepository.save(group);
} catch {
this.logger.debug(
`A group with the name '${name}' already exists.`,
'createGroup',
);
throw new AlreadyInDBError(
`A group with the name '${name}' already exists.`,
);
}
}
/**
* @async
* Get a group by their name.

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export enum SpecialGroup {
LOGGED_IN = '_LOGGED_IN',
EVERYONE = '_EVERYONE',
}

View file

@ -14,6 +14,7 @@ import { AuthConfig } from './config/auth.config';
import { MediaConfig } from './config/media.config';
import { ConsoleLoggerService } from './logger/console-logger.service';
import { BackendType } from './media/backends/backend-type.enum';
import { setupSpecialGroups } from './utils/createSpecialGroups';
import { setupFrontendProxy } from './utils/frontend-integration';
import { setupSessionMiddleware } from './utils/session';
import { setupValidationPipe } from './utils/setup-pipes';
@ -51,6 +52,8 @@ async function bootstrap(): Promise<void> {
setupFrontendProxy(app, logger);
}
await setupSpecialGroups(app);
setupSessionMiddleware(app, authConfig);
app.enableCors({

View file

@ -11,6 +11,7 @@ import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
import appConfigMock from '../config/mock/app.config.mock';
import { Group } from '../groups/group.entity';
import { SpecialGroup } from '../groups/groups.special';
import { Identity } from '../identity/identity.entity';
import { LoggerModule } from '../logger/logger.module';
import { Alias } from '../notes/alias.entity';
@ -134,7 +135,7 @@ describe('PermissionsService', () => {
note7.userPermissions.push(noteUserPermission2);
const everybody = {} as Group;
everybody.name = 'everybody';
everybody.name = SpecialGroup.EVERYONE;
everybody.special = true;
const noteEverybodyRead = createNote(user1);
@ -261,13 +262,19 @@ describe('PermissionsService', () => {
function createGroups(): { [id: string]: Group } {
const result: { [id: string]: Group } = {};
const everybody: Group = Group.create('everybody', 'Everybody');
const everybody: Group = Group.create(
SpecialGroup.EVERYONE,
SpecialGroup.EVERYONE,
);
everybody.special = true;
result['everybody'] = everybody;
result[SpecialGroup.EVERYONE] = everybody;
const loggedIn = Group.create('loggedIn', 'loggedIn');
const loggedIn = Group.create(
SpecialGroup.LOGGED_IN,
SpecialGroup.LOGGED_IN,
);
loggedIn.special = true;
result['loggedIn'] = loggedIn;
result[SpecialGroup.LOGGED_IN] = loggedIn;
const user1group = Group.create('user1group', 'user1group');
user1group.members = [user1];
@ -304,11 +311,23 @@ describe('PermissionsService', () => {
return NoteGroupPermission.create(group, write);
}
const everybodyRead = createNoteGroupPermission(groups['everybody'], false);
const everybodyWrite = createNoteGroupPermission(groups['everybody'], true);
const everybodyRead = createNoteGroupPermission(
groups[SpecialGroup.EVERYONE],
false,
);
const everybodyWrite = createNoteGroupPermission(
groups[SpecialGroup.EVERYONE],
true,
);
const loggedInRead = createNoteGroupPermission(groups['loggedIn'], false);
const loggedInWrite = createNoteGroupPermission(groups['loggedIn'], true);
const loggedInRead = createNoteGroupPermission(
groups[SpecialGroup.LOGGED_IN],
false,
);
const loggedInWrite = createNoteGroupPermission(
groups[SpecialGroup.LOGGED_IN],
true,
);
const user1groupRead = createNoteGroupPermission(
groups['user1group'],

View file

@ -5,6 +5,7 @@
*/
import { Injectable } from '@nestjs/common';
import { SpecialGroup } from '../groups/groups.special';
import { Note } from '../notes/note.entity';
import { User } from '../users/user.entity';
@ -102,16 +103,14 @@ export class PermissionsService {
if (groupPermission.canEdit || !wantEdit) {
// Handle special groups
if (groupPermission.group.special) {
if (groupPermission.group.name == 'loggedIn') {
// TODO: Name of group for logged in users
if (groupPermission.group.name == SpecialGroup.LOGGED_IN) {
return true;
}
if (
groupPermission.group.name == 'everybody' &&
groupPermission.group.name == SpecialGroup.EVERYONE &&
(groupPermission.canEdit || !wantEdit) &&
guestsAllowed
) {
// TODO: Name of group in which everybody even guests can edit
return true;
}
} else {

View file

@ -52,12 +52,13 @@ describe('UsersService', () => {
.spyOn(userRepo, 'save')
.mockImplementationOnce(async (user: User): Promise<User> => user);
});
it('works', async () => {
it('successfully creates a user', async () => {
const user = await service.createUser(username, displayname);
expect(user.username).toEqual(username);
expect(user.displayName).toEqual(displayname);
});
it('fails if username is already taken', async () => {
// add additional mock implementation for failure
jest.spyOn(userRepo, 'save').mockImplementationOnce(() => {
throw new Error();
});

View file

@ -26,7 +26,7 @@ export class UsersService {
* @async
* Create a new user with a given username and displayName
* @param username - the username the new user shall have
* @param displayName - the display the new user shall have
* @param displayName - the display name the new user shall have
* @return {User} the user
* @throws {AlreadyInDBError} the username is already taken.
*/

View file

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { NestExpressApplication } from '@nestjs/platform-express';
import { AlreadyInDBError } from '../errors/errors';
import { GroupsService } from '../groups/groups.service';
import { SpecialGroup } from '../groups/groups.special';
export async function setupSpecialGroups(
app: NestExpressApplication,
): Promise<void> {
const groupService = app.get<GroupsService>(GroupsService);
try {
await groupService.createGroup(
SpecialGroup.EVERYONE,
SpecialGroup.EVERYONE,
true,
);
await groupService.createGroup(
SpecialGroup.LOGGED_IN,
SpecialGroup.LOGGED_IN,
true,
);
} catch (e) {
if (e instanceof AlreadyInDBError) {
// It's no problem if the special groups already exist
return;
}
throw e;
}
}