mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-28 14:04:43 -04:00
wip: chore(backend): update and unify ESDoc and parameter names
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
327cc1f925
commit
d68873de85
32 changed files with 289 additions and 310 deletions
|
@ -7,8 +7,6 @@ import { AliasDto } from '@hedgedoc/commons';
|
|||
import {
|
||||
Alias,
|
||||
FieldNameAlias,
|
||||
FieldNameNote,
|
||||
Note,
|
||||
TableAlias,
|
||||
TypeInsertAlias,
|
||||
} from '@hedgedoc/database';
|
||||
|
@ -64,8 +62,8 @@ export class AliasService {
|
|||
* @throws {ForbiddenIdError} The requested alias is forbidden
|
||||
*/
|
||||
async addAlias(
|
||||
noteId: Note[FieldNameNote.id],
|
||||
alias: Alias[FieldNameAlias.alias],
|
||||
noteId: number,
|
||||
alias: string,
|
||||
transaction?: Knex,
|
||||
): Promise<void> {
|
||||
const dbActor: Knex = transaction ? transaction : this.knex;
|
||||
|
@ -93,10 +91,7 @@ export class AliasService {
|
|||
* @throws {NotInDBError} when the alias is not assigned to this note
|
||||
* @throws {GenericDBError} when the database has an inconsistent state
|
||||
*/
|
||||
async makeAliasPrimary(
|
||||
noteId: Note[FieldNameNote.id],
|
||||
alias: Alias[FieldNameAlias.alias],
|
||||
): Promise<void> {
|
||||
async makeAliasPrimary(noteId: number, alias: string): Promise<void> {
|
||||
await this.knex.transaction(async (transaction) => {
|
||||
// First, set all existing aliases to not primary
|
||||
const numberOfUpdatedEntries = await transaction(TableAlias)
|
||||
|
@ -106,7 +101,7 @@ export class AliasService {
|
|||
.where(FieldNameAlias.noteId, noteId);
|
||||
if (numberOfUpdatedEntries === 0) {
|
||||
throw new GenericDBError(
|
||||
`The note does not exist or has no primary alias. This should never happen`,
|
||||
'The note does not exist or has no primary alias. This should never happen',
|
||||
this.logger.getContext(),
|
||||
'makeAliasPrimary',
|
||||
);
|
||||
|
@ -130,7 +125,7 @@ export class AliasService {
|
|||
|
||||
/**
|
||||
* Removes the specified alias from the note
|
||||
* This method only does not require the noteId since it can be obtained from the alias prior to deletion
|
||||
* This method only requires the alias since it can obtain the noteId from the alias prior to deletion
|
||||
*
|
||||
* @param alias The alias to remove from the note
|
||||
* @throws {ForbiddenIdError} The requested alias is forbidden
|
||||
|
@ -172,13 +167,14 @@ export class AliasService {
|
|||
* Gets the primary alias of the note specified by the noteId
|
||||
*
|
||||
* @param noteId The id of the note to get the primary alias of
|
||||
* @param transaction The optional transaction to access the db
|
||||
* @returns The primary alias of the note
|
||||
* @throws {NotInDBError} The note has no primary alias which should mean that the note does not exist
|
||||
*/
|
||||
async getPrimaryAliasByNoteId(
|
||||
noteId: number,
|
||||
transaction?: Knex,
|
||||
): Promise<Alias[FieldNameAlias.alias]> {
|
||||
): Promise<string> {
|
||||
const dbActor = transaction ?? this.knex;
|
||||
const primaryAlias = await dbActor(TableAlias)
|
||||
.select(FieldNameAlias.alias)
|
||||
|
@ -187,7 +183,7 @@ export class AliasService {
|
|||
.first();
|
||||
if (primaryAlias === undefined) {
|
||||
throw new NotInDBError(
|
||||
`The noteId '${noteId}' has no primary alias.`,
|
||||
'The note does not exist or has no primary alias. This should never happen',
|
||||
this.logger.getContext(),
|
||||
'getPrimaryAliasByNoteId',
|
||||
);
|
||||
|
@ -198,9 +194,10 @@ export class AliasService {
|
|||
/**
|
||||
* Gets all aliases of the note specified by the noteId
|
||||
*
|
||||
* @param noteId The id of the note to get the primary alias of
|
||||
* @returns The primary alias of the note
|
||||
* @throws {NotInDBError} The note has no primary alias which should mean that the note does not exist
|
||||
* @param noteId The id of the note to get the list of aliases for
|
||||
* @param transaction The optional transaction to access the db
|
||||
* @returns The list of aliases for the note
|
||||
* @throws {NotInDBError} The note with the specified id does not exist
|
||||
*/
|
||||
async getAllAliases(
|
||||
noteId: number,
|
||||
|
@ -212,7 +209,7 @@ export class AliasService {
|
|||
.where(FieldNameAlias.noteId, noteId);
|
||||
if (aliases.length === 0) {
|
||||
throw new NotInDBError(
|
||||
`The noteId '${noteId}' has no aliases. This should never happen.`,
|
||||
'The note does not exist or has no aliases. This should never happen',
|
||||
this.logger.getContext(),
|
||||
'getAllAliases',
|
||||
);
|
||||
|
@ -230,7 +227,7 @@ export class AliasService {
|
|||
* @throws {AlreadyInDBError} The requested alias already exists
|
||||
*/
|
||||
async ensureAliasIsAvailable(
|
||||
alias: Alias[FieldNameAlias.alias],
|
||||
alias: string,
|
||||
transaction?: Knex,
|
||||
): Promise<void> {
|
||||
if (this.isAliasForbidden(alias)) {
|
||||
|
@ -243,7 +240,7 @@ export class AliasService {
|
|||
const isUsed = await this.isAliasUsed(alias, transaction);
|
||||
if (isUsed) {
|
||||
throw new AlreadyInDBError(
|
||||
`A note with the id or alias '${alias}' already exists.`,
|
||||
`A note with the alias '${alias}' already exists.`,
|
||||
this.logger.getContext(),
|
||||
'ensureAliasIsAvailable',
|
||||
);
|
||||
|
@ -254,17 +251,10 @@ export class AliasService {
|
|||
* Checks if the provided alias is forbidden by configuration
|
||||
*
|
||||
* @param alias The alias to check
|
||||
* @return {boolean} true if the alias is forbidden, false otherwise
|
||||
* @returns true if the alias is forbidden, false otherwise
|
||||
*/
|
||||
isAliasForbidden(alias: Alias[FieldNameAlias.alias]): boolean {
|
||||
const forbidden = this.noteConfig.forbiddenNoteIds.includes(alias);
|
||||
if (forbidden) {
|
||||
this.logger.warn(
|
||||
`A note with the alias '${alias}' is forbidden by the administrator.`,
|
||||
'isAliasForbidden',
|
||||
);
|
||||
}
|
||||
return forbidden;
|
||||
isAliasForbidden(alias: string): boolean {
|
||||
return this.noteConfig.forbiddenNoteIds.includes(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,19 +262,16 @@ export class AliasService {
|
|||
*
|
||||
* @param alias The alias to check
|
||||
* @param transaction The optional transaction to access the db
|
||||
* @return {boolean} true if the id or alias is already used, false otherwise
|
||||
* @returns true if the alias is already used, false otherwise
|
||||
*/
|
||||
async isAliasUsed(
|
||||
alias: Alias[FieldNameAlias.alias],
|
||||
transaction?: Knex,
|
||||
): Promise<boolean> {
|
||||
async isAliasUsed(alias: string, transaction?: Knex): Promise<boolean> {
|
||||
const dbActor = transaction ? transaction : this.knex;
|
||||
const result = await dbActor(TableAlias)
|
||||
.select(FieldNameAlias.alias)
|
||||
.where(FieldNameAlias.alias, alias);
|
||||
if (result.length === 1) {
|
||||
this.logger.log(
|
||||
`A note with the id or alias '${alias}' already exists.`,
|
||||
`A note with the alias '${alias}' already exists.`,
|
||||
'isAliasUsed',
|
||||
);
|
||||
return true;
|
||||
|
@ -293,11 +280,11 @@ export class AliasService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Build the AliasDto from a note.
|
||||
* Returns alias information in the AliasDto format
|
||||
*
|
||||
* @param alias The alias to use
|
||||
* @param isPrimaryAlias If the alias is the primary alias.
|
||||
* @throws {NotInDBError} The specified alias does not exist
|
||||
* @return {AliasDto} The built AliasDto
|
||||
* @param isPrimaryAlias Whether the alias is the primary alias.
|
||||
* @returns The built AliasDto
|
||||
*/
|
||||
toAliasDto(alias: string, isPrimaryAlias: boolean): AliasDto {
|
||||
return {
|
||||
|
|
|
@ -4,12 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiTokenDto, ApiTokenWithSecretDto } from '@hedgedoc/commons';
|
||||
import {
|
||||
ApiToken,
|
||||
FieldNameApiToken,
|
||||
TableApiToken,
|
||||
TypeInsertApiToken,
|
||||
} from '@hedgedoc/database';
|
||||
import { ApiToken, FieldNameApiToken, TableApiToken } from '@hedgedoc/database';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, Timeout } from '@nestjs/schedule';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
@ -28,7 +23,8 @@ import {
|
|||
hashApiToken,
|
||||
} from '../utils/password';
|
||||
|
||||
export const AUTH_TOKEN_PREFIX = 'hd2';
|
||||
const AUTH_TOKEN_PREFIX = 'hd2';
|
||||
const MESSAGE_TOKEN_INVALID = 'API token is invalid, expired or not found';
|
||||
|
||||
@Injectable()
|
||||
export class ApiTokenService {
|
||||
|
@ -46,21 +42,22 @@ export class ApiTokenService {
|
|||
* The usage of this token is tracked in the database
|
||||
*
|
||||
* @param tokenString The token string to validate and parse
|
||||
* @return The userId associated with the token
|
||||
* @throws TokenNotValidError if the token is not valid
|
||||
* @returns The userId associated with the token
|
||||
* @throws {TokenNotValidError} if the token is not valid
|
||||
*/
|
||||
async getUserIdForToken(tokenString: string): Promise<number> {
|
||||
const [prefix, keyId, secret, ...rest] = tokenString.split('.');
|
||||
if (!keyId || !secret || prefix !== AUTH_TOKEN_PREFIX || rest.length > 0) {
|
||||
// We always expect 86 characters for the secret and 11 characters for the keyId
|
||||
// as they are generated with 64 bytes and 8 bytes respectively and then converted to a base64url string
|
||||
if (
|
||||
keyId.length !== 11 ||
|
||||
!secret ||
|
||||
secret.length !== 86 ||
|
||||
prefix !== AUTH_TOKEN_PREFIX ||
|
||||
rest.length > 0
|
||||
) {
|
||||
throw new TokenNotValidError('Invalid API token format');
|
||||
}
|
||||
if (secret.length != 86) {
|
||||
// We always expect 86 characters, as the secret is generated with 64 bytes
|
||||
// and then converted to a base64url string
|
||||
throw new TokenNotValidError(
|
||||
`API token '${tokenString}' has incorrect length`,
|
||||
);
|
||||
}
|
||||
return await this.knex.transaction(async (transaction) => {
|
||||
const token = await transaction(TableApiToken)
|
||||
.select(
|
||||
|
@ -71,7 +68,7 @@ export class ApiTokenService {
|
|||
.where(FieldNameApiToken.id, keyId)
|
||||
.first();
|
||||
if (token === undefined) {
|
||||
throw new TokenNotValidError('Token not found');
|
||||
throw new TokenNotValidError(MESSAGE_TOKEN_INVALID);
|
||||
}
|
||||
|
||||
const tokenHash = token[FieldNameApiToken.secretHash];
|
||||
|
@ -88,16 +85,20 @@ export class ApiTokenService {
|
|||
|
||||
/**
|
||||
* Creates a new API token for the given user
|
||||
* We limit the number of tokens to 200 per user to avoid users losing track over their tokens.
|
||||
* There is no technical limit to this.
|
||||
*
|
||||
* The returned secret is stored hashed in the database and therefore cannot be retrieved again.
|
||||
*
|
||||
* @param userId The id of the user to create the token for
|
||||
* @param tokenLabel The label of the token
|
||||
* @param label The label of the token
|
||||
* @param userDefinedValidUntil Maximum date until the token is valid, will be truncated to 2 years
|
||||
* @throws TooManyTokensError if the user already has 200 tokens
|
||||
* @returns The created token together with the secret
|
||||
* @throws {TooManyTokensError} if the user already has 200 tokens
|
||||
*/
|
||||
async createToken(
|
||||
userId: number,
|
||||
tokenLabel: string,
|
||||
label: string,
|
||||
userDefinedValidUntil?: Date,
|
||||
): Promise<ApiTokenWithSecretDto> {
|
||||
return await this.knex.transaction(async (transaction) => {
|
||||
|
@ -105,16 +106,15 @@ export class ApiTokenService {
|
|||
.select(FieldNameApiToken.id)
|
||||
.where(FieldNameApiToken.userId, userId);
|
||||
if (existingTokensForUser.length >= 200) {
|
||||
// This is a very high ceiling unlikely to hinder legitimate usage,
|
||||
// but should prevent possible attack vectors
|
||||
throw new TooManyTokensError(
|
||||
`User '${userId}' has already 200 API tokens and can't have more`,
|
||||
'There is a maximum of 200 API tokens per user',
|
||||
);
|
||||
}
|
||||
|
||||
const secret = bufferToBase64Url(randomBytes(64));
|
||||
const keyId = bufferToBase64Url(randomBytes(8));
|
||||
const accessTokenHash = hashApiToken(secret);
|
||||
const secretHash = hashApiToken(secret);
|
||||
const fullToken = `${AUTH_TOKEN_PREFIX}.${keyId}.${secret}`;
|
||||
// Tokens can only be valid for a maximum of 2 years
|
||||
const maximumTokenValidity = new Date();
|
||||
maximumTokenValidity.setTime(
|
||||
|
@ -125,37 +125,34 @@ export class ApiTokenService {
|
|||
const validUntil = isTokenLimitedToMaximumValidity
|
||||
? maximumTokenValidity
|
||||
: userDefinedValidUntil;
|
||||
const token: TypeInsertApiToken = {
|
||||
const createdAt = new Date();
|
||||
await this.knex(TableApiToken).insert({
|
||||
[FieldNameApiToken.id]: keyId,
|
||||
[FieldNameApiToken.label]: tokenLabel,
|
||||
[FieldNameApiToken.label]: label,
|
||||
[FieldNameApiToken.userId]: userId,
|
||||
[FieldNameApiToken.secretHash]: accessTokenHash,
|
||||
[FieldNameApiToken.secretHash]: secretHash,
|
||||
[FieldNameApiToken.validUntil]: validUntil,
|
||||
[FieldNameApiToken.createdAt]: new Date(),
|
||||
[FieldNameApiToken.createdAt]: createdAt,
|
||||
});
|
||||
return {
|
||||
label,
|
||||
keyId,
|
||||
createdAt: createdAt.toISOString(),
|
||||
validUntil: validUntil.toISOString(),
|
||||
lastUsedAt: null,
|
||||
secret: fullToken,
|
||||
};
|
||||
await this.knex(TableApiToken).insert(token);
|
||||
return this.toAuthTokenWithSecretDto(
|
||||
{
|
||||
...token,
|
||||
[FieldNameApiToken.validUntil]:
|
||||
token[FieldNameApiToken.validUntil].toISOString(),
|
||||
[FieldNameApiToken.createdAt]:
|
||||
token[FieldNameApiToken.createdAt].toISOString(),
|
||||
[FieldNameApiToken.lastUsedAt]: null,
|
||||
},
|
||||
secret,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given token secret is valid for the given token
|
||||
* Ensures that a token is valid by evaluating the expiry date as well as comparing secret and stored hash
|
||||
* This method does not return any value but throws an error if the token is not valid
|
||||
*
|
||||
* @param secret The secret to compare against the hash from the database
|
||||
* @param tokenHash The hash from the database
|
||||
* @param validUntil Expiry of the API token
|
||||
* @throws TokenNotValidError if the token is invalid
|
||||
* @throws {TokenNotValidError} if the token is invalid
|
||||
*/
|
||||
ensureTokenIsValid(
|
||||
secret: string,
|
||||
|
@ -164,14 +161,12 @@ export class ApiTokenService {
|
|||
): void {
|
||||
// First, verify token expiry is not in the past (cheap operation)
|
||||
if (validUntil.getTime() < new Date().getTime()) {
|
||||
throw new TokenNotValidError(
|
||||
`Auth token is not valid since ${validUntil.toISOString()}`,
|
||||
);
|
||||
throw new TokenNotValidError(MESSAGE_TOKEN_INVALID);
|
||||
}
|
||||
|
||||
// Second, verify the secret (costly operation)
|
||||
if (!checkTokenEquality(secret, tokenHash)) {
|
||||
throw new TokenNotValidError(`Secret does not match token hash`);
|
||||
throw new TokenNotValidError(MESSAGE_TOKEN_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,7 +174,7 @@ export class ApiTokenService {
|
|||
* Returns all tokens of a user
|
||||
*
|
||||
* @param userId The id of the user to get the tokens for
|
||||
* @return The tokens of the user
|
||||
* @returns A list of the user's tokens as ApiToken objects
|
||||
*/
|
||||
getTokensOfUserById(userId: number): Promise<ApiToken[]> {
|
||||
return this.knex(TableApiToken)
|
||||
|
@ -205,10 +200,10 @@ export class ApiTokenService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts an ApiToken to an ApiTokenDto
|
||||
* Formats an ApiToken object from the database to an ApiTokenDto
|
||||
*
|
||||
* @param apiToken The token to convert
|
||||
* @return The converted token
|
||||
* @param apiToken The token object to convert
|
||||
* @returns The built ApiTokenDto
|
||||
*/
|
||||
toAuthTokenDto(apiToken: ApiToken): ApiTokenDto {
|
||||
return {
|
||||
|
@ -224,25 +219,6 @@ export class ApiTokenService {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an ApiToken to an ApiTokenWithSecretDto
|
||||
*
|
||||
* @param apiToken The token to convert
|
||||
* @param secret The secret of the token
|
||||
* @return The converted token
|
||||
*/
|
||||
toAuthTokenWithSecretDto(
|
||||
apiToken: ApiToken,
|
||||
secret: string,
|
||||
): ApiTokenWithSecretDto {
|
||||
const tokenDto = this.toAuthTokenDto(apiToken);
|
||||
const fullToken = `${AUTH_TOKEN_PREFIX}.${tokenDto.keyId}.${secret}`;
|
||||
return {
|
||||
...tokenDto,
|
||||
secret: fullToken,
|
||||
};
|
||||
}
|
||||
|
||||
// Deletes all invalid tokens every sunday on 3:00 AM
|
||||
@Cron('0 0 3 * * 0')
|
||||
async handleCron(): Promise<void> {
|
||||
|
@ -264,7 +240,7 @@ export class ApiTokenService {
|
|||
.where(FieldNameApiToken.validUntil, '<', new Date())
|
||||
.delete();
|
||||
this.logger.log(
|
||||
`${numberOfDeletedTokens} invalid AuthTokens were purged from the DB.`,
|
||||
`${numberOfDeletedTokens} expired API tokens were purged from the DB`,
|
||||
'removeInvalidTokens',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ export class LocalController {
|
|||
@Body() registerDto: RegisterDto,
|
||||
): Promise<void> {
|
||||
await this.localIdentityService.checkPasswordStrength(registerDto.password);
|
||||
const userId = await this.localIdentityService.createLocalIdentity(
|
||||
const userId = await this.localIdentityService.createUserWithLocalIdentity(
|
||||
registerDto.username,
|
||||
registerDto.password,
|
||||
registerDto.displayName,
|
||||
|
|
|
@ -8,14 +8,7 @@ import {
|
|||
PendingUserConfirmationDto,
|
||||
PendingUserInfoDto,
|
||||
} from '@hedgedoc/commons';
|
||||
import {
|
||||
FieldNameIdentity,
|
||||
FieldNameUser,
|
||||
Identity,
|
||||
TableIdentity,
|
||||
TypeInsertIdentity,
|
||||
User,
|
||||
} from '@hedgedoc/database';
|
||||
import { FieldNameIdentity, Identity, TableIdentity } from '@hedgedoc/database';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { InjectConnection } from 'nest-knexjs';
|
||||
|
@ -44,7 +37,7 @@ export class IdentityService {
|
|||
* Determines if the identity should be updated
|
||||
*
|
||||
* @param authProviderIdentifier The identifier of the auth source
|
||||
* @return true if the authProviderIdentifier is the sync source, false otherwise
|
||||
* @returns true if the authProviderIdentifier is the sync source, false otherwise
|
||||
*/
|
||||
mayUpdateIdentity(authProviderIdentifier: string): boolean {
|
||||
return this.authConfig.common.syncSource === authProviderIdentifier;
|
||||
|
@ -53,10 +46,11 @@ export class IdentityService {
|
|||
/**
|
||||
* Retrieve an identity from the information received from an auth provider.
|
||||
*
|
||||
* @param authProviderUserId - the userId of the wanted identity
|
||||
* @param authProviderType - the providerType of the wanted identity
|
||||
* @param authProviderIdentifier - optional name of the provider if multiple exist
|
||||
* @return
|
||||
* @param authProviderUserId the userId of the wanted identity
|
||||
* @param authProviderType the providerType of the wanted identity
|
||||
* @param authProviderIdentifier optional name of the provider if multiple exist
|
||||
* @returns The found identity
|
||||
* @throws {NotInDBError} if the identity is not found
|
||||
*/
|
||||
async getIdentityFromUserIdAndProviderType(
|
||||
authProviderUserId: string,
|
||||
|
@ -80,13 +74,12 @@ export class IdentityService {
|
|||
/**
|
||||
* Creates a new generic identity.
|
||||
*
|
||||
* @param userId - the user the identity should be added to
|
||||
* @param authProviderType - the providerType of the identity
|
||||
* @param authProviderIdentifier - the providerIdentifier of the identity
|
||||
* @param authProviderUserId - the userId the identity should have
|
||||
* @param passwordHash - the password hash if the identiy uses that.
|
||||
* @param transaction - the database transaction to use if any
|
||||
* @return the new local identity
|
||||
* @param userId the user the identity should be added to
|
||||
* @param authProviderType the providerType of the identity
|
||||
* @param authProviderIdentifier the providerIdentifier of the identity
|
||||
* @param authProviderUserId the userId the identity should have
|
||||
* @param passwordHash the password hash if the identiy uses that.
|
||||
* @param transaction the database transaction to use if any
|
||||
*/
|
||||
async createIdentity(
|
||||
userId: number,
|
||||
|
@ -97,14 +90,13 @@ export class IdentityService {
|
|||
transaction?: Knex,
|
||||
): Promise<void> {
|
||||
const dbActor = transaction ?? this.knex;
|
||||
const identity: TypeInsertIdentity = {
|
||||
await dbActor(TableIdentity).insert({
|
||||
[FieldNameIdentity.userId]: userId,
|
||||
[FieldNameIdentity.providerType]: authProviderType,
|
||||
[FieldNameIdentity.providerIdentifier]: authProviderIdentifier,
|
||||
[FieldNameIdentity.providerUserId]: authProviderUserId,
|
||||
[FieldNameIdentity.passwordHash]: passwordHash ?? null,
|
||||
};
|
||||
await dbActor(TableIdentity).insert(identity);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +110,7 @@ export class IdentityService {
|
|||
* @param email The email address of the new user
|
||||
* @param photoUrl The URL to the new user's profile picture
|
||||
* @param passwordHash The optional password hash, only required for local identities
|
||||
* @return The id of the newly created user
|
||||
* @returns The id of the newly created user
|
||||
*/
|
||||
async createUserWithIdentity(
|
||||
authProviderType: AuthProviderType,
|
||||
|
@ -129,7 +121,7 @@ export class IdentityService {
|
|||
email: string | null,
|
||||
photoUrl: string | null,
|
||||
passwordHash?: string,
|
||||
): Promise<User[FieldNameUser.id]> {
|
||||
): Promise<number> {
|
||||
return await this.knex.transaction(async (transaction) => {
|
||||
const userId = await this.usersService.createUser(
|
||||
username,
|
||||
|
@ -151,14 +143,14 @@ export class IdentityService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a user with identity from pending user confirmation data.
|
||||
* Create a user with identity from pending user confirmation data
|
||||
*
|
||||
* @param sessionUserData The data we got from the authProvider itself
|
||||
* @param pendingUserConfirmationData The data the user entered while confirming their account
|
||||
* @param authProviderType The type of the auth provider
|
||||
* @param authProviderIdentifier The identifier of the auth provider
|
||||
* @param authProviderUserId The id of the user in the auth system
|
||||
* @return The id of the newly created user
|
||||
* @returns The id of the newly created user
|
||||
*/
|
||||
async createUserWithIdentityFromPendingUserConfirmation(
|
||||
sessionUserData: PendingUserInfoDto,
|
||||
|
@ -166,7 +158,7 @@ export class IdentityService {
|
|||
authProviderType: AuthProviderType,
|
||||
authProviderIdentifier: string,
|
||||
authProviderUserId: string,
|
||||
): Promise<User[FieldNameUser.id]> {
|
||||
): Promise<number> {
|
||||
const profileEditsAllowed = this.authConfig.common.allowProfileEdits;
|
||||
const chooseUsernameAllowed = this.authConfig.common.allowChooseUsername;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ const LDAP_ERROR_MAP: Record<string, string> = {
|
|||
'775': 'User account locked',
|
||||
default: 'Invalid username/password',
|
||||
/* eslint-enable @typescript-eslint/naming-convention */
|
||||
};
|
||||
} as const;
|
||||
|
||||
@Injectable()
|
||||
export class LdapService {
|
||||
|
@ -45,15 +45,14 @@ export class LdapService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Try to log in the user with the given credentials.
|
||||
* Tries to log in the user with the given credentials
|
||||
*
|
||||
* @param ldapConfig {LdapConfig} - the ldap config to use
|
||||
* @param username {string} - the username to log in with
|
||||
* @param password {string} - the password to log in with
|
||||
* @param ldapConfig The ldap config to use
|
||||
* @param username The user-provided username
|
||||
* @param password The user-provided password
|
||||
* @returns The user info of the user that logged in
|
||||
* @throws {UnauthorizedException} - the user has given us incorrect credentials
|
||||
* @throws {InternalServerErrorException} - if there are errors that we can't assign to wrong credentials
|
||||
* @private
|
||||
* @throws {UnauthorizedException} if the user has given us incorrect credentials
|
||||
* @throws {InternalServerErrorException} if there are errors that we can't assign to wrong credentials
|
||||
*/
|
||||
getUserInfoFromLdap(
|
||||
ldapConfig: LdapConfig,
|
||||
|
@ -119,11 +118,11 @@ export class LdapService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get and return the correct ldap config from the list of available configs.
|
||||
* @param {string} ldapIdentifier the identifier for the ldap config to be used
|
||||
* @returns {LdapConfig} - the ldap config with the given identifier
|
||||
* @throws {NotFoundException} - there is no ldap config with the given identifier
|
||||
* @private
|
||||
* Fetches the correct LDAP config from the list of available configs
|
||||
*
|
||||
* @param ldapIdentifier The identifier for the LDAP config to be used
|
||||
* @returns The LDAP config with the given identifier
|
||||
* @throws {NotFoundException} if there is no LDAP config with the given identifier
|
||||
*/
|
||||
getLdapConfig(ldapIdentifier: string): LdapConfig {
|
||||
const ldapConfig = this.authConfig.ldap.find(
|
||||
|
@ -131,18 +130,21 @@ export class LdapService {
|
|||
);
|
||||
if (!ldapConfig) {
|
||||
this.logger.warn(
|
||||
`The LDAP Config '${ldapIdentifier}' was requested, but doesn't exist`,
|
||||
`The LDAP config '${ldapIdentifier}' was requested, but doesn't exist`,
|
||||
);
|
||||
throw new NotFoundException(
|
||||
`There is no LDAP config '${ldapIdentifier}'`,
|
||||
);
|
||||
throw new NotFoundException(`There is no ldapConfig '${ldapIdentifier}'`);
|
||||
}
|
||||
return ldapConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method transforms the ldap error codes we receive into correct errors.
|
||||
* This method transforms the LDAP error codes we receive into correct errors.
|
||||
*
|
||||
* It's very much inspired by https://github.com/vesse/passport-ldapauth/blob/b58c60000a7cc62165b112274b80c654adf59fff/lib/passport-ldapauth/strategy.js#L261
|
||||
* @returns {HttpException} - the matching HTTP exception to throw to the client
|
||||
* @throws {UnauthorizedException} if error indicates that the user is not allowed to log in
|
||||
* @returns {HttpException} The matching HTTP exception to throw to the client
|
||||
* @throws {UnauthorizedException} if the error indicates that the user is not allowed to log in
|
||||
* @throws {InternalServerErrorException} in every other case
|
||||
*/
|
||||
private getLdapException(
|
||||
|
|
|
@ -4,13 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { AuthProviderType } from '@hedgedoc/commons';
|
||||
import {
|
||||
FieldNameIdentity,
|
||||
FieldNameUser,
|
||||
Identity,
|
||||
TableIdentity,
|
||||
User,
|
||||
} from '@hedgedoc/database';
|
||||
import { FieldNameIdentity, Identity, TableIdentity } from '@hedgedoc/database';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
OptionsGraph,
|
||||
|
@ -63,18 +57,18 @@ export class LocalService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new identity for internal auth
|
||||
* Creates a new user with an identity for internal auth
|
||||
*
|
||||
* @param username The username of the new identity
|
||||
* @param password The password the identity should have
|
||||
* @param displayName The display name of the new identity
|
||||
* @returns {Identity} the new local identity
|
||||
* @returns The id of the newly created user
|
||||
*/
|
||||
async createLocalIdentity(
|
||||
async createUserWithLocalIdentity(
|
||||
username: string,
|
||||
password: string,
|
||||
displayName: string,
|
||||
): Promise<User[FieldNameUser.id]> {
|
||||
): Promise<number> {
|
||||
const passwordHash = await hashPassword(password);
|
||||
return await this.identityService.createUserWithIdentity(
|
||||
AuthProviderType.LOCAL,
|
||||
|
@ -89,12 +83,12 @@ export class LocalService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Update the internal password of the specified the user
|
||||
* @param {User} userId - the user, which identity should be updated
|
||||
* @param {string} newPassword - the new password
|
||||
* @throws {NoLocalIdentityError} the specified user has no internal identity
|
||||
* @return {Identity} the changed identity
|
||||
* Updates the password hash for the local identity of the specified the user
|
||||
*
|
||||
* @param userId The user, whose local identity should be updated
|
||||
* @param newPassword The new password
|
||||
* @throws {NoLocalIdentityError} if the specified user has no local identity
|
||||
* @throws {PasswordTooWeakError} if the password is too weak
|
||||
*/
|
||||
async updateLocalPassword(
|
||||
userId: number,
|
||||
|
@ -112,12 +106,11 @@ export class LocalService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Checks if the user and password combination matches
|
||||
* @param {string} username - the user to use
|
||||
* @param {string} password - the password to use
|
||||
* @throws {InvalidCredentialsError} the password and user do not match
|
||||
* @throws {NoLocalIdentityError} the specified user has no internal identity
|
||||
* Checks if the user and password combination matches for the local identity and returns the local identity on success
|
||||
*
|
||||
* @param username The user to use
|
||||
* @param password The password to use
|
||||
* @throws {InvalidCredentialsError} if the credentials are invalid
|
||||
*/
|
||||
async checkLocalPassword(
|
||||
username: string,
|
||||
|
@ -129,12 +122,11 @@ export class LocalService {
|
|||
AuthProviderType.LOCAL,
|
||||
null,
|
||||
);
|
||||
if (
|
||||
!(await checkPassword(
|
||||
password,
|
||||
identity[FieldNameIdentity.passwordHash] ?? '',
|
||||
))
|
||||
) {
|
||||
const passwordValid = await checkPassword(
|
||||
password,
|
||||
identity[FieldNameIdentity.passwordHash] ?? '',
|
||||
);
|
||||
if (!passwordValid) {
|
||||
throw new InvalidCredentialsError(
|
||||
'Username or password is not correct',
|
||||
this.logger.getContext(),
|
||||
|
@ -145,11 +137,12 @@ export class LocalService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Check if the password is strong and long enough.
|
||||
* Checks if the password is strong and long enough
|
||||
*
|
||||
* This check is performed against the minimalPasswordStrength of the {@link AuthConfig}.
|
||||
* @param {string} password - the password to check
|
||||
* @throws {PasswordTooWeakError} the password is too weak
|
||||
*
|
||||
* @param password The password to check
|
||||
* @throws {PasswordTooWeakError} if the password is too weak
|
||||
*/
|
||||
async checkPasswordStrength(password: string): Promise<void> {
|
||||
if (password.length < 6) {
|
||||
|
|
|
@ -69,16 +69,18 @@ export class OidcService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Fetches the client and its config (issuer, metadata) for the given OIDC configuration.
|
||||
* Fetches the client and its config (issuer, metadata) for the given OIDC configuration
|
||||
*
|
||||
* @param {OidcConfig} oidcConfig The OIDC configuration to fetch the client config for
|
||||
* @returns {OidcClientConfigEntry} A promise that resolves to the client configuration.
|
||||
* @param oidcConfig The OIDC configuration to fetch the client config for
|
||||
* @returns A promise that resolves to the client configuration.
|
||||
*/
|
||||
private async fetchClientConfig(
|
||||
oidcConfig: OidcConfig,
|
||||
): Promise<OidcClientConfigEntry> {
|
||||
const useAutodiscover = oidcConfig.authorizeUrl === undefined;
|
||||
const useAutodiscover =
|
||||
oidcConfig.authorizeUrl === undefined ||
|
||||
oidcConfig.tokenUrl === undefined ||
|
||||
oidcConfig.userinfoUrl === undefined;
|
||||
const issuer = useAutodiscover
|
||||
? await Issuer.discover(oidcConfig.issuer)
|
||||
: new Issuer({
|
||||
|
@ -117,7 +119,7 @@ export class OidcService {
|
|||
/**
|
||||
* Generates a secure code verifier for the OIDC login.
|
||||
*
|
||||
* @returns {string} The generated code verifier.
|
||||
* @returns The generated code verifier.
|
||||
*/
|
||||
generateCode(): string {
|
||||
return generators.codeVerifier();
|
||||
|
@ -126,7 +128,7 @@ export class OidcService {
|
|||
/**
|
||||
* Generates a random state for the OIDC login.
|
||||
*
|
||||
* @returns {string} The generated state.
|
||||
* @returns The generated state.
|
||||
*/
|
||||
generateState(): string {
|
||||
return generators.state();
|
||||
|
@ -135,10 +137,10 @@ export class OidcService {
|
|||
/**
|
||||
* Generates the authorization URL for the given OIDC identifier and code.
|
||||
*
|
||||
* @param {string} oidcIdentifier The identifier of the OIDC configuration
|
||||
* @param {string} code The code verifier generated for the login
|
||||
* @param {string} state The state generated for the login
|
||||
* @returns {string} The generated authorization URL
|
||||
* @param oidcIdentifier The identifier of the OIDC configuration
|
||||
* @param code The code verifier generated for the login
|
||||
* @param state The state generated for the login
|
||||
* @returns The generated authorization URL
|
||||
*/
|
||||
getAuthorizationUrl(
|
||||
oidcIdentifier: string,
|
||||
|
@ -163,7 +165,6 @@ export class OidcService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Extracts the user information from the callback and stores them in the session.
|
||||
* Afterward, the user information is returned.
|
||||
*
|
||||
|
@ -231,20 +232,18 @@ export class OidcService {
|
|||
};
|
||||
request.session.providerUserId = userId;
|
||||
request.session.newUserData = newUserData;
|
||||
// Cleanup: The code isn't necessary anymore
|
||||
// Cleanup: The OIDC login code and state are not required anymore
|
||||
request.session.oidcLoginCode = undefined;
|
||||
request.session.oidcLoginState = undefined;
|
||||
return newUserData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Checks if an identity exists for a given OIDC user and returns it if it does.
|
||||
* Checks if an identity exists for a given OIDC user and returns it if it does
|
||||
*
|
||||
* @param {string} oidcIdentifier The identifier of the OIDC configuration
|
||||
* @param {string} oidcUserId The id of the user in the OIDC system
|
||||
* @returns {Identity} The identity if it exists
|
||||
* @returns {null} when the identity does not exist
|
||||
* @param oidcIdentifier The identifier of the OIDC configuration
|
||||
* @param oidcUserId The id of the user in the OIDC system
|
||||
* @returns The identity if it exists, null otherwise
|
||||
*/
|
||||
async getExistingOidcIdentity(
|
||||
oidcIdentifier: string,
|
||||
|
@ -263,6 +262,7 @@ export class OidcService {
|
|||
oidcIdentifier,
|
||||
);
|
||||
} catch (e) {
|
||||
// Catch not-found errors when registration via OIDC is enabled and return null instead
|
||||
if (e instanceof NotInDBError) {
|
||||
if (!clientConfig.config.enableRegistration) {
|
||||
throw new ForbiddenException(
|
||||
|
@ -277,11 +277,10 @@ export class OidcService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the logout URL for the given request if the user is logged in with OIDC.
|
||||
* Returns the logout URL for the given request if the user is logged in with OIDC
|
||||
*
|
||||
* @param {RequestWithSession} request The request containing the session
|
||||
* @returns {string} The logout URL if the user is logged in with OIDC
|
||||
* @returns {null} when there is no logout URL to redirect to
|
||||
* @param request The request containing the session
|
||||
* @returns The logout URL if the user is logged in with OIDC, or null if there is no URL to redirect to
|
||||
*/
|
||||
getLogoutUrl(request: RequestWithSession): string | null {
|
||||
const oidcIdentifier = request.session.authProviderIdentifier;
|
||||
|
@ -304,12 +303,12 @@ export class OidcService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a specific field from the userinfo object or a default value.
|
||||
* Returns a specific field from the userinfo object or a default value
|
||||
*
|
||||
* @param {UserinfoResponse} response The response from the OIDC userinfo endpoint
|
||||
* @param {string} field The field to get from the response
|
||||
* @param {string|undefined} defaultValue The default value to return if the value is empty
|
||||
* @returns {string|undefined} The value of the field from the response or the default value
|
||||
* @param response The response from the OIDC userinfo endpoint
|
||||
* @param field The field to get from the response
|
||||
* @param defaultValue The default value to return if the value is empty
|
||||
* @returns The value of the field from the response or the default value
|
||||
*/
|
||||
private static getResponseFieldValue<T extends string | undefined>(
|
||||
response: UserinfoResponse,
|
||||
|
|
|
@ -16,10 +16,10 @@ import { ConsoleLoggerService } from '../logger/console-logger.service';
|
|||
/**
|
||||
* This guard checks if a session is present.
|
||||
*
|
||||
* If there is a username in `request.session.username` it will try to get this user from the database and put it into `request.user`. See {@link RequestUser}.
|
||||
* If there is no `request.session.username`, but any PermissionLevel is configured, `request.session.authProvider` is set to `guest` to indicate a guest user.
|
||||
* It checks if the session contains a `userId` and an `authProviderType`. If both are present, they are added to the request object.
|
||||
* Otherwise, an `UnauthorizedException` is thrown.
|
||||
*
|
||||
* @throws UnauthorizedException
|
||||
* @throws UnauthorizedException if the session is not present or does not contain a `userId` or `authProviderType`.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SessionGuard implements CanActivate {
|
||||
|
@ -33,7 +33,7 @@ export class SessionGuard implements CanActivate {
|
|||
const authProviderType = request.session?.authProviderType;
|
||||
if (!userId || !authProviderType) {
|
||||
this.logger.debug('The user has no session.');
|
||||
throw new UnauthorizedException("You're not logged in");
|
||||
throw new UnauthorizedException('You have no active session');
|
||||
}
|
||||
request.userId = userId;
|
||||
request.authProviderType = authProviderType;
|
||||
|
|
|
@ -18,6 +18,12 @@ import {
|
|||
extractDescriptionFromZodIssue,
|
||||
} from './zod-error-message';
|
||||
|
||||
/**
|
||||
* Validates that a given URL is valid, uses the HTTP or HTTPS protocol, and does not end with a slash.
|
||||
*
|
||||
* @param value The URL string to validate.
|
||||
* @param ctx The Zod refinement context.
|
||||
*/
|
||||
function validateUrl(value: string | undefined, ctx: RefinementCtx): void {
|
||||
if (!value) {
|
||||
return z.NEVER;
|
||||
|
|
6
backend/src/database/migrations/README.md
Normal file
6
backend/src/database/migrations/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Migrations
|
||||
|
||||
The migrations are loaded and executed by Knex itself. It seems Knex expects CommonJS modules and does not support
|
||||
TypeScript. Additionally, there were problems when importing types from TypeScript from the backend, or from the
|
||||
commons package due to other (ESM-only) dependencies. Therefore, the migrations and database types use their own
|
||||
CommonJS package.
|
|
@ -24,7 +24,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||
{
|
||||
[FieldNameUser.username]: null,
|
||||
[FieldNameUser.guestUuid]: '55b4618a-d5f3-4320-93d3-f3501c73d72b',
|
||||
[FieldNameUser.displayName]: 'Gast 1',
|
||||
[FieldNameUser.displayName]: 'Guest 1',
|
||||
[FieldNameUser.photoUrl]: null,
|
||||
[FieldNameUser.email]: null,
|
||||
[FieldNameUser.authorStyle]: 1,
|
||||
|
|
|
@ -40,6 +40,11 @@ export class FrontendConfigService {
|
|||
this.logger.setContext(FrontendConfigService.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config options for the frontend
|
||||
*
|
||||
* @returns A frontend config DTO
|
||||
*/
|
||||
async getFrontendConfig(): Promise<FrontendConfigDto> {
|
||||
return {
|
||||
guestAccess: this.noteConfig.guestAccess,
|
||||
|
@ -58,6 +63,11 @@ export class FrontendConfigService {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the auth providers from the config and returns them
|
||||
*
|
||||
* @returns An array of auth provider DTOs
|
||||
*/
|
||||
private getAuthProviders(): AuthProviderDto[] {
|
||||
const providers: AuthProviderDto[] = [];
|
||||
if (this.authConfig.local.enableLogin) {
|
||||
|
@ -84,6 +94,11 @@ export class FrontendConfigService {
|
|||
return providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the branding from the config and returns it
|
||||
*
|
||||
* @returns A branding DTO
|
||||
*/
|
||||
private getBranding(): BrandingDto {
|
||||
return {
|
||||
logo: this.customizationConfig.branding.customLogo
|
||||
|
@ -93,6 +108,11 @@ export class FrontendConfigService {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the special URLs like imprint or privacy policy from the config and returns them
|
||||
*
|
||||
* @returns A special URL DTO
|
||||
*/
|
||||
private getSpecialUrls(): SpecialUrlDto {
|
||||
return {
|
||||
imprint: this.customizationConfig.specialUrls.imprint
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { GroupInfoDto } from '@hedgedoc/commons';
|
||||
import {
|
||||
FieldNameGroup,
|
||||
TableGroup,
|
||||
TypeInsertGroup,
|
||||
} from '@hedgedoc/database';
|
||||
import { FieldNameGroup, TableGroup } from '@hedgedoc/database';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { InjectConnection } from 'nest-knexjs';
|
||||
|
@ -32,16 +28,15 @@ export class GroupsService {
|
|||
*
|
||||
* @param name The group name as identifier the new group shall have
|
||||
* @param displayName The display name the new group shall have
|
||||
* @throws {AlreadyInDBError} The group name is already taken
|
||||
* @throws {AlreadyInDBError} if the group name is already taken
|
||||
*/
|
||||
async createGroup(name: string, displayName: string): Promise<void> {
|
||||
const group: TypeInsertGroup = {
|
||||
[FieldNameGroup.name]: name,
|
||||
[FieldNameGroup.displayName]: displayName,
|
||||
[FieldNameGroup.isSpecial]: false,
|
||||
};
|
||||
try {
|
||||
await this.knex(TableGroup).insert(group);
|
||||
await this.knex(TableGroup).insert({
|
||||
[FieldNameGroup.name]: name,
|
||||
[FieldNameGroup.displayName]: displayName,
|
||||
[FieldNameGroup.isSpecial]: false,
|
||||
});
|
||||
} catch {
|
||||
const message = `A group with the name '${name}' already exists.`;
|
||||
this.logger.debug(message, 'createGroup');
|
||||
|
@ -53,7 +48,7 @@ export class GroupsService {
|
|||
* Fetches a group by its identifier name
|
||||
*
|
||||
* @param name Name of the group to query
|
||||
* @return The group
|
||||
* @returns The group's metadata
|
||||
* @throws {NotInDBError} if there is no group with this name
|
||||
*/
|
||||
async getGroupInfoDtoByName(name: string): Promise<GroupInfoDto> {
|
||||
|
@ -76,7 +71,7 @@ export class GroupsService {
|
|||
*
|
||||
* @param name Name of the group to query
|
||||
* @param transaction The optional database transaction to use
|
||||
* @return The groupId
|
||||
* @returns The groupId
|
||||
* @throws {NotInDBError} if there is no group with this name
|
||||
*/
|
||||
async getGroupIdByName(name: string, transaction?: Knex): Promise<number> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -12,7 +12,7 @@ export interface MediaBackend {
|
|||
* @param buffer File data
|
||||
* @param fileType File type result
|
||||
* @throws {MediaBackendError} - there was an error saving the file
|
||||
* @return The internal backend data, which should be saved
|
||||
* @returns The internal backend data, which should be saved
|
||||
*/
|
||||
saveFile(
|
||||
uuid: string,
|
||||
|
@ -33,7 +33,7 @@ export interface MediaBackend {
|
|||
* @param uuid Unique identifier of the uploaded file
|
||||
* @param backendData Internal backend data
|
||||
* @throws {MediaBackendError} - there was an error getting the file
|
||||
* @return Public accessible URL of the file
|
||||
* @returns Public accessible URL of the file
|
||||
*/
|
||||
getFileUrl(uuid: string, backendData: string | null): Promise<string>;
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ export class MediaService {
|
|||
* @param fileBuffer The buffer with the file contents to save
|
||||
* @param userId Id of the user who uploaded this file
|
||||
* @param noteId Id of the note which will be associated with the new file
|
||||
* @return The created MediaUpload entity
|
||||
* @returns The created MediaUpload entity
|
||||
* @throws {ClientError} if the MIME type of the file is not supported
|
||||
* @throws {NotInDBError} if the note or user is not in the database
|
||||
* @throws {MediaBackendError} if there was an error saving the file
|
||||
|
@ -123,7 +123,7 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* Try to delete the specified file.
|
||||
* @param {uuid} uuid - the name of the file to delete.
|
||||
* @throws {MediaBackendError} - there was an error deleting the file
|
||||
|
@ -150,10 +150,10 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* Get the URL of the file.
|
||||
* @param {string} uuid - the uuid of the file to get the URL for.
|
||||
* @return {string} the URL of the file.
|
||||
* @param uuid - the uuid of the file to get the URL for.
|
||||
* @returns the URL of the file.
|
||||
* @throws {MediaBackendError} - there was an error retrieving the url
|
||||
*/
|
||||
async getFileUrl(uuid: string): Promise<string> {
|
||||
|
@ -178,9 +178,9 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* Find a file entry by its UUID.
|
||||
* @param {string} uuid - The UUID of the MediaUpload entity to find.
|
||||
* @param uuid - The UUID of the MediaUpload entity to find.
|
||||
* @returns {MediaUpload} - the MediaUpload entity if found.
|
||||
* @throws {NotInDBError} - the MediaUpload entity with the provided UUID is not found in the database.
|
||||
*/
|
||||
|
@ -196,10 +196,10 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* List all uploads by a specific user
|
||||
* @param {number} userId - the specific user
|
||||
* @return {MediaUpload[]} arary of media uploads owned by the user
|
||||
* @returns {MediaUpload[]} arary of media uploads owned by the user
|
||||
*/
|
||||
async getMediaUploadUuidsByUserId(
|
||||
userId: number,
|
||||
|
@ -211,10 +211,10 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* List all uploads to a specific note
|
||||
* @param {number} noteId - the specific user
|
||||
* @return {MediaUpload[]} array of media uploads owned by the user
|
||||
* @returns {MediaUpload[]} array of media uploads owned by the user
|
||||
*/
|
||||
async getMediaUploadUuidsByNoteId(
|
||||
noteId: number,
|
||||
|
@ -228,9 +228,9 @@ export class MediaService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* Set the note of a mediaUpload to null
|
||||
* @param {string} uuid - the media upload to be changed
|
||||
* @param uuid - the media upload to be changed
|
||||
*/
|
||||
async removeNoteFromMediaUpload(uuid: string): Promise<void> {
|
||||
this.logger.debug(
|
||||
|
|
|
@ -79,7 +79,7 @@ export class NoteService {
|
|||
* Get all notes owned by a user
|
||||
*
|
||||
* @param userId The id of the user who owns the notes
|
||||
* @return Array of notes owned by the user
|
||||
* @returns Array of notes owned by the user
|
||||
*/
|
||||
async getUserNoteIds(userId: number): Promise<number[]> {
|
||||
const result = await this.knex(TableNote)
|
||||
|
@ -94,7 +94,7 @@ export class NoteService {
|
|||
* @param noteContent The content of the new note, in most cases an empty string
|
||||
* @param givenAlias An optional alias the note should have
|
||||
* @param ownerUserId The owner of the note
|
||||
* @return The newly created note
|
||||
* @returns The newly created note
|
||||
* @throws {AlreadyInDBError} a note with the requested id or aliases already exists
|
||||
* @throws {ForbiddenIdError} the requested id or aliases is forbidden
|
||||
* @throws {MaximumDocumentLengthExceededError} the noteContent is longer than the maxDocumentLength
|
||||
|
@ -197,7 +197,7 @@ export class NoteService {
|
|||
* @param noteId the note to use
|
||||
* @param transaction The optional database transaction to use
|
||||
* @throws {NotInDBError} the note is not in the DB
|
||||
* @return {string} the content of the note
|
||||
* @returns the content of the note
|
||||
*/
|
||||
async getNoteContent(noteId: number, transaction?: Knex): Promise<string> {
|
||||
const realtimeContent = this.realtimeNoteStore
|
||||
|
@ -220,7 +220,7 @@ export class NoteService {
|
|||
*
|
||||
* @param alias the notes id or aliases
|
||||
* @param transaction The optional database transaction to use
|
||||
* @return the note id
|
||||
* @returns the note id
|
||||
* @throws {NotInDBError} there is no note with this id or aliases
|
||||
* @throws {ForbiddenIdError} the requested id or aliases is forbidden
|
||||
*/
|
||||
|
@ -280,7 +280,7 @@ export class NoteService {
|
|||
*
|
||||
* @param noteId - the note
|
||||
* @param noteContent - the new content
|
||||
* @return the note with a new revision and new content
|
||||
* @returns the note with a new revision and new content
|
||||
* @throws {NotInDBError} there is no note with this id or aliases
|
||||
*/
|
||||
async updateNote(noteId: number, noteContent: string): Promise<void> {
|
||||
|
@ -292,7 +292,7 @@ export class NoteService {
|
|||
* Build NotePermissionsDto from a note.
|
||||
* @param noteId The id of the ntoe to get the permissions for
|
||||
* @param transaction The optional database transaction to use
|
||||
* @return The built NotePermissionDto
|
||||
* @returns The built NotePermissionDto
|
||||
*/
|
||||
async toNotePermissionsDto(
|
||||
noteId: number,
|
||||
|
@ -374,11 +374,11 @@ export class NoteService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
*
|
||||
* Build NoteMetadataDto from a note.
|
||||
* @param noteId The if of the note to get the metadata for
|
||||
* @param transaction The optional database transaction to use
|
||||
* @return The built NoteMetadataDto
|
||||
* @returns The built NoteMetadataDto
|
||||
*/
|
||||
async toNoteMetadataDto(
|
||||
noteId: number,
|
||||
|
@ -473,7 +473,7 @@ export class NoteService {
|
|||
* Gets the note data for the note DTO
|
||||
*
|
||||
* @param noteId The id of the note to transform
|
||||
* @return {NoteDto} the built NoteDto
|
||||
* @returns {NoteDto} the built NoteDto
|
||||
*/
|
||||
async toNoteDto(noteId: number): Promise<NoteDto> {
|
||||
return await this.knex.transaction(async (transaction) => {
|
||||
|
|
|
@ -18,7 +18,7 @@ export enum NotePermissionLevel {
|
|||
* Returns the display name for the given {@link NotePermissionLevel}.
|
||||
*
|
||||
* @param {NotePermissionLevel} value the note permission to display
|
||||
* @return {string} The display name
|
||||
* @returns The display name
|
||||
*/
|
||||
export function getNotePermissionLevelDisplayName(
|
||||
value: NotePermissionLevel,
|
||||
|
|
|
@ -90,7 +90,7 @@ export class PermissionService {
|
|||
* Checks if the given {@link User} is allowed to create notes.
|
||||
*
|
||||
* @param username - The user whose permission should be checked. Value is null if guest access should be checked
|
||||
* @return if the user is allowed to create notes
|
||||
* @returns if the user is allowed to create notes
|
||||
*/
|
||||
public mayCreate(username: string | null): boolean {
|
||||
return (
|
||||
|
@ -105,7 +105,7 @@ export class PermissionService {
|
|||
* @param userId The id of the user
|
||||
* @param noteId The id of the note
|
||||
* @param transaction Optional transaction to use
|
||||
* @return true if the user is the owner of the note
|
||||
* @returns true if the user is the owner of the note
|
||||
*/
|
||||
async isOwner(
|
||||
userId: number | null,
|
||||
|
@ -135,7 +135,7 @@ export class PermissionService {
|
|||
*
|
||||
* @param {number | null} userId The user whose permission should be checked
|
||||
* @param {number} noteId The note that is accessed by the given user
|
||||
* @return {Promise<NotePermissionLevel>} The determined permission
|
||||
* @returns {Promise<NotePermissionLevel>} The determined permission
|
||||
*/
|
||||
public async determinePermission(
|
||||
userId: number,
|
||||
|
@ -284,7 +284,7 @@ export class PermissionService {
|
|||
* @param noteId the note
|
||||
* @param userId the user for which the permission should be set
|
||||
* @param canEdit specifies if the user can edit the note
|
||||
* @return the note with the new permission
|
||||
* @returns the note with the new permission
|
||||
*/
|
||||
async setUserPermission(
|
||||
noteId: number,
|
||||
|
@ -361,7 +361,7 @@ export class PermissionService {
|
|||
* Remove permission for a specific group on a note.
|
||||
* @param noteId - the note
|
||||
* @param groupId - the group for which the permission should be set
|
||||
* @return the note with the new permission
|
||||
* @returns the note with the new permission
|
||||
*/
|
||||
async removeGroupPermission(noteId: number, groupId: number): Promise<void> {
|
||||
const result = await this.knex(TableNoteGroupPermission)
|
||||
|
@ -382,7 +382,7 @@ export class PermissionService {
|
|||
* Updates the owner of a note.
|
||||
* @param noteId - the note to use
|
||||
* @param newOwnerId - the new owner
|
||||
* @return the updated note
|
||||
* @returns the updated note
|
||||
*/
|
||||
async changeOwner(noteId: number, newOwnerId: number): Promise<void> {
|
||||
const result = await this.knex(TableNote)
|
||||
|
|
|
@ -11,7 +11,7 @@ import { NotePermissionLevel } from '../note-permission.enum';
|
|||
* Converts the given guest access level to the highest possible {@link NotePermissionLevel}.
|
||||
*
|
||||
* @param guestAccess the guest access level to should be converted
|
||||
* @return the {@link NotePermissionLevel} representation
|
||||
* @returns the {@link NotePermissionLevel} representation
|
||||
*/
|
||||
export function convertPermissionLevelToNotePermissionLevel(
|
||||
guestAccess: PermissionLevel,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -8,7 +8,7 @@ import { adjectives, items } from './random-words';
|
|||
/**
|
||||
* Generates a random names based on an adjective and a noun.
|
||||
*
|
||||
* @return the generated name
|
||||
* @returns the generated name
|
||||
*/
|
||||
export function generateRandomName(): string {
|
||||
const adjective = generateRandomWord(adjectives);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@ export class RealtimeNoteStore {
|
|||
* @param initialTextContent the initial text content of realtime doc
|
||||
* @param initialYjsState the initial yjs state. If provided this will be used instead of the text content
|
||||
* @throws Error if there is already an realtime note for the given note.
|
||||
* @return The created realtime note
|
||||
* @returns The created realtime note
|
||||
*/
|
||||
public create(
|
||||
noteId: number,
|
||||
|
@ -43,7 +43,7 @@ export class RealtimeNoteStore {
|
|||
/**
|
||||
* Retrieves a {@link RealtimeNote} that is linked to the given {@link Note} id.
|
||||
* @param noteId The id of the {@link Note}
|
||||
* @return A {@link RealtimeNote} or {@code undefined} if no instance is existing.
|
||||
* @returns A {@link RealtimeNote} or {@code undefined} if no instance is existing.
|
||||
*/
|
||||
public find(noteId: number): RealtimeNote | undefined {
|
||||
return this.noteIdToRealtimeNote.get(noteId);
|
||||
|
|
|
@ -61,7 +61,7 @@ export class RealtimeNoteService implements BeforeApplicationShutdown {
|
|||
* Creates or reuses a {@link RealtimeNote} that is handling the real time editing of the {@link Note} which is identified by the given note id.
|
||||
* @param noteId The {@link Note} for which a {@link RealtimeNote realtime note} should be retrieved.
|
||||
* @throws NotInDBError if note doesn't exist or has no revisions.
|
||||
* @return A {@link RealtimeNote} that is linked to the given note.
|
||||
* @returns A {@link RealtimeNote} that is linked to the given note.
|
||||
*/
|
||||
public async getOrCreateRealtimeNote(noteId: number): Promise<RealtimeNote> {
|
||||
return (
|
||||
|
@ -75,7 +75,7 @@ export class RealtimeNoteService implements BeforeApplicationShutdown {
|
|||
*
|
||||
* @param noteId The note for which the realtime note should be created
|
||||
* @throws NotInDBError if note doesn't exist or has no revisions.
|
||||
* @return The created realtime note
|
||||
* @returns The created realtime note
|
||||
*/
|
||||
private async createNewRealtimeNote(noteId: number): Promise<RealtimeNote> {
|
||||
const lastRevision = await this.revisionsService.getLatestRevision(noteId);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -114,7 +114,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
/**
|
||||
* Checks if there's still clients connected to this note.
|
||||
*
|
||||
* @return {@code true} if there a still clinets connected, otherwise {@code false}
|
||||
* @returns {@code true} if there a still clinets connected, otherwise {@code false}
|
||||
*/
|
||||
public hasConnections(): boolean {
|
||||
return this.clients.size !== 0;
|
||||
|
@ -123,7 +123,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
/**
|
||||
* Returns all {@link RealtimeConnection WebsocketConnections} currently hold by this note.
|
||||
*
|
||||
* @return an array of {@link RealtimeConnection WebsocketConnections}
|
||||
* @returns an array of {@link RealtimeConnection WebsocketConnections}
|
||||
*/
|
||||
public getConnections(): RealtimeConnection[] {
|
||||
return [...this.clients];
|
||||
|
@ -132,7 +132,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
/**
|
||||
* Get the {@link RealtimeDoc realtime note} of the note.
|
||||
*
|
||||
* @return the {@link RealtimeDoc realtime note} of the note
|
||||
* @returns the {@link RealtimeDoc realtime note} of the note
|
||||
*/
|
||||
public getRealtimeDoc(): RealtimeDoc {
|
||||
return this.doc;
|
||||
|
@ -141,7 +141,7 @@ export class RealtimeNote extends EventEmitter2<RealtimeNoteEventMap> {
|
|||
/**
|
||||
* Get the {@link Note note} that is edited.
|
||||
*
|
||||
* @return the {@link Note note}
|
||||
* @returns the {@link Note note}
|
||||
*/
|
||||
public getNoteId(): number {
|
||||
return this.noteId;
|
||||
|
|
|
@ -79,7 +79,7 @@ export class MockConnectionBuilder {
|
|||
/**
|
||||
* Creates a new connection based on the given configuration.
|
||||
*
|
||||
* @return {RealtimeConnection} The constructed mocked connection
|
||||
* @returns {RealtimeConnection} The constructed mocked connection
|
||||
* @throws Error if neither withGuestUser nor withLoggedInUser has been called.
|
||||
*/
|
||||
public build(): RealtimeConnection {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -9,7 +9,7 @@ import { IncomingMessage } from 'http';
|
|||
* Extracts the note id from the url of the given request.
|
||||
*
|
||||
* @param request The request whose URL should be extracted
|
||||
* @return The extracted note id
|
||||
* @returns The extracted note id
|
||||
* @throws Error if the given string isn't a valid realtime URL path
|
||||
*/
|
||||
export function extractNoteAliasFromRequestUrl(
|
||||
|
|
|
@ -118,7 +118,7 @@ export class WebsocketGateway implements OnGatewayConnection {
|
|||
* Finds the user id whose session cookie is saved in the given {@link IncomingMessage}.
|
||||
*
|
||||
* @param request The request that contains the session cookie
|
||||
* @return The found user id
|
||||
* @returns The found user id
|
||||
*/
|
||||
private async findUserIdByRequestSession(
|
||||
request: IncomingMessage,
|
||||
|
|
|
@ -59,7 +59,7 @@ export class RevisionsService {
|
|||
* Returns all revisions of a note
|
||||
*
|
||||
* @param noteId The id of the note
|
||||
* @return The list of revisions
|
||||
* @returns The list of revisions
|
||||
*/
|
||||
async getAllRevisionMetadataDto(
|
||||
noteId: number,
|
||||
|
@ -294,14 +294,14 @@ export class RevisionsService {
|
|||
* Creates (but does not persist(!)) a new {@link Revision} for the given {@link Note}.
|
||||
* Useful if the revision is saved together with the note in one action.
|
||||
*
|
||||
* @async
|
||||
*
|
||||
* @param noteId The note for which the revision should be created
|
||||
* @param newContent The new note content
|
||||
* @param firstRevision Whether this is called for the first revision of a note
|
||||
* @param transaction The optional pre-existing database transaction to use
|
||||
* @param yjsStateVector The yjs state vector that describes the new content
|
||||
* @return {Revision} the created revision
|
||||
* @return {undefined} if the revision couldn't be created because e.g. the content hasn't changed
|
||||
* @returns {Revision} the created revision
|
||||
* @returns {undefined} if the revision couldn't be created because e.g. the content hasn't changed
|
||||
*/
|
||||
async createRevision(
|
||||
noteId: number,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -31,7 +31,7 @@ interface FrontmatterParserResult {
|
|||
/**
|
||||
* Parses the frontmatter of the given content and extracts the metadata that are necessary to create a new revision..
|
||||
*
|
||||
* @param {string} content the revision content that contains the frontmatter.
|
||||
* @param content the revision content that contains the frontmatter.
|
||||
*/
|
||||
export function extractRevisionMetadataFromContent(
|
||||
content: string,
|
||||
|
|
|
@ -40,7 +40,7 @@ export class SessionService {
|
|||
* Returns the currently used session store for usage outside of the HTTP session context
|
||||
* Note that this method is also used for connecting the session store with NestJS initially
|
||||
*
|
||||
* @return The used session store
|
||||
* @returns The used session store
|
||||
*/
|
||||
getSessionStore(): KeyvSessionStore<SessionState> {
|
||||
return this.sessionStore;
|
||||
|
@ -50,7 +50,7 @@ export class SessionService {
|
|||
* Finds the username of the user that has the given session id
|
||||
*
|
||||
* @param sessionId The session id for which the owning user should be found
|
||||
* @return A Promise that either resolves with the username or rejects with an error
|
||||
* @returns A Promise that either resolves with the username or rejects with an error
|
||||
*/
|
||||
async getUserIdForSessionId(
|
||||
sessionId: string,
|
||||
|
@ -63,7 +63,7 @@ export class SessionService {
|
|||
* Extracts the hedgedoc session cookie from the given {@link IncomingMessage request} and checks if the signature is correct.
|
||||
*
|
||||
* @param request The http request that contains a session cookie
|
||||
* @return An {@link Optional optional} that either contains the extracted session id or is empty if no session cookie has been found
|
||||
* @returns An {@link Optional optional} that either contains the extracted session id or is empty if no session cookie has been found
|
||||
* @throws Error if the cookie has been found but the content is malformed
|
||||
* @throws Error if the cookie has been found but the content isn't signed
|
||||
*/
|
||||
|
@ -79,7 +79,7 @@ export class SessionService {
|
|||
* Parses the given session cookie content and extracts the session id
|
||||
*
|
||||
* @param rawCookie The cookie to parse
|
||||
* @return The extracted session id
|
||||
* @returns The extracted session id
|
||||
* @throws Error if the cookie has been found but the content is malformed
|
||||
* @throws Error if the cookie has been found but the content isn't signed
|
||||
*/
|
||||
|
|
|
@ -44,7 +44,7 @@ export class UsersService {
|
|||
* @param [email] New user's email address if exists
|
||||
* @param [photoUrl] URL of the user's profile picture if exists
|
||||
* @param transaction The optional transaction to access the db
|
||||
* @return The id of newly created user
|
||||
* @returns The id of newly created user
|
||||
* @throws {BadRequestException} if the username contains invalid characters or is too short
|
||||
* @throws {AlreadyInDBError} the username is already taken.
|
||||
* @thorws {GenericDBError} the database returned a non-expected value
|
||||
|
@ -96,7 +96,7 @@ export class UsersService {
|
|||
/**
|
||||
* Creates a new guest user with a random displayName
|
||||
*
|
||||
* @return The guest uuid and the id of the newly created user
|
||||
* @returns The guest uuid and the id of the newly created user
|
||||
* @throws {GenericDBError} the database returned a non-expected value
|
||||
*/
|
||||
async createGuestUser(): Promise<[string, number]> {
|
||||
|
@ -195,7 +195,7 @@ export class UsersService {
|
|||
* Checks if a given username is already taken
|
||||
*
|
||||
* @param username The username to check
|
||||
* @return true if the user exists, false otherwise
|
||||
* @returns true if the user exists, false otherwise
|
||||
*/
|
||||
async isUsernameTaken(username: string): Promise<boolean> {
|
||||
const result = await this.knex(TableUser)
|
||||
|
@ -209,7 +209,7 @@ export class UsersService {
|
|||
*
|
||||
* @param userId The id of the user to check
|
||||
* @param transaction the optional transaction to access the db
|
||||
* @return true if the user is registered, false otherwise
|
||||
* @returns true if the user is registered, false otherwise
|
||||
*/
|
||||
async isRegisteredUser(
|
||||
userId: User[FieldNameUser.id],
|
||||
|
@ -228,7 +228,7 @@ export class UsersService {
|
|||
* Fetches the userId for a given username from the database
|
||||
*
|
||||
* @param username The username to fetch
|
||||
* @return The found user object
|
||||
* @returns The found user object
|
||||
* @throws {NotInDBError} if the user could not be found
|
||||
*/
|
||||
async getUserIdByUsername(username: string): Promise<number> {
|
||||
|
@ -250,7 +250,7 @@ export class UsersService {
|
|||
* Fetches the userId for a given username from the database
|
||||
*
|
||||
* @param uuid The uuid to fetch
|
||||
* @return The found user object
|
||||
* @returns The found user object
|
||||
* @throws {NotInDBError} if the user could not be found
|
||||
*/
|
||||
async getUserIdByGuestUuid(uuid: string): Promise<User[FieldNameUser.id]> {
|
||||
|
@ -272,7 +272,7 @@ export class UsersService {
|
|||
* Fetches the user object for a given username from the database
|
||||
*
|
||||
* @param username The username to fetch
|
||||
* @return The found user object
|
||||
* @returns The found user object
|
||||
* @throws {NotInDBError} if the user could not be found
|
||||
*/
|
||||
async getUserDtoByUsername(username: string): Promise<UserInfoDto> {
|
||||
|
@ -295,7 +295,7 @@ export class UsersService {
|
|||
* Fetches the user object for a given username from the database
|
||||
*
|
||||
* @param userId The username to fetch
|
||||
* @return The found user object
|
||||
* @returns The found user object
|
||||
* @throws {NotInDBError} if the user could not be found
|
||||
*/
|
||||
async getUserById(userId: number): Promise<User> {
|
||||
|
@ -318,7 +318,7 @@ export class UsersService {
|
|||
* @param username The username to use as a seed when generating a random avatar
|
||||
* @param email The email address of the user for using livbravatar if configured
|
||||
* @param photoUrl The user-provided photo URL
|
||||
* @return A URL to the user's profile picture.
|
||||
* @returns A URL to the user's profile picture.
|
||||
*/
|
||||
private generatePhotoUrl(
|
||||
username: string,
|
||||
|
@ -343,7 +343,7 @@ export class UsersService {
|
|||
* Creates a random author style index based on a hashing of the username
|
||||
*
|
||||
* @param username The username is used as input for the hash
|
||||
* @return An index between 0 and 8 (including 0 and 8)
|
||||
* @returns An index between 0 and 8 (including 0 and 8)
|
||||
*/
|
||||
private generateAuthorStyleIndex(username: string): number {
|
||||
let hash = 0;
|
||||
|
@ -358,7 +358,7 @@ export class UsersService {
|
|||
*
|
||||
* @param user The user to fetch their data for
|
||||
* @param authProvider The auth provider used for the current login session
|
||||
* @return The built OwnUserInfoDto
|
||||
* @returns The built OwnUserInfoDto
|
||||
*/
|
||||
toLoginUserInfoDto(
|
||||
user: User,
|
||||
|
|
|
@ -13,7 +13,7 @@ let versionCache: ServerVersionDto | undefined = undefined;
|
|||
/**
|
||||
* Reads the HedgeDoc version from the root package.json. This is done only once per run.
|
||||
*
|
||||
* @return {Promise<ServerVersionDto>} A Promise that contains the parsed server version.
|
||||
* @returns {Promise<ServerVersionDto>} A Promise that contains the parsed server version.
|
||||
* @throws {Error} if the package.json couldn't be found or doesn't contain a correct version.
|
||||
*/
|
||||
export async function getServerVersionFromPackageJson(): Promise<ServerVersionDto> {
|
||||
|
|
|
@ -414,17 +414,20 @@ export class TestSetupBuilder {
|
|||
);
|
||||
|
||||
// Create identities for login
|
||||
await this.testSetup.localIdentityService.createLocalIdentity(
|
||||
await this.testSetup.localIdentityService.createUserWithLocalIdentity(
|
||||
this.testSetup.users[0],
|
||||
password1,
|
||||
'',
|
||||
);
|
||||
await this.testSetup.localIdentityService.createLocalIdentity(
|
||||
await this.testSetup.localIdentityService.createUserWithLocalIdentity(
|
||||
this.testSetup.users[1],
|
||||
password2,
|
||||
'',
|
||||
);
|
||||
await this.testSetup.localIdentityService.createLocalIdentity(
|
||||
await this.testSetup.localIdentityService.createUserWithLocalIdentity(
|
||||
this.testSetup.users[2],
|
||||
password3,
|
||||
'',
|
||||
);
|
||||
|
||||
// create auth tokens
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue