From 29a15a8fe214ae894e42dd7049f4ec2a6d48a775 Mon Sep 17 00:00:00 2001 From: johaven Date: Sun, 18 Jan 2026 18:57:57 +0100 Subject: [PATCH 01/18] chore(backend): ensure URL hostname is explicit even when 0.0.0.0 is used --- backend/src/main.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/main.ts b/backend/src/main.ts index 2671202a..d3b6dd23 100755 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -20,6 +20,11 @@ async function bootstrap() { port: configuration.server.port }, (error, address) => { + if (configuration.server.host === '0.0.0.0') { + const url = new URL(address) + url.hostname = '0.0.0.0' + address = url.toString() + } if (error) { logger.error(`Server listening error at ${address} : ${error}`, 'HTTP') process.exit(1) From 28bbf1df2f2ab7e9060cc48b5893708fb83a123e Mon Sep 17 00:00:00 2001 From: johaven Date: Sun, 18 Jan 2026 23:44:11 +0100 Subject: [PATCH 02/18] feat(auth): implement OIDC authentication support and refactor auth providers --- backend/package.json | 1 + .../services/links-manager.service.spec.ts | 2 +- .../links/services/links-manager.service.ts | 2 +- .../sync-clients-manager.service.spec.ts | 10 +- .../services/sync-clients-manager.service.ts | 10 +- .../users/admin-users.controller.spec.ts | 8 +- .../users/admin-users.controller.ts | 2 +- .../admin-users-manager.service.spec.ts | 2 +- .../services/admin-users-manager.service.ts | 2 +- .../services/users-manager.service.spec.ts | 2 +- .../users/users.controller.spec.ts | 6 +- .../applications/users/users.controller.ts | 2 +- .../src/applications/users/users.e2e-spec.ts | 2 +- backend/src/authentication/auth.config.ts | 108 +---- .../authentication/auth.controller.spec.ts | 8 +- backend/src/authentication/auth.controller.ts | 14 +- backend/src/authentication/auth.module.ts | 24 +- ...r.service.spec.ts => auth.service.spec.ts} | 2 +- ...uth-manager.service.ts => auth.service.ts} | 20 +- .../src/authentication/constants/routes.ts | 29 +- .../authentication/dto/login-response.dto.ts | 7 +- .../guards/auth-basic.guard.spec.ts | 8 +- .../guards/auth-basic.strategy.ts | 6 +- .../guards/auth-local.guard.spec.ts | 8 +- .../guards/auth-local.strategy.ts | 4 +- .../guards/auth-token-access.guard.spec.ts | 2 +- .../guards/auth-token-access.strategy.ts | 2 +- .../guards/auth-token-refresh.guard.spec.ts | 2 +- .../guards/auth-token-refresh.strategy.ts | 2 +- .../providers/auth-providers.constants.ts | 11 + .../auth-providers.models.ts} | 2 +- .../providers/auth-providers.utils.ts | 30 ++ .../providers/ldap/auth-ldap.config.ts | 67 ++++ .../ldap/auth-ldap.constants.ts} | 0 .../ldap/auth-provider-ldap.service.spec.ts} | 12 +- .../ldap/auth-provider-ldap.service.ts} | 15 +- .../auth-provider-mysql.service.spec.ts} | 24 +- .../mysql/auth-provider-mysql.service.ts} | 6 +- .../providers/oidc/auth-method-oidc.module.ts | 16 + .../providers/oidc/auth-oidc.config.ts | 48 +++ .../providers/oidc/auth-oidc.constants.ts | 18 + .../providers/oidc/auth-oidc.controller.ts | 38 ++ .../oidc/auth-provider-oidc.service.ts | 376 ++++++++++++++++++ .../auth-provider-two-fa.service.spec.ts} | 12 +- .../two-fa/auth-provider-two-fa.service.ts} | 8 +- .../two-fa}/auth-two-fa-guard.ts | 16 +- .../providers/two-fa/auth-two-fa.config.ts | 26 ++ .../two-fa/auth-two-fa.dtos.ts} | 6 + .../two-fa/auth-two-fa.interfaces.ts} | 0 environment/environment.dist.yaml | 44 +- .../dialogs/admin-user-dialog.component.ts | 2 +- .../user-auth-2fa-enable-dialog.component.ts | 2 +- .../components/user-account.component.ts | 2 +- .../app/applications/users/user.service.ts | 8 +- frontend/src/app/auth/auth.component.ts | 3 +- frontend/src/app/auth/auth.guards.ts | 7 +- frontend/src/app/auth/auth.interface.ts | 11 + frontend/src/app/auth/auth.service.ts | 18 +- 58 files changed, 886 insertions(+), 239 deletions(-) rename backend/src/authentication/{services/auth-manager.service.spec.ts => auth.service.spec.ts} (93%) rename backend/src/authentication/{services/auth-manager.service.ts => auth.service.ts} (92%) create mode 100644 backend/src/authentication/providers/auth-providers.constants.ts rename backend/src/authentication/{models/auth-method.ts => providers/auth-providers.models.ts} (92%) create mode 100644 backend/src/authentication/providers/auth-providers.utils.ts create mode 100644 backend/src/authentication/providers/ldap/auth-ldap.config.ts rename backend/src/authentication/{constants/auth-ldap.ts => providers/ldap/auth-ldap.constants.ts} (100%) rename backend/src/authentication/{services/auth-methods/auth-method-ldap.service.spec.ts => providers/ldap/auth-provider-ldap.service.spec.ts} (98%) rename backend/src/authentication/{services/auth-methods/auth-method-ldap.service.ts => providers/ldap/auth-provider-ldap.service.ts} (98%) rename backend/src/authentication/{services/auth-methods/auth-method-database.service.spec.ts => providers/mysql/auth-provider-mysql.service.spec.ts} (75%) rename backend/src/authentication/{services/auth-methods/auth-method-database.service.ts => providers/mysql/auth-provider-mysql.service.ts} (88%) create mode 100644 backend/src/authentication/providers/oidc/auth-method-oidc.module.ts create mode 100644 backend/src/authentication/providers/oidc/auth-oidc.config.ts create mode 100644 backend/src/authentication/providers/oidc/auth-oidc.constants.ts create mode 100644 backend/src/authentication/providers/oidc/auth-oidc.controller.ts create mode 100644 backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts rename backend/src/authentication/{services/auth-methods/auth-method-two-fa.service.spec.ts => providers/two-fa/auth-provider-two-fa.service.spec.ts} (97%) rename backend/src/authentication/{services/auth-methods/auth-method-two-fa.service.ts => providers/two-fa/auth-provider-two-fa.service.ts} (97%) rename backend/src/authentication/{guards => providers/two-fa}/auth-two-fa-guard.ts (67%) create mode 100644 backend/src/authentication/providers/two-fa/auth-two-fa.config.ts rename backend/src/authentication/{dto/two-fa-verify.dto.ts => providers/two-fa/auth-two-fa.dtos.ts} (76%) rename backend/src/authentication/{interfaces/two-fa-setup.interface.ts => providers/two-fa/auth-two-fa.interfaces.ts} (100%) create mode 100644 frontend/src/app/auth/auth.interface.ts diff --git a/backend/package.json b/backend/package.json index af87f11f..22e68977 100755 --- a/backend/package.json +++ b/backend/package.json @@ -69,6 +69,7 @@ "mysql2": "^3.11.4", "nestjs-pino": "^4.3.0", "nodemailer": "^7.0.0", + "openid-client": "^6.8.1", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", diff --git a/backend/src/applications/links/services/links-manager.service.spec.ts b/backend/src/applications/links/services/links-manager.service.spec.ts index 3c824c3b..9073f8a5 100644 --- a/backend/src/applications/links/services/links-manager.service.spec.ts +++ b/backend/src/applications/links/services/links-manager.service.spec.ts @@ -8,7 +8,7 @@ import { HttpService } from '@nestjs/axios' import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' -import { AuthManager } from '../../../authentication/services/auth-manager.service' +import { AuthManager } from '../../../authentication/auth.service' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { ContextManager } from '../../../infrastructure/context/services/context-manager.service' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' diff --git a/backend/src/applications/links/services/links-manager.service.ts b/backend/src/applications/links/services/links-manager.service.ts index 30092543..4538af48 100644 --- a/backend/src/applications/links/services/links-manager.service.ts +++ b/backend/src/applications/links/services/links-manager.service.ts @@ -6,9 +6,9 @@ import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' +import { AuthManager } from '../../../authentication/auth.service' import { LoginResponseDto } from '../../../authentication/dto/login-response.dto' import { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface' -import { AuthManager } from '../../../authentication/services/auth-manager.service' import { serverConfig } from '../../../configuration/config.environment' import { FilesManager } from '../../files/services/files-manager.service' import { SendFile } from '../../files/utils/send-file' diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts index e9f3dd10..33bf2597 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts @@ -10,9 +10,9 @@ import { Test, TestingModule } from '@nestjs/testing' import { FastifyReply } from 'fastify' import crypto from 'node:crypto' import fs from 'node:fs/promises' -import { AuthMethod } from '../../../authentication/models/auth-method' -import { AuthManager } from '../../../authentication/services/auth-manager.service' -import { AuthMethod2FA } from '../../../authentication/services/auth-methods/auth-method-two-fa.service' +import { AuthManager } from '../../../authentication/auth.service' +import { AuthProvider } from '../../../authentication/providers/auth-providers.models' +import { AuthProvider2FA } from '../../../authentication/providers/two-fa/auth-provider-two-fa.service' import * as commonFunctions from '../../../common/functions' import * as commonShared from '../../../common/shared' import { configuration } from '../../../configuration/config.environment' @@ -119,8 +119,8 @@ describe(SyncClientsManager.name, () => { { provide: SyncQueries, useValue: syncQueries }, { provide: UsersManager, useValue: usersManager }, { provide: AuthManager, useValue: authManager }, - { provide: AuthMethod, useValue: authMethod }, - { provide: AuthMethod2FA, useValue: {} } + { provide: AuthProvider, useValue: authMethod }, + { provide: AuthProvider2FA, useValue: {} } ] }).compile() diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.ts b/backend/src/applications/sync/services/sync-clients-manager.service.ts index 6281a53c..7045eb25 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.ts @@ -11,9 +11,9 @@ import { FastifyReply } from 'fastify' import crypto from 'node:crypto' import fs from 'node:fs/promises' import path from 'node:path' -import { AuthMethod } from '../../../authentication/models/auth-method' -import { AuthManager } from '../../../authentication/services/auth-manager.service' -import { AuthMethod2FA } from '../../../authentication/services/auth-methods/auth-method-two-fa.service' +import { AuthManager } from '../../../authentication/auth.service' +import { AuthProvider } from '../../../authentication/providers/auth-providers.models' +import { AuthProvider2FA } from '../../../authentication/providers/two-fa/auth-provider-two-fa.service' import { convertHumanTimeToSeconds } from '../../../common/functions' import { currentTimeStamp } from '../../../common/shared' import { STATIC_PATH } from '../../../configuration/config.constants' @@ -42,8 +42,8 @@ export class SyncClientsManager { constructor( private readonly http: HttpService, private readonly authManager: AuthManager, - private readonly authMethod: AuthMethod, - private readonly authMethod2Fa: AuthMethod2FA, + private readonly authMethod: AuthProvider, + private readonly authMethod2Fa: AuthProvider2FA, private readonly usersManager: UsersManager, private readonly syncQueries: SyncQueries ) {} diff --git a/backend/src/applications/users/admin-users.controller.spec.ts b/backend/src/applications/users/admin-users.controller.spec.ts index ac982296..91c3af91 100644 --- a/backend/src/applications/users/admin-users.controller.spec.ts +++ b/backend/src/applications/users/admin-users.controller.spec.ts @@ -7,9 +7,9 @@ import { ConfigModule } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' -import { AuthTwoFaGuard } from '../../authentication/guards/auth-two-fa-guard' -import { AuthManager } from '../../authentication/services/auth-manager.service' -import { AuthMethod2FA } from '../../authentication/services/auth-methods/auth-method-two-fa.service' +import { AuthManager } from '../../authentication/auth.service' +import { AuthProvider2FA } from '../../authentication/providers/two-fa/auth-provider-two-fa.service' +import { AuthTwoFaGuard } from '../../authentication/providers/two-fa/auth-two-fa-guard' import { exportConfiguration } from '../../configuration/config.environment' import { Cache } from '../../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../../infrastructure/database/constants' @@ -33,7 +33,7 @@ describe(AdminUsersController.name, () => { provide: Cache, useValue: {} }, - { provide: AuthMethod2FA, useValue: {} }, + { provide: AuthProvider2FA, useValue: {} }, { provide: AuthTwoFaGuard, useValue: {} }, { provide: NotificationsManager, useValue: {} }, JwtService, diff --git a/backend/src/applications/users/admin-users.controller.ts b/backend/src/applications/users/admin-users.controller.ts index b38afa76..90c196dd 100644 --- a/backend/src/applications/users/admin-users.controller.ts +++ b/backend/src/applications/users/admin-users.controller.ts @@ -7,7 +7,7 @@ import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put, Res, Search, UseGuards } from '@nestjs/common' import { FastifyReply } from 'fastify' import { LoginResponseDto } from '../../authentication/dto/login-response.dto' -import { AuthTwoFaGuard, AuthTwoFaGuardWithoutPassword } from '../../authentication/guards/auth-two-fa-guard' +import { AuthTwoFaGuard, AuthTwoFaGuardWithoutPassword } from '../../authentication/providers/two-fa/auth-two-fa-guard' import { GROUP_TYPE } from './constants/group' import { ADMIN_USERS_ROUTE } from './constants/routes' import { USER_ROLE } from './constants/user' diff --git a/backend/src/applications/users/services/admin-users-manager.service.spec.ts b/backend/src/applications/users/services/admin-users-manager.service.spec.ts index 5cdc1e8b..d2e39219 100644 --- a/backend/src/applications/users/services/admin-users-manager.service.spec.ts +++ b/backend/src/applications/users/services/admin-users-manager.service.spec.ts @@ -6,7 +6,7 @@ import { HttpException } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' -import { AuthManager } from '../../../authentication/services/auth-manager.service' +import { AuthManager } from '../../../authentication/auth.service' import { GROUP_TYPE } from '../constants/group' import { USER_GROUP_ROLE, USER_ROLE } from '../constants/user' import type { CreateOrUpdateGroupDto } from '../dto/create-or-update-group.dto' diff --git a/backend/src/applications/users/services/admin-users-manager.service.ts b/backend/src/applications/users/services/admin-users-manager.service.ts index ef04eed9..70ef7ddf 100644 --- a/backend/src/applications/users/services/admin-users-manager.service.ts +++ b/backend/src/applications/users/services/admin-users-manager.service.ts @@ -6,8 +6,8 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { FastifyReply } from 'fastify' +import { AuthManager } from '../../../authentication/auth.service' import { LoginResponseDto } from '../../../authentication/dto/login-response.dto' -import { AuthManager } from '../../../authentication/services/auth-manager.service' import { anonymizePassword, hashPassword } from '../../../common/functions' import { isPathExists, moveFiles, removeFiles } from '../../files/utils/files' import { GROUP_TYPE } from '../constants/group' diff --git a/backend/src/applications/users/services/users-manager.service.spec.ts b/backend/src/applications/users/services/users-manager.service.spec.ts index b8232a6e..49409318 100755 --- a/backend/src/applications/users/services/users-manager.service.spec.ts +++ b/backend/src/applications/users/services/users-manager.service.spec.ts @@ -8,7 +8,7 @@ import { Test, TestingModule } from '@nestjs/testing' import bcrypt from 'bcryptjs' import path from 'node:path' import { Readable } from 'node:stream' -import { AuthManager } from '../../../authentication/services/auth-manager.service' +import { AuthManager } from '../../../authentication/auth.service' import { comparePassword } from '../../../common/functions' import * as imageModule from '../../../common/image' import { pngMimeType, svgMimeType } from '../../../common/image' diff --git a/backend/src/applications/users/users.controller.spec.ts b/backend/src/applications/users/users.controller.spec.ts index 44efc52d..62a3e78a 100755 --- a/backend/src/applications/users/users.controller.spec.ts +++ b/backend/src/applications/users/users.controller.spec.ts @@ -7,8 +7,8 @@ import { ConfigModule } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' -import { AuthManager } from '../../authentication/services/auth-manager.service' -import { AuthMethod2FA } from '../../authentication/services/auth-methods/auth-method-two-fa.service' +import { AuthManager } from '../../authentication/auth.service' +import { AuthProvider2FA } from '../../authentication/providers/two-fa/auth-provider-two-fa.service' import { exportConfiguration } from '../../configuration/config.environment' import { Cache } from '../../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../../infrastructure/database/constants' @@ -44,7 +44,7 @@ describe(UsersController.name, () => { AdminUsersQueries, AuthManager, JwtService, - AuthMethod2FA, + AuthProvider2FA, { provide: NotificationsManager, useValue: {} } ] }).compile() diff --git a/backend/src/applications/users/users.controller.ts b/backend/src/applications/users/users.controller.ts index 50e9c0fb..59b90d12 100755 --- a/backend/src/applications/users/users.controller.ts +++ b/backend/src/applications/users/users.controller.ts @@ -7,8 +7,8 @@ import { Body, Controller, Delete, Get, Header, Param, ParseIntPipe, Patch, Post, Put, Req, Search, StreamableFile, UseGuards } from '@nestjs/common' import { createReadStream } from 'fs' import { LoginResponseDto } from '../../authentication/dto/login-response.dto' -import { AuthTwoFaGuardWithoutPassword } from '../../authentication/guards/auth-two-fa-guard' import { FastifyAuthenticatedRequest } from '../../authentication/interfaces/auth-request.interface' +import { AuthTwoFaGuardWithoutPassword } from '../../authentication/providers/two-fa/auth-two-fa-guard' import { makeContentDispositionAttachment } from '../files/utils/send-file' import { USERS_ROUTE } from './constants/routes' import { USER_PERMISSION, USER_ROLE } from './constants/user' diff --git a/backend/src/applications/users/users.e2e-spec.ts b/backend/src/applications/users/users.e2e-spec.ts index 5c4b8fd4..fd62d6ac 100644 --- a/backend/src/applications/users/users.e2e-spec.ts +++ b/backend/src/applications/users/users.e2e-spec.ts @@ -6,8 +6,8 @@ import { NestFastifyApplication } from '@nestjs/platform-fastify' import { appBootstrap } from '../../app.bootstrap' +import { AuthManager } from '../../authentication/auth.service' import { TokenResponseDto } from '../../authentication/dto/token-response.dto' -import { AuthManager } from '../../authentication/services/auth-manager.service' import { dbCheckConnection } from '../../infrastructure/database/utils' import { API_USERS_AVATAR, API_USERS_ME } from './constants/routes' import { USER_ROLE } from './constants/user' diff --git a/backend/src/authentication/auth.config.ts b/backend/src/authentication/auth.config.ts index e1eb9402..8a7f0c3b 100644 --- a/backend/src/authentication/auth.config.ts +++ b/backend/src/authentication/auth.config.ts @@ -4,42 +4,13 @@ * See the LICENSE file for licensing details */ -import { Exclude, Transform, Type } from 'class-transformer' -import { - ArrayNotEmpty, - IsArray, - IsBoolean, - IsDefined, - IsEnum, - IsIn, - IsNotEmpty, - IsNotEmptyObject, - IsObject, - IsOptional, - IsString, - ValidateIf, - ValidateNested -} from 'class-validator' -import { SERVER_NAME } from '../common/shared' +import { Exclude, Type } from 'class-transformer' +import { IsDefined, IsEnum, IsIn, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { ACCESS_KEY, CSRF_KEY, REFRESH_KEY, WS_KEY } from './constants/auth' -import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './constants/auth-ldap' - -export class AuthMfaTotpConfig { - @IsBoolean() - enabled = true - - @IsString() - issuer = SERVER_NAME -} - -export class AuthMfaConfig { - @IsDefined() - @IsNotEmptyObject() - @IsObject() - @ValidateNested() - @Type(() => AuthMfaTotpConfig) - totp: AuthMfaTotpConfig = new AuthMfaTotpConfig() -} +import { AUTH_PROVIDER } from './providers/auth-providers.constants' +import { AuthMethodLDAPConfig } from './providers/ldap/auth-ldap.config' +import { AuthMethodOIDCConfig } from './providers/oidc/auth-oidc.config' +import { AuthMFAConfig } from './providers/two-fa/auth-two-fa.config' export class AuthTokenAccessConfig { @Exclude({ toClassOnly: true }) @@ -111,58 +82,10 @@ export class AuthTokenConfig { ws: AuthTokenWSConfig } -export class AuthMethodLdapAttributesConfig { - @IsOptional() - @IsString() - @Transform(({ value }) => value || LDAP_LOGIN_ATTR.UID) - @IsEnum(LDAP_LOGIN_ATTR) - login: LDAP_LOGIN_ATTR = LDAP_LOGIN_ATTR.UID - - @IsOptional() - @IsString() - @Transform(({ value }) => value || LDAP_COMMON_ATTR.MAIL) - email: string = LDAP_COMMON_ATTR.MAIL -} - -export class AuthMethodLdapConfig { - @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value)) - @ArrayNotEmpty() - @IsArray() - @IsString({ each: true }) - servers: string[] - - @IsString() - @IsNotEmpty() - baseDN: string - - @IsOptional() - @IsString() - filter?: string - - @IsDefined() - @IsNotEmptyObject() - @IsObject() - @ValidateNested() - @Type(() => AuthMethodLdapAttributesConfig) - attributes: AuthMethodLdapAttributesConfig = new AuthMethodLdapAttributesConfig() - - @IsOptional() - @IsString() - adminGroup?: string - - @IsOptional() - @IsString() - upnSuffix?: string - - @IsOptional() - @IsString() - netbiosName?: string -} - export class AuthConfig { @IsString() - @IsIn(['mysql', 'ldap']) - method: 'mysql' | 'ldap' = 'mysql' + @IsEnum(AUTH_PROVIDER) + method: AUTH_PROVIDER = AUTH_PROVIDER.MYSQL @IsOptional() @IsString() @@ -172,8 +95,8 @@ export class AuthConfig { @IsNotEmptyObject() @IsObject() @ValidateNested() - @Type(() => AuthMfaConfig) - mfa: AuthMfaConfig = new AuthMfaConfig() + @Type(() => AuthMFAConfig) + mfa: AuthMFAConfig = new AuthMFAConfig() @IsString() @IsIn(['lax', 'strict']) @@ -190,6 +113,13 @@ export class AuthConfig { @IsDefined() @IsObject() @ValidateNested() - @Type(() => AuthMethodLdapConfig) - ldap: AuthMethodLdapConfig + @Type(() => AuthMethodLDAPConfig) + ldap: AuthMethodLDAPConfig + + @ValidateIf((o: AuthConfig) => o.method === 'oidc') + @IsDefined() + @IsObject() + @ValidateNested() + @Type(() => AuthMethodOIDCConfig) + oidc: AuthMethodOIDCConfig = new AuthMethodOIDCConfig() } diff --git a/backend/src/authentication/auth.controller.spec.ts b/backend/src/authentication/auth.controller.spec.ts index d6a679a7..6764edfb 100755 --- a/backend/src/authentication/auth.controller.spec.ts +++ b/backend/src/authentication/auth.controller.spec.ts @@ -19,12 +19,12 @@ import { Cache } from '../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../infrastructure/database/constants' import { AuthConfig } from './auth.config' import { AuthController } from './auth.controller' +import { AuthManager } from './auth.service' import { TOKEN_PATHS } from './constants/auth' import { LoginResponseDto } from './dto/login-response.dto' -import { AuthTwoFaGuard } from './guards/auth-two-fa-guard' import { TOKEN_TYPE } from './interfaces/token.interface' -import { AuthManager } from './services/auth-manager.service' -import { AuthMethod2FA } from './services/auth-methods/auth-method-two-fa.service' +import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service' +import { AuthTwoFaGuard } from './providers/two-fa/auth-two-fa-guard' describe(AuthController.name, () => { let module: TestingModule @@ -40,7 +40,7 @@ describe(AuthController.name, () => { ConfigService, AuthManager, JwtService, - AuthMethod2FA, + AuthProvider2FA, AuthTwoFaGuard, { provide: DB_TOKEN_PROVIDER, useValue: {} }, { provide: Cache, useValue: {} }, diff --git a/backend/src/authentication/auth.controller.ts b/backend/src/authentication/auth.controller.ts index 98d8e8e7..7a6fa152 100755 --- a/backend/src/authentication/auth.controller.ts +++ b/backend/src/authentication/auth.controller.ts @@ -11,26 +11,26 @@ import { UserHaveRole } from '../applications/users/decorators/roles.decorator' import { GetUser } from '../applications/users/decorators/user.decorator' import { UserRolesGuard } from '../applications/users/guards/roles.guard' import { UserModel } from '../applications/users/models/user.model' +import { AuthManager } from './auth.service' import { ACCESS_KEY, TOKEN_PATHS } from './constants/auth' import { AUTH_ROUTE } from './constants/routes' import { AuthTokenSkip } from './decorators/auth-token-skip.decorator' -import { LoginResponseDto, LoginVerify2FaDto, TwoFaResponseDto } from './dto/login-response.dto' +import { LoginResponseDto, LoginVerify2FaDto } from './dto/login-response.dto' import { TokenResponseDto } from './dto/token-response.dto' -import { TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from './dto/two-fa-verify.dto' import { AuthLocalGuard } from './guards/auth-local.guard' import { AuthTokenRefreshGuard } from './guards/auth-token-refresh.guard' -import { AuthTwoFaGuard } from './guards/auth-two-fa-guard' import { FastifyAuthenticatedRequest } from './interfaces/auth-request.interface' import { TOKEN_TYPE } from './interfaces/token.interface' -import { TwoFaSetup, TwoFaVerifyResult } from './interfaces/two-fa-setup.interface' -import { AuthManager } from './services/auth-manager.service' -import { AuthMethod2FA } from './services/auth-methods/auth-method-two-fa.service' +import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service' +import { AuthTwoFaGuard } from './providers/two-fa/auth-two-fa-guard' +import { TwoFaResponseDto, TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from './providers/two-fa/auth-two-fa.dtos' +import { TwoFaSetup, TwoFaVerifyResult } from './providers/two-fa/auth-two-fa.interfaces' @Controller(AUTH_ROUTE.BASE) export class AuthController { constructor( private readonly authManager: AuthManager, - private readonly authMethod2FA: AuthMethod2FA + private readonly authMethod2FA: AuthProvider2FA ) {} @Post(AUTH_ROUTE.LOGIN) diff --git a/backend/src/authentication/auth.module.ts b/backend/src/authentication/auth.module.ts index d1afc9a7..89ed19fd 100755 --- a/backend/src/authentication/auth.module.ts +++ b/backend/src/authentication/auth.module.ts @@ -11,6 +11,7 @@ import { PassportModule } from '@nestjs/passport' import { UsersModule } from '../applications/users/users.module' import { configuration } from '../configuration/config.environment' import { AuthController } from './auth.controller' +import { AuthManager } from './auth.service' import { AuthAnonymousGuard } from './guards/auth-anonymous.guard' import { AuthAnonymousStrategy } from './guards/auth-anonymous.strategy' import { AuthBasicGuard } from './guards/auth-basic.guard' @@ -21,15 +22,20 @@ import { AuthTokenAccessGuard } from './guards/auth-token-access.guard' import { AuthTokenAccessStrategy } from './guards/auth-token-access.strategy' import { AuthTokenRefreshGuard } from './guards/auth-token-refresh.guard' import { AuthTokenRefreshStrategy } from './guards/auth-token-refresh.strategy' -import { AuthMethod } from './models/auth-method' -import { AuthManager } from './services/auth-manager.service' -import { AuthMethodDatabase } from './services/auth-methods/auth-method-database.service' -import { AuthMethodLdapService } from './services/auth-methods/auth-method-ldap.service' -import { AuthMethod2FA } from './services/auth-methods/auth-method-two-fa.service' +import { AUTH_PROVIDER } from './providers/auth-providers.constants' +import { AuthProvider } from './providers/auth-providers.models' +import { selectAuthProvider } from './providers/auth-providers.utils' +import { AuthMethodOIDCModule } from './providers/oidc/auth-method-oidc.module' +import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service' @Global() @Module({ - imports: [JwtModule.register({ global: true }), UsersModule, PassportModule], + imports: [ + JwtModule.register({ global: true }), + UsersModule, + PassportModule, + ...(configuration.auth.method === AUTH_PROVIDER.OIDC ? [AuthMethodOIDCModule] : []) + ], controllers: [AuthController], providers: [ { @@ -46,9 +52,9 @@ import { AuthMethod2FA } from './services/auth-methods/auth-method-two-fa.servic AuthBasicStrategy, AuthAnonymousStrategy, AuthManager, - AuthMethod2FA, - { provide: AuthMethod, useClass: configuration.auth.method === 'ldap' ? AuthMethodLdapService : AuthMethodDatabase } + AuthProvider2FA, + selectAuthProvider(configuration.auth.method) ], - exports: [AuthManager, AuthMethod, AuthMethod2FA] + exports: [AuthManager, AuthProvider, AuthProvider2FA] }) export class AuthModule {} diff --git a/backend/src/authentication/services/auth-manager.service.spec.ts b/backend/src/authentication/auth.service.spec.ts similarity index 93% rename from backend/src/authentication/services/auth-manager.service.spec.ts rename to backend/src/authentication/auth.service.spec.ts index 9a717e86..aa7d301b 100755 --- a/backend/src/authentication/services/auth-manager.service.spec.ts +++ b/backend/src/authentication/auth.service.spec.ts @@ -7,7 +7,7 @@ import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' -import { AuthManager } from './auth-manager.service' +import { AuthManager } from './auth.service' describe(AuthManager.name, () => { let authManager: AuthManager diff --git a/backend/src/authentication/services/auth-manager.service.ts b/backend/src/authentication/auth.service.ts similarity index 92% rename from backend/src/authentication/services/auth-manager.service.ts rename to backend/src/authentication/auth.service.ts index 558b2dee..e1273e2a 100755 --- a/backend/src/authentication/services/auth-manager.service.ts +++ b/backend/src/authentication/auth.service.ts @@ -8,16 +8,16 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' import { FastifyReply, FastifyRequest } from 'fastify' import crypto from 'node:crypto' -import { HTTP_CSRF_IGNORED_METHODS } from '../../applications/applications.constants' -import { UserModel } from '../../applications/users/models/user.model' -import { convertHumanTimeToSeconds } from '../../common/functions' -import { currentTimeStamp } from '../../common/shared' -import { configuration, serverConfig } from '../../configuration/config.environment' -import { CSRF_ERROR, CSRF_KEY, TOKEN_2FA_TYPES, TOKEN_PATHS, TOKEN_TYPES } from '../constants/auth' -import { LoginResponseDto, LoginVerify2FaDto } from '../dto/login-response.dto' -import { TokenResponseDto } from '../dto/token-response.dto' -import { JwtIdentity2FaPayload, JwtIdentityPayload, JwtPayload } from '../interfaces/jwt-payload.interface' -import { TOKEN_TYPE } from '../interfaces/token.interface' +import { HTTP_CSRF_IGNORED_METHODS } from '../applications/applications.constants' +import { UserModel } from '../applications/users/models/user.model' +import { convertHumanTimeToSeconds } from '../common/functions' +import { currentTimeStamp } from '../common/shared' +import { configuration, serverConfig } from '../configuration/config.environment' +import { CSRF_ERROR, CSRF_KEY, TOKEN_2FA_TYPES, TOKEN_PATHS, TOKEN_TYPES } from './constants/auth' +import { LoginResponseDto, LoginVerify2FaDto } from './dto/login-response.dto' +import { TokenResponseDto } from './dto/token-response.dto' +import { JwtIdentity2FaPayload, JwtIdentityPayload, JwtPayload } from './interfaces/jwt-payload.interface' +import { TOKEN_TYPE } from './interfaces/token.interface' @Injectable() export class AuthManager { diff --git a/backend/src/authentication/constants/routes.ts b/backend/src/authentication/constants/routes.ts index f5e6efe2..375e0806 100644 --- a/backend/src/authentication/constants/routes.ts +++ b/backend/src/authentication/constants/routes.ts @@ -4,19 +4,22 @@ * See the LICENSE file for licensing details */ -export enum AUTH_ROUTE { - BASE = '/api/auth', - LOGIN = 'login', - LOGOUT = 'logout', - REFRESH = 'refresh', - TOKEN = 'token', - TOKEN_REFRESH = `${AUTH_ROUTE.TOKEN}/refresh`, - WS = 'socket.io', - TWO_FA_BASE = '2fa', - TWO_FA_ENABLE = 'enable', - TWO_FA_DISABLE = 'disable', - TWO_FA_LOGIN_VERIFY = 'login/verify', - TWO_FA_ADMIN_RESET_USER = 'reset/user' +export const AUTH_ROUTE = { + BASE: '/api/auth', + LOGIN: 'login', + LOGOUT: 'logout', + REFRESH: 'refresh', + TOKEN: 'token', + TOKEN_REFRESH: 'token/refresh', + WS: 'socket.io', + TWO_FA_BASE: '2fa', + TWO_FA_ENABLE: 'enable', + TWO_FA_DISABLE: 'disable', + TWO_FA_LOGIN_VERIFY: 'login/verify', + TWO_FA_ADMIN_RESET_USER: 'reset/user', + OIDC_LOGIN: 'oidc/login', + OIDC_CALLBACK: 'oidc/callback', + OIDC_LOGOUT: 'oidc/logout' } export const API_AUTH_LOGIN = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.LOGIN}` diff --git a/backend/src/authentication/dto/login-response.dto.ts b/backend/src/authentication/dto/login-response.dto.ts index 631baa9d..29848550 100644 --- a/backend/src/authentication/dto/login-response.dto.ts +++ b/backend/src/authentication/dto/login-response.dto.ts @@ -22,7 +22,7 @@ export class LoginResponseDto { export class LoginVerify2FaDto { server: ServerConfig - user = { twoFaEnabled: true } + user: { twoFaEnabled: boolean } = { twoFaEnabled: true } token: TokenResponseDto constructor(serverConfig: ServerConfig) { @@ -30,8 +30,3 @@ export class LoginVerify2FaDto { this.token = new TokenResponseDto() } } - -export class TwoFaResponseDto extends LoginResponseDto { - success: boolean - message: string -} diff --git a/backend/src/authentication/guards/auth-basic.guard.spec.ts b/backend/src/authentication/guards/auth-basic.guard.spec.ts index 6a3192b9..28d7d5a9 100644 --- a/backend/src/authentication/guards/auth-basic.guard.spec.ts +++ b/backend/src/authentication/guards/auth-basic.guard.spec.ts @@ -12,14 +12,14 @@ import { UserModel } from '../../applications/users/models/user.model' import { generateUserTest } from '../../applications/users/utils/test' import { WEBDAV_BASE_PATH } from '../../applications/webdav/constants/routes' import { Cache } from '../../infrastructure/cache/services/cache.service' -import { AuthMethod } from '../models/auth-method' +import { AuthProvider } from '../providers/auth-providers.models' import { AuthBasicGuard } from './auth-basic.guard' import { AuthBasicStrategy } from './auth-basic.strategy' describe(AuthBasicGuard.name, () => { let authBasicGuard: AuthBasicGuard let authBasicStrategy: AuthBasicStrategy - let authMethod: AuthMethod + let authMethod: AuthProvider let cache: Cache let userTest: UserModel let encodedAuth: string @@ -31,7 +31,7 @@ describe(AuthBasicGuard.name, () => { AuthBasicGuard, AuthBasicStrategy, { - provide: AuthMethod, + provide: AuthProvider, useValue: { validateUser: async () => null } @@ -56,7 +56,7 @@ describe(AuthBasicGuard.name, () => { authBasicGuard = module.get(AuthBasicGuard) authBasicStrategy = module.get(AuthBasicStrategy) - authMethod = module.get(AuthMethod) + authMethod = module.get(AuthProvider) cache = module.get(Cache) userTest = new UserModel(generateUserTest(), false) encodedAuth = Buffer.from(`${userTest.login}:${userTest.password}`).toString('base64') diff --git a/backend/src/authentication/guards/auth-basic.strategy.ts b/backend/src/authentication/guards/auth-basic.strategy.ts index 98813ccb..3367ae7a 100644 --- a/backend/src/authentication/guards/auth-basic.strategy.ts +++ b/backend/src/authentication/guards/auth-basic.strategy.ts @@ -13,7 +13,7 @@ import { UserModel } from '../../applications/users/models/user.model' import { SERVER_NAME } from '../../common/shared' import { Cache } from '../../infrastructure/cache/services/cache.service' import { AUTH_SCOPE } from '../constants/scope' -import { AuthMethod } from '../models/auth-method' +import { AuthProvider } from '../providers/auth-providers.models' import { HttpBasicStrategy } from './implementations/http-basic.strategy' @Injectable() @@ -22,7 +22,7 @@ export class AuthBasicStrategy extends PassportStrategy(HttpBasicStrategy, 'basi private readonly CACHE_KEY_PREFIX = 'auth-webdav' constructor( - private readonly authMethod: AuthMethod, + private readonly authProvider: AuthProvider, private readonly cache: Cache, private readonly logger: PinoLogger ) { @@ -44,7 +44,7 @@ export class AuthBasicStrategy extends PassportStrategy(HttpBasicStrategy, 'basi // warning: plainToInstance do not use constructor to instantiate the class return plainToInstance(UserModel, userFromCache) } - const userFromDB: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip, AUTH_SCOPE.WEBDAV) + const userFromDB: UserModel = await this.authProvider.validateUser(loginOrEmail, password, req.ip, AUTH_SCOPE.WEBDAV) if (userFromDB !== null) { userFromDB.removePassword() } diff --git a/backend/src/authentication/guards/auth-local.guard.spec.ts b/backend/src/authentication/guards/auth-local.guard.spec.ts index d126182c..443267a9 100644 --- a/backend/src/authentication/guards/auth-local.guard.spec.ts +++ b/backend/src/authentication/guards/auth-local.guard.spec.ts @@ -10,13 +10,13 @@ import { Test, TestingModule } from '@nestjs/testing' import { PinoLogger } from 'nestjs-pino' import { UserModel } from '../../applications/users/models/user.model' import { generateUserTest } from '../../applications/users/utils/test' -import { AuthMethod } from '../models/auth-method' +import { AuthProvider } from '../providers/auth-providers.models' import { AuthLocalGuard } from './auth-local.guard' import { AuthLocalStrategy } from './auth-local.strategy' describe(AuthLocalGuard.name, () => { let authLocalGuard: AuthLocalGuard - let authMethod: AuthMethod + let authMethod: AuthProvider let userTest: UserModel let context: DeepMocked @@ -25,7 +25,7 @@ describe(AuthLocalGuard.name, () => { providers: [ AuthLocalGuard, AuthLocalStrategy, - { provide: AuthMethod, useValue: {} }, + { provide: AuthProvider, useValue: {} }, { provide: PinoLogger, useValue: { @@ -36,7 +36,7 @@ describe(AuthLocalGuard.name, () => { }).compile() authLocalGuard = module.get(AuthLocalGuard) - authMethod = module.get(AuthMethod) + authMethod = module.get(AuthProvider) userTest = new UserModel(generateUserTest(), false) context = createMock() }) diff --git a/backend/src/authentication/guards/auth-local.strategy.ts b/backend/src/authentication/guards/auth-local.strategy.ts index e555cb4f..b083e720 100755 --- a/backend/src/authentication/guards/auth-local.strategy.ts +++ b/backend/src/authentication/guards/auth-local.strategy.ts @@ -10,12 +10,12 @@ import type { FastifyRequest } from 'fastify' import { PinoLogger } from 'nestjs-pino' import { Strategy } from 'passport-local' import type { UserModel } from '../../applications/users/models/user.model' -import { AuthMethod } from '../models/auth-method' +import { AuthProvider } from '../providers/auth-providers.models' @Injectable() export class AuthLocalStrategy extends PassportStrategy(Strategy, 'local') implements AbstractStrategy { constructor( - private readonly authMethod: AuthMethod, + private readonly authMethod: AuthProvider, private readonly logger: PinoLogger ) { super({ usernameField: 'login', passwordField: 'password', passReqToCallback: true }) diff --git a/backend/src/authentication/guards/auth-token-access.guard.spec.ts b/backend/src/authentication/guards/auth-token-access.guard.spec.ts index 7dc5df40..ce21d492 100644 --- a/backend/src/authentication/guards/auth-token-access.guard.spec.ts +++ b/backend/src/authentication/guards/auth-token-access.guard.spec.ts @@ -18,12 +18,12 @@ import { UsersManager } from '../../applications/users/services/users-manager.se import { WEB_DAV_CONTEXT, WebDAVContext } from '../../applications/webdav/decorators/webdav-context.decorator' import { exportConfiguration } from '../../configuration/config.environment' import { AuthConfig } from '../auth.config' +import { AuthManager } from '../auth.service' import { CSRF_ERROR } from '../constants/auth' import { AuthTokenOptional } from '../decorators/auth-token-optional.decorator' import { AUTH_TOKEN_SKIP, AuthTokenSkip } from '../decorators/auth-token-skip.decorator' import { JwtPayload } from '../interfaces/jwt-payload.interface' import { TOKEN_TYPE } from '../interfaces/token.interface' -import { AuthManager } from '../services/auth-manager.service' import { AuthAnonymousGuard } from './auth-anonymous.guard' import { AuthAnonymousStrategy } from './auth-anonymous.strategy' import { AuthTokenAccessGuard } from './auth-token-access.guard' diff --git a/backend/src/authentication/guards/auth-token-access.strategy.ts b/backend/src/authentication/guards/auth-token-access.strategy.ts index 2fc5b3a3..3f1f56e6 100755 --- a/backend/src/authentication/guards/auth-token-access.strategy.ts +++ b/backend/src/authentication/guards/auth-token-access.strategy.ts @@ -11,9 +11,9 @@ import { PinoLogger } from 'nestjs-pino' import { ExtractJwt, Strategy } from 'passport-jwt' import { UserModel } from '../../applications/users/models/user.model' import { configuration } from '../../configuration/config.environment' +import { AuthManager } from '../auth.service' import { JwtPayload } from '../interfaces/jwt-payload.interface' import { TOKEN_TYPE } from '../interfaces/token.interface' -import { AuthManager } from '../services/auth-manager.service' @Injectable() export class AuthTokenAccessStrategy extends PassportStrategy(Strategy, 'tokenAccess') implements AbstractStrategy { diff --git a/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts b/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts index 49b67f26..d93da5b0 100644 --- a/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts +++ b/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts @@ -16,10 +16,10 @@ import crypto from 'node:crypto' import { UsersManager } from '../../applications/users/services/users-manager.service' import { exportConfiguration } from '../../configuration/config.environment' import { AuthConfig } from '../auth.config' +import { AuthManager } from '../auth.service' import { CSRF_ERROR } from '../constants/auth' import { JwtPayload } from '../interfaces/jwt-payload.interface' import { TOKEN_TYPE } from '../interfaces/token.interface' -import { AuthManager } from '../services/auth-manager.service' import { AuthTokenRefreshGuard } from './auth-token-refresh.guard' import { AuthTokenRefreshStrategy } from './auth-token-refresh.strategy' diff --git a/backend/src/authentication/guards/auth-token-refresh.strategy.ts b/backend/src/authentication/guards/auth-token-refresh.strategy.ts index ad2a291e..48a523ba 100755 --- a/backend/src/authentication/guards/auth-token-refresh.strategy.ts +++ b/backend/src/authentication/guards/auth-token-refresh.strategy.ts @@ -11,9 +11,9 @@ import { PinoLogger } from 'nestjs-pino' import { ExtractJwt, Strategy } from 'passport-jwt' import { UserModel } from '../../applications/users/models/user.model' import { configuration } from '../../configuration/config.environment' +import { AuthManager } from '../auth.service' import { JwtPayload } from '../interfaces/jwt-payload.interface' import { TOKEN_TYPE } from '../interfaces/token.interface' -import { AuthManager } from '../services/auth-manager.service' @Injectable() export class AuthTokenRefreshStrategy extends PassportStrategy(Strategy, 'tokenRefresh') implements AbstractStrategy { diff --git a/backend/src/authentication/providers/auth-providers.constants.ts b/backend/src/authentication/providers/auth-providers.constants.ts new file mode 100644 index 00000000..72d5a506 --- /dev/null +++ b/backend/src/authentication/providers/auth-providers.constants.ts @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +export enum AUTH_PROVIDER { + MYSQL = 'mysql', + LDAP = 'ldap', + OIDC = 'oidc' +} diff --git a/backend/src/authentication/models/auth-method.ts b/backend/src/authentication/providers/auth-providers.models.ts similarity index 92% rename from backend/src/authentication/models/auth-method.ts rename to backend/src/authentication/providers/auth-providers.models.ts index 1b6aed9f..c42479e3 100644 --- a/backend/src/authentication/models/auth-method.ts +++ b/backend/src/authentication/providers/auth-providers.models.ts @@ -7,6 +7,6 @@ import type { UserModel } from '../../applications/users/models/user.model' import type { AUTH_SCOPE } from '../constants/scope' -export abstract class AuthMethod { +export abstract class AuthProvider { abstract validateUser(loginOrEmail: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise } diff --git a/backend/src/authentication/providers/auth-providers.utils.ts b/backend/src/authentication/providers/auth-providers.utils.ts new file mode 100644 index 00000000..c52419bd --- /dev/null +++ b/backend/src/authentication/providers/auth-providers.utils.ts @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Provider } from '@nestjs/common' +import { AUTH_PROVIDER } from './auth-providers.constants' + +import { AuthProvider } from './auth-providers.models' +import { AuthProviderLDAP } from './ldap/auth-provider-ldap.service' +import { AuthProviderMySQL } from './mysql/auth-provider-mysql.service' +import { AuthProviderOIDC } from './oidc/auth-provider-oidc.service' + +export function selectAuthProvider(provider: AUTH_PROVIDER): Provider { + switch (provider) { + case AUTH_PROVIDER.OIDC: + // AuthMethodOIDC is already provided by AuthMethodOIDCModule + return { provide: AuthProvider, useExisting: AuthProviderOIDC } + + case AUTH_PROVIDER.LDAP: + return { provide: AuthProvider, useClass: AuthProviderLDAP } + + case AUTH_PROVIDER.MYSQL: + return { provide: AuthProvider, useClass: AuthProviderMySQL } + + default: + return { provide: AuthProvider, useClass: AuthProviderMySQL } + } +} diff --git a/backend/src/authentication/providers/ldap/auth-ldap.config.ts b/backend/src/authentication/providers/ldap/auth-ldap.config.ts new file mode 100644 index 00000000..8a88cadd --- /dev/null +++ b/backend/src/authentication/providers/ldap/auth-ldap.config.ts @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Transform, Type } from 'class-transformer' +import { + ArrayNotEmpty, + IsArray, + IsDefined, + IsEnum, + IsNotEmpty, + IsNotEmptyObject, + IsObject, + IsOptional, + IsString, + ValidateNested +} from 'class-validator' +import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' + +export class AuthMethodLDAPAttributesConfig { + @IsOptional() + @Transform(({ value }) => value || LDAP_LOGIN_ATTR.UID) + @IsEnum(LDAP_LOGIN_ATTR) + login: LDAP_LOGIN_ATTR = LDAP_LOGIN_ATTR.UID + + @IsOptional() + @IsString() + @Transform(({ value }) => value || LDAP_COMMON_ATTR.MAIL) + email: string = LDAP_COMMON_ATTR.MAIL +} + +export class AuthMethodLDAPConfig { + @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value)) + @ArrayNotEmpty() + @IsArray() + @IsString({ each: true }) + servers: string[] + + @IsString() + @IsNotEmpty() + baseDN: string + + @IsOptional() + @IsString() + filter?: string + + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => AuthMethodLDAPAttributesConfig) + attributes: AuthMethodLDAPAttributesConfig = new AuthMethodLDAPAttributesConfig() + + @IsOptional() + @IsString() + adminGroup?: string + + @IsOptional() + @IsString() + upnSuffix?: string + + @IsOptional() + @IsString() + netbiosName?: string +} diff --git a/backend/src/authentication/constants/auth-ldap.ts b/backend/src/authentication/providers/ldap/auth-ldap.constants.ts similarity index 100% rename from backend/src/authentication/constants/auth-ldap.ts rename to backend/src/authentication/providers/ldap/auth-ldap.constants.ts diff --git a/backend/src/authentication/services/auth-methods/auth-method-ldap.service.spec.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts similarity index 98% rename from backend/src/authentication/services/auth-methods/auth-method-ldap.service.spec.ts rename to backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts index f452c87f..64ad037c 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-ldap.service.spec.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts @@ -13,8 +13,8 @@ import { AdminUsersManager } from '../../../applications/users/services/admin-us import { UsersManager } from '../../../applications/users/services/users-manager.service' import * as commonFunctions from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' -import { LDAP_LOGIN_ATTR } from '../../constants/auth-ldap' -import { AuthMethodLdapService } from './auth-method-ldap.service' +import { LDAP_LOGIN_ATTR } from './auth-ldap.constants' +import { AuthProviderLDAP } from './auth-provider-ldap.service' // Mock ldapts Client to simulate LDAP behaviors jest.mock('ldapts', () => { @@ -61,8 +61,8 @@ const buildUser = (overrides: Partial = {}) => // -------------------------- -describe(AuthMethodLdapService.name, () => { - let authMethodLdapService: AuthMethodLdapService +describe(AuthProviderLDAP.name, () => { + let authMethodLdapService: AuthProviderLDAP let usersManager: Mocked let adminUsersManager: Mocked const ldapClient = { @@ -89,7 +89,7 @@ describe(AuthMethodLdapService.name, () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - AuthMethodLdapService, + AuthProviderLDAP, { provide: UsersManager, useValue: { @@ -111,7 +111,7 @@ describe(AuthMethodLdapService.name, () => { }).compile() module.useLogger(['fatal']) - authMethodLdapService = module.get(AuthMethodLdapService) + authMethodLdapService = module.get(AuthProviderLDAP) adminUsersManager = module.get>(AdminUsersManager) usersManager = module.get>(UsersManager) }) diff --git a/backend/src/authentication/services/auth-methods/auth-method-ldap.service.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts similarity index 98% rename from backend/src/authentication/services/auth-methods/auth-method-ldap.service.ts rename to backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts index 2dc85bb9..3623fd85 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-ldap.service.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts @@ -14,15 +14,15 @@ import { AdminUsersManager } from '../../../applications/users/services/admin-us import { UsersManager } from '../../../applications/users/services/users-manager.service' import { comparePassword, splitFullName } from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' -import { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from '../../constants/auth-ldap' import type { AUTH_SCOPE } from '../../constants/scope' -import { AuthMethod } from '../../models/auth-method' +import { AuthProvider } from '../auth-providers.models' +import { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' type LdapUserEntry = Entry & Record @Injectable() -export class AuthMethodLdapService implements AuthMethod { - private readonly logger = new Logger(AuthMethodLdapService.name) +export class AuthProviderLDAP implements AuthProvider { + private readonly logger = new Logger(AuthProviderLDAP.name) private readonly ldapConfig = configuration.auth.ldap private readonly isAD = this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.SAM || this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.UPN private clientOptions: ClientOptions = { timeout: 6000, connectTimeout: 6000, url: '' } @@ -148,10 +148,12 @@ export class AuthMethodLdapService implements AuthMethod { } return freshUser } + if (identity.login !== user.login) { this.logger.error(`${this.updateOrCreateUser.name} - user login mismatch : ${identity.login} !== ${user.login}`) throw new HttpException('Account matching error', HttpStatus.FORBIDDEN) } + // Update: check if user information has changed const identityHasChanged: UpdateUserDto = Object.fromEntries( ( @@ -166,6 +168,7 @@ export class AuthMethodLdapService implements AuthMethod { ) ).filter(Boolean) ) + if (Object.keys(identityHasChanged).length > 0) { try { if (identityHasChanged?.role != null) { @@ -174,13 +177,17 @@ export class AuthMethodLdapService implements AuthMethod { delete identityHasChanged.role } } + // Update user properties await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged) + // Extra stuff if (identityHasChanged?.password) { delete identityHasChanged.password } + Object.assign(user, identityHasChanged) + if ('lastName' in identityHasChanged || 'firstName' in identityHasChanged) { // Force fullName update in the current user model user.setFullName(true) diff --git a/backend/src/authentication/services/auth-methods/auth-method-database.service.spec.ts b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts similarity index 75% rename from backend/src/authentication/services/auth-methods/auth-method-database.service.spec.ts rename to backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts index 4778964d..e222f931 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-database.service.spec.ts +++ b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts @@ -16,18 +16,18 @@ import { generateUserTest } from '../../../applications/users/utils/test' import { hashPassword } from '../../../common/functions' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' -import { AuthManager } from '../auth-manager.service' -import { AuthMethodDatabase } from './auth-method-database.service' +import { AuthManager } from '../../auth.service' +import { AuthProviderMySQL } from './auth-provider-mysql.service' -describe(AuthMethodDatabase.name, () => { - let authMethodDatabase: AuthMethodDatabase +describe(AuthProviderMySQL.name, () => { + let authProviderMySQL: AuthProviderMySQL let usersManager: UsersManager let userTest: UserModel beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - AuthMethodDatabase, + AuthProviderMySQL, UsersManager, UsersQueries, AdminUsersManager, @@ -39,7 +39,7 @@ describe(AuthMethodDatabase.name, () => { ] }).compile() - authMethodDatabase = module.get(AuthMethodDatabase) + authProviderMySQL = module.get(AuthProviderMySQL) usersManager = module.get(UsersManager) module.useLogger(['fatal']) // mocks @@ -48,7 +48,7 @@ describe(AuthMethodDatabase.name, () => { }) it('should be defined', () => { - expect(authMethodDatabase).toBeDefined() + expect(authProviderMySQL).toBeDefined() expect(usersManager).toBeDefined() expect(userTest).toBeDefined() }) @@ -56,7 +56,7 @@ describe(AuthMethodDatabase.name, () => { it('should validate the user', async () => { userTest.makePaths = jest.fn() usersManager.findUser = jest.fn().mockReturnValue({ ...userTest, password: await hashPassword(userTest.password) }) - expect(await authMethodDatabase.validateUser(userTest.login, userTest.password)).toBeDefined() + expect(await authProviderMySQL.validateUser(userTest.login, userTest.password)).toBeDefined() expect(userTest.makePaths).toHaveBeenCalled() }) @@ -71,9 +71,9 @@ describe(AuthMethodDatabase.name, () => { cause: { code: Array.from(CONNECT_ERROR_CODE)[0] } }) ) - expect(await authMethodDatabase.validateUser(userTest.login, userTest.password)).toBeNull() - expect(await authMethodDatabase.validateUser(userTest.login, userTest.password)).toBeNull() - await expect(authMethodDatabase.validateUser(userTest.login, userTest.password)).rejects.toThrow(/db error/i) - await expect(authMethodDatabase.validateUser(userTest.login, userTest.password)).rejects.toThrow(/authentication service/i) + expect(await authProviderMySQL.validateUser(userTest.login, userTest.password)).toBeNull() + expect(await authProviderMySQL.validateUser(userTest.login, userTest.password)).toBeNull() + await expect(authProviderMySQL.validateUser(userTest.login, userTest.password)).rejects.toThrow(/db error/i) + await expect(authProviderMySQL.validateUser(userTest.login, userTest.password)).rejects.toThrow(/authentication service/i) }) }) diff --git a/backend/src/authentication/services/auth-methods/auth-method-database.service.ts b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts similarity index 88% rename from backend/src/authentication/services/auth-methods/auth-method-database.service.ts rename to backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts index 0c374338..c993062d 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-database.service.ts +++ b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts @@ -9,11 +9,11 @@ import { CONNECT_ERROR_CODE } from '../../../app.constants' import { UserModel } from '../../../applications/users/models/user.model' import { UsersManager } from '../../../applications/users/services/users-manager.service' import { AUTH_SCOPE } from '../../constants/scope' -import { AuthMethod } from '../../models/auth-method' +import { AuthProvider } from '../auth-providers.models' @Injectable() -export class AuthMethodDatabase implements AuthMethod { - private readonly logger = new Logger(AuthMethodDatabase.name) +export class AuthProviderMySQL implements AuthProvider { + private readonly logger = new Logger(AuthProviderMySQL.name) constructor(private readonly usersManager: UsersManager) {} diff --git a/backend/src/authentication/providers/oidc/auth-method-oidc.module.ts b/backend/src/authentication/providers/oidc/auth-method-oidc.module.ts new file mode 100644 index 00000000..d08660bf --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-method-oidc.module.ts @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Module } from '@nestjs/common' +import { AuthOIDCController } from './auth-oidc.controller' +import { AuthProviderOIDC } from './auth-provider-oidc.service' + +@Module({ + controllers: [AuthOIDCController], + providers: [AuthProviderOIDC], + exports: [AuthProviderOIDC] +}) +export class AuthMethodOIDCModule {} diff --git a/backend/src/authentication/providers/oidc/auth-oidc.config.ts b/backend/src/authentication/providers/oidc/auth-oidc.config.ts new file mode 100644 index 00000000..0b228042 --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-oidc.config.ts @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Transform } from 'class-transformer' +import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator' +import { OAuthTokenEndpoint } from './auth-oidc.constants' + +export class AuthMethodOIDCConfig { + @IsString() + @IsNotEmpty() + issuerUrl: string + + @IsString() + @IsNotEmpty() + clientId: string + + @IsString() + @IsNotEmpty() + clientSecret: string + + @IsOptional() + @Transform(({ value }) => value || OAuthTokenEndpoint.ClientSecretBasic) + @IsEnum(OAuthTokenEndpoint) + clientAuthMethod: OAuthTokenEndpoint = OAuthTokenEndpoint.ClientSecretBasic + + @IsString() + @IsNotEmpty() + redirectUri: string + + @IsOptional() + @IsString() + scope?: string + + @IsOptional() + @IsBoolean() + autoCreateUser? = true + + @IsOptional() + @IsBoolean() + skipSubjectCheck? = false + + @IsOptional() + @IsString() + adminRoleOrGroup?: string +} diff --git a/backend/src/authentication/providers/oidc/auth-oidc.constants.ts b/backend/src/authentication/providers/oidc/auth-oidc.constants.ts new file mode 100644 index 00000000..05f95a63 --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-oidc.constants.ts @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +export enum OAuthTokenEndpoint { + ClientSecretPost = 'client_secret_post', + ClientSecretBasic = 'client_secret_basic' +} + +export const OAuthCookie = { + State: 'oidc_state', + Nonce: 'oidc_nonce', + CodeVerifier: 'oidc_code_verifier' +} as const + +export const OAuthCookieSettings = { httpOnly: true, path: '/', maxAge: 600, sameSite: 'lax' } as const diff --git a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts new file mode 100644 index 00000000..feeb32b6 --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Controller, Get, HttpStatus, Query, Req, Res } from '@nestjs/common' +import { FastifyReply, FastifyRequest } from 'fastify' +import type { UserModel } from '../../../applications/users/models/user.model' +import { AuthManager } from '../../auth.service' +import { AUTH_ROUTE } from '../../constants/routes' +import { AuthTokenSkip } from '../../decorators/auth-token-skip.decorator' +import type { LoginResponseDto } from '../../dto/login-response.dto' +import { AuthProviderOIDC } from './auth-provider-oidc.service' + +@Controller(AUTH_ROUTE.BASE) +export class AuthOIDCController { + constructor( + private readonly authManager: AuthManager, + private readonly authMethodOidc: AuthProviderOIDC + ) {} + + @Get(AUTH_ROUTE.OIDC_LOGIN) + @AuthTokenSkip() + async oidcLogin(@Res() res: FastifyReply): Promise { + const url = await this.authMethodOidc.getAuthorizationUrl(res) + // Redirect to OIDC provider + return res.redirect(url, HttpStatus.FOUND) + } + + @Get(AUTH_ROUTE.OIDC_CALLBACK) + @AuthTokenSkip() + async oidcCallback(@Query() query: Record, @Req() req: FastifyRequest, @Res() res: FastifyReply): Promise { + const user: UserModel = await this.authMethodOidc.handleCallback(req, res, query) + const r: LoginResponseDto = await this.authManager.setCookies(user, res, false) + return res.redirect(this.authMethodOidc.getRedirectCallbackUrl(r.token.access_expiration, r.token.refresh_expiration), HttpStatus.FOUND) + } +} diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts new file mode 100644 index 00000000..1c28b7be --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' +import { FastifyReply, FastifyRequest } from 'fastify' +import { + allowInsecureRequests, + authorizationCodeGrant, + calculatePKCECodeChallenge, + ClientSecretBasic, + ClientSecretPost, + Configuration, + discovery, + fetchUserInfo, + IDToken, + None, + randomNonce, + randomPKCECodeVerifier, + randomState, + skipSubjectCheck, + UserInfoResponse +} from 'openid-client' +import { USER_ROLE } from '../../../applications/users/constants/user' +import type { CreateUserDto, UpdateUserDto } from '../../../applications/users/dto/create-or-update-user.dto' +import { UserModel } from '../../../applications/users/models/user.model' +import { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service' +import { UsersManager } from '../../../applications/users/services/users-manager.service' +import { generateShortUUID, splitFullName } from '../../../common/functions' +import { configuration } from '../../../configuration/config.environment' +import { AUTH_ROUTE } from '../../constants/routes' +import type { AUTH_SCOPE } from '../../constants/scope' +import { TOKEN_TYPE } from '../../interfaces/token.interface' +import { AUTH_PROVIDER } from '../auth-providers.constants' +import { AuthProvider } from '../auth-providers.models' +import { AuthMethodOIDCConfig } from './auth-oidc.config' +import { OAuthCookie, OAuthCookieSettings, OAuthTokenEndpoint } from './auth-oidc.constants' + +@Injectable() +export class AuthProviderOIDC implements AuthProvider { + private readonly logger = new Logger(AuthProviderOIDC.name) + private readonly oidcConfig: AuthMethodOIDCConfig = configuration.auth.oidc + private frontendBaseUrl: string + private config: Configuration = null + + constructor( + private readonly usersManager: UsersManager, + private readonly adminUsersManager: AdminUsersManager + ) {} + + async getConfig(): Promise { + if (!this.config) { + this.config = await this.initializeOIDCClient() + if (!this.config) { + throw new HttpException('Failed to initialize OIDC client', HttpStatus.INTERNAL_SERVER_ERROR) + } + } + return this.config + } + + async getAuthorizationUrl(res: FastifyReply): Promise { + const config = await this.getConfig() + + // Generate state and nonce for CSRF protection + const state = randomState() + const nonce = randomNonce() + + const codeVerifier = randomPKCECodeVerifier() + + const authUrl = new URL(config.serverMetadata().authorization_endpoint!) + authUrl.searchParams.set('client_id', this.oidcConfig.clientId!) + authUrl.searchParams.set('redirect_uri', this.oidcConfig.redirectUri) + authUrl.searchParams.set('response_type', 'code') + authUrl.searchParams.set('scope', this.oidcConfig.scope || 'openid email profile') + if (config.serverMetadata().supportsPKCE()) { + const codeChallenge = await calculatePKCECodeChallenge(codeVerifier) + authUrl.searchParams.set('code_challenge', codeChallenge) + authUrl.searchParams.set('code_challenge_method', 'S256') + } + authUrl.searchParams.set('state', state) + authUrl.searchParams.set('nonce', nonce) + + // Avoid cache + res + .header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + .header('Pragma', 'no-cache') + .header('Expires', '0') + .header('X-Robots-Tag', 'noindex, nofollow') + .header('Referrer-Policy', 'no-referrer') + + // Store state, nonce, and codeVerifier in httpOnly cookies (10 minutes expiration) + res.setCookie(OAuthCookie.State, state, OAuthCookieSettings) + res.setCookie(OAuthCookie.Nonce, nonce, OAuthCookieSettings) + res.setCookie(OAuthCookie.CodeVerifier, codeVerifier, OAuthCookieSettings) + + return authUrl.toString() + } + + async handleCallback(req: FastifyRequest, res: FastifyReply, query: Record): Promise { + const config = await this.getConfig() + + // Clear temporary OIDC cookies + Object.values(OAuthCookie).forEach((value) => { + res.clearCookie(value, { path: '/' }) + }) + + const [expectedState, expectedNonce, codeVerifier] = [ + req.cookies[OAuthCookie.State], + req.cookies[OAuthCookie.Nonce], + req.cookies[OAuthCookie.CodeVerifier] + ] + + try { + if (!expectedState?.length) { + throw new HttpException('OAuth state is missing', HttpStatus.BAD_REQUEST) + } + + if (!codeVerifier?.length) { + throw new HttpException('OAuth code verifier is missing', HttpStatus.BAD_REQUEST) + } + + const pkceCodeVerifier = config.serverMetadata().supportsPKCE() ? codeVerifier : undefined + const callbackParams = new URLSearchParams(query) + + // Exchange authorization code for tokens + const callbackUrl = new URL(this.oidcConfig.redirectUri) + callbackUrl.search = callbackParams.toString() + const tokens = await authorizationCodeGrant(config, callbackUrl, { + expectedState, + pkceCodeVerifier, + expectedNonce + }) + + // Get validated ID token claims + const claims: IDToken = tokens.claims() + if (!claims) { + throw new HttpException('No ID token claims found', HttpStatus.BAD_REQUEST) + } + if (!claims.sub) { + throw new HttpException('Unexpected profile response, no `sub`', HttpStatus.BAD_REQUEST) + } + + // Get user info from the userinfo endpoint (requires access token and subject from ID token) + const subject = this.oidcConfig.skipSubjectCheck ? skipSubjectCheck : claims.sub + const userInfo: UserInfoResponse = await fetchUserInfo(config, tokens.access_token, subject) + + if (!userInfo.sub) { + throw new Error('Unexpected profile response, no `sub`') + } + + // Process the user info and create/update the user + return await this.processUserInfo(userInfo, req.ip) + } catch (error) { + const errorStatus = error?.getStatus() || HttpStatus.UNAUTHORIZED + this.logger.error(`${this.handleCallback.name} - OIDC callback error: ${error}`) + throw new HttpException('OIDC authentication failed', errorStatus) + } + } + + async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise { + // TODO: should not validate password for OIDC users + // Find user from login or email + const user: UserModel = await this.usersManager.findUser(login, false) + + if (!user) { + return null + } + + if (user.isGuest) { + // Allow guests to be authenticated from db + return this.usersManager.logUser(user, password, ip) + } + + if (!user.isActive) { + this.logger.error(`${this.validateUser.name} - user *${user.login}* is locked`) + throw new HttpException('Account locked', HttpStatus.FORBIDDEN) + } + + // For OIDC users, only allow app password authentication + let authSuccess = false + if (scope) { + authSuccess = await this.usersManager.validateAppPassword(user, password, ip, scope) + } + + this.usersManager.updateAccesses(user, ip, authSuccess).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`)) + + if (authSuccess) { + return user + } + + return null + } + + getRedirectCallbackUrl(accessExpiration: number, refreshExpiration: number) { + if (!this.frontendBaseUrl) { + const url = new URL(this.oidcConfig.redirectUri) + const apiIndex = url.pathname.indexOf(AUTH_ROUTE.BASE) + this.frontendBaseUrl = apiIndex >= 0 ? `${url.origin}${url.pathname.slice(0, apiIndex)}` : url.origin + } + const url = new URL('/#/', this.frontendBaseUrl) + const params = new URLSearchParams({ + [AUTH_PROVIDER.OIDC]: 'true', + [`${TOKEN_TYPE.ACCESS}_expiration`]: `${accessExpiration}`, + [`${TOKEN_TYPE.REFRESH}_expiration`]: `${refreshExpiration}` + }) + url.hash = `/?${params.toString()}` + return url.toString() + } + + private async initializeOIDCClient(): Promise { + try { + const issuerUrl = new URL(this.oidcConfig.issuerUrl) + const config: Configuration = await discovery( + issuerUrl, + this.oidcConfig.clientId, + { client_secret: this.oidcConfig.clientSecret, response_types: ['code'] }, + this.getTokenAuthMethod(this.oidcConfig.clientAuthMethod, this.oidcConfig.clientSecret), + { + execute: [allowInsecureRequests] // allow HTTP for development + } + ) + this.logger.log(`${this.initializeOIDCClient.name} - OIDC client initialized successfully for issuer: ${this.oidcConfig.issuerUrl}`) + return config + } catch (error) { + this.logger.error(`${this.initializeOIDCClient.name} - Failed to initialize OIDC client: ${error}`) + return null + } + } + + private getTokenAuthMethod(tokenEndpointAuthMethod: OAuthTokenEndpoint, clientSecret?: string) { + if (!clientSecret) { + return None() + } + + switch (tokenEndpointAuthMethod) { + case OAuthTokenEndpoint.ClientSecretPost: { + return ClientSecretPost(clientSecret) + } + + case OAuthTokenEndpoint.ClientSecretBasic: { + return ClientSecretBasic(clientSecret) + } + + default: { + return None() + } + } + } + + private async processUserInfo(userInfo: UserInfoResponse, ip?: string): Promise { + // Extract user information + const { login, email } = this.extractLoginAndEmail(userInfo) + + // Check if user exists + let user: UserModel = await this.usersManager.findUser(email || login, false) + + if (!user && !this.oidcConfig.autoCreateUser) { + throw new HttpException('User not found and autoCreateUser is disabled', HttpStatus.UNAUTHORIZED) + } + + // Determine if user should be admin based on groups/roles + const isAdmin = this.checkAdminRole(userInfo) + + // Create identity + const identity = this.createIdentity(login, email, userInfo, isAdmin) + + // Create or update user + user = await this.updateOrCreateUser(identity, user) + + // Update user access log + this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.processUserInfo.name} : ${e}`)) + + return user + } + + private checkAdminRole(userInfo: UserInfoResponse): boolean { + if (!this.oidcConfig.adminRoleOrGroup) { + return false + } + + // Check claims + const claims = [...(Array.isArray(userInfo.groups) ? userInfo.groups : []), ...(Array.isArray(userInfo.roles) ? userInfo.roles : [])] + + return claims.includes(this.oidcConfig.adminRoleOrGroup) + } + + private createIdentity( + login: string, + email: string, + userInfo: UserInfoResponse, + isAdmin: boolean + ): Omit & { password?: string } { + // Get first name and last name + let firstName = userInfo.given_name || '' + let lastName = userInfo.family_name || '' + + // If structured names not available, try to split the full name + if (!firstName && !lastName && userInfo.name) { + const names = splitFullName(userInfo.name) + firstName = names.firstName + lastName = names.lastName + } + + return { + login, + email, + role: isAdmin ? USER_ROLE.ADMINISTRATOR : USER_ROLE.USER, + firstName, + lastName + } + } + + private async updateOrCreateUser(identity: Omit & { password?: string }, user: UserModel | null): Promise { + if (user === null) { + // Create new user with a random password (required by the system but not used for OIDC login) + const userWithPassword = { ...identity, password: generateShortUUID(24) } as CreateUserDto + const createdUser = await this.adminUsersManager.createUserOrGuest(userWithPassword, identity.role) + const freshUser = await this.usersManager.fromUserId(createdUser.id) + if (!freshUser) { + this.logger.error(`${this.updateOrCreateUser.name} - user was not found : ${createdUser.login} (${createdUser.id})`) + throw new HttpException('User not found', HttpStatus.NOT_FOUND) + } + return freshUser + } + + // Check if user information has changed (excluding password) + const identityHasChanged: UpdateUserDto = Object.fromEntries( + Object.keys(identity) + .filter((key) => key !== 'password') + .map((key: string) => (identity[key] !== user[key] ? [key, identity[key]] : null)) + .filter(Boolean) + ) + + if (Object.keys(identityHasChanged).length > 0) { + try { + if (identityHasChanged?.role != null) { + if (user.role === USER_ROLE.ADMINISTRATOR && !this.oidcConfig.adminRoleOrGroup) { + // Prevent removing the admin role when adminGroup was removed or not defined + delete identityHasChanged.role + } + } + + // Update user properties + await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged) + + // Update local user object + Object.assign(user, identityHasChanged) + + if ('lastName' in identityHasChanged || 'firstName' in identityHasChanged) { + // Force fullName update in the current user model + user.setFullName(true) + } + } catch (e) { + this.logger.warn(`${this.updateOrCreateUser.name} - unable to update user *${user.login}* : ${e}`) + } + } + + return user + } + + private extractLoginAndEmail(userInfo: UserInfoResponse) { + const email = userInfo.email ? userInfo.email.trim() : undefined + if (!email) { + throw new HttpException('No email address found in the OIDC profile', HttpStatus.BAD_REQUEST) + } + + const login = userInfo.preferred_username ?? (email ? email.split('@')[0] : undefined) ?? userInfo.sub + if (!login) { + throw new HttpException('Unable to determine the OIDC profile login', HttpStatus.BAD_REQUEST) + } + + return { login: login.trim().toLowerCase(), email } + } +} diff --git a/backend/src/authentication/services/auth-methods/auth-method-two-fa.service.spec.ts b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts similarity index 97% rename from backend/src/authentication/services/auth-methods/auth-method-two-fa.service.spec.ts rename to backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts index 484c680a..2c5c48f2 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-two-fa.service.spec.ts +++ b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts @@ -11,16 +11,16 @@ import { NotificationsManager } from '../../../applications/notifications/servic import { UserModel } from '../../../applications/users/models/user.model' import { UsersManager } from '../../../applications/users/services/users-manager.service' import { Cache } from '../../../infrastructure/cache/services/cache.service' -import { TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from '../../dto/two-fa-verify.dto' import { FastifyAuthenticatedRequest } from '../../interfaces/auth-request.interface' import { decryptSecret, encryptSecret } from '../../utils/crypt-secret' -import { AuthMethod2FA } from './auth-method-two-fa.service' +import { AuthProvider2FA } from './auth-provider-two-fa.service' +import { TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from './auth-two-fa.dtos' jest.mock('../../utils/crypt-secret') jest.mock('../../../common/qrcode') -describe(AuthMethod2FA.name, () => { - let service: AuthMethod2FA +describe(AuthProvider2FA.name, () => { + let service: AuthProvider2FA let cache: jest.Mocked let usersManager: jest.Mocked let notificationsManager: jest.Mocked @@ -44,7 +44,7 @@ describe(AuthMethod2FA.name, () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - AuthMethod2FA, + AuthProvider2FA, { provide: Cache, useValue: { @@ -72,7 +72,7 @@ describe(AuthMethod2FA.name, () => { }).compile() module.useLogger(['fatal']) - service = module.get(AuthMethod2FA) + service = module.get(AuthProvider2FA) cache = module.get(Cache) usersManager = module.get(UsersManager) notificationsManager = module.get(NotificationsManager) diff --git a/backend/src/authentication/services/auth-methods/auth-method-two-fa.service.ts b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts similarity index 97% rename from backend/src/authentication/services/auth-methods/auth-method-two-fa.service.ts rename to backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts index 3a3a7657..b3568581 100644 --- a/backend/src/authentication/services/auth-methods/auth-method-two-fa.service.ts +++ b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts @@ -17,14 +17,14 @@ import { qrcodeToDataURL } from '../../../common/qrcode' import { configuration } from '../../../configuration/config.environment' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { TWO_FA_CODE_LENGTH } from '../../constants/auth' -import { TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from '../../dto/two-fa-verify.dto' import { FastifyAuthenticatedRequest } from '../../interfaces/auth-request.interface' -import { TwoFaEnableResult, TwoFaSetup, TwoFaVerifyResult } from '../../interfaces/two-fa-setup.interface' import { decryptSecret, encryptSecret } from '../../utils/crypt-secret' +import { TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from './auth-two-fa.dtos' +import { TwoFaEnableResult, TwoFaSetup, TwoFaVerifyResult } from './auth-two-fa.interfaces' @Injectable() -export class AuthMethod2FA { - private readonly logger = new Logger(AuthMethod2FA.name) +export class AuthProvider2FA { + private readonly logger = new Logger(AuthProvider2FA.name) private readonly cacheKeyPrefix = 'auth-2fa-pending-user-' constructor( diff --git a/backend/src/authentication/guards/auth-two-fa-guard.ts b/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts similarity index 67% rename from backend/src/authentication/guards/auth-two-fa-guard.ts rename to backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts index ca893759..c6ac2d05 100644 --- a/backend/src/authentication/guards/auth-two-fa-guard.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts @@ -5,10 +5,10 @@ */ import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, mixin, Type } from '@nestjs/common' -import { configuration } from '../../configuration/config.environment' -import { TWO_FA_HEADER_CODE, TWO_FA_HEADER_PASSWORD } from '../constants/auth' -import { FastifyAuthenticatedRequest } from '../interfaces/auth-request.interface' -import { AuthMethod2FA } from '../services/auth-methods/auth-method-two-fa.service' +import { configuration } from '../../../configuration/config.environment' +import { TWO_FA_HEADER_CODE, TWO_FA_HEADER_PASSWORD } from '../../constants/auth' +import { FastifyAuthenticatedRequest } from '../../interfaces/auth-request.interface' +import { AuthProvider2FA } from './auth-provider-two-fa.service' export const AuthTwoFaGuard = AuthTwoFaGuardFactory() export const AuthTwoFaGuardWithoutPassword = AuthTwoFaGuardFactory({ withPassword: false }) @@ -20,17 +20,17 @@ interface TwoFaGuardOptions { function AuthTwoFaGuardFactory(options: TwoFaGuardOptions = { withPassword: true }): Type { @Injectable() class MixinAuthTwoFaGuard implements CanActivate { - constructor(private readonly authMethod2FA: AuthMethod2FA) {} + constructor(private readonly authProvider2FA: AuthProvider2FA) {} async canActivate(ctx: ExecutionContext): Promise { const req: FastifyAuthenticatedRequest = ctx.switchToHttp().getRequest() - const user = await this.authMethod2FA.loadUser(req.user.id, req.ip) + const user = await this.authProvider2FA.loadUser(req.user.id, req.ip) if (options.withPassword) { if (!req.headers[TWO_FA_HEADER_PASSWORD]) { throw new HttpException('Missing TWO-FA password', HttpStatus.FORBIDDEN) } - await this.authMethod2FA.verifyUserPassword(user, req.headers[TWO_FA_HEADER_PASSWORD] as string, req.ip) + await this.authProvider2FA.verifyUserPassword(user, req.headers[TWO_FA_HEADER_PASSWORD] as string, req.ip) } if (!configuration.auth.mfa.totp.enabled || !user.twoFaEnabled) { @@ -41,7 +41,7 @@ function AuthTwoFaGuardFactory(options: TwoFaGuardOptions = { withPassword: true throw new HttpException('Missing TWO-FA code', HttpStatus.FORBIDDEN) } - const auth = await this.authMethod2FA.verify({ code: req.headers[TWO_FA_HEADER_CODE] as string }, req) + const auth = await this.authProvider2FA.verify({ code: req.headers[TWO_FA_HEADER_CODE] as string }, req) if (!auth.success) { throw new HttpException(auth.message, HttpStatus.FORBIDDEN) diff --git a/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts new file mode 100644 index 00000000..8491350f --- /dev/null +++ b/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { Type } from 'class-transformer' +import { IsBoolean, IsDefined, IsNotEmptyObject, IsObject, IsString, ValidateNested } from 'class-validator' +import { SERVER_NAME } from '../../../common/shared' + +export class AuthMFATotpConfig { + @IsBoolean() + enabled = true + + @IsString() + issuer = SERVER_NAME +} + +export class AuthMFAConfig { + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => AuthMFATotpConfig) + totp: AuthMFATotpConfig = new AuthMFATotpConfig() +} diff --git a/backend/src/authentication/dto/two-fa-verify.dto.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts similarity index 76% rename from backend/src/authentication/dto/two-fa-verify.dto.ts rename to backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts index 2e65e45a..33beec39 100644 --- a/backend/src/authentication/dto/two-fa-verify.dto.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts @@ -5,6 +5,12 @@ */ import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator' +import { LoginResponseDto } from '../../dto/login-response.dto' + +export class TwoFaResponseDto extends LoginResponseDto { + success: boolean + message: string +} export class TwoFaVerifyDto { @IsString() diff --git a/backend/src/authentication/interfaces/two-fa-setup.interface.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts similarity index 100% rename from backend/src/authentication/interfaces/two-fa-setup.interface.ts rename to backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index 7cf20140..aeff6bac 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -77,7 +77,7 @@ mail: # default: `false` debug: false auth: - # adapter : `mysql` | `ldap` + # adapter : `mysql` | `ldap` | `oidc` # default: `mysql` method: mysql # Key used to encrypt user secret keys in the database @@ -135,6 +135,48 @@ auth: upnSuffix: # netbiosName: NetBIOS domain name used with `sAMAccountName` to build legacy logins (e.g., `SYNC_IN`\user) netbiosName: + oidc: + # issuerUrl: The URL of the OIDC provider's discovery endpoint (e.g., https://accounts.google.com, https://login.microsoftonline.com/tenant-id/v2.0) + # The server will automatically discover the authorization, token, userinfo, and logout endpoints + # required + issuerUrl: + # clientId: OAuth 2.0 Client ID obtained from your OIDC provider + # required + clientId: + # clientSecret: OAuth 2.0 Client Secret obtained from your OIDC provider + # required + clientSecret: + # OAuth 2.0 / OIDC client authentication method used at the token endpoint. + # Possible values: + # - client_secret_basic (DEFAULT): HTTP Basic auth using client_id and client_secret. + # Recommended for backend (confidential) clients. + # - client_secret_post: client_id and client_secret sent in the request body. + # - none: no client authentication (public clients: mobile / SPA with PKCE). + # default: `client_secret_basic` + # optional + clientAuthMethod: client_secret_basic + # redirectUri: The callback URL where users are redirected after authentication + # This URL must be registered in your OIDC provider's allowed redirect URIs + # required + redirectUri: http://192.168.0.25:8080/api/auth/oidc/callback + # scope: OAuth 2.0 scopes to request (space-separated string) + # Common scopes: openid (required), email, profile, groups, roles + # default: `openid email profile` + scope: openid email profile + # autoCreateUser: Automatically create a local user account on first successful OIDC login. + # When disabled, only existing users are allowed to authenticate via OIDC. + # default: true + autoCreateUser: true + # skipSubjectCheck: Disable verification that the `sub` claim returned by the UserInfo endpoint + # matches the `sub` claim from the ID token. + # Set to true only for non-compliant or legacy OIDC providers. + # default: false + skipSubjectCheck: false + # adminRoleOrGroup: Name of the role or group that grants Sync-in administrator access + # Users with this value will be granted administrator privileges + # The value is matched against the `groups` or `roles` claims returned by the OIDC provider + # optional + adminRoleOrGroup: applications: files: # required diff --git a/frontend/src/app/applications/admin/components/dialogs/admin-user-dialog.component.ts b/frontend/src/app/applications/admin/components/dialogs/admin-user-dialog.component.ts index b3c5e9a3..6b49d589 100644 --- a/frontend/src/app/applications/admin/components/dialogs/admin-user-dialog.component.ts +++ b/frontend/src/app/applications/admin/components/dialogs/admin-user-dialog.component.ts @@ -23,7 +23,7 @@ import { import { USER_LOGIN_VALIDATION, USER_PERMISSION, USER_PERMS_SEP, USER_ROLE } from '@sync-in-server/backend/src/applications/users/constants/user' import type { CreateUserDto, UpdateUserDto } from '@sync-in-server/backend/src/applications/users/dto/create-or-update-user.dto' import type { SearchMembersDto } from '@sync-in-server/backend/src/applications/users/dto/search-members.dto' -import type { TwoFaVerifyResult } from '@sync-in-server/backend/src/authentication/interfaces/two-fa-setup.interface' +import type { TwoFaVerifyResult } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective, L10nTranslatePipe } from 'angular-l10n' import { BsModalRef } from 'ngx-bootstrap/modal' import { TabDirective, TabHeadingDirective, TabsetComponent } from 'ngx-bootstrap/tabs' diff --git a/frontend/src/app/applications/users/components/dialogs/user-auth-2fa-enable-dialog.component.ts b/frontend/src/app/applications/users/components/dialogs/user-auth-2fa-enable-dialog.component.ts index 3cb00fb2..30a5198e 100644 --- a/frontend/src/app/applications/users/components/dialogs/user-auth-2fa-enable-dialog.component.ts +++ b/frontend/src/app/applications/users/components/dialogs/user-auth-2fa-enable-dialog.component.ts @@ -9,7 +9,7 @@ import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from ' import { FaIconComponent } from '@fortawesome/angular-fontawesome' import { faCopy, faKey, faLock } from '@fortawesome/free-solid-svg-icons' import { TWO_FA_CODE_LENGTH } from '@sync-in-server/backend/src/authentication/constants/auth' -import { TwoFaEnableResult } from '@sync-in-server/backend/src/authentication/interfaces/two-fa-setup.interface' +import type { TwoFaEnableResult } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective } from 'angular-l10n' import { ClipboardService } from 'ngx-clipboard' import { InputPasswordComponent } from '../../../../common/components/input-password.component' diff --git a/frontend/src/app/applications/users/components/user-account.component.ts b/frontend/src/app/applications/users/components/user-account.component.ts index 99eb6bb2..329a7896 100644 --- a/frontend/src/app/applications/users/components/user-account.component.ts +++ b/frontend/src/app/applications/users/components/user-account.component.ts @@ -16,7 +16,7 @@ import { USER_PASSWORD_MIN_LENGTH } from '@sync-in-server/backend/src/applicatio import { UserAppPassword } from '@sync-in-server/backend/src/applications/users/interfaces/user-secrets.interface' import { WEBDAV_BASE_PATH } from '@sync-in-server/backend/src/applications/webdav/constants/routes' import { TWO_FA_HEADER_CODE, TWO_FA_HEADER_PASSWORD } from '@sync-in-server/backend/src/authentication/constants/auth' -import { TwoFaSetup } from '@sync-in-server/backend/src/authentication/interfaces/two-fa-setup.interface' +import type { TwoFaSetup } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces' import type { FileEditorProvider } from '@sync-in-server/backend/src/configuration/config.interfaces' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective, L10nTranslatePipe } from 'angular-l10n' import { BsModalRef } from 'ngx-bootstrap/modal' diff --git a/frontend/src/app/applications/users/user.service.ts b/frontend/src/app/applications/users/user.service.ts index b08616d2..66fc0d7c 100644 --- a/frontend/src/app/applications/users/user.service.ts +++ b/frontend/src/app/applications/users/user.service.ts @@ -50,8 +50,12 @@ import type { UserOnline } from '@sync-in-server/backend/src/applications/users/interfaces/websocket.interface' import { API_TWO_FA_ADMIN_RESET_USER, API_TWO_FA_DISABLE, API_TWO_FA_ENABLE } from '@sync-in-server/backend/src/authentication/constants/routes' -import type { TwoFaVerifyWithPasswordDto } from '@sync-in-server/backend/src/authentication/dto/two-fa-verify.dto' -import type { TwoFaEnableResult, TwoFaSetup, TwoFaVerifyResult } from '@sync-in-server/backend/src/authentication/interfaces/two-fa-setup.interface' +import type { TwoFaVerifyWithPasswordDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' +import type { + TwoFaEnableResult, + TwoFaSetup, + TwoFaVerifyResult +} from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces' import type { FileEditorProvider } from '@sync-in-server/backend/src/configuration/config.interfaces' import { BsModalRef } from 'ngx-bootstrap/modal' import { Socket } from 'ngx-socket-io' diff --git a/frontend/src/app/auth/auth.component.ts b/frontend/src/app/auth/auth.component.ts index 5e2dfde6..6ce5276c 100644 --- a/frontend/src/app/auth/auth.component.ts +++ b/frontend/src/app/auth/auth.component.ts @@ -11,8 +11,7 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome' import { faKey, faLock, faQrcode, faUserAlt } from '@fortawesome/free-solid-svg-icons' import { USER_PASSWORD_MIN_LENGTH } from '@sync-in-server/backend/src/applications/users/constants/user' import { TWO_FA_CODE_LENGTH } from '@sync-in-server/backend/src/authentication/constants/auth' -import { TwoFaResponseDto } from '@sync-in-server/backend/src/authentication/dto/login-response.dto' -import { TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/dto/two-fa-verify.dto' +import type { TwoFaResponseDto, TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective, L10nTranslatePipe } from 'angular-l10n' import { finalize } from 'rxjs/operators' import { logoDarkUrl } from '../applications/files/files.constants' diff --git a/frontend/src/app/auth/auth.guards.ts b/frontend/src/app/auth/auth.guards.ts index 38827d36..a1f07d5f 100644 --- a/frontend/src/app/auth/auth.guards.ts +++ b/frontend/src/app/auth/auth.guards.ts @@ -7,10 +7,13 @@ import { inject } from '@angular/core' import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router' import { Observable } from 'rxjs' +import { AuthOIDCQueryParams } from './auth.interface' import { AuthService } from './auth.service' -export const authGuard: CanActivateFn = (_route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { - return inject(AuthService).checkUserAuthAndLoad(state.url) +export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable => { + // Authentication initiated via OIDC callback + const authFromOIDC = route.queryParams?.oidc ? (route.queryParams as AuthOIDCQueryParams) : undefined + return inject(AuthService).checkUserAuthAndLoad(state.url, authFromOIDC) } export const noAuthGuard: CanActivateFn = (): boolean => { diff --git a/frontend/src/app/auth/auth.interface.ts b/frontend/src/app/auth/auth.interface.ts new file mode 100644 index 00000000..add99280 --- /dev/null +++ b/frontend/src/app/auth/auth.interface.ts @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +export interface AuthOIDCQueryParams { + oidc: string + access_expiration: string + refresh_expiration: string +} diff --git a/frontend/src/app/auth/auth.service.ts b/frontend/src/app/auth/auth.service.ts index a60646e8..5c70a8d0 100644 --- a/frontend/src/app/auth/auth.service.ts +++ b/frontend/src/app/auth/auth.service.ts @@ -19,9 +19,9 @@ import { API_AUTH_REFRESH, API_TWO_FA_LOGIN_VERIFY } from '@sync-in-server/backend/src/authentication/constants/routes' -import { LoginResponseDto, TwoFaResponseDto } from '@sync-in-server/backend/src/authentication/dto/login-response.dto' +import type { LoginResponseDto } from '@sync-in-server/backend/src/authentication/dto/login-response.dto' import type { TokenResponseDto } from '@sync-in-server/backend/src/authentication/dto/token-response.dto' -import { TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/dto/two-fa-verify.dto' +import type { TwoFaResponseDto, TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' import { currentTimeStamp } from '@sync-in-server/backend/src/common/shared' import { catchError, finalize, map, Observable, of, throwError } from 'rxjs' import { switchMap, tap } from 'rxjs/operators' @@ -33,6 +33,7 @@ import { Electron } from '../electron/electron.service' import { LayoutService } from '../layout/layout.service' import { StoreService } from '../store/store.service' import { AUTH_PATHS } from './auth.constants' +import type { AuthOIDCQueryParams } from './auth.interface' @Injectable({ providedIn: 'root' @@ -187,7 +188,11 @@ export class AuthService { ) } - checkUserAuthAndLoad(returnUrl: string): Observable { + checkUserAuthAndLoad(returnUrl: string, authFromOIDC?: AuthOIDCQueryParams): Observable { + if (authFromOIDC) { + this.accessExpiration = parseInt(authFromOIDC.access_expiration) + this.refreshExpiration = parseInt(authFromOIDC.refresh_expiration) + } if (this.refreshTokenHasExpired()) { if (this.electron.enabled) { return this.loginElectron() @@ -197,7 +202,12 @@ export class AuthService { return of(false) } else if (!this.store.user.getValue()) { return this.http.get>(API_USERS_ME).pipe( - tap((r: Omit) => this.initUser(r)), + tap((r: Omit) => { + this.initUser(r) + if (authFromOIDC) { + this.router.navigate([]).catch(console.error) + } + }), map(() => true), catchError((e: HttpErrorResponse) => { if (e.status === 401) { From 23a93b5ae104e669a58947590959f2f9a06f2d33 Mon Sep 17 00:00:00 2001 From: johaven Date: Mon, 19 Jan 2026 00:47:47 +0100 Subject: [PATCH 03/18] feat(backend:auth): add LDAP/OIDC local password fallback and admin break-glass access --- .../users/services/users-manager.service.ts | 2 +- backend/src/authentication/auth.config.ts | 6 +- .../providers/ldap/auth-ldap.config.ts | 5 ++ .../ldap/auth-provider-ldap.service.ts | 57 ++++++++++++------- .../providers/oidc/auth-oidc.config.ts | 4 ++ .../oidc/auth-provider-oidc.service.ts | 32 +++-------- environment/environment.dist.yaml | 10 ++++ 7 files changed, 68 insertions(+), 48 deletions(-) diff --git a/backend/src/applications/users/services/users-manager.service.ts b/backend/src/applications/users/services/users-manager.service.ts index 2194c414..37f802f6 100755 --- a/backend/src/applications/users/services/users-manager.service.ts +++ b/backend/src/applications/users/services/users-manager.service.ts @@ -71,7 +71,7 @@ export class UsersManager { return user ? new UserModel(user, removePassword) : null } - async logUser(user: UserModel, password: string, ip: string, scope?: AUTH_SCOPE): Promise { + async logUser(user: UserModel, password: string, ip: string, scope?: AUTH_SCOPE): Promise { this.validateUserAccess(user, ip) let authSuccess: boolean = await comparePassword(password, user.password) if (!authSuccess && scope) { diff --git a/backend/src/authentication/auth.config.ts b/backend/src/authentication/auth.config.ts index 8a7f0c3b..af21ab88 100644 --- a/backend/src/authentication/auth.config.ts +++ b/backend/src/authentication/auth.config.ts @@ -109,17 +109,17 @@ export class AuthConfig { @Type(() => AuthTokenConfig) token: AuthTokenConfig - @ValidateIf((o: AuthConfig) => o.method === 'ldap') + @ValidateIf((o: AuthConfig) => o.method === AUTH_PROVIDER.LDAP) @IsDefined() @IsObject() @ValidateNested() @Type(() => AuthMethodLDAPConfig) ldap: AuthMethodLDAPConfig - @ValidateIf((o: AuthConfig) => o.method === 'oidc') + @ValidateIf((o: AuthConfig) => o.method === AUTH_PROVIDER.OIDC) @IsDefined() @IsObject() @ValidateNested() @Type(() => AuthMethodOIDCConfig) - oidc: AuthMethodOIDCConfig = new AuthMethodOIDCConfig() + oidc: AuthMethodOIDCConfig } diff --git a/backend/src/authentication/providers/ldap/auth-ldap.config.ts b/backend/src/authentication/providers/ldap/auth-ldap.config.ts index 8a88cadd..9ec70da0 100644 --- a/backend/src/authentication/providers/ldap/auth-ldap.config.ts +++ b/backend/src/authentication/providers/ldap/auth-ldap.config.ts @@ -8,6 +8,7 @@ import { Transform, Type } from 'class-transformer' import { ArrayNotEmpty, IsArray, + IsBoolean, IsDefined, IsEnum, IsNotEmpty, @@ -64,4 +65,8 @@ export class AuthMethodLDAPConfig { @IsOptional() @IsString() netbiosName?: string + + @IsOptional() + @IsBoolean() + enablePasswordAuthFallback? = true } diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts index 3623fd85..c2715996 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts @@ -16,6 +16,7 @@ import { comparePassword, splitFullName } from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' import type { AUTH_SCOPE } from '../../constants/scope' import { AuthProvider } from '../auth-providers.models' +import type { AuthMethodLDAPConfig } from './auth-ldap.config' import { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' type LdapUserEntry = Entry & Record @@ -23,7 +24,7 @@ type LdapUserEntry = Entry & Record this.logger.error(`${this.validateUser.name} : ${e}`)) - if (authSuccess) { - // Logged with app password - return user - } + // If LDAP is unavailable (connectivity/service error), allow local password fallback. + // Allow local password authentication for: + // - admin users (break-glass access) + // - regular users when password authentication fallback is enabled + if (user && Boolean(ldapErrorMessage) && (user.isAdmin || this.ldapConfig.enablePasswordAuthFallback)) { + const localUser = await this.usersManager.logUser(user, password, ip) + if (localUser) return localUser } + + if (ldapErrorMessage) { + throw new HttpException(ldapErrorMessage, HttpStatus.SERVICE_UNAVAILABLE) + } + return null - } else if (!entry[this.ldapConfig.attributes.login] || !entry[this.ldapConfig.attributes.email]) { + } + + if (!entry[this.ldapConfig.attributes.login] || !entry[this.ldapConfig.attributes.email]) { this.logger.error(`${this.validateUser.name} - required ldap fields are missing : [${this.ldapConfig.attributes.login}, ${this.ldapConfig.attributes.email}] => (${JSON.stringify(entry)})`) return null } + const identity = this.createIdentity(entry, password) user = await this.updateOrCreateUser(identity, user) this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`)) @@ -103,8 +115,11 @@ export class AuthProviderLDAP implements AuthProvider { await client.unbind() } } - if (error && CONNECT_ERROR_CODE.has(error.code)) { - throw new HttpException('Authentication service error', HttpStatus.INTERNAL_SERVER_ERROR) + if (error) { + this.logger.error(`${this.checkAuth.name} - ${error}`) + if (CONNECT_ERROR_CODE.has(error.code)) { + throw new Error('Authentication service error') + } } return false } diff --git a/backend/src/authentication/providers/oidc/auth-oidc.config.ts b/backend/src/authentication/providers/oidc/auth-oidc.config.ts index 0b228042..981e6076 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.config.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.config.ts @@ -38,6 +38,10 @@ export class AuthMethodOIDCConfig { @IsBoolean() autoCreateUser? = true + @IsOptional() + @IsBoolean() + enablePasswordAuth? = true + @IsOptional() @IsBoolean() skipSubjectCheck? = false diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index 1c28b7be..39f70d12 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -35,7 +35,7 @@ import type { AUTH_SCOPE } from '../../constants/scope' import { TOKEN_TYPE } from '../../interfaces/token.interface' import { AUTH_PROVIDER } from '../auth-providers.constants' import { AuthProvider } from '../auth-providers.models' -import { AuthMethodOIDCConfig } from './auth-oidc.config' +import type { AuthMethodOIDCConfig } from './auth-oidc.config' import { OAuthCookie, OAuthCookieSettings, OAuthTokenEndpoint } from './auth-oidc.constants' @Injectable() @@ -160,7 +160,6 @@ export class AuthProviderOIDC implements AuthProvider { } async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise { - // TODO: should not validate password for OIDC users // Find user from login or email const user: UserModel = await this.usersManager.findUser(login, false) @@ -168,26 +167,13 @@ export class AuthProviderOIDC implements AuthProvider { return null } - if (user.isGuest) { - // Allow guests to be authenticated from db - return this.usersManager.logUser(user, password, ip) - } - - if (!user.isActive) { - this.logger.error(`${this.validateUser.name} - user *${user.login}* is locked`) - throw new HttpException('Account locked', HttpStatus.FORBIDDEN) - } - - // For OIDC users, only allow app password authentication - let authSuccess = false - if (scope) { - authSuccess = await this.usersManager.validateAppPassword(user, password, ip, scope) - } - - this.usersManager.updateAccesses(user, ip, authSuccess).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`)) - - if (authSuccess) { - return user + if (user.isGuest || user.isAdmin || scope || this.oidcConfig.enablePasswordAuth) { + // Allow local password authentication for: + // - guest users + // - admin users (break-glass access) + // - application scopes (app passwords) + // - regular users when password authentication is enabled + return this.usersManager.logUser(user, password, ip, scope) } return null @@ -199,7 +185,7 @@ export class AuthProviderOIDC implements AuthProvider { const apiIndex = url.pathname.indexOf(AUTH_ROUTE.BASE) this.frontendBaseUrl = apiIndex >= 0 ? `${url.origin}${url.pathname.slice(0, apiIndex)}` : url.origin } - const url = new URL('/#/', this.frontendBaseUrl) + const url = new URL(this.frontendBaseUrl) const params = new URLSearchParams({ [AUTH_PROVIDER.OIDC]: 'true', [`${TOKEN_TYPE.ACCESS}_expiration`]: `${accessExpiration}`, diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index aeff6bac..c7ed5e32 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -135,6 +135,11 @@ auth: upnSuffix: # netbiosName: NetBIOS domain name used with `sAMAccountName` to build legacy logins (e.g., `SYNC_IN`\user) netbiosName: + # enablePasswordAuthFallback: Enable local password authentication as a fallback when the LDAP authentication service is unavailable. + # This fallback applies only to existing users and is NOT triggered when credentials are invalid. + # Users must have signed in at least once. + # default: true + enablePasswordAuthFallback: true oidc: # issuerUrl: The URL of the OIDC provider's discovery endpoint (e.g., https://accounts.google.com, https://login.microsoftonline.com/tenant-id/v2.0) # The server will automatically discover the authorization, token, userinfo, and logout endpoints @@ -167,6 +172,11 @@ auth: # When disabled, only existing users are allowed to authenticate via OIDC. # default: true autoCreateUser: true + # enablePasswordAuthentication: Enable local password-based authentication (login or email/password). + # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. + # Users must have signed in at least once and have a local password set. + # default: true + enablePasswordAuth: true # skipSubjectCheck: Disable verification that the `sub` claim returned by the UserInfo endpoint # matches the `sub` claim from the ID token. # Set to true only for non-compliant or legacy OIDC providers. From 549ada3a68dc853ec7e4beb987bcdfba9ebbe033 Mon Sep 17 00:00:00 2001 From: johaven Date: Mon, 19 Jan 2026 15:30:08 +0100 Subject: [PATCH 04/18] feat(backend:sync): improve sync path error handling and enforce subdirectory selection --- .../sync/services/sync-paths-manager.service.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/src/applications/sync/services/sync-paths-manager.service.ts b/backend/src/applications/sync/services/sync-paths-manager.service.ts index c2dc7094..0439324c 100644 --- a/backend/src/applications/sync/services/sync-paths-manager.service.ts +++ b/backend/src/applications/sync/services/sync-paths-manager.service.ts @@ -45,6 +45,10 @@ export class SyncPathsManager { if (req.space.quotaIsExceeded) { throw new HttpException('Storage quota exceeded', HttpStatus.INSUFFICIENT_STORAGE) } + + // Check DB path + const syncDBProps: SyncDBProps = await this.getDBProps(req.space) + if (!(await isPathExists(req.space.realPath))) { throw new HttpException(`Remote path not found : ${syncPathDto.remotePath}`, HttpStatus.NOT_FOUND) } @@ -55,9 +59,8 @@ export class SyncPathsManager { if (!client) { throw new HttpException('Client not found', HttpStatus.NOT_FOUND) } - const syncDBProps: SyncDBProps = await this.getDBProps(req.space) - // important : ensures the right remote path is used and stored + // important: ensures the right remote path is used and stored syncPathDto.remotePath = req.params['*'] // add permissions (skip end point protection using getEnvPermission) syncPathDto.permissions = getEnvPermissions(req.space, req.space.root) @@ -194,17 +197,17 @@ export class SyncPathsManager { private async getDBProps(space: SpaceEnv): Promise { if (space.inSharesList) { - throw new HttpException('Sync all shares is not supported, you must select a sub-directory', HttpStatus.BAD_REQUEST) + throw new HttpException('Syncing all shares is not supported. Please select a subdirectory', HttpStatus.BAD_REQUEST) } else if (space.inPersonalSpace) { if (space.paths.length) { return { ownerId: space.dbFile.ownerId, fileId: await this.getOrCreateFileId(space) } } else { - return { ownerId: space.dbFile.ownerId } + throw new HttpException('Syncing all personal files is not supported. Please select a subdirectory', HttpStatus.BAD_REQUEST) } } else if (space.inFilesRepository) { if (!space?.root?.alias) { // The synchronization direction should be adapted for each root depending on the permissions, this is not yet supported - throw new HttpException('Sync all space is not yet supported, you must select a sub-directory', HttpStatus.BAD_REQUEST) + throw new HttpException('Syncing the entire space is not yet supported. Please select a subdirectory', HttpStatus.BAD_REQUEST) } if (space.root.id && !space.paths.length) { return { spaceId: space.id, spaceRootId: space.root.id } From 3b5a80a762373047217234456f347c80e21d77e2 Mon Sep 17 00:00:00 2001 From: johaven Date: Wed, 21 Jan 2026 00:10:11 +0100 Subject: [PATCH 05/18] fix(frontend:routes): remove redundant `canActivateChild` guard from app routes --- frontend/src/app/app.routes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index dbdebfd9..342914db 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -23,7 +23,6 @@ export const routes: Routes = [ path: APP_PATH.BASE, component: LayoutComponent, canActivate: [authGuard], - canActivateChild: [authGuard], children: [...recentsRoutes, ...searchRoutes, ...spacesRoutes, ...userRoutes, ...syncRoutes, ...adminRoutes] }, ...authRoutes, From 8bcf35d2639c6067d53c13cafb14897155c952c5 Mon Sep 17 00:00:00 2001 From: johaven Date: Wed, 21 Jan 2026 00:10:23 +0100 Subject: [PATCH 06/18] feat(auth:oidc): enhance OIDC configuration --- backend/src/authentication/auth.controller.ts | 7 + backend/src/authentication/auth.service.ts | 15 + .../src/authentication/constants/routes.ts | 5 +- .../providers/oidc/auth-oidc.config.ts | 89 ++++-- .../providers/oidc/auth-oidc.controller.ts | 8 +- .../providers/oidc/auth-oidc.interfaces.ts | 12 + .../oidc/auth-provider-oidc.service.ts | 86 +++--- environment/environment.dist.yaml | 107 +++++--- frontend/src/app/auth/auth-resolvers.ts | 14 + frontend/src/app/auth/auth.component.html | 36 ++- frontend/src/app/auth/auth.component.ts | 21 +- frontend/src/app/auth/auth.routes.ts | 2 + frontend/src/app/auth/auth.service.ts | 19 ++ frontend/src/i18n/de.json | 3 +- frontend/src/i18n/es.json | 3 +- frontend/src/i18n/fr.json | 3 +- frontend/src/i18n/hi.json | 3 +- frontend/src/i18n/it.json | 3 +- frontend/src/i18n/ja.json | 3 +- frontend/src/i18n/ko.json | 3 +- frontend/src/i18n/pl.json | 3 +- frontend/src/i18n/pt-BR.json | 3 +- frontend/src/i18n/pt.json | 3 +- frontend/src/i18n/ru.json | 3 +- frontend/src/i18n/tr.json | 3 +- frontend/src/i18n/zh.json | 3 +- frontend/src/styles/components/_app.scss | 5 +- package-lock.json | 257 +++++++++++++++++- 28 files changed, 589 insertions(+), 133 deletions(-) create mode 100644 backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts create mode 100644 frontend/src/app/auth/auth-resolvers.ts diff --git a/backend/src/authentication/auth.controller.ts b/backend/src/authentication/auth.controller.ts index 7a6fa152..6eedc150 100755 --- a/backend/src/authentication/auth.controller.ts +++ b/backend/src/authentication/auth.controller.ts @@ -21,6 +21,7 @@ import { AuthLocalGuard } from './guards/auth-local.guard' import { AuthTokenRefreshGuard } from './guards/auth-token-refresh.guard' import { FastifyAuthenticatedRequest } from './interfaces/auth-request.interface' import { TOKEN_TYPE } from './interfaces/token.interface' +import type { AuthOIDCSettings } from './providers/oidc/auth-oidc.interfaces' import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service' import { AuthTwoFaGuard } from './providers/two-fa/auth-two-fa-guard' import { TwoFaResponseDto, TwoFaVerifyDto, TwoFaVerifyWithPasswordDto } from './providers/two-fa/auth-two-fa.dtos' @@ -67,6 +68,12 @@ export class AuthController { return this.authManager.getTokens(user, true) } + @Get(AUTH_ROUTE.SETTINGS) + @AuthTokenSkip() + authSettings(): AuthOIDCSettings | false { + return this.authManager.authSettings() + } + /* TWO-FA Part */ @Get(`${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_ENABLE}`) diff --git a/backend/src/authentication/auth.service.ts b/backend/src/authentication/auth.service.ts index e1273e2a..74101015 100755 --- a/backend/src/authentication/auth.service.ts +++ b/backend/src/authentication/auth.service.ts @@ -14,10 +14,13 @@ import { convertHumanTimeToSeconds } from '../common/functions' import { currentTimeStamp } from '../common/shared' import { configuration, serverConfig } from '../configuration/config.environment' import { CSRF_ERROR, CSRF_KEY, TOKEN_2FA_TYPES, TOKEN_PATHS, TOKEN_TYPES } from './constants/auth' +import { API_OIDC_LOGIN } from './constants/routes' import { LoginResponseDto, LoginVerify2FaDto } from './dto/login-response.dto' import { TokenResponseDto } from './dto/token-response.dto' import { JwtIdentity2FaPayload, JwtIdentityPayload, JwtPayload } from './interfaces/jwt-payload.interface' import { TOKEN_TYPE } from './interfaces/token.interface' +import { AUTH_PROVIDER } from './providers/auth-providers.constants' +import type { AuthOIDCSettings } from './providers/oidc/auth-oidc.interfaces' @Injectable() export class AuthManager { @@ -138,6 +141,18 @@ export class AuthManager { } } + authSettings(): AuthOIDCSettings | false { + if (configuration.auth.method !== AUTH_PROVIDER.OIDC) { + return false + } + return { + loginUrl: API_OIDC_LOGIN, + autoRedirect: configuration.auth.oidc.options.autoRedirect, + enablePasswordAuth: configuration.auth.oidc.options.enablePasswordAuth, + buttonText: configuration.auth.oidc.options.buttonText + } + } + private jwtSign(user: UserModel, type: TOKEN_TYPE, expiration: number, csrfToken?: string): Promise { return this.jwt.signAsync( { diff --git a/backend/src/authentication/constants/routes.ts b/backend/src/authentication/constants/routes.ts index 375e0806..d4e4adcf 100644 --- a/backend/src/authentication/constants/routes.ts +++ b/backend/src/authentication/constants/routes.ts @@ -11,6 +11,7 @@ export const AUTH_ROUTE = { REFRESH: 'refresh', TOKEN: 'token', TOKEN_REFRESH: 'token/refresh', + SETTINGS: 'settings', WS: 'socket.io', TWO_FA_BASE: '2fa', TWO_FA_ENABLE: 'enable', @@ -19,7 +20,7 @@ export const AUTH_ROUTE = { TWO_FA_ADMIN_RESET_USER: 'reset/user', OIDC_LOGIN: 'oidc/login', OIDC_CALLBACK: 'oidc/callback', - OIDC_LOGOUT: 'oidc/logout' + OIDC_SETTINGS: 'oidc/settings' } export const API_AUTH_LOGIN = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.LOGIN}` @@ -27,8 +28,10 @@ export const API_AUTH_LOGOUT = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.LOGOUT}` export const API_AUTH_REFRESH = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.REFRESH}` export const API_AUTH_TOKEN = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TOKEN}` export const API_AUTH_TOKEN_REFRESH = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TOKEN_REFRESH}` +export const API_AUTH_SETTINGS = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.SETTINGS}` export const API_AUTH_WS = `/${AUTH_ROUTE.WS}` export const API_TWO_FA_ENABLE = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_ENABLE}` export const API_TWO_FA_DISABLE = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_DISABLE}` export const API_TWO_FA_LOGIN_VERIFY = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_LOGIN_VERIFY}` export const API_TWO_FA_ADMIN_RESET_USER = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_ADMIN_RESET_USER}` +export const API_OIDC_LOGIN = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.OIDC_LOGIN}` diff --git a/backend/src/authentication/providers/oidc/auth-oidc.config.ts b/backend/src/authentication/providers/oidc/auth-oidc.config.ts index 981e6076..5e59dedb 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.config.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.config.ts @@ -4,49 +4,100 @@ * See the LICENSE file for licensing details */ -import { Transform } from 'class-transformer' -import { IsBoolean, IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator' +import { Transform, Type } from 'class-transformer' +import { + IsArray, + IsBoolean, + IsDefined, + IsEnum, + IsNotEmpty, + IsNotEmptyObject, + IsObject, + IsOptional, + IsString, + Matches, + ValidateNested +} from 'class-validator' +import { USER_PERMISSION } from '../../../applications/users/constants/user' import { OAuthTokenEndpoint } from './auth-oidc.constants' -export class AuthMethodOIDCConfig { - @IsString() - @IsNotEmpty() - issuerUrl: string - - @IsString() - @IsNotEmpty() - clientId: string - +export class AuthMethodOIDCSecurityConfig { @IsString() - @IsNotEmpty() - clientSecret: string + @Matches(/\bopenid\b/, { message: 'OIDC scope must include "openid"' }) + scope = 'openid email profile' - @IsOptional() @Transform(({ value }) => value || OAuthTokenEndpoint.ClientSecretBasic) @IsEnum(OAuthTokenEndpoint) - clientAuthMethod: OAuthTokenEndpoint = OAuthTokenEndpoint.ClientSecretBasic + tokenEndpointAuthMethod: OAuthTokenEndpoint = OAuthTokenEndpoint.ClientSecretBasic @IsString() @IsNotEmpty() - redirectUri: string + tokenSigningAlg = 'RS256' @IsOptional() @IsString() - scope?: string + userInfoSigningAlg? = undefined + + @IsOptional() + @IsBoolean() + skipSubjectCheck? = false +} +export class AuthMethodOIDCOptionsConfig { @IsOptional() @IsBoolean() autoCreateUser? = true + @IsOptional() + @IsArray() + @IsEnum(USER_PERMISSION, { each: true }) + autoCreatePermissions?: USER_PERMISSION[] = [] + @IsOptional() @IsBoolean() - enablePasswordAuth? = true + autoRedirect? = false @IsOptional() @IsBoolean() - skipSubjectCheck? = false + enablePasswordAuth? = true @IsOptional() @IsString() adminRoleOrGroup?: string + + @IsString() + @IsNotEmpty() + buttonText: string = 'Continue with OpenID Connect' +} + +export class AuthMethodOIDCConfig { + @IsString() + @IsNotEmpty() + issuerUrl: string + + @IsString() + @IsNotEmpty() + clientId: string + + @IsString() + @IsNotEmpty() + clientSecret: string + + @IsString() + @IsNotEmpty() + redirectUri: string + + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => AuthMethodOIDCOptionsConfig) + options: AuthMethodOIDCOptionsConfig = new AuthMethodOIDCOptionsConfig() + + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => AuthMethodOIDCSecurityConfig) + security: AuthMethodOIDCSecurityConfig = new AuthMethodOIDCSecurityConfig() } diff --git a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts index feeb32b6..3b775628 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts @@ -17,13 +17,13 @@ import { AuthProviderOIDC } from './auth-provider-oidc.service' export class AuthOIDCController { constructor( private readonly authManager: AuthManager, - private readonly authMethodOidc: AuthProviderOIDC + private readonly authProviderOIDC: AuthProviderOIDC ) {} @Get(AUTH_ROUTE.OIDC_LOGIN) @AuthTokenSkip() async oidcLogin(@Res() res: FastifyReply): Promise { - const url = await this.authMethodOidc.getAuthorizationUrl(res) + const url = await this.authProviderOIDC.getAuthorizationUrl(res) // Redirect to OIDC provider return res.redirect(url, HttpStatus.FOUND) } @@ -31,8 +31,8 @@ export class AuthOIDCController { @Get(AUTH_ROUTE.OIDC_CALLBACK) @AuthTokenSkip() async oidcCallback(@Query() query: Record, @Req() req: FastifyRequest, @Res() res: FastifyReply): Promise { - const user: UserModel = await this.authMethodOidc.handleCallback(req, res, query) + const user: UserModel = await this.authProviderOIDC.handleCallback(req, res, query) const r: LoginResponseDto = await this.authManager.setCookies(user, res, false) - return res.redirect(this.authMethodOidc.getRedirectCallbackUrl(r.token.access_expiration, r.token.refresh_expiration), HttpStatus.FOUND) + return res.redirect(this.authProviderOIDC.getRedirectCallbackUrl(r.token.access_expiration, r.token.refresh_expiration), HttpStatus.FOUND) } } diff --git a/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts new file mode 100644 index 00000000..c6c3fd03 --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +export interface AuthOIDCSettings { + loginUrl: string + autoRedirect: boolean + enablePasswordAuth: boolean + buttonText: string +} diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index 39f70d12..d0e75a4f 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -9,6 +9,7 @@ import { FastifyReply, FastifyRequest } from 'fastify' import { allowInsecureRequests, authorizationCodeGrant, + AuthorizationResponseError, calculatePKCECodeChallenge, ClientSecretBasic, ClientSecretPost, @@ -63,24 +64,25 @@ export class AuthProviderOIDC implements AuthProvider { async getAuthorizationUrl(res: FastifyReply): Promise { const config = await this.getConfig() - // Generate state and nonce for CSRF protection + // state: CSRF protection, nonce: binds the ID Token to this auth request (replay protection) const state = randomState() const nonce = randomNonce() - const codeVerifier = randomPKCECodeVerifier() + const supportsPKCE = config.serverMetadata().supportsPKCE() + const codeVerifier = supportsPKCE ? randomPKCECodeVerifier() : undefined const authUrl = new URL(config.serverMetadata().authorization_endpoint!) authUrl.searchParams.set('client_id', this.oidcConfig.clientId!) authUrl.searchParams.set('redirect_uri', this.oidcConfig.redirectUri) authUrl.searchParams.set('response_type', 'code') - authUrl.searchParams.set('scope', this.oidcConfig.scope || 'openid email profile') - if (config.serverMetadata().supportsPKCE()) { - const codeChallenge = await calculatePKCECodeChallenge(codeVerifier) + authUrl.searchParams.set('scope', this.oidcConfig.security.scope) + authUrl.searchParams.set('state', state) + authUrl.searchParams.set('nonce', nonce) + if (supportsPKCE) { + const codeChallenge = await calculatePKCECodeChallenge(codeVerifier!) authUrl.searchParams.set('code_challenge', codeChallenge) authUrl.searchParams.set('code_challenge_method', 'S256') } - authUrl.searchParams.set('state', state) - authUrl.searchParams.set('nonce', nonce) // Avoid cache res @@ -90,22 +92,18 @@ export class AuthProviderOIDC implements AuthProvider { .header('X-Robots-Tag', 'noindex, nofollow') .header('Referrer-Policy', 'no-referrer') - // Store state, nonce, and codeVerifier in httpOnly cookies (10 minutes expiration) + // Store state, nonce, and codeVerifier in httpOnly cookies (expires in 10 minutes) res.setCookie(OAuthCookie.State, state, OAuthCookieSettings) res.setCookie(OAuthCookie.Nonce, nonce, OAuthCookieSettings) - res.setCookie(OAuthCookie.CodeVerifier, codeVerifier, OAuthCookieSettings) - + if (supportsPKCE) { + res.setCookie(OAuthCookie.CodeVerifier, codeVerifier, OAuthCookieSettings) + } return authUrl.toString() } async handleCallback(req: FastifyRequest, res: FastifyReply, query: Record): Promise { const config = await this.getConfig() - - // Clear temporary OIDC cookies - Object.values(OAuthCookie).forEach((value) => { - res.clearCookie(value, { path: '/' }) - }) - + const supportsPKCE = config.serverMetadata().supportsPKCE() const [expectedState, expectedNonce, codeVerifier] = [ req.cookies[OAuthCookie.State], req.cookies[OAuthCookie.Nonce], @@ -117,11 +115,11 @@ export class AuthProviderOIDC implements AuthProvider { throw new HttpException('OAuth state is missing', HttpStatus.BAD_REQUEST) } - if (!codeVerifier?.length) { + if (supportsPKCE && !codeVerifier?.length) { throw new HttpException('OAuth code verifier is missing', HttpStatus.BAD_REQUEST) } - const pkceCodeVerifier = config.serverMetadata().supportsPKCE() ? codeVerifier : undefined + const pkceCodeVerifier = supportsPKCE ? codeVerifier : undefined const callbackParams = new URLSearchParams(query) // Exchange authorization code for tokens @@ -142,8 +140,9 @@ export class AuthProviderOIDC implements AuthProvider { throw new HttpException('Unexpected profile response, no `sub`', HttpStatus.BAD_REQUEST) } - // Get user info from the userinfo endpoint (requires access token and subject from ID token) - const subject = this.oidcConfig.skipSubjectCheck ? skipSubjectCheck : claims.sub + // ID token claims may be minimal depending on the IdP; use the UserInfo endpoint to retrieve user details. + // Get user info from the userinfo endpoint (requires access token and subject from ID token). + const subject = this.oidcConfig.security.skipSubjectCheck ? skipSubjectCheck : claims.sub const userInfo: UserInfoResponse = await fetchUserInfo(config, tokens.access_token, subject) if (!userInfo.sub) { @@ -152,10 +151,19 @@ export class AuthProviderOIDC implements AuthProvider { // Process the user info and create/update the user return await this.processUserInfo(userInfo, req.ip) - } catch (error) { - const errorStatus = error?.getStatus() || HttpStatus.UNAUTHORIZED - this.logger.error(`${this.handleCallback.name} - OIDC callback error: ${error}`) - throw new HttpException('OIDC authentication failed', errorStatus) + } catch (error: AuthorizationResponseError | HttpException | any) { + if (error instanceof AuthorizationResponseError) { + this.logger.error(`${this.handleCallback.name} - OIDC callback error: ${error.code} - ${error.error_description}`) + throw new HttpException(error.error_description, HttpStatus.BAD_REQUEST) + } else { + this.logger.error(`${this.handleCallback.name} - OIDC callback error: ${error}`) + throw new HttpException('OIDC authentication failed', error instanceof HttpException ? error.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR) + } + } finally { + // Always clear temporary OIDC cookies (success or failure) + Object.values(OAuthCookie).forEach((value) => { + res.clearCookie(value, { path: '/' }) + }) } } @@ -167,7 +175,7 @@ export class AuthProviderOIDC implements AuthProvider { return null } - if (user.isGuest || user.isAdmin || scope || this.oidcConfig.enablePasswordAuth) { + if (user.isGuest || user.isAdmin || scope || this.oidcConfig.options.enablePasswordAuth) { // Allow local password authentication for: // - guest users // - admin users (break-glass access) @@ -201,16 +209,22 @@ export class AuthProviderOIDC implements AuthProvider { const config: Configuration = await discovery( issuerUrl, this.oidcConfig.clientId, - { client_secret: this.oidcConfig.clientSecret, response_types: ['code'] }, - this.getTokenAuthMethod(this.oidcConfig.clientAuthMethod, this.oidcConfig.clientSecret), { - execute: [allowInsecureRequests] // allow HTTP for development + client_secret: this.oidcConfig.clientSecret, + response_types: ['code'], + id_token_signed_response_alg: this.oidcConfig.security.tokenSigningAlg, + userinfo_signed_response_alg: this.oidcConfig.security.userInfoSigningAlg + }, + this.getTokenAuthMethod(this.oidcConfig.security.tokenEndpointAuthMethod, this.oidcConfig.clientSecret), + { + execute: [allowInsecureRequests], // allow HTTP for development + timeout: 6000 } ) this.logger.log(`${this.initializeOIDCClient.name} - OIDC client initialized successfully for issuer: ${this.oidcConfig.issuerUrl}`) return config } catch (error) { - this.logger.error(`${this.initializeOIDCClient.name} - Failed to initialize OIDC client: ${error}`) + this.logger.error(`${this.initializeOIDCClient.name} - Failed to initialize OIDC client: ${error?.cause || error}`) return null } } @@ -242,7 +256,7 @@ export class AuthProviderOIDC implements AuthProvider { // Check if user exists let user: UserModel = await this.usersManager.findUser(email || login, false) - if (!user && !this.oidcConfig.autoCreateUser) { + if (!user && !this.oidcConfig.options.autoCreateUser) { throw new HttpException('User not found and autoCreateUser is disabled', HttpStatus.UNAUTHORIZED) } @@ -262,14 +276,14 @@ export class AuthProviderOIDC implements AuthProvider { } private checkAdminRole(userInfo: UserInfoResponse): boolean { - if (!this.oidcConfig.adminRoleOrGroup) { + if (!this.oidcConfig.options.adminRoleOrGroup) { return false } // Check claims const claims = [...(Array.isArray(userInfo.groups) ? userInfo.groups : []), ...(Array.isArray(userInfo.roles) ? userInfo.roles : [])] - return claims.includes(this.oidcConfig.adminRoleOrGroup) + return claims.includes(this.oidcConfig.options.adminRoleOrGroup) } private createIdentity( @@ -301,7 +315,11 @@ export class AuthProviderOIDC implements AuthProvider { private async updateOrCreateUser(identity: Omit & { password?: string }, user: UserModel | null): Promise { if (user === null) { // Create new user with a random password (required by the system but not used for OIDC login) - const userWithPassword = { ...identity, password: generateShortUUID(24) } as CreateUserDto + const userWithPassword = { + ...identity, + password: generateShortUUID(24), + permissions: this.oidcConfig.options.autoCreatePermissions.join(',') + } as CreateUserDto const createdUser = await this.adminUsersManager.createUserOrGuest(userWithPassword, identity.role) const freshUser = await this.usersManager.fromUserId(createdUser.id) if (!freshUser) { @@ -322,7 +340,7 @@ export class AuthProviderOIDC implements AuthProvider { if (Object.keys(identityHasChanged).length > 0) { try { if (identityHasChanged?.role != null) { - if (user.role === USER_ROLE.ADMINISTRATOR && !this.oidcConfig.adminRoleOrGroup) { + if (user.role === USER_ROLE.ADMINISTRATOR && !this.oidcConfig.options.adminRoleOrGroup) { // Prevent removing the admin role when adminGroup was removed or not defined delete identityHasChanged.role } diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index c7ed5e32..c489690a 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -141,52 +141,85 @@ auth: # default: true enablePasswordAuthFallback: true oidc: - # issuerUrl: The URL of the OIDC provider's discovery endpoint (e.g., https://accounts.google.com, https://login.microsoftonline.com/tenant-id/v2.0) - # The server will automatically discover the authorization, token, userinfo, and logout endpoints + # issuerUrl: The URL of the OIDC provider's discovery endpoint + # Examples: + # - Keycloak: https://auth.example.com/realms/my-realm + # - Authentik: https://auth.example.com/application/o/my-app/ + # - Google: https://accounts.google.com + # - Microsoft: https://login.microsoftonline.com//v2.0 + # The server will automatically discover the authorization, token, and userinfo endpoints. # required issuerUrl: # clientId: OAuth 2.0 Client ID obtained from your OIDC provider # required - clientId: + clientId: sync-in # clientSecret: OAuth 2.0 Client Secret obtained from your OIDC provider # required - clientSecret: - # OAuth 2.0 / OIDC client authentication method used at the token endpoint. - # Possible values: - # - client_secret_basic (DEFAULT): HTTP Basic auth using client_id and client_secret. - # Recommended for backend (confidential) clients. - # - client_secret_post: client_id and client_secret sent in the request body. - # - none: no client authentication (public clients: mobile / SPA with PKCE). - # default: `client_secret_basic` - # optional - clientAuthMethod: client_secret_basic + clientSecret: changeClientSecret # redirectUri: The callback URL where users are redirected after authentication # This URL must be registered in your OIDC provider's allowed redirect URIs + # Example (API callback): https://sync-in.domain.com/api/auth/oidc/callback # required - redirectUri: http://192.168.0.25:8080/api/auth/oidc/callback - # scope: OAuth 2.0 scopes to request (space-separated string) - # Common scopes: openid (required), email, profile, groups, roles - # default: `openid email profile` - scope: openid email profile - # autoCreateUser: Automatically create a local user account on first successful OIDC login. - # When disabled, only existing users are allowed to authenticate via OIDC. - # default: true - autoCreateUser: true - # enablePasswordAuthentication: Enable local password-based authentication (login or email/password). - # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. - # Users must have signed in at least once and have a local password set. - # default: true - enablePasswordAuth: true - # skipSubjectCheck: Disable verification that the `sub` claim returned by the UserInfo endpoint - # matches the `sub` claim from the ID token. - # Set to true only for non-compliant or legacy OIDC providers. - # default: false - skipSubjectCheck: false - # adminRoleOrGroup: Name of the role or group that grants Sync-in administrator access - # Users with this value will be granted administrator privileges - # The value is matched against the `groups` or `roles` claims returned by the OIDC provider - # optional - adminRoleOrGroup: + redirectUri: https://sync-in.domain.com/api/auth/oidc/callback + options: + # autoCreateUser: Automatically create a local user account on first successful OIDC login. + # When enabled, the user `login` is derived from OIDC claims: preferred_username, then the email local-part, with `sub` as a last-resort fallback. + # When disabled, only existing users are allowed to authenticate via OIDC. + # default: true + autoCreateUser: true + # autoCreatePermissions: Permissions assigned to users automatically created via OIDC. + # Applied only when autoCreateUser is enabled and only applied at user creation time. + # This option has no effect on existing users. + # A complete list of permissions is available in the documentation: https://sync-in.com/docs/admin-guide/permissions + # e.g.: [personal_space, spaces_access] (array required) + # default: [] + autoCreatePermissions: [] + # autoRedirect: Automatically redirect users to the OIDC login flow. + # When enabled, the login page is skipped and users are sent directly to the OIDC provider. + # default: false + autoRedirect: false + # enablePasswordAuth: Enable local password-based authentication (login or email/password). + # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. + # Users must have signed in at least once and have a local password set. + # default: true + enablePasswordAuth: true + # adminRoleOrGroup: Name of the role or group that grants Sync-in administrator access + # Users with this value will be granted administrator privileges. + # The value is matched against `roles` or `groups` claims provided by the IdP. + # Note: depending on the provider (e.g., Keycloak), roles/groups may be exposed only in tokens + # and require proper IdP mappers to be included in the ID token or UserInfo response. + # optional + adminRoleOrGroup: + # buttonText: Label displayed on the OIDC login button. + # default: Continue with OpenID Connect + buttonText: Continue with OpenID Connect + security: + # scope: OAuth 2.0 scopes to request (space-separated string) + # Common scopes: openid (required), email, profile, groups, roles + # default: `openid email profile` + scope: openid email profile + # OAuth 2.0 / OIDC client authentication method used at the token endpoint. + # Possible values: + # - client_secret_basic (DEFAULT): HTTP Basic auth using client_id and client_secret. + # Recommended for backend (confidential) clients. + # - client_secret_post: client_id and client_secret sent in the request body. + # - none (or undefined): no client authentication (public clients: mobile / SPA with PKCE). + # default: `client_secret_basic` + tokenEndpointAuthMethod: client_secret_basic + # tokenSigningAlg: Algorithm used to verify the signature of ID tokens (JWT) returned by the OpenID Connect provider. + # Common values: RS256, RS384, RS512, ES256, ES384, ES512 + # default: `RS256` + tokenSigningAlg: RS256 + # userInfoSigningAlg: Algorithm used to request a signed UserInfo response from the OpenID Connect provider. + # When not set, the UserInfo endpoint returns a standard JSON response (not signed). This is the most common and recommended configuration. + # Common values: (empty), RS256, RS384, RS512, ES256, ES384, ES512 + # default: empty + userInfoSigningAlg: + # skipSubjectCheck: Disable verification that the `sub` claim returned by the UserInfo endpoint + # matches the `sub` claim from the ID token. + # Set to true only for non-compliant or legacy OIDC providers. + # default: false + skipSubjectCheck: false applications: files: # required diff --git a/frontend/src/app/auth/auth-resolvers.ts b/frontend/src/app/auth/auth-resolvers.ts new file mode 100644 index 00000000..976d39ca --- /dev/null +++ b/frontend/src/app/auth/auth-resolvers.ts @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { inject } from '@angular/core' +import { ResolveFn } from '@angular/router' +import type { AuthOIDCSettings } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc.interfaces' +import { AuthService } from './auth.service' + +export const authResolver: ResolveFn = () => { + return inject(AuthService).getAuthSettings() +} diff --git a/frontend/src/app/auth/auth.component.html b/frontend/src/app/auth/auth.component.html index 05210d69..eb29ec35 100644 --- a/frontend/src/app/auth/auth.component.html +++ b/frontend/src/app/auth/auth.component.html @@ -56,24 +56,38 @@ } @else { -
-

{{ hasError || 'Sign-in to your account' }}

-
+ @if (!oidcSettings || oidcSettings.enablePasswordAuth) { + +

{{ hasError || 'Sign-in to your account' }}

+
- -
-
+ +
+
- -
-
- + +
+
+ +
+ + } + @if (oidcSettings) { +
+
+ +
- + } }
diff --git a/frontend/src/app/auth/auth.component.ts b/frontend/src/app/auth/auth.component.ts index 6ce5276c..0ee6f086 100644 --- a/frontend/src/app/auth/auth.component.ts +++ b/frontend/src/app/auth/auth.component.ts @@ -6,11 +6,12 @@ import { Component, inject } from '@angular/core' import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms' -import { Router } from '@angular/router' +import { ActivatedRoute, Router } from '@angular/router' import { FaIconComponent } from '@fortawesome/angular-fontawesome' import { faKey, faLock, faQrcode, faUserAlt } from '@fortawesome/free-solid-svg-icons' import { USER_PASSWORD_MIN_LENGTH } from '@sync-in-server/backend/src/applications/users/constants/user' import { TWO_FA_CODE_LENGTH } from '@sync-in-server/backend/src/authentication/constants/auth' +import type { AuthOIDCSettings } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc.interfaces' import type { TwoFaResponseDto, TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective, L10nTranslatePipe } from 'angular-l10n' import { finalize } from 'rxjs/operators' @@ -32,6 +33,10 @@ export class AuthComponent { protected hasError: any = null protected submitted = false protected twoFaVerify = false + private route = inject(ActivatedRoute) + protected oidcSettings: AuthOIDCSettings | false = this.route.snapshot.data['authSettings'] + private readonly router = inject(Router) + private readonly auth = inject(AuthService) private readonly fb = inject(UntypedFormBuilder) protected loginForm: FormGroup = this.fb.group({ username: this.fb.control('', [Validators.required]), @@ -42,8 +47,12 @@ export class AuthComponent { recoveryCode: this.fb.control('', [Validators.required, Validators.minLength(USER_PASSWORD_MIN_LENGTH)]), isRecoveryCode: this.fb.control(false) }) - private readonly router = inject(Router) - private readonly auth = inject(AuthService) + + constructor() { + if (this.oidcSettings && this.oidcSettings.autoRedirect) { + this.loginWithOIDC() + } + } onSubmit() { this.submitted = true @@ -107,4 +116,10 @@ export class AuthComponent { } this.loginForm.patchValue({ password: '' }) } + + loginWithOIDC() { + if (this.oidcSettings) { + window.location.href = this.oidcSettings.loginUrl + } + } } diff --git a/frontend/src/app/auth/auth.routes.ts b/frontend/src/app/auth/auth.routes.ts index 4ceb85f3..c8fe8c8f 100644 --- a/frontend/src/app/auth/auth.routes.ts +++ b/frontend/src/app/auth/auth.routes.ts @@ -5,6 +5,7 @@ */ import { Routes } from '@angular/router' +import { authResolver } from './auth-resolvers' import { AuthComponent } from './auth.component' import { AUTH_PATHS } from './auth.constants' import { noAuthGuard } from './auth.guards' @@ -13,6 +14,7 @@ export const authRoutes: Routes = [ { path: AUTH_PATHS.BASE, canActivate: [noAuthGuard], + resolve: { authSettings: authResolver }, children: [{ path: AUTH_PATHS.LOGIN, component: AuthComponent }] } ] diff --git a/frontend/src/app/auth/auth.service.ts b/frontend/src/app/auth/auth.service.ts index 5c70a8d0..b14ff042 100644 --- a/frontend/src/app/auth/auth.service.ts +++ b/frontend/src/app/auth/auth.service.ts @@ -17,10 +17,12 @@ import { API_AUTH_LOGIN, API_AUTH_LOGOUT, API_AUTH_REFRESH, + API_AUTH_SETTINGS, API_TWO_FA_LOGIN_VERIFY } from '@sync-in-server/backend/src/authentication/constants/routes' import type { LoginResponseDto } from '@sync-in-server/backend/src/authentication/dto/login-response.dto' import type { TokenResponseDto } from '@sync-in-server/backend/src/authentication/dto/token-response.dto' +import type { AuthOIDCSettings } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc.interfaces' import type { TwoFaResponseDto, TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' import { currentTimeStamp } from '@sync-in-server/backend/src/common/shared' import { catchError, finalize, map, Observable, of, throwError } from 'rxjs' @@ -40,6 +42,7 @@ import type { AuthOIDCQueryParams } from './auth.interface' }) export class AuthService { public returnUrl: string + private authSettings: AuthOIDCSettings | false = null private readonly http = inject(HttpClient) private readonly router = inject(Router) private readonly store = inject(StoreService) @@ -241,6 +244,22 @@ export class AuthService { } } + getAuthSettings(): Observable { + // If OIDC authentication is not enabled, the route should return a 404. + if (this.authSettings !== null) return of(this.authSettings) + return this.http.get(API_AUTH_SETTINGS).pipe( + map((r): AuthOIDCSettings => { + this.authSettings = r + return r + }), + catchError((e: HttpErrorResponse) => { + console.error(e) + this.authSettings = false + return of(false as const) + }) + ) + } + private refreshTokenHasExpired(): boolean { return this.refreshExpiration === 0 || currentTimeStamp() >= this.refreshExpiration } diff --git a/frontend/src/i18n/de.json b/frontend/src/i18n/de.json index 5afca308..e127a441 100644 --- a/frontend/src/i18n/de.json +++ b/frontend/src/i18n/de.json @@ -634,5 +634,6 @@ "Preferences": "Einstellungen", "of": "von", "Sidebar auto-hide enabled": "Automatisches Ausblenden der Seitenleiste aktiviert", - "Sidebar pinned": "Seitenleiste angeheftet" + "Sidebar pinned": "Seitenleiste angeheftet", + "Continue with OpenID Connect": "Mit OpenID Connect fortfahren" } \ No newline at end of file diff --git a/frontend/src/i18n/es.json b/frontend/src/i18n/es.json index b166f749..70f62076 100644 --- a/frontend/src/i18n/es.json +++ b/frontend/src/i18n/es.json @@ -634,5 +634,6 @@ "Preferences": "Preferencias", "of": "de", "Sidebar auto-hide enabled": "Ocultación automática de la barra lateral activada", - "Sidebar pinned": "Barra lateral fijada" + "Sidebar pinned": "Barra lateral fijada", + "Continue with OpenID Connect": "Continuar con OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/fr.json b/frontend/src/i18n/fr.json index 6501ecdc..3b218144 100644 --- a/frontend/src/i18n/fr.json +++ b/frontend/src/i18n/fr.json @@ -634,5 +634,6 @@ "Preferences": "Préférences", "of": "de", "Sidebar auto-hide enabled": "Masquage automatique de la barre latérale activé", - "Sidebar pinned": "Barre latérale épinglée" + "Sidebar pinned": "Barre latérale épinglée", + "Continue with OpenID Connect": "Continuer avec OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/hi.json b/frontend/src/i18n/hi.json index 485c5fc1..0bb17385 100644 --- a/frontend/src/i18n/hi.json +++ b/frontend/src/i18n/hi.json @@ -634,5 +634,6 @@ "Preferences": "वरीयताएँ", "of": "का", "Sidebar auto-hide enabled": "साइडबार स्वतः छिपाना सक्षम", - "Sidebar pinned": "साइडबार पिन किया गया" + "Sidebar pinned": "साइडबार पिन किया गया", + "Continue with OpenID Connect": "OpenID Connect के साथ जारी रखें" } \ No newline at end of file diff --git a/frontend/src/i18n/it.json b/frontend/src/i18n/it.json index 96e6da10..e156b2d1 100644 --- a/frontend/src/i18n/it.json +++ b/frontend/src/i18n/it.json @@ -634,5 +634,6 @@ "Preferences": "Preferenze", "of": "di", "Sidebar auto-hide enabled": "Nascondimento automatico della barra laterale attivato", - "Sidebar pinned": "Barra laterale bloccata" + "Sidebar pinned": "Barra laterale bloccata", + "Continue with OpenID Connect": "Continua con OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/ja.json b/frontend/src/i18n/ja.json index 9a2afa37..d500390b 100644 --- a/frontend/src/i18n/ja.json +++ b/frontend/src/i18n/ja.json @@ -634,5 +634,6 @@ "Preferences": "環境設定", "of": "の", "Sidebar auto-hide enabled": "サイドバー自動非表示が有効", - "Sidebar pinned": "サイドバー固定" + "Sidebar pinned": "サイドバー固定", + "Continue with OpenID Connect": "OpenID Connectで続行" } \ No newline at end of file diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index a06bc0ee..da729159 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -634,5 +634,6 @@ "Preferences": "환경 설정", "of": "의", "Sidebar auto-hide enabled": "사이드바 자동 숨김 활성화", - "Sidebar pinned": "사이드바 고정됨" + "Sidebar pinned": "사이드바 고정됨", + "Continue with OpenID Connect": "OpenID Connect로 계속하기" } \ No newline at end of file diff --git a/frontend/src/i18n/pl.json b/frontend/src/i18n/pl.json index 8e15517c..f1fbf370 100644 --- a/frontend/src/i18n/pl.json +++ b/frontend/src/i18n/pl.json @@ -634,5 +634,6 @@ "Preferences": "Preferencje", "of": "z", "Sidebar auto-hide enabled": "Włączono automatyczne ukrywanie panelu bocznego", - "Sidebar pinned": "Panel boczny przypięty" + "Sidebar pinned": "Panel boczny przypięty", + "Continue with OpenID Connect": "Kontynuuj z OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/pt-BR.json b/frontend/src/i18n/pt-BR.json index 5ee275ad..96312b97 100644 --- a/frontend/src/i18n/pt-BR.json +++ b/frontend/src/i18n/pt-BR.json @@ -634,5 +634,6 @@ "Preferences": "Preferências", "of": "de", "Sidebar auto-hide enabled": "Ocultação automática da barra lateral ativada", - "Sidebar pinned": "Barra lateral fixada" + "Sidebar pinned": "Barra lateral fixada", + "Continue with OpenID Connect": "Continuar com OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/pt.json b/frontend/src/i18n/pt.json index 965f2caf..ca39a2aa 100644 --- a/frontend/src/i18n/pt.json +++ b/frontend/src/i18n/pt.json @@ -634,5 +634,6 @@ "Preferences": "Preferências", "of": "de", "Sidebar auto-hide enabled": "Ocultação automática da barra lateral ativada", - "Sidebar pinned": "Barra lateral fixa" + "Sidebar pinned": "Barra lateral fixa", + "Continue with OpenID Connect": "Continuar com OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/ru.json b/frontend/src/i18n/ru.json index 296f467d..46e8d34a 100644 --- a/frontend/src/i18n/ru.json +++ b/frontend/src/i18n/ru.json @@ -634,5 +634,6 @@ "Preferences": "Настройки", "of": "из", "Sidebar auto-hide enabled": "Автоскрытие боковой панели включено", - "Sidebar pinned": "Боковая панель закреплена" + "Sidebar pinned": "Боковая панель закреплена", + "Continue with OpenID Connect": "Продолжить с OpenID Connect" } \ No newline at end of file diff --git a/frontend/src/i18n/tr.json b/frontend/src/i18n/tr.json index c607f2e3..2561044f 100644 --- a/frontend/src/i18n/tr.json +++ b/frontend/src/i18n/tr.json @@ -634,5 +634,6 @@ "Preferences": "Tercihler", "of": "/", "Sidebar auto-hide enabled": "Kenar çubuğu otomatik gizleme etkin", - "Sidebar pinned": "Kenar çubuğu sabitlendi" + "Sidebar pinned": "Kenar çubuğu sabitlendi", + "Continue with OpenID Connect": "OpenID Connect ile devam et" } \ No newline at end of file diff --git a/frontend/src/i18n/zh.json b/frontend/src/i18n/zh.json index 250a42bb..edcc19c8 100644 --- a/frontend/src/i18n/zh.json +++ b/frontend/src/i18n/zh.json @@ -634,5 +634,6 @@ "Preferences": "偏好设置", "of": "的", "Sidebar auto-hide enabled": "侧边栏自动隐藏已启用", - "Sidebar pinned": "侧边栏已固定" + "Sidebar pinned": "侧边栏已固定", + "Continue with OpenID Connect": "使用 OpenID Connect 继续" } \ No newline at end of file diff --git a/frontend/src/styles/components/_app.scss b/frontend/src/styles/components/_app.scss index ce308618..716cd08d 100755 --- a/frontend/src/styles/components/_app.scss +++ b/frontend/src/styles/components/_app.scss @@ -18,15 +18,14 @@ background: rgba(0, 0, 0, 0.5); padding: 2rem 5rem 2rem 5rem; border-radius: 8px; - min-height: 295px; - min-width: 420px; + min-width: 400px; .content { display: flex; flex-direction: column; .header { - margin-bottom: 1rem; + margin-bottom: .6rem; } .body { diff --git a/package-lock.json b/package-lock.json index dd25f56b..395b5357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,6 +82,7 @@ "mysql2": "^3.11.4", "nestjs-pino": "^4.3.0", "nodemailer": "^7.0.0", + "openid-client": "^6.8.1", "passport": "^0.7.0", "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", @@ -7422,16 +7423,235 @@ "win32" ] }, - "node_modules/@napi-rs/canvas": { - "name": "noop3", - "version": "1000.0.0", - "resolved": "https://registry.npmjs.org/noop3/-/noop3-1000.0.0.tgz", - "integrity": "sha512-OkcS5jjmDBhdLt5rwoHtItQfUHP3+NpWA9voB0SHth+KLIoupZEe+3D76bNvS37qG8vg2FFSqQ61blrEtZj1Yw==", + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.88.tgz", + "integrity": "sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "android" + ], "engines": { - "node": ">=6" + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.88.tgz", + "integrity": "sha512-Xgywz0dDxOKSgx3eZnK85WgGMmGrQEW7ZLA/E7raZdlEE+xXCozobgqz2ZvYigpB6DJFYkqnwHjqCOTSDGlFdg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.88.tgz", + "integrity": "sha512-Yz4wSCIQOUgNucgk+8NFtQxQxZV5NO8VKRl9ePKE6XoNyNVC8JDqtvhh3b3TPqKK8W5p2EQpAr1rjjm0mfBxdg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.88.tgz", + "integrity": "sha512-9gQM2SlTo76hYhxHi2XxWTAqpTOb+JtxMPEIr+H5nAhHhyEtNmTSDRtz93SP7mGd2G3Ojf2oF5tP9OdgtgXyKg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.88.tgz", + "integrity": "sha512-7qgaOBMXuVRk9Fzztzr3BchQKXDxGbY+nwsovD3I/Sx81e+sX0ReEDYHTItNb0Je4NHbAl7D0MKyd4SvUc04sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.88.tgz", + "integrity": "sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.88.tgz", + "integrity": "sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.88.tgz", + "integrity": "sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.88.tgz", + "integrity": "sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.88.tgz", + "integrity": "sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.88", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.88.tgz", + "integrity": "sha512-ROVqbfS4QyZxYkqmaIBBpbz/BQvAR+05FXM5PAtTYVc0uyY8Y4BHJSMdGAaMf6TdIVRsQsiq+FG/dH9XhvWCFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/nice": { @@ -21041,7 +21261,6 @@ "version": "6.1.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -23586,6 +23805,15 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oauth4webapi": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.3.tgz", + "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -23664,6 +23892,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.1.tgz", + "integrity": "sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==", + "license": "MIT", + "dependencies": { + "jose": "^6.1.0", + "oauth4webapi": "^3.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/opentype.js": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-0.11.0.tgz", From 96d52c95585eef6a6c8498f14a1ba07dce44f6cb Mon Sep 17 00:00:00 2001 From: johaven Date: Sat, 24 Jan 2026 16:44:24 +0100 Subject: [PATCH 07/18] feat(backend:auth:ldap): move adminGroup to options, add autoCreateUser and autoCreatePermissions --- .../providers/ldap/auth-ldap.config.ts | 33 +++++-- .../ldap/auth-provider-ldap.service.ts | 22 +++-- .../oidc/auth-provider-oidc.service.ts | 43 ++++----- .../src/configuration/config.environment.ts | 9 ++ environment/environment.dist.min.yaml | 2 +- environment/environment.dist.yaml | 93 ++++++++++++------- 6 files changed, 131 insertions(+), 71 deletions(-) diff --git a/backend/src/authentication/providers/ldap/auth-ldap.config.ts b/backend/src/authentication/providers/ldap/auth-ldap.config.ts index 9ec70da0..237a446f 100644 --- a/backend/src/authentication/providers/ldap/auth-ldap.config.ts +++ b/backend/src/authentication/providers/ldap/auth-ldap.config.ts @@ -18,6 +18,7 @@ import { IsString, ValidateNested } from 'class-validator' +import { USER_PERMISSION } from '../../../applications/users/constants/user' import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' export class AuthMethodLDAPAttributesConfig { @@ -32,6 +33,25 @@ export class AuthMethodLDAPAttributesConfig { email: string = LDAP_COMMON_ATTR.MAIL } +export class AuthMethodLDAPOptionsConfig { + @IsOptional() + @IsString() + adminGroup?: string + + @IsOptional() + @IsBoolean() + autoCreateUser? = true + + @IsOptional() + @IsArray() + @IsEnum(USER_PERMISSION, { each: true }) + autoCreatePermissions?: USER_PERMISSION[] = [] + + @IsOptional() + @IsBoolean() + enablePasswordAuthFallback? = true +} + export class AuthMethodLDAPConfig { @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value)) @ArrayNotEmpty() @@ -54,10 +74,6 @@ export class AuthMethodLDAPConfig { @Type(() => AuthMethodLDAPAttributesConfig) attributes: AuthMethodLDAPAttributesConfig = new AuthMethodLDAPAttributesConfig() - @IsOptional() - @IsString() - adminGroup?: string - @IsOptional() @IsString() upnSuffix?: string @@ -66,7 +82,10 @@ export class AuthMethodLDAPConfig { @IsString() netbiosName?: string - @IsOptional() - @IsBoolean() - enablePasswordAuthFallback? = true + @IsDefined() + @IsNotEmptyObject() + @IsObject() + @ValidateNested() + @Type(() => AuthMethodLDAPOptionsConfig) + options: AuthMethodLDAPOptionsConfig = new AuthMethodLDAPOptionsConfig() } diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts index c2715996..58ceaa38 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts @@ -61,7 +61,7 @@ export class AuthProviderLDAP implements AuthProvider { // Allow local password authentication for: // - admin users (break-glass access) // - regular users when password authentication fallback is enabled - if (user && Boolean(ldapErrorMessage) && (user.isAdmin || this.ldapConfig.enablePasswordAuthFallback)) { + if (user && Boolean(ldapErrorMessage) && (user.isAdmin || this.ldapConfig.options.enablePasswordAuthFallback)) { const localUser = await this.usersManager.logUser(user, password, ip) if (localUser) return localUser } @@ -80,6 +80,11 @@ export class AuthProviderLDAP implements AuthProvider { return null } + if (!user && !this.ldapConfig.options.autoCreateUser) { + this.logger.warn(`${this.validateUser.name} - User not found and autoCreateUser is disabled`) + throw new HttpException('User not found', HttpStatus.UNAUTHORIZED) + } + const identity = this.createIdentity(entry, password) user = await this.updateOrCreateUser(identity, user) this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`)) @@ -92,7 +97,7 @@ export class AuthProviderLDAP implements AuthProvider { // Generic LDAP: build DN from login attribute + baseDN const bindUserDN = this.isAD ? ldapLogin : `${this.ldapConfig.attributes.login}=${ldapLogin},${this.ldapConfig.baseDN}` let client: Client - let error: any + let error: InvalidCredentialsError | any for (const s of this.ldapConfig.servers) { client = new Client({ ...this.clientOptions, url: s }) try { @@ -101,12 +106,12 @@ export class AuthProviderLDAP implements AuthProvider { } catch (e) { if (e.errors?.length) { for (const err of e.errors) { - this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${err}`) + this.logger.warn(`${this.checkAuth.name} - ${bindUserDN} : ${err}`) error = err } } else { error = e - this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${e}`) + this.logger.warn(`${this.checkAuth.name} - ${bindUserDN} : ${e}`) } if (error instanceof InvalidCredentialsError) { return false @@ -155,6 +160,7 @@ export class AuthProviderLDAP implements AuthProvider { private async updateOrCreateUser(identity: CreateUserDto, user: UserModel): Promise { if (user === null) { // Create + identity.permissions = this.ldapConfig.options.autoCreatePermissions.join(',') const createdUser = await this.adminUsersManager.createUserOrGuest(identity, identity.role) const freshUser = await this.usersManager.fromUserId(createdUser.id) if (!freshUser) { @@ -187,7 +193,7 @@ export class AuthProviderLDAP implements AuthProvider { if (Object.keys(identityHasChanged).length > 0) { try { if (identityHasChanged?.role != null) { - if (user.role === USER_ROLE.ADMINISTRATOR && !this.ldapConfig.adminGroup) { + if (user.role === USER_ROLE.ADMINISTRATOR && !this.ldapConfig.options.adminGroup) { // Prevent removing the admin role when adminGroup was removed or not defined delete identityHasChanged.role } @@ -233,9 +239,9 @@ export class AuthProviderLDAP implements AuthProvider { private createIdentity(entry: LdapUserEntry, password: string): CreateUserDto { const isAdmin = - typeof this.ldapConfig.adminGroup === 'string' && - this.ldapConfig.adminGroup && - entry[LDAP_COMMON_ATTR.MEMBER_OF]?.includes(this.ldapConfig.adminGroup) + typeof this.ldapConfig.options.adminGroup === 'string' && + this.ldapConfig.options.adminGroup && + entry[LDAP_COMMON_ATTR.MEMBER_OF]?.includes(this.ldapConfig.options.adminGroup) return { login: this.dbLogin(entry[this.ldapConfig.attributes.login]), email: entry[this.ldapConfig.attributes.email] as string, diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index d0e75a4f..4c116580 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -51,6 +51,26 @@ export class AuthProviderOIDC implements AuthProvider { private readonly adminUsersManager: AdminUsersManager ) {} + async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise { + // Find user from login or email + const user: UserModel = await this.usersManager.findUser(login, false) + + if (!user) { + return null + } + + if (user.isGuest || user.isAdmin || scope || this.oidcConfig.options.enablePasswordAuth) { + // Allow local password authentication for: + // - guest users + // - admin users (break-glass access) + // - application scopes (app passwords) + // - regular users when password authentication is enabled + return this.usersManager.logUser(user, password, ip, scope) + } + + return null + } + async getConfig(): Promise { if (!this.config) { this.config = await this.initializeOIDCClient() @@ -167,26 +187,6 @@ export class AuthProviderOIDC implements AuthProvider { } } - async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise { - // Find user from login or email - const user: UserModel = await this.usersManager.findUser(login, false) - - if (!user) { - return null - } - - if (user.isGuest || user.isAdmin || scope || this.oidcConfig.options.enablePasswordAuth) { - // Allow local password authentication for: - // - guest users - // - admin users (break-glass access) - // - application scopes (app passwords) - // - regular users when password authentication is enabled - return this.usersManager.logUser(user, password, ip, scope) - } - - return null - } - getRedirectCallbackUrl(accessExpiration: number, refreshExpiration: number) { if (!this.frontendBaseUrl) { const url = new URL(this.oidcConfig.redirectUri) @@ -257,7 +257,8 @@ export class AuthProviderOIDC implements AuthProvider { let user: UserModel = await this.usersManager.findUser(email || login, false) if (!user && !this.oidcConfig.options.autoCreateUser) { - throw new HttpException('User not found and autoCreateUser is disabled', HttpStatus.UNAUTHORIZED) + this.logger.warn(`${this.validateUser.name} - User not found and autoCreateUser is disabled`) + throw new HttpException('User not found', HttpStatus.UNAUTHORIZED) } // Determine if user should be admin based on groups/roles diff --git a/backend/src/configuration/config.environment.ts b/backend/src/configuration/config.environment.ts index 4c9a535e..e7398c5b 100755 --- a/backend/src/configuration/config.environment.ts +++ b/backend/src/configuration/config.environment.ts @@ -47,5 +47,14 @@ function loadConfiguration(): GlobalConfig { config.applications.files.usersPath = join(config.applications.files.dataPath, 'users') config.applications.files.spacesPath = join(config.applications.files.dataPath, 'spaces') config.applications.files.tmpPath = join(config.applications.files.dataPath, 'tmp') + // DEPRECATIONS + // ldap.adminGroup → ldap.options.adminGroup + if (typeof config.auth.ldap['adminGroup'] === 'string' && config.auth.ldap['adminGroup'].length > 0) { + config.auth.ldap.options.adminGroup = config.auth.ldap['adminGroup'] + console.warn( + '[DEPRECATED][CONFIGURATION] auth.ldap.adminGroup is deprecated and will be removed in a future version. ' + + 'Please use auth.ldap.options.adminGroup instead.' + ) + } return transformAndValidate(GlobalConfig, config, { exposeDefaultValues: true }, { skipMissingProperties: false }) } diff --git a/environment/environment.dist.min.yaml b/environment/environment.dist.min.yaml index d106553f..e12332b4 100644 --- a/environment/environment.dist.min.yaml +++ b/environment/environment.dist.min.yaml @@ -1,5 +1,5 @@ mysql: - url: mysql://user:MySQLRootPassword@localhost:3306/database + url: mysql://user:MySQLPassword@localhost:3306/database auth: encryptionKey: changeEncryptionKeyWithStrongKey token: diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index c489690a..f5e97a52 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -26,7 +26,7 @@ logger: filePath: mysql: # required - url: mysql://user:MySQLRootPassword@localhost:3306/database + url: mysql://user:MySQLPassword@localhost:3306/database # default: `false` logQueries: false cache: @@ -84,16 +84,6 @@ auth: # Optional but strongly recommended # Warning: do not change or remove the encryption key after MFA activation, or the codes will become invalid encryptionKey: changeEncryptionKeyWithStrongKey - # Multifactor authentication - mfa: - # TOTP configuration - totp: - # Enable TOTP authentication - # default: true - enabled: true - # Name displayed in the authentication app (FreeOTP, Proton Authenticator, Aegis Authenticator etc.) - # default: Sync-in - issuer: Sync-in # cookie sameSite setting: `lax` | `strict` # default: `strict` cookieSameSite: strict @@ -112,13 +102,33 @@ auth: # token expiration = cookie maxAge # default: `4h` expiration: 4h + # Multifactor authentication + mfa: + # TOTP configuration + totp: + # Enable TOTP authentication + # default: true + enabled: true + # Name displayed in the authentication app (FreeOTP, Proton Authenticator, Aegis Authenticator etc.) + # default: Sync-in + issuer: Sync-in + # LDAP authentication ldap: # e.g: [ldap://localhost:389, ldaps://localhost:636] (array required) + # required servers: [] # baseDN: distinguished name (e.g., ou=people,dc=ldap,dc=sync-in,dc=com) - baseDN: + # required + baseDN: ou=people,dc=ldap,dc=sync-in,dc=com # filter, e.g: (acl=admin) + # optional filter: + # upnSuffix: AD domain suffix used with `userPrincipalName` to build UPN-style logins (e.g., user@`sync-in.com`) + # optional + upnSuffix: + # netbiosName: NetBIOS domain name used with `sAMAccountName` to build legacy logins (e.g., `SYNC_IN`\user) + # optional + netbiosName: attributes: # Login attribute used to construct the user's DN for binding. # The value of this attribute is used as the naming attribute (first RDN) when forming the Distinguished Name (DN) during authentication @@ -129,17 +139,32 @@ auth: # email: `mail` or `email` # default: `mail` email: mail - # adminGroup: The CN of a group containing Sync-in administrators (e.g., administrators) - adminGroup: - # upnSuffix: AD domain suffix used with `userPrincipalName` to build UPN-style logins (e.g., user@`sync-in.com`) - upnSuffix: - # netbiosName: NetBIOS domain name used with `sAMAccountName` to build legacy logins (e.g., `SYNC_IN`\user) - netbiosName: - # enablePasswordAuthFallback: Enable local password authentication as a fallback when the LDAP authentication service is unavailable. - # This fallback applies only to existing users and is NOT triggered when credentials are invalid. - # Users must have signed in at least once. - # default: true - enablePasswordAuthFallback: true + options: + # autoCreateUser: Automatically create a local user on first successful LDAP authentication. + # The local account is created from LDAP attributes: + # - login: from the configured LDAP login attribute (e.g. uid, cn, sAMAccountName, userPrincipalName) + # - email: from the configured email attribute (required) + # - firstName / lastName: from givenName+sn, or displayName, or cn (fallback) + # When disabled, only existing users can authenticate via LDAP. + # default: true + autoCreateUser: true + # autoCreatePermissions: Permissions assigned to users automatically created via LDAP. + # Applied only at user creation time when autoCreateUser is enabled. + # Has no effect on existing users. + # A complete list of permissions is available in the documentation: https://sync-in.com/docs/admin-guide/permissions + # e.g.: [personal_space, spaces_access] (array required) + # default: [] + autoCreatePermissions: [] + # adminGroup: Name of the LDAP group (CN) that grants Sync-in administrator privileges. + # If set, users whose LDAP `memberOf` contains this group name are assigned the administrator role. + # If not set, existing administrator users keep their role and it cannot be removed via LDAP. + # optional + adminGroup: + # enablePasswordAuthFallback: Allow local password authentication when LDAP authentication fails. + # When enabled, users can authenticate with their local password if the LDAP service is unavailable. + # Always allowed for administrator users (break-glass access). + # default: true + enablePasswordAuthFallback: true oidc: # issuerUrl: The URL of the OIDC provider's discovery endpoint # Examples: @@ -152,10 +177,10 @@ auth: issuerUrl: # clientId: OAuth 2.0 Client ID obtained from your OIDC provider # required - clientId: sync-in + clientId: # clientSecret: OAuth 2.0 Client Secret obtained from your OIDC provider # required - clientSecret: changeClientSecret + clientSecret: changeOIDCClientSecret # redirectUri: The callback URL where users are redirected after authentication # This URL must be registered in your OIDC provider's allowed redirect URIs # Example (API callback): https://sync-in.domain.com/api/auth/oidc/callback @@ -174,15 +199,6 @@ auth: # e.g.: [personal_space, spaces_access] (array required) # default: [] autoCreatePermissions: [] - # autoRedirect: Automatically redirect users to the OIDC login flow. - # When enabled, the login page is skipped and users are sent directly to the OIDC provider. - # default: false - autoRedirect: false - # enablePasswordAuth: Enable local password-based authentication (login or email/password). - # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. - # Users must have signed in at least once and have a local password set. - # default: true - enablePasswordAuth: true # adminRoleOrGroup: Name of the role or group that grants Sync-in administrator access # Users with this value will be granted administrator privileges. # The value is matched against `roles` or `groups` claims provided by the IdP. @@ -190,6 +206,15 @@ auth: # and require proper IdP mappers to be included in the ID token or UserInfo response. # optional adminRoleOrGroup: + # enablePasswordAuth: Enable local password-based authentication (login or email/password). + # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. + # Users must have signed in at least once and have a local password set. + # default: true + enablePasswordAuth: true + # autoRedirect: Automatically redirect users to the OIDC login flow. + # When enabled, the login page is skipped and users are sent directly to the OIDC provider. + # default: false + autoRedirect: false # buttonText: Label displayed on the OIDC login button. # default: Continue with OpenID Connect buttonText: Continue with OpenID Connect From abb9979ed8f1d00aa241876b4b0519f013f88bfc Mon Sep 17 00:00:00 2001 From: johaven Date: Sat, 24 Jan 2026 23:49:54 +0100 Subject: [PATCH 08/18] feat(auth:oidc): revise authentication flow logic --- backend/src/authentication/auth.service.ts | 1 - .../providers/oidc/auth-oidc.interfaces.ts | 1 - .../oidc/auth-provider-oidc.service.ts | 2 +- environment/environment.dist.yaml | 11 +++++--- frontend/src/app/auth/auth.component.html | 26 +++++++++---------- frontend/src/app/auth/auth.component.ts | 2 +- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/backend/src/authentication/auth.service.ts b/backend/src/authentication/auth.service.ts index 74101015..35365420 100755 --- a/backend/src/authentication/auth.service.ts +++ b/backend/src/authentication/auth.service.ts @@ -148,7 +148,6 @@ export class AuthManager { return { loginUrl: API_OIDC_LOGIN, autoRedirect: configuration.auth.oidc.options.autoRedirect, - enablePasswordAuth: configuration.auth.oidc.options.enablePasswordAuth, buttonText: configuration.auth.oidc.options.buttonText } } diff --git a/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts index c6c3fd03..9e5c1b7c 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts @@ -7,6 +7,5 @@ export interface AuthOIDCSettings { loginUrl: string autoRedirect: boolean - enablePasswordAuth: boolean buttonText: string } diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index 4c116580..e5764c6a 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -52,7 +52,7 @@ export class AuthProviderOIDC implements AuthProvider { ) {} async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise { - // Find user from login or email + // Local password authentication path (non-OIDC) const user: UserModel = await this.usersManager.findUser(login, false) if (!user) { diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index f5e97a52..bb2d7f66 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -206,9 +206,14 @@ auth: # and require proper IdP mappers to be included in the ID token or UserInfo response. # optional adminRoleOrGroup: - # enablePasswordAuth: Enable local password-based authentication (login or email/password). - # When enabled, users can sign in with their Sync-in password in addition to OIDC authentication. - # Users must have signed in at least once and have a local password set. + # enablePasswordAuth: Allow local password-based authentication when using OIDC. + # When enabled, users may authenticate with their Sync-in password instead of OIDC. + # Local password authentication is always allowed for: + # - guest users + # - administrator users (break-glass access) + # - application scopes (app passwords) + # Regular users are allowed only when this option is enabled. + # Users must already exist locally and have a password set. # default: true enablePasswordAuth: true # autoRedirect: Automatically redirect users to the OIDC login flow. diff --git a/frontend/src/app/auth/auth.component.html b/frontend/src/app/auth/auth.component.html index eb29ec35..93963a57 100644 --- a/frontend/src/app/auth/auth.component.html +++ b/frontend/src/app/auth/auth.component.html @@ -56,26 +56,24 @@ } @else { - @if (!oidcSettings || oidcSettings.enablePasswordAuth) { -
-

{{ hasError || 'Sign-in to your account' }}

-
+ +

{{ hasError || 'Sign-in to your account' }}

+
- -
-
+ +
+
- -
-
- -
- - } + +
+
+ +
+ @if (oidcSettings) {
diff --git a/frontend/src/app/auth/auth.component.ts b/frontend/src/app/auth/auth.component.ts index 0ee6f086..2a19e04c 100644 --- a/frontend/src/app/auth/auth.component.ts +++ b/frontend/src/app/auth/auth.component.ts @@ -34,7 +34,7 @@ export class AuthComponent { protected submitted = false protected twoFaVerify = false private route = inject(ActivatedRoute) - protected oidcSettings: AuthOIDCSettings | false = this.route.snapshot.data['authSettings'] + protected oidcSettings: AuthOIDCSettings | false = this.route.snapshot.data.authSettings private readonly router = inject(Router) private readonly auth = inject(AuthService) private readonly fb = inject(UntypedFormBuilder) From a213078954e6ff497b31b2c043a97fce770234cb Mon Sep 17 00:00:00 2001 From: johaven Date: Sun, 25 Jan 2026 18:40:44 +0100 Subject: [PATCH 09/18] test(backend): complete unit tests for OIDC/LDAP/SYNC --- .../sync-paths-manager.service.spec.ts | 24 +- .../ldap/auth-provider-ldap.service.spec.ts | 500 +++++++++--------- .../oidc/auth-provider-oidc.service.spec.ts | 252 +++++++++ .../oidc/auth-provider-oidc.service.ts | 19 +- 4 files changed, 535 insertions(+), 260 deletions(-) create mode 100644 backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts diff --git a/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts b/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts index 623a0fdb..94b29732 100644 --- a/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts @@ -460,18 +460,22 @@ describe(SyncPathsManager.name, () => { it('should throw BAD_REQUEST for shares list selection', async () => { await expect((service as any).getDBProps({ inSharesList: true } as any)).rejects.toMatchObject({ status: HttpStatus.BAD_REQUEST, - message: 'Sync all shares is not supported, you must select a sub-directory' + message: 'Syncing all shares is not supported. Please select a subdirectory' }) }) - it('should return ownerId only for personal space at root', async () => { - const res = await (service as any).getDBProps({ - inSharesList: false, - inPersonalSpace: true, - paths: [], - dbFile: { ownerId: 42 } - } as any) - expect(res).toEqual({ ownerId: 42 }) + it('should throw BAD_REQUEST for personal space selection', async () => { + await expect( + (service as any).getDBProps({ + inSharesList: false, + inPersonalSpace: true, + paths: [], + dbFile: { ownerId: 42 } + } as any) + ).rejects.toMatchObject({ + status: HttpStatus.BAD_REQUEST, + message: 'Syncing all personal files is not supported. Please select a subdirectory' + }) }) it('should return ownerId and fileId for personal space subdir', async () => { @@ -488,7 +492,7 @@ describe(SyncPathsManager.name, () => { it('should throw BAD_REQUEST for whole files repository without alias', async () => { await expect((service as any).getDBProps({ inFilesRepository: true, root: { alias: null }, paths: [] } as any)).rejects.toMatchObject({ status: HttpStatus.BAD_REQUEST, - message: 'Sync all space is not yet supported, you must select a sub-directory' + message: 'Syncing the entire space is not yet supported. Please select a subdirectory' }) }) diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts index 64ad037c..2d6e67da 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts @@ -8,15 +8,16 @@ import { Test, TestingModule } from '@nestjs/testing' import { Mocked } from 'jest-mock' import { Client, InvalidCredentialsError } from 'ldapts' import { CONNECT_ERROR_CODE } from '../../../app.constants' +import { USER_PERMISSION, USER_ROLE } from '../../../applications/users/constants/user' import { UserModel } from '../../../applications/users/models/user.model' import { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service' import { UsersManager } from '../../../applications/users/services/users-manager.service' import * as commonFunctions from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' -import { LDAP_LOGIN_ATTR } from './auth-ldap.constants' +import type { AuthMethodLDAPConfig } from './auth-ldap.config' +import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' import { AuthProviderLDAP } from './auth-provider-ldap.service' -// Mock ldapts Client to simulate LDAP behaviors jest.mock('ldapts', () => { const actual = jest.requireActual('ldapts') const mockClientInstance = { @@ -25,68 +26,79 @@ jest.mock('ldapts', () => { unbind: jest.fn() } const Client = jest.fn().mockImplementation(() => mockClientInstance) - // Conserver tous les autres exports réels (dont EqualityFilter, AndFilter, InvalidCredentialsError, etc.) return { ...actual, Client } }) -// --- Test helpers (DRY) --- -// Reusable LDAP mocks -const mockBindResolve = (ldapClient: any) => { - ldapClient.bind.mockResolvedValue(undefined) - ldapClient.unbind.mockResolvedValue(undefined) -} -const mockBindRejectInvalid = (ldapClient: any, InvalidCredentialsErrorCtor: any, message = 'invalid') => { - ldapClient.bind.mockRejectedValue(new InvalidCredentialsErrorCtor(message)) - ldapClient.unbind.mockResolvedValue(undefined) -} -const mockSearchEntries = (ldapClient: any, entries: any[]) => { - ldapClient.search.mockResolvedValue({ searchEntries: entries }) -} -const mockSearchReject = (ldapClient: any, err: Error) => { - ldapClient.search.mockRejectedValue(err) -} -// User factory const buildUser = (overrides: Partial = {}) => ({ id: 0, login: 'john', email: 'old@example.org', password: 'hashed', + role: USER_ROLE.USER, isGuest: false, isActive: true, + isAdmin: false, makePaths: jest.fn().mockResolvedValue(undefined), - setFullName: jest.fn(), // needed when firstName/lastName change + setFullName: jest.fn(), ...overrides }) as any -// -------------------------- +const ldapClient = { + bind: jest.fn(), + search: jest.fn(), + unbind: jest.fn() +} +;(Client as Mocked).mockImplementation(() => ldapClient) describe(AuthProviderLDAP.name, () => { let authMethodLdapService: AuthProviderLDAP let usersManager: Mocked let adminUsersManager: Mocked - const ldapClient = { - bind: jest.fn(), - search: jest.fn(), - unbind: jest.fn() - } - ;(Client as Mocked).mockImplementation(() => ldapClient) - // Local helpers (need access to authMethodLdapService and ldapClient in this scope) - const setupLdapSuccess = (entries: any[]) => { - mockBindResolve(ldapClient) - mockSearchEntries(ldapClient, entries) + type LdapConfigOverrides = Omit, 'attributes' | 'options'> & { + attributes?: Partial + options?: Partial } - const spyLoggerError = () => jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) - beforeAll(async () => { - configuration.auth.ldap = { + const setLdapConfig = (overrides: LdapConfigOverrides = {}) => { + const base: AuthMethodLDAPConfig = { servers: ['ldap://localhost:389'], - attributes: { login: LDAP_LOGIN_ATTR.UID, email: 'mail' }, + attributes: { login: LDAP_LOGIN_ATTR.UID, email: LDAP_COMMON_ATTR.MAIL }, baseDN: 'ou=people,dc=example,dc=org', - filter: '' + filter: '', + options: { + autoCreateUser: true, + autoCreatePermissions: [], + enablePasswordAuthFallback: true + } + } + const next: AuthMethodLDAPConfig = { + ...base, + ...overrides, + attributes: { ...base.attributes, ...(overrides.attributes || {}) }, + options: { ...base.options, ...(overrides.options || {}) } } + configuration.auth.ldap = next + ;(authMethodLdapService as any).ldapConfig = next + ;(authMethodLdapService as any).isAD = [LDAP_LOGIN_ATTR.SAM, LDAP_LOGIN_ATTR.UPN].includes(next.attributes.login) + } + + const mockBindResolve = () => { + ldapClient.bind.mockResolvedValue(undefined) + ldapClient.unbind.mockResolvedValue(undefined) + } + const mockBindRejectInvalid = (message = 'invalid') => { + ldapClient.bind.mockRejectedValue(new InvalidCredentialsError(message)) + ldapClient.unbind.mockResolvedValue(undefined) + } + + const mockSearchEntries = (entries: any[]) => { + ldapClient.search.mockResolvedValue({ searchEntries: entries }) + } + + beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ AuthProviderLDAP, @@ -116,6 +128,16 @@ describe(AuthProviderLDAP.name, () => { usersManager = module.get>(UsersManager) }) + beforeEach(() => { + jest.clearAllMocks() + setLdapConfig() + usersManager.updateAccesses.mockResolvedValue(undefined) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + it('should be defined', () => { expect(authMethodLdapService).toBeDefined() expect(usersManager).toBeDefined() @@ -124,270 +146,260 @@ describe(AuthProviderLDAP.name, () => { }) it('should authenticate a guest user via database and bypass LDAP', async () => { - // Arrange const guestUser: any = { id: 1, login: 'guest1', isGuest: true, isActive: true } usersManager.findUser.mockResolvedValue(guestUser) const dbAuthResult: any = { ...guestUser, token: 'jwt' } usersManager.logUser.mockResolvedValue(dbAuthResult) + const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1') + expect(res).toEqual(dbAuthResult) - expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1') - expect(Client).not.toHaveBeenCalled() // client should not be constructed + expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1', undefined) + expect(Client).not.toHaveBeenCalled() + }) + + it('should bypass LDAP when scope is provided', async () => { + const user = buildUser({ id: 12 }) + usersManager.findUser.mockResolvedValue(user) + usersManager.logUser.mockResolvedValue(user) + + const res = await authMethodLdapService.validateUser('john', 'app-password', '10.0.0.2', 'webdav' as any) + + expect(res).toBe(user) + expect(usersManager.logUser).toHaveBeenCalledWith(user, 'app-password', '10.0.0.2', 'webdav') + expect(Client).not.toHaveBeenCalled() }) - it('should throw FORBIDDEN for locked account and resolve null for LDAP login mismatch', async () => { - // Phase 1: locked account + it('should throw FORBIDDEN for locked account', async () => { usersManager.findUser.mockResolvedValue({ login: 'john', isGuest: false, isActive: false } as UserModel) - const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) + const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) + await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i) - expect(loggerErrorSpy1).toHaveBeenCalled() + expect(loggerErrorSpy).toHaveBeenCalled() + }) - // Phase 2: mismatch between requested login and LDAP returned login -> service renvoie null - const existingUser: any = buildUser({ id: 8 }) + it('should return null on invalid LDAP credentials without fallback', async () => { + const existingUser: any = buildUser({ id: 1 }) usersManager.findUser.mockResolvedValue(existingUser) - mockBindResolve(ldapClient) - mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'john', mail: 'jane@example.org' }]) - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i) + mockBindRejectInvalid('invalid credentials') + + const res = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1') + + expect(res).toBeNull() + expect(usersManager.logUser).not.toHaveBeenCalled() + expect(usersManager.updateAccesses).not.toHaveBeenCalled() }) - it('should handle invalid LDAP credentials for both existing and unknown users', async () => { - // Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted - const existingUser: any = buildUser({ id: 1 }) + it('should return null when LDAP search yields no entries or throws', async () => { + const existingUser: any = buildUser({ id: 10 }) usersManager.findUser.mockResolvedValue(existingUser) - // Make LDAP bind throw InvalidCredentialsError - mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials') - // Force updateAccesses to reject to hit the catch and logger.error - const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) - usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom')) - const res1 = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1') - expect(res1).toBeNull() - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.1', false) - expect(loggerErrorSpy).toHaveBeenCalled() + mockBindResolve() + mockSearchEntries([]) - // Phase 2: unknown user → no access update - usersManager.updateAccesses.mockClear() - usersManager.findUser.mockResolvedValue(null) - ldapClient.bind.mockRejectedValue(new InvalidCredentialsError('invalid')) - ldapClient.unbind.mockResolvedValue(undefined) - const res2 = await authMethodLdapService.validateUser('jane', 'badpwd') - expect(res2).toBeNull() + const resA = await authMethodLdapService.validateUser('john', 'pwd') + + expect(resA).toBeNull() + + ldapClient.search.mockRejectedValue(new Error('search failed')) + const resB = await authMethodLdapService.validateUser('john', 'pwd') + + expect(resB).toBeNull() expect(usersManager.updateAccesses).not.toHaveBeenCalled() }) - it('should handle LDAP new-user flow: missing fields, creation success, and multi-email selection', async () => { - // Phase 1: incomplete LDAP entry -> null + error log, no creation + it('should fallback to local auth when LDAP is unavailable and fallback is enabled', async () => { + const existingUser: any = buildUser({ id: 2 }) + usersManager.findUser.mockResolvedValue(existingUser) + usersManager.logUser.mockResolvedValue(existingUser) + const err = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] }) + ldapClient.bind.mockRejectedValue({ errors: [err] }) + ldapClient.unbind.mockResolvedValue(undefined) + + const res = await authMethodLdapService.validateUser('john', 'pwd', '10.0.0.3') + + expect(res).toBe(existingUser) + expect(usersManager.logUser).toHaveBeenCalledWith(existingUser, 'pwd', '10.0.0.3') + }) + + it('should throw SERVICE_UNAVAILABLE when LDAP is unavailable and fallback is disabled', async () => { + setLdapConfig({ options: { enablePasswordAuthFallback: false } }) + const existingUser: any = buildUser({ id: 3 }) + usersManager.findUser.mockResolvedValue(existingUser) + const err = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] }) + ldapClient.bind.mockRejectedValue({ errors: [err] }) + ldapClient.unbind.mockResolvedValue(undefined) + + await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service error/i) + }) + + it('should allow admin local fallback when LDAP is unavailable even if fallback is disabled', async () => { + setLdapConfig({ options: { enablePasswordAuthFallback: false } }) + const existingUser: any = buildUser({ id: 4, isAdmin: true }) + usersManager.findUser.mockResolvedValue(existingUser) + usersManager.logUser.mockResolvedValue(existingUser) + const err = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] }) + ldapClient.bind.mockRejectedValue({ errors: [err] }) + ldapClient.unbind.mockResolvedValue(undefined) + + const res = await authMethodLdapService.validateUser('john', 'pwd') + + expect(res).toBe(existingUser) + expect(usersManager.logUser).toHaveBeenCalledWith(existingUser, 'pwd', undefined) + }) + + it('should return null when LDAP entry lacks required fields', async () => { usersManager.findUser.mockResolvedValue(null) - mockBindResolve(ldapClient) - // Simulate an entry with missing mail - mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'Jane Doe', mail: undefined }]) + mockBindResolve() + mockSearchEntries([{ uid: 'jane', cn: 'Jane Doe', mail: undefined }]) const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) - const resA = await authMethodLdapService.validateUser('jane', 'pwd') - expect(resA).toBeNull() + + const res = await authMethodLdapService.validateUser('jane', 'pwd') + + expect(res).toBeNull() expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled() expect(loggerErrorSpy).toHaveBeenCalled() + }) + + it('should throw UNAUTHORIZED when autoCreateUser is disabled', async () => { + setLdapConfig({ options: { autoCreateUser: false } }) + usersManager.findUser.mockResolvedValue(null) + const checkAuthSpy = jest.spyOn(authMethodLdapService as any, 'checkAuth').mockResolvedValue({ + uid: 'john', + mail: 'john@example.org' + }) + + await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/user not found/i) + checkAuthSpy.mockRestore() + }) - // Phase 2: create a new user (success, single email) - // Stub directement checkAuth pour retourner une entrée LDAP valide - const checkAuthSpy = jest.spyOn(authMethodLdapService as any, 'checkAuth') - checkAuthSpy.mockResolvedValueOnce({ uid: 'john', cn: 'John Doe', mail: 'john@example.org' } as any) - adminUsersManager.createUserOrGuest.mockClear() + it('should create a new admin user with permissions and name parsed from LDAP', async () => { + setLdapConfig({ + options: { + adminGroup: 'Admins', + autoCreatePermissions: [USER_PERMISSION.PERSONAL_SPACE, USER_PERMISSION.WEBDAV] + } + }) usersManager.findUser.mockResolvedValue(null) + mockBindResolve() + mockSearchEntries([ + { + uid: 'john', + givenName: 'John', + sn: 'Doe', + mail: 'john@example.org', + memberOf: ['CN=Admins,OU=Groups,DC=example,DC=org'] + } + ]) const createdUser: any = { id: 2, login: 'john', isGuest: false, isActive: true, makePaths: jest.fn() } adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser) - // If the service reloads the user via fromUserId after creation usersManager.fromUserId.mockResolvedValue(createdUser) - // Cover the success-flow catch branch - const loggerErrorSpy2 = spyLoggerError() - usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom')) - const resB = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10') + + const res = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10') + expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith( - { login: 'john', email: 'john@example.org', password: 'pwd', firstName: 'John', lastName: 'Doe', role: 1 }, - expect.anything() // USER_ROLE.USER + { + login: 'john', + email: 'john@example.org', + password: 'pwd', + role: USER_ROLE.ADMINISTRATOR, + firstName: 'John', + lastName: 'Doe', + permissions: 'personal_space,webdav_access' + }, + USER_ROLE.ADMINISTRATOR ) - expect(resB).toBe(createdUser) + expect(res).toBe(createdUser) expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true) - expect(loggerErrorSpy2).toHaveBeenCalled() - // Phase 3: multiple emails -> keep the first - adminUsersManager.createUserOrGuest.mockClear() - usersManager.findUser.mockResolvedValue(null) - setupLdapSuccess([{ uid: 'multi', cn: 'Multi Mail', mail: ['first@example.org', 'second@example.org'] }]) - const createdUser2: any = { id: 9, login: 'multi', makePaths: jest.fn() } - adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2) - usersManager.fromUserId.mockResolvedValue(createdUser2) - const resC = await authMethodLdapService.validateUser('multi', 'pwd') - expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({ email: 'first@example.org' }), expect.anything()) - expect(resC).toBe(createdUser2) }) - it('should update existing user profile when LDAP identity changed (except password assigned back)', async () => { - // Arrange: existing user with different profile and an old password - const existingUser: any = buildUser({ id: 5 }) + it('should keep admin role when adminGroup is not configured', async () => { + setLdapConfig({ options: { adminGroup: undefined } }) + const existingUser: any = buildUser({ id: 5, role: USER_ROLE.ADMINISTRATOR }) + usersManager.findUser.mockResolvedValue(existingUser) + mockBindResolve() + mockSearchEntries([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }]) + jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(true) + + await authMethodLdapService.validateUser('john', 'pwd') + + expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled() + const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0][1] + expect(updateArgs).toEqual(expect.objectContaining({ email: 'john@example.org' })) + expect(updateArgs).toEqual(expect.not.objectContaining({ role: expect.anything() })) + }) + + it('should update existing user and avoid reassigning password locally', async () => { + const existingUser: any = buildUser({ id: 6 }) usersManager.findUser.mockResolvedValue(existingUser) - // LDAP succeeds and returns different email and same uid - setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }]) - // Admin manager successfully updates a user - adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined) - // Ensure password is considered changed so the update payload includes it, - // which then triggers the deletion and local assignment branches after update + mockBindResolve() + mockSearchEntries([{ uid: 'john', displayName: 'Jane Doe', mail: 'john@example.org' }]) const compareSpy = jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false) + const splitSpy = jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'Jane', lastName: 'Doe' }) + const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2') + expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith( - 5, + 6, expect.objectContaining({ email: 'john@example.org', - firstName: 'John', + firstName: 'Jane', lastName: 'Doe' }) ) - // Password should not be assigned back onto the user object (it is deleted before Object.assign) expect(existingUser.password).toBe('hashed') - // Other fields should be updated locally - expect(existingUser.email).toBe('john@example.org') - expect(existingUser).toMatchObject({ firstName: 'John', lastName: 'Doe' }) - // Accesses updated as success + expect(existingUser).toMatchObject({ email: 'john@example.org', firstName: 'Jane', lastName: 'Doe' }) + expect(existingUser.setFullName).toHaveBeenCalledWith(true) expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.2', true) - // Returned user is the same instance expect(res).toBe(existingUser) - // Second run: password unchanged (comparePassword => true) to cover the null branch for password - adminUsersManager.updateUserOrGuest.mockClear() - usersManager.updateAccesses.mockClear() - // Force another non-password change so an update occurs - existingUser.email = 'old@example.org' - compareSpy.mockResolvedValue(true) - const res2 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.3') - // Update should be called without password, only with changed fields - expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled() - const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0] - expect(updateArgs[0]).toBe(5) - expect(updateArgs[1]).toEqual( - expect.objectContaining({ - email: 'john@example.org' - }) - ) - expect(updateArgs[1]).toEqual(expect.not.objectContaining({ password: expect.anything() })) - // Password remains unchanged locally - expect(existingUser.password).toBe('hashed') - // Accesses updated as success - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.3', true) - // Returned user is the same instance - expect(res2).toBe(existingUser) - // Third run: no changes at all (identityHasChanged is empty) to cover the else branch - adminUsersManager.updateUserOrGuest.mockClear() - usersManager.updateAccesses.mockClear() - compareSpy.mockResolvedValue(true) - // Local user already matches LDAP identity; call again - const res3 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.4') - // No update should be triggered - expect(adminUsersManager.updateUserOrGuest).not.toHaveBeenCalled() - // Access should still be updated as success - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.4', true) - // Returned user is the same instance - expect(res3).toBe(existingUser) + compareSpy.mockRestore() + splitSpy.mockRestore() }) - it('should log failed access when LDAP search returns no entry or throws after bind', async () => { - // Phase 1: no entry found after a successful bind -> failed access - const existingUser: any = { id: 7, login: 'ghost', isGuest: false, isActive: true } + it('should throw FORBIDDEN when LDAP login does not match user login', async () => { + const existingUser: any = buildUser({ id: 7, login: 'john' }) usersManager.findUser.mockResolvedValue(existingUser) - setupLdapSuccess([]) - const resA = await authMethodLdapService.validateUser('ghost', 'pwd', '10.10.0.1') - expect(resA).toBeNull() - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.10.0.1', false) + mockBindResolve() + mockSearchEntries([{ uid: 'jane', cn: 'Jane Doe', mail: 'jane@example.org' }]) - // Phase 2: exception during search after a bind -> failed access - jest.clearAllMocks() - const existingUser2: any = { id: 10, login: 'john', isGuest: false, isActive: true } - usersManager.findUser.mockResolvedValue(existingUser2) - mockBindResolve(ldapClient) - mockSearchReject(ldapClient, new Error('search failed')) - const resB = await authMethodLdapService.validateUser('john', 'pwd', '1.1.1.1') - expect(resB).toBeNull() - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false) + await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i) }) - it('should allow app password when LDAP fails and scope is provided', async () => { - const existingUser: any = buildUser({ id: 42 }) - usersManager.findUser.mockResolvedValue(existingUser) - // LDAP invalid credentials - mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials') - // App password success - usersManager.validateAppPassword.mockResolvedValue(true) - const res = await authMethodLdapService.validateUser('john', 'app-password', '10.0.0.2', 'webdav' as any) - expect(res).toBe(existingUser) - expect(usersManager.validateAppPassword).toHaveBeenCalledWith(existingUser, 'app-password', '10.0.0.2', 'webdav') - expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.2', true) + it('should build LDAP logins and filters for AD and standard LDAP', () => { + setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.UPN }, upnSuffix: 'sync-in.com', filter: '(memberOf=cn=staff)' }) + const adLogin = (authMethodLdapService as any).buildLdapLogin('john') + expect(adLogin).toBe('john@sync-in.com') + const adFilter = (authMethodLdapService as any).buildUserFilter('SYNC-IN\\john', '(memberOf=cn=staff)') + expect(adFilter).toContain('(sAMAccountName=john)') + expect(adFilter).toContain('(userPrincipalName=john)') + expect(adFilter).toContain('(mail=john)') + expect(adFilter).toContain('(memberOf=cn=staff)') + + setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.UID }, filter: '(department=IT)' }) + const ldapFilter = (authMethodLdapService as any).buildUserFilter('john', '(department=IT)') + expect(ldapFilter).toContain('(uid=john)') + expect(ldapFilter).toContain('(cn=john)') + expect(ldapFilter).toContain('(mail=john)') + expect(ldapFilter).toContain('(department=IT)') }) - it('should throw 500 when LDAP connection error occurs during bind', async () => { - // Arrange: no existing user to reach checkAuth flow - usersManager.findUser.mockResolvedValue(null) - const err1 = new Error('socket hang up') - const err2 = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] }) - ldapClient.bind.mockRejectedValue({ errors: [err1, err2] }) - ldapClient.unbind.mockResolvedValue(undefined) - - // First scenario: recognized connection error -> throws 500 - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service/i) - - // Second scenario: generic error (no code, not InvalidCredentialsError) -> resolves to null and no access update - ldapClient.bind.mockReset() - ldapClient.unbind.mockReset() - usersManager.updateAccesses.mockClear() - usersManager.findUser.mockResolvedValue(null as any) - ldapClient.bind.mockRejectedValue(new Error('unexpected failure')) - ldapClient.unbind.mockResolvedValue(undefined) + it('should normalize LDAP entries for memberOf and array attributes', () => { + const entry = { + uid: ['john'], + mail: ['john@example.org', 'john2@example.org'], + memberOf: ['CN=Admins,OU=Groups,DC=example,DC=org', 'CN=Staff,OU=Groups,DC=example,DC=org'] + } - const res = await authMethodLdapService.validateUser('john', 'pwd') - expect(res).toBeNull() - expect(usersManager.updateAccesses).not.toHaveBeenCalled() - }) + const normalized = (authMethodLdapService as any).convertToLdapUserEntry(entry) - it('should log update failure when updating existing user', async () => { - // Arrange: existing user with changed identity - const existingUser: any = buildUser({ id: 11, email: 'old@ex.org' }) - usersManager.findUser.mockResolvedValue(existingUser) - // Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn') - setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }]) - adminUsersManager.updateUserOrGuest.mockRejectedValue(new Error('db error')) - // Force identity to be considered changed only for this test - jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false) - jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' }) - const res = await authMethodLdapService.validateUser('john', 'pwd') - expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled() - // Local fields unchanged since update failed - expect(existingUser.email).toBe('old@ex.org') - expect(res).toBe(existingUser) + expect(normalized.uid).toBe('john') + expect(normalized.mail).toBe('john@example.org') + expect(normalized.memberOf).toEqual(['Admins', 'Staff']) }) - it('should skip non-matching LDAP entries then update user with changed password without reassigning it', async () => { - // Phase A: LDAP returns an entry but loginAttribute value does not match -> checkAccess returns false (covers return after loop) - const userA: any = { id: 20, login: 'john', isGuest: false, isActive: true } - usersManager.findUser.mockResolvedValue(userA) - ldapClient.bind.mockResolvedValue(undefined) - - // Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally - jest.clearAllMocks() - const userB: any = buildUser({ id: 21, email: 'old@ex.org' }) - usersManager.findUser.mockResolvedValue(userB) - setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }]) - adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined) - - // Force password to be considered changed to execute deletion + Object.assign branch - jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false) - jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' }) - const resB = await authMethodLdapService.validateUser('john', 'newpwd', '4.4.4.4') - - // Line 132: updateUserOrGuest call - expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith( - 21, - expect.objectContaining({ email: 'john@example.org', firstName: 'John', lastName: 'Doe' }) - ) - - // Lines 139-142: password removed from local assign, other fields assigned - expect(userB.password).toBe('hashed') - expect(userB.email).toBe('john@example.org') - expect(userB).toMatchObject({ firstName: 'John', lastName: 'Doe' }) - expect(resB).toBe(userB) + it('should build LDAP logins for SAM account name when netbiosName is set', () => { + setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.SAM }, netbiosName: 'SYNC' }) + const samLogin = (authMethodLdapService as any).buildLdapLogin('john') + expect(samLogin).toBe('SYNC\\john') }) }) diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts new file mode 100644 index 00000000..78eee58b --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { HttpStatus } from '@nestjs/common' +import { Test, TestingModule } from '@nestjs/testing' +import { + authorizationCodeGrant, + AuthorizationResponseError, + calculatePKCECodeChallenge, + fetchUserInfo, + randomNonce, + randomPKCECodeVerifier, + randomState +} from 'openid-client' +import { USER_ROLE } from '../../../applications/users/constants/user' +import { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service' +import { UsersManager } from '../../../applications/users/services/users-manager.service' +import { OAuthCookie } from './auth-oidc.constants' +import { AuthProviderOIDC } from './auth-provider-oidc.service' + +jest.mock('../../../configuration/config.environment', () => ({ + configuration: { + auth: { + oidc: { + issuerUrl: 'https://issuer.example.test', + clientId: 'client-id', + clientSecret: 'client-secret', + redirectUri: 'https://api.example.test/auth/oidc/callback', + security: { + scope: 'openid profile email', + tokenSigningAlg: 'RS256', + userInfoSigningAlg: 'RS256', + tokenEndpointAuthMethod: 'client_secret_basic', + skipSubjectCheck: false + }, + options: { + enablePasswordAuth: false, + autoCreateUser: true, + adminRoleOrGroup: 'admins', + autoCreatePermissions: ['read'] + } + } + } + } +})) + +jest.mock('openid-client', () => { + class AuthorizationResponseError extends Error { + code: string + error_description: string + constructor(message: string, options: { cause: URLSearchParams }) { + super(message) + this.code = 'authorization_response_error' + this.error_description = options?.cause?.get('error_description') ?? message + } + } + + return { + allowInsecureRequests: jest.fn(), + authorizationCodeGrant: jest.fn(), + AuthorizationResponseError, + calculatePKCECodeChallenge: jest.fn(), + ClientSecretBasic: jest.fn(), + ClientSecretPost: jest.fn(), + Configuration: class {}, + discovery: jest.fn(), + fetchUserInfo: jest.fn(), + IDToken: class {}, + None: jest.fn(), + randomNonce: jest.fn(), + randomPKCECodeVerifier: jest.fn(), + randomState: jest.fn(), + skipSubjectCheck: Symbol('skipSubjectCheck'), + UserInfoResponse: class {} + } +}) + +describe(AuthProviderOIDC.name, () => { + let service: AuthProviderOIDC + let usersManager: { + findUser: jest.Mock + logUser: jest.Mock + updateAccesses: jest.Mock + fromUserId: jest.Mock + } + let adminUsersManager: { + createUserOrGuest: jest.Mock + updateUserOrGuest: jest.Mock + } + + const makeConfig = (supportsPKCE = true) => ({ + serverMetadata: () => ({ + supportsPKCE: () => supportsPKCE, + authorization_endpoint: 'https://issuer.example.test/authorize' + }) + }) + + const makeReply = () => ({ + header: jest.fn().mockReturnThis(), + setCookie: jest.fn(), + clearCookie: jest.fn() + }) + + beforeAll(async () => { + usersManager = { + findUser: jest.fn(), + logUser: jest.fn(), + updateAccesses: jest.fn().mockResolvedValue(undefined), + fromUserId: jest.fn() + } + adminUsersManager = { + createUserOrGuest: jest.fn(), + updateUserOrGuest: jest.fn() + } + + const module: TestingModule = await Test.createTestingModule({ + providers: [{ provide: UsersManager, useValue: usersManager }, { provide: AdminUsersManager, useValue: adminUsersManager }, AuthProviderOIDC] + }).compile() + + module.useLogger(['fatal']) + service = module.get(AuthProviderOIDC) + }) + + beforeEach(() => { + jest.restoreAllMocks() + jest.clearAllMocks() + }) + + it('returns null when user is not found', async () => { + usersManager.findUser.mockResolvedValue(null) + + const result = await service.validateUser('john', 'secret') + + expect(result).toBeNull() + expect(usersManager.findUser).toHaveBeenCalledWith('john', false) + expect(usersManager.logUser).not.toHaveBeenCalled() + }) + + it('allows local password auth for guest users', async () => { + const guestUser = { id: 1, isGuest: true, isAdmin: false } as any + usersManager.findUser.mockResolvedValue(guestUser) + usersManager.logUser.mockResolvedValue(guestUser) + + const result = await service.validateUser('guest', 'secret') + + expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'secret', undefined, undefined) + expect(result).toBe(guestUser) + }) + + it('builds the authorization url with PKCE data and cookies', async () => { + jest.spyOn(service, 'getConfig').mockResolvedValue(makeConfig(true) as any) + ;(randomState as jest.Mock).mockReturnValue('state-1') + ;(randomNonce as jest.Mock).mockReturnValue('nonce-1') + ;(randomPKCECodeVerifier as jest.Mock).mockReturnValue('verifier-1') + ;(calculatePKCECodeChallenge as jest.Mock).mockResolvedValue('challenge-1') + const reply = makeReply() + + const authUrl = await service.getAuthorizationUrl(reply as any) + + expect(reply.header).toHaveBeenCalled() + expect(reply.setCookie).toHaveBeenCalledWith(OAuthCookie.State, 'state-1', expect.any(Object)) + expect(reply.setCookie).toHaveBeenCalledWith(OAuthCookie.Nonce, 'nonce-1', expect.any(Object)) + expect(reply.setCookie).toHaveBeenCalledWith(OAuthCookie.CodeVerifier, 'verifier-1', expect.any(Object)) + const url = new URL(authUrl) + expect(url.searchParams.get('code_challenge')).toBe('challenge-1') + expect(url.searchParams.get('client_id')).toBe('client-id') + }) + + it('handles callback success and clears cookies', async () => { + const config = makeConfig(true) + jest.spyOn(service, 'getConfig').mockResolvedValue(config as any) + const processSpy = jest.spyOn(service as any, 'processUserInfo').mockResolvedValue({ id: 7 } as any) + ;(authorizationCodeGrant as jest.Mock).mockResolvedValue({ + claims: () => ({ sub: 'subject-1' }), + access_token: 'access-token' + }) + ;(fetchUserInfo as jest.Mock).mockResolvedValue({ sub: 'subject-1', email: 'a@b.c', preferred_username: 'alice' }) + const req = { + cookies: { + [OAuthCookie.State]: 'state-1', + [OAuthCookie.Nonce]: 'nonce-1', + [OAuthCookie.CodeVerifier]: 'verifier-1' + }, + ip: '127.0.0.1' + } + const reply = makeReply() + + const result = await service.handleCallback(req as any, reply as any, { code: 'abc' }) + + expect(result).toEqual({ id: 7 }) + expect(processSpy).toHaveBeenCalledWith({ sub: 'subject-1', email: 'a@b.c', preferred_username: 'alice' }, '127.0.0.1') + expect(reply.clearCookie).toHaveBeenCalledWith(OAuthCookie.State, { path: '/' }) + expect(reply.clearCookie).toHaveBeenCalledWith(OAuthCookie.Nonce, { path: '/' }) + expect(reply.clearCookie).toHaveBeenCalledWith(OAuthCookie.CodeVerifier, { path: '/' }) + }) + + it('rejects callback when state is missing', async () => { + jest.spyOn(service, 'getConfig').mockResolvedValue(makeConfig(false) as any) + const reply = makeReply() + const req = { cookies: {}, ip: '127.0.0.1' } + + await expect(service.handleCallback(req as any, reply as any, { code: 'abc' })).rejects.toMatchObject({ status: HttpStatus.BAD_REQUEST }) + expect(reply.clearCookie).toHaveBeenCalledWith(OAuthCookie.State, { path: '/' }) + }) + + it('maps AuthorizationResponseError to BAD_REQUEST', async () => { + jest.spyOn(service, 'getConfig').mockResolvedValue(makeConfig(false) as any) + ;(authorizationCodeGrant as jest.Mock).mockRejectedValue( + new AuthorizationResponseError('access_denied', { + cause: new URLSearchParams('error=access_denied&error_description=No access') + }) + ) + const req = { + cookies: { + [OAuthCookie.State]: 'state-1', + [OAuthCookie.Nonce]: 'nonce-1' + }, + ip: '127.0.0.1' + } + const reply = makeReply() + + await expect(service.handleCallback(req as any, reply as any, { code: 'abc' })).rejects.toMatchObject({ + status: HttpStatus.BAD_REQUEST, + message: 'No access' + }) + }) + + it('builds the redirect callback url with token expirations', () => { + const url = service.getRedirectCallbackUrl(10, 20) + const parsed = new URL(url) + expect(parsed.hash).toContain('access_expiration=10') + expect(parsed.hash).toContain('refresh_expiration=20') + }) + + it('creates identities with admin role when claims match', async () => { + usersManager.findUser.mockResolvedValue(null) + adminUsersManager.createUserOrGuest.mockResolvedValue({ id: 10, login: 'bob' }) + usersManager.fromUserId.mockResolvedValue({ id: 10, role: USER_ROLE.ADMINISTRATOR, login: 'bob', setFullName: jest.fn() } as any) + const userInfo = { sub: 'x', email: 'b@c.d', preferred_username: 'bob', groups: ['admins'] } + + const result = await (service as any).processUserInfo(userInfo, '127.0.0.1') + + expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith( + expect.objectContaining({ role: USER_ROLE.ADMINISTRATOR }), + USER_ROLE.ADMINISTRATOR + ) + expect(result.role).toBe(USER_ROLE.ADMINISTRATOR) + }) +}) diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index e5764c6a..07359af8 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -74,9 +74,6 @@ export class AuthProviderOIDC implements AuthProvider { async getConfig(): Promise { if (!this.config) { this.config = await this.initializeOIDCClient() - if (!this.config) { - throw new HttpException('Failed to initialize OIDC client', HttpStatus.INTERNAL_SERVER_ERROR) - } } return this.config } @@ -217,15 +214,25 @@ export class AuthProviderOIDC implements AuthProvider { }, this.getTokenAuthMethod(this.oidcConfig.security.tokenEndpointAuthMethod, this.oidcConfig.clientSecret), { - execute: [allowInsecureRequests], // allow HTTP for development + execute: [allowInsecureRequests], timeout: 6000 } ) this.logger.log(`${this.initializeOIDCClient.name} - OIDC client initialized successfully for issuer: ${this.oidcConfig.issuerUrl}`) return config } catch (error) { - this.logger.error(`${this.initializeOIDCClient.name} - Failed to initialize OIDC client: ${error?.cause || error}`) - return null + this.logger.error(`${this.initializeOIDCClient.name} - OIDC client initialization failed: ${error?.cause || error}`) + switch (error.cause?.code) { + case 'ECONNREFUSED': + case 'ENOTFOUND': + throw new HttpException('OIDC provider unavailable', HttpStatus.SERVICE_UNAVAILABLE) + + case 'ETIMEDOUT': + throw new HttpException('OIDC provider timeout', HttpStatus.GATEWAY_TIMEOUT) + + default: + throw new HttpException('OIDC client initialization failed', HttpStatus.INTERNAL_SERVER_ERROR) + } } } From 3cb3ea41eefe7625497a699dd12c55da85007188 Mon Sep 17 00:00:00 2001 From: johaven Date: Tue, 27 Jan 2026 23:10:32 +0100 Subject: [PATCH 10/18] feat(backend:sync): add support for TOTP recovery codes during client registration --- .../services/sync-clients-manager.service.ts | 15 ++++--- .../two-fa/auth-provider-two-fa.service.ts | 39 ++++++++++--------- frontend/src/app/auth/auth.interface.ts | 6 +++ 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.ts b/backend/src/applications/sync/services/sync-clients-manager.service.ts index 7045eb25..37e7b338 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.ts @@ -59,15 +59,20 @@ export class SyncClientsManager { throw new HttpException('Missing permission', HttpStatus.FORBIDDEN) } if (configuration.auth.mfa.totp.enabled && user.twoFaEnabled) { + // Checking TOTP code and recovery code if (!clientRegistrationDto.code) { this.logger.warn(`${this.register.name} - missing two-fa code for user *${user.login}* (${user.id})`) throw new HttpException('Missing TWO-FA code', HttpStatus.UNAUTHORIZED) } - const auth = this.authMethod2Fa.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret) - if (!auth.success) { - this.logger.warn(`${this.register.name} - wrong two-fa code for user *${user.login}* (${user.id})`) - this.usersManager.updateAccesses(user, ip, false).catch((e: Error) => this.logger.error(`${this.register.name} - ${e}`)) - throw new HttpException(auth.message, HttpStatus.UNAUTHORIZED) + const authCode = this.authMethod2Fa.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret) + if (!authCode.success) { + this.logger.warn(`${this.register.name} - two-fa code for *${user.login}* (${user.id}) - ${authCode.message}`) + const authRCode = await this.authMethod2Fa.validateRecoveryCode(user.id, clientRegistrationDto.code, user.secrets.recoveryCodes) + if (!authRCode.success) { + this.logger.warn(`${this.register.name} - two-fa recovery code for *${user.login}* (${user.id}) - ${authRCode.message}`) + this.usersManager.updateAccesses(user, ip, false).catch((e: Error) => this.logger.error(`${this.register.name} - ${e}`)) + throw new HttpException(authCode.message, HttpStatus.UNAUTHORIZED) + } } } try { diff --git a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts index b3568581..75181f1e 100644 --- a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts +++ b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts @@ -143,30 +143,31 @@ export class AuthProvider2FA { return auth } - private async validateRecoveryCode(userId: number, code: string, encryptedCodes: string[]): Promise { + async validateRecoveryCode(userId: number, code: string, encryptedCodes: string[]): Promise { const auth: TwoFaVerifyResult = { success: false, message: '' } if (!encryptedCodes || encryptedCodes.length === 0) { auth.message = 'Invalid code' - } else { - try { - for (const encCode of encryptedCodes) { - if (code === this.decryptSecret(encCode)) { - auth.success = true - // removed used code - encryptedCodes.splice(encryptedCodes.indexOf(encCode), 1) - break - } - } - if (auth.success) { - // update recovery codes - await this.usersManager.updateSecrets(userId, { recoveryCodes: encryptedCodes }) - } else { - auth.message = 'Invalid code' + return auth + } + try { + for (const encCode of encryptedCodes) { + const decryptedCode = this.decryptSecret(encCode) + if (code === decryptedCode) { + auth.success = true + // removed used code + encryptedCodes.splice(encryptedCodes.indexOf(encCode), 1) + break } - } catch (e) { - this.logger.error(`${this.validateRecoveryCode.name} - ${e}`) - auth.message = e.message } + if (auth.success) { + // update recovery codes + await this.usersManager.updateSecrets(userId, { recoveryCodes: encryptedCodes }) + } else { + auth.message = 'Invalid code' + } + } catch (e) { + this.logger.error(`${this.validateRecoveryCode.name} - ${e}`) + auth.message = e.message } return auth } diff --git a/frontend/src/app/auth/auth.interface.ts b/frontend/src/app/auth/auth.interface.ts index add99280..5001bdb7 100644 --- a/frontend/src/app/auth/auth.interface.ts +++ b/frontend/src/app/auth/auth.interface.ts @@ -4,6 +4,12 @@ * See the LICENSE file for licensing details */ +export interface AuthResult { + success: boolean + message: any + twoFaEnabled?: boolean +} + export interface AuthOIDCQueryParams { oidc: string access_expiration: string From 08c6e0faf84d3131ab9462052a60c73ab59b031e Mon Sep 17 00:00:00 2001 From: johaven Date: Tue, 27 Jan 2026 23:56:58 +0100 Subject: [PATCH 11/18] feat(auth): refactor authentication services and add desktop client registration support --- frontend/src/app/auth/auth.component.ts | 50 +++++--- frontend/src/app/auth/auth.service.ts | 120 ++++++++++-------- frontend/src/app/electron/constants/events.ts | 3 +- frontend/src/app/electron/electron.service.ts | 9 +- 4 files changed, 106 insertions(+), 76 deletions(-) diff --git a/frontend/src/app/auth/auth.component.ts b/frontend/src/app/auth/auth.component.ts index 2a19e04c..3ff2f64e 100644 --- a/frontend/src/app/auth/auth.component.ts +++ b/frontend/src/app/auth/auth.component.ts @@ -18,6 +18,7 @@ import { finalize } from 'rxjs/operators' import { logoDarkUrl } from '../applications/files/files.constants' import { RECENTS_PATH } from '../applications/recents/recents.constants' import { AutofocusDirective } from '../common/directives/auto-focus.directive' +import type { AuthResult } from './auth.interface' import { AuthService } from './auth.service' @Component({ @@ -60,21 +61,26 @@ export class AuthComponent { .login(this.loginForm.value.username, this.loginForm.value.password) .pipe(finalize(() => setTimeout(() => (this.submitted = false), 1500))) .subscribe({ - next: (res: { success: boolean; message: any; twoFaEnabled?: boolean }) => this.isLogged(res), + next: (res: AuthResult) => this.isLogged(res), error: (e) => this.isLogged({ success: false, message: e.error ? e.error.message : e }) }) } - onSubmit2Fa() { + async onSubmit2Fa() { this.submitted = true - const verifyCode: TwoFaVerifyDto = { - code: this.twoFaForm.value.isRecoveryCode ? this.twoFaForm.value.recoveryCode : this.twoFaForm.value.totpCode, - isRecoveryCode: this.twoFaForm.value.isRecoveryCode + const code = this.twoFaForm.value.isRecoveryCode ? this.twoFaForm.value.recoveryCode : this.twoFaForm.value.totpCode + + if (this.auth.electron.enabled) { + this.auth.electron.register(this.loginForm.value.username, this.loginForm.value.password, code).subscribe({ + next: (res: AuthResult) => this.is2FaVerified(res as TwoFaResponseDto), + error: (e) => this.is2FaVerified({ success: false, message: e.error ? e.error.message : e } as TwoFaResponseDto) + }) + } else { + this.auth.loginWith2Fa({ code: code, isRecoveryCode: this.twoFaForm.value.isRecoveryCode } satisfies TwoFaVerifyDto).subscribe({ + next: (res: TwoFaResponseDto) => this.is2FaVerified(res), + error: (e) => this.is2FaVerified({ success: false, message: e.error ? e.error.message : e } as TwoFaResponseDto) + }) } - this.auth.loginWith2Fa(verifyCode).subscribe({ - next: (res: TwoFaResponseDto) => this.is2FaVerified(res), - error: (e) => this.is2FaVerified({ success: false, message: e.error ? e.error.message : e } as TwoFaResponseDto) - }) } onCancel2Fa() { @@ -85,10 +91,18 @@ export class AuthComponent { this.hasError = null } - is2FaVerified(res: TwoFaResponseDto) { + loginWithOIDC() { + if (this.oidcSettings) { + window.location.href = this.oidcSettings.loginUrl + } + } + + private is2FaVerified(res: TwoFaResponseDto) { if (res.success) { - // In this case, the user and tokens are provided - this.auth.initUserFromResponse(res) + if (!this.auth.electron.enabled) { + // Web: in this case, the user and tokens are provided + this.auth.initUserFromResponse(res) + } this.isLogged({ success: true, message: res.message }) } else { this.hasError = res.message || 'Unable to verify code' @@ -97,11 +111,15 @@ export class AuthComponent { this.twoFaForm.patchValue({ totpCode: '', recoveryCode: '' }) } - isLogged(res: { success: boolean; message: any; twoFaEnabled?: boolean }) { + private isLogged(res: AuthResult) { if (res.success) { this.hasError = null if (res.twoFaEnabled) { this.twoFaVerify = true + if (this.auth.electron.enabled) { + // Do not clear the password; it will be used to register the client. + return + } } else if (this.auth.returnUrl) { this.router.navigateByUrl(this.auth.returnUrl).then(() => { this.auth.returnUrl = null @@ -116,10 +134,4 @@ export class AuthComponent { } this.loginForm.patchValue({ password: '' }) } - - loginWithOIDC() { - if (this.oidcSettings) { - window.location.href = this.oidcSettings.loginUrl - } - } } diff --git a/frontend/src/app/auth/auth.service.ts b/frontend/src/app/auth/auth.service.ts index b14ff042..30e6d9e2 100644 --- a/frontend/src/app/auth/auth.service.ts +++ b/frontend/src/app/auth/auth.service.ts @@ -35,20 +35,20 @@ import { Electron } from '../electron/electron.service' import { LayoutService } from '../layout/layout.service' import { StoreService } from '../store/store.service' import { AUTH_PATHS } from './auth.constants' -import type { AuthOIDCQueryParams } from './auth.interface' +import type { AuthOIDCQueryParams, AuthResult } from './auth.interface' @Injectable({ providedIn: 'root' }) export class AuthService { public returnUrl: string + public electron = inject(Electron) private authSettings: AuthOIDCSettings | false = null private readonly http = inject(HttpClient) private readonly router = inject(Router) private readonly store = inject(StoreService) private readonly userService = inject(UserService) private readonly layout = inject(LayoutService) - private readonly electron = inject(Electron) private _refreshExpiration = parseInt(localStorage.getItem('refresh_expiration') || '0', 10) || 0 @@ -74,50 +74,26 @@ export class AuthService { localStorage.setItem('access_expiration', value.toString()) } - login(login: string, password: string): Observable<{ success: boolean; message: any; twoFaEnabled?: boolean }> { + login(login: string, password: string): Observable { return this.http.post(API_AUTH_LOGIN, { login, password }).pipe( - map((r: LoginResponseDto) => { + switchMap((r: LoginResponseDto) => { + // 2FA - first step (code page) if (r.server.twoFaEnabled && r.user.twoFaEnabled) { - // check 2FA before logging in the user this.accessExpiration = r.token.access_2fa_expiration this.refreshExpiration = this.accessExpiration - return { success: true, twoFaEnabled: true, message: null } - } else { - this.initUserFromResponse(r) + return of({ success: true, twoFaEnabled: true, message: null }) } - return { success: true, message: null } + // Desktop Client Login + if (this.electron.enabled) { + return this.electron.register(login, password) + } + // Web Login + this.initUserFromResponse(r) + return of({ success: true, message: null }) }), catchError((e) => { - console.warn(e) - return of({ success: false, message: e.error.message || e.message }) - }) - ) - } - - loginElectron(): Observable { - return this.electron.authenticate().pipe( - switchMap((auth: SyncClientAuthDto) => { - return this.http.post(API_SYNC_AUTH_COOKIE, auth).pipe( - map((r: ClientAuthCookieDto) => { - this.accessExpiration = r.token.access_expiration - this.refreshExpiration = r.token.refresh_expiration - this.initUser(r) - if (r?.client_token_update) { - // update client token - this.electron.send(EVENT.SERVER.AUTHENTICATION_TOKEN_UPDATE, r.client_token_update) - } - return true - }), - catchError((e: HttpErrorResponse) => { - console.warn(e) - if (e.error.message === CLIENT_TOKEN_EXPIRED_ERROR) { - this.electron.send(EVENT.SERVER.AUTHENTICATION_TOKEN_EXPIRED) - } else { - this.electron.send(EVENT.SERVER.AUTHENTICATION_FAILED) - } - return of(false) - }) - ) + console.error(e) + return of({ success: false, message: e?.error?.message ?? e?.message }) }) ) } @@ -146,20 +122,6 @@ export class AuthService { .subscribe() } - logoutImpersonateUser() { - this.http.post(API_ADMIN_IMPERSONATE_LOGOUT, null).subscribe({ - next: (r: LoginResponseDto) => { - this.userService.disconnectWebSocket() - this.initUserFromResponse(r) - this.router.navigate([USER_PATH.BASE, USER_PATH.ACCOUNT]).catch(console.error) - }, - error: (e: HttpErrorResponse) => { - console.error(e) - this.layout.sendNotification('error', 'Impersonate identity', 'logout', e) - } - }) - } - initUserFromResponse(r: LoginResponseDto, impersonate = false) { if (r !== null) { this.accessExpiration = r.token.access_expiration @@ -183,7 +145,7 @@ export class AuthService { console.debug('token has expired') if (this.electron.enabled) { console.debug('login with app') - return this.loginElectron() + return this.authenticateDesktopClient() } this.logout(true, true) return throwError(() => e) @@ -198,7 +160,7 @@ export class AuthService { } if (this.refreshTokenHasExpired()) { if (this.electron.enabled) { - return this.loginElectron() + return this.authenticateDesktopClient() } this.returnUrl = returnUrl.length > 1 ? returnUrl : null this.logout() @@ -260,6 +222,54 @@ export class AuthService { ) } + private logoutImpersonateUser() { + this.http.post(API_ADMIN_IMPERSONATE_LOGOUT, null).subscribe({ + next: (r: LoginResponseDto) => { + this.userService.disconnectWebSocket() + this.initUserFromResponse(r) + this.router.navigate([USER_PATH.BASE, USER_PATH.ACCOUNT]).catch(console.error) + }, + error: (e: HttpErrorResponse) => { + console.error(e) + this.layout.sendNotification('error', 'Impersonate identity', 'logout', e) + } + }) + } + + private authenticateDesktopClient(): Observable { + return this.electron.authenticate().pipe( + switchMap((auth: SyncClientAuthDto) => { + if (!auth.clientId) { + // No auth was provided, the Sync-in desktop app must be registered + this.logout(true) + return of(false) + } + return this.http.post(API_SYNC_AUTH_COOKIE, auth).pipe( + map((r: ClientAuthCookieDto) => { + this.accessExpiration = r.token.access_expiration + this.refreshExpiration = r.token.refresh_expiration + this.initUser(r) + if (r?.client_token_update) { + // Update the client token + this.electron.send(EVENT.SERVER.AUTHENTICATION_TOKEN_UPDATE, r.client_token_update) + } + return true + }), + catchError((e: HttpErrorResponse) => { + if (e.error.message === CLIENT_TOKEN_EXPIRED_ERROR) { + this.electron.send(EVENT.SERVER.AUTHENTICATION_TOKEN_EXPIRED) + } else { + // In other cases, we consider the server unavailable + this.electron.send(EVENT.SERVER.AUTHENTICATION_FAILED) + } + this.logout(true, e.error.message === CLIENT_TOKEN_EXPIRED_ERROR) + return of(false) + }) + ) + }) + ) + } + private refreshTokenHasExpired(): boolean { return this.refreshExpiration === 0 || currentTimeStamp() >= this.refreshExpiration } diff --git a/frontend/src/app/electron/constants/events.ts b/frontend/src/app/electron/constants/events.ts index 5afe9710..5c0e3218 100644 --- a/frontend/src/app/electron/constants/events.ts +++ b/frontend/src/app/electron/constants/events.ts @@ -5,8 +5,9 @@ */ export const EVENT = { - // authentication + // server SERVER: { + REGISTRATION: 'server-registration', AUTHENTICATION: 'server-authentication', AUTHENTICATION_FAILED: 'server-authentication-failed', AUTHENTICATION_TOKEN_UPDATE: 'server-authentication-token-update', diff --git a/frontend/src/app/electron/electron.service.ts b/frontend/src/app/electron/electron.service.ts index 8aea3bcc..7c27c3cd 100644 --- a/frontend/src/app/electron/electron.service.ts +++ b/frontend/src/app/electron/electron.service.ts @@ -8,12 +8,13 @@ import { effect, inject, Injectable, NgZone } from '@angular/core' import { toObservable } from '@angular/core/rxjs-interop' import { FileTask } from '@sync-in-server/backend/src/applications/files/models/file-task' import type { SyncClientAuthDto } from '@sync-in-server/backend/src/applications/sync/dtos/sync-client-auth.dto' -import { combineLatest, from, Observable } from 'rxjs' +import { combineLatest, from, map, Observable } from 'rxjs' import { NotificationModel } from '../applications/notifications/models/notification.model' import { CLIENT_APP_COUNTER, CLIENT_SCHEDULER_STATE } from '../applications/sync/constants/client' import { SyncStatus } from '../applications/sync/interfaces/sync-status.interface' import { SyncTask } from '../applications/sync/interfaces/sync-task.interface' import { SYNC_MENU } from '../applications/sync/sync.constants' +import type { AuthResult } from '../auth/auth.interface' import { StoreService } from '../store/store.service' import { EVENT } from './constants/events' import { ElectronIpcRenderer } from './interface' @@ -73,6 +74,12 @@ export class Electron { return from(this.invoke(EVENT.SERVER.AUTHENTICATION)) } + register(login: string, password: string, code?: string): Observable { + return from(this.invoke(EVENT.SERVER.REGISTRATION, { login, password, code })).pipe( + map((e: { ok: boolean; msg?: string }) => ({ success: e.ok, message: e.msg ?? null }) satisfies AuthResult) + ) + } + openPath(path: string) { this.send(EVENT.MISC.FILE_OPEN, path) } From b6525ecd8b5e524ca390decde7c969395a5ad1ba Mon Sep 17 00:00:00 2001 From: johaven Date: Wed, 28 Jan 2026 22:48:14 +0100 Subject: [PATCH 12/18] feat(auth:sync): introduce `registerWithAuth` to enable desktop client registration from external process (OIDC) --- .../src/applications/sync/constants/routes.ts | 2 + .../sync/dtos/sync-client-auth.dto.ts | 8 ++- .../sync/dtos/sync-client-registration.dto.ts | 14 ++++- .../interfaces/sync-client-auth.interface.ts | 17 +++--- .../sync/interfaces/sync-client.interface.ts | 2 +- .../services/sync-clients-manager.service.ts | 40 ++++++++----- .../src/applications/sync/sync.controller.ts | 20 +++++-- frontend/src/app/auth/auth.service.ts | 56 ++++++++++++++++--- frontend/src/app/electron/electron.service.ts | 13 +++++ 9 files changed, 133 insertions(+), 39 deletions(-) diff --git a/backend/src/applications/sync/constants/routes.ts b/backend/src/applications/sync/constants/routes.ts index 0a7dfc41..a0d85eb6 100644 --- a/backend/src/applications/sync/constants/routes.ts +++ b/backend/src/applications/sync/constants/routes.ts @@ -12,6 +12,7 @@ export const SYNC_ROUTE = { HANDSHAKE: 'handshake', REGISTER: 'register', UNREGISTER: 'unregister', + REGISTER_AUTH: 'register/auth', APP_STORE: 'app-store', AUTH: 'auth', CLIENTS: 'clients', @@ -23,3 +24,4 @@ export const SYNC_ROUTE = { export const API_SYNC_AUTH_COOKIE = `${SYNC_ROUTE.BASE}/${SYNC_ROUTE.AUTH}/cookie` export const API_SYNC_CLIENTS = `${SYNC_ROUTE.BASE}/${SYNC_ROUTE.CLIENTS}` +export const API_SYNC_REGISTER_AUTH = `${SYNC_ROUTE.BASE}/${SYNC_ROUTE.REGISTER_AUTH}` diff --git a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts index 3dfb6bf8..174763be 100644 --- a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts @@ -4,8 +4,8 @@ * See the LICENSE file for licensing details */ -import { IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsString, IsUUID } from 'class-validator' -import { SyncClientInfo } from '../interfaces/sync-client.interface' +import { IsBoolean, IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID } from 'class-validator' +import type { SyncClientInfo } from '../interfaces/sync-client.interface' export class SyncClientAuthDto { @IsNotEmpty() @@ -22,4 +22,8 @@ export class SyncClientAuthDto { @IsNotEmptyObject() @IsObject() info: SyncClientInfo + + @IsOptional() + @IsBoolean() + tokenHasExpired?: boolean } diff --git a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts index 7acfad96..8598e738 100644 --- a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts @@ -28,5 +28,17 @@ export class SyncClientRegistrationDto { @IsDefined() @IsNotEmptyObject() @IsObject() - info: SyncClientInfo + info: SyncClientInfo // TODO: create a DTO for validation +} + +export class SyncClientAuthRegistrationDto { + @IsOptional() + @IsString() + @IsUUID() + clientId?: string + + @IsDefined() + @IsNotEmptyObject() + @IsObject() + info: SyncClientInfo // TODO: create a DTO for validation } diff --git a/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts b/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts index 3867fea6..4e65cd8a 100644 --- a/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts @@ -4,15 +4,14 @@ * See the LICENSE file for licensing details */ -import { LoginResponseDto } from '../../../authentication/dto/login-response.dto' -import { TokenResponseDto } from '../../../authentication/dto/token-response.dto' +import type { LoginResponseDto } from '../../../authentication/dto/login-response.dto' +import type { TokenResponseDto } from '../../../authentication/dto/token-response.dto' -export class ClientAuthCookieDto extends LoginResponseDto { - // send the new client token - client_token_update?: string -} +// send the new client token +export type SyncClientAuthCookie = LoginResponseDto & { client_token_update?: string } +export type SyncClientAuthToken = TokenResponseDto & { client_token_update?: string } -export class ClientAuthTokenDto extends TokenResponseDto { - // send the new client token - client_token_update?: string +export interface SyncClientAuthRegistration { + clientId: string + clientToken: string } diff --git a/backend/src/applications/sync/interfaces/sync-client.interface.ts b/backend/src/applications/sync/interfaces/sync-client.interface.ts index 5988f9a9..3b6454a1 100644 --- a/backend/src/applications/sync/interfaces/sync-client.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-client.interface.ts @@ -4,7 +4,7 @@ * See the LICENSE file for licensing details */ -import { SYNC_CLIENT_TYPE } from '../constants/sync' +import type { SYNC_CLIENT_TYPE } from '../constants/sync' export interface SyncClientInfo { node: string diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.ts b/backend/src/applications/sync/services/sync-clients-manager.service.ts index 37e7b338..15f5222c 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.ts @@ -12,6 +12,7 @@ import crypto from 'node:crypto' import fs from 'node:fs/promises' import path from 'node:path' import { AuthManager } from '../../../authentication/auth.service' +import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface' import { AuthProvider } from '../../../authentication/providers/auth-providers.models' import { AuthProvider2FA } from '../../../authentication/providers/two-fa/auth-provider-two-fa.service' import { convertHumanTimeToSeconds } from '../../../common/functions' @@ -28,10 +29,11 @@ import { CLIENT_AUTH_TYPE, CLIENT_TOKEN_EXPIRATION_TIME, CLIENT_TOKEN_EXPIRED_ER import { APP_STORE_DIRNAME, APP_STORE_MANIFEST_FILE, APP_STORE_REPOSITORY, APP_STORE_URL } from '../constants/store' import { SYNC_CLIENT_TYPE } from '../constants/sync' import type { SyncClientAuthDto } from '../dtos/sync-client-auth.dto' -import type { SyncClientRegistrationDto } from '../dtos/sync-client-registration.dto' +import { SyncClientAuthRegistrationDto, SyncClientRegistrationDto } from '../dtos/sync-client-registration.dto' import { AppStoreManifest } from '../interfaces/store-manifest.interface' -import { ClientAuthCookieDto, ClientAuthTokenDto } from '../interfaces/sync-client-auth.interface' +import { SyncClientAuthCookie, SyncClientAuthRegistration, SyncClientAuthToken } from '../interfaces/sync-client-auth.interface' import { SyncClientPaths } from '../interfaces/sync-client-paths.interface' +import { SyncClientInfo } from '../interfaces/sync-client.interface' import { SyncClient } from '../schemas/sync-client.interface' import { SyncQueries } from './sync-queries.service' @@ -48,7 +50,7 @@ export class SyncClientsManager { private readonly syncQueries: SyncQueries ) {} - async register(clientRegistrationDto: SyncClientRegistrationDto, ip: string): Promise<{ clientToken: string }> { + async register(clientRegistrationDto: SyncClientRegistrationDto, ip: string): Promise { const user: UserModel = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password) if (!user) { this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`) @@ -75,14 +77,15 @@ export class SyncClientsManager { } } } - try { - const token = await this.syncQueries.getOrCreateClient(user.id, clientRegistrationDto.clientId, clientRegistrationDto.info, ip) - this.logger.log(`${this.register.name} - client *${clientRegistrationDto.info.type}* was registered for user *${user.login}* (${user.id})`) - return { clientToken: token } - } catch (e) { - this.logger.error(`${this.register.name} - ${e}`) - throw new HttpException('Error during the client registration', HttpStatus.INTERNAL_SERVER_ERROR) - } + return this.getOrCreateClient(user, clientRegistrationDto.clientId, clientRegistrationDto.info, ip) + } + + async registerWithAuth( + clientAuthenticatedRegistrationDto: SyncClientAuthRegistrationDto, + req: FastifyAuthenticatedRequest + ): Promise { + const clientId = clientAuthenticatedRegistrationDto.clientId || crypto.randomUUID() + return this.getOrCreateClient(req.user, clientId, clientAuthenticatedRegistrationDto.info, req.ip) } async unregister(user: UserModel): Promise { @@ -99,7 +102,7 @@ export class SyncClientsManager { syncClientAuthDto: SyncClientAuthDto, ip: string, res: FastifyReply - ): Promise { + ): Promise { const client = await this.syncQueries.getClient(syncClientAuthDto.clientId, null, syncClientAuthDto.token) if (!client) { throw new HttpException('Client is unknown', HttpStatus.FORBIDDEN) @@ -126,7 +129,7 @@ export class SyncClientsManager { user.clientId = client.id // update accesses this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.authenticate.name} - ${e}`)) - let r: ClientAuthTokenDto | ClientAuthCookieDto + let r: SyncClientAuthToken | SyncClientAuthCookie if (authType === CLIENT_AUTH_TYPE.COOKIE) { // used by the desktop app to perform the login setup using cookies r = await this.authManager.setCookies(user, res) @@ -209,4 +212,15 @@ export class SyncClientsManager { } return manifest } + + private async getOrCreateClient(user: UserModel, clientId: string, clientInfo: SyncClientInfo, ip: string): Promise { + try { + const token = await this.syncQueries.getOrCreateClient(user.id, clientId, clientInfo, ip) + this.logger.log(`${this.register.name} - client *${clientInfo.type}* was registered for user *${user.login}* (${user.id})`) + return { clientId: clientId, clientToken: token } satisfies SyncClientAuthRegistration + } catch (e) { + this.logger.error(`${this.register.name} - ${e}`) + throw new HttpException('Error during the client registration', HttpStatus.INTERNAL_SERVER_ERROR) + } + } } diff --git a/backend/src/applications/sync/sync.controller.ts b/backend/src/applications/sync/sync.controller.ts index dc73e2fa..3a6064ee 100644 --- a/backend/src/applications/sync/sync.controller.ts +++ b/backend/src/applications/sync/sync.controller.ts @@ -28,6 +28,7 @@ import { } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' import { AuthTokenSkip } from '../../authentication/decorators/auth-token-skip.decorator' +import { FastifyAuthenticatedRequest } from '../../authentication/interfaces/auth-request.interface' import { ContextInterceptor } from '../../infrastructure/context/interceptors/context.interceptor' import { SkipSpacePermissionsCheck } from '../spaces/decorators/space-skip-permissions.decorator' import { FastifySpaceRequest } from '../spaces/interfaces/space-request.interface' @@ -41,13 +42,13 @@ import { SYNC_ROUTE } from './constants/routes' import { CHECK_SERVER_RESP, SYNC_IN_SERVER_AGENT } from './constants/sync' import { SyncEnvironment } from './decorators/sync-environment.decorator' import { SyncClientAuthDto } from './dtos/sync-client-auth.dto' -import type { SyncClientRegistrationDto } from './dtos/sync-client-registration.dto' +import { SyncClientAuthRegistrationDto, SyncClientRegistrationDto } from './dtos/sync-client-registration.dto' import { SyncCopyMoveDto, SyncDiffDto, SyncMakeDto, SyncPropsDto } from './dtos/sync-operations.dto' import { SyncPathDto, SyncPathUpdateDto } from './dtos/sync-path.dto' import { SyncUploadDto } from './dtos/sync-upload.dto' import { SyncDiffGzipBodyInterceptor } from './interceptors/sync-diff-gzip-body.interceptor' import { AppStoreManifest } from './interfaces/store-manifest.interface' -import { ClientAuthCookieDto, ClientAuthTokenDto } from './interfaces/sync-client-auth.interface' +import { SyncClientAuthCookie, SyncClientAuthRegistration, SyncClientAuthToken } from './interfaces/sync-client-auth.interface' import { SyncClientPaths } from './interfaces/sync-client-paths.interface' import { SyncPathSettings } from './interfaces/sync-path.interface' import { SyncClientsManager } from './services/sync-clients-manager.service' @@ -75,10 +76,20 @@ export class SyncController { @Post(SYNC_ROUTE.REGISTER) @AuthTokenSkip() - register(@Body() syncClientRegistrationDto: SyncClientRegistrationDto, @Req() req: FastifyRequest): Promise<{ clientToken: string }> { + register(@Body() syncClientRegistrationDto: SyncClientRegistrationDto, @Req() req: FastifyRequest): Promise { return this.syncClientsManager.register(syncClientRegistrationDto, req.ip) } + @Post(SYNC_ROUTE.REGISTER_AUTH) + @UserHavePermission(USER_PERMISSION.DESKTOP_APP) + @UseGuards(UserPermissionsGuard) + registerWithAuth( + @Body() clientAuthenticatedRegistrationDto: SyncClientAuthRegistrationDto, + @Req() req: FastifyAuthenticatedRequest + ): Promise { + return this.syncClientsManager.registerWithAuth(clientAuthenticatedRegistrationDto, req) + } + @Post(SYNC_ROUTE.UNREGISTER) @UserHavePermission(USER_PERMISSION.DESKTOP_APP) @UseGuards(UserPermissionsGuard) @@ -89,6 +100,7 @@ export class SyncController { @Get(SYNC_ROUTE.APP_STORE) @AuthTokenSkip() checkAppStore(): Promise { + // This route must be public to allow clients to receive updates return this.syncClientsManager.checkAppStore() } @@ -99,7 +111,7 @@ export class SyncController { @Body() clientAuthDto: SyncClientAuthDto, @Req() req: FastifyRequest, @Res({ passthrough: true }) res: FastifyReply - ): Promise { + ): Promise { return this.syncClientsManager.authenticate(type, clientAuthDto, req.ip, res) } diff --git a/frontend/src/app/auth/auth.service.ts b/frontend/src/app/auth/auth.service.ts index 30e6d9e2..30bb3a61 100644 --- a/frontend/src/app/auth/auth.service.ts +++ b/frontend/src/app/auth/auth.service.ts @@ -8,9 +8,9 @@ import { HttpClient, HttpErrorResponse, HttpRequest } from '@angular/common/http import { inject, Injectable } from '@angular/core' import { Router } from '@angular/router' import { CLIENT_TOKEN_EXPIRED_ERROR } from '@sync-in-server/backend/src/applications/sync/constants/auth' -import { API_SYNC_AUTH_COOKIE } from '@sync-in-server/backend/src/applications/sync/constants/routes' +import { API_SYNC_AUTH_COOKIE, API_SYNC_REGISTER_AUTH } from '@sync-in-server/backend/src/applications/sync/constants/routes' import type { SyncClientAuthDto } from '@sync-in-server/backend/src/applications/sync/dtos/sync-client-auth.dto' -import type { ClientAuthCookieDto } from '@sync-in-server/backend/src/applications/sync/interfaces/sync-client-auth.interface' +import { SyncClientAuthCookie, SyncClientAuthRegistration } from '@sync-in-server/backend/src/applications/sync/interfaces/sync-client-auth.interface' import { API_ADMIN_IMPERSONATE_LOGOUT, API_USERS_ME } from '@sync-in-server/backend/src/applications/users/constants/routes' import { CSRF_KEY } from '@sync-in-server/backend/src/authentication/constants/auth' import { @@ -142,10 +142,8 @@ export class AuthService { return true }), catchError((e: HttpErrorResponse) => { - console.debug('token has expired') if (this.electron.enabled) { - console.debug('login with app') - return this.authenticateDesktopClient() + return this.authDesktopClient() } this.logout(true, true) return throwError(() => e) @@ -155,12 +153,17 @@ export class AuthService { checkUserAuthAndLoad(returnUrl: string, authFromOIDC?: AuthOIDCQueryParams): Observable { if (authFromOIDC) { + // At this point, the cookies are stored in the session. + console.debug(`${this.checkUserAuthAndLoad.name} - auth from OIDC`) this.accessExpiration = parseInt(authFromOIDC.access_expiration) this.refreshExpiration = parseInt(authFromOIDC.refresh_expiration) + if (this.electron.enabled) { + return this.authOIDCDesktopClient() + } } if (this.refreshTokenHasExpired()) { if (this.electron.enabled) { - return this.authenticateDesktopClient() + return this.authDesktopClient() } this.returnUrl = returnUrl.length > 1 ? returnUrl : null this.logout() @@ -236,16 +239,17 @@ export class AuthService { }) } - private authenticateDesktopClient(): Observable { + private authDesktopClient(): Observable { return this.electron.authenticate().pipe( switchMap((auth: SyncClientAuthDto) => { if (!auth.clientId) { // No auth was provided, the Sync-in desktop app must be registered + console.debug(`${this.authDesktopClient.name} - client must be registered`) this.logout(true) return of(false) } - return this.http.post(API_SYNC_AUTH_COOKIE, auth).pipe( - map((r: ClientAuthCookieDto) => { + return this.http.post(API_SYNC_AUTH_COOKIE, auth).pipe( + map((r: SyncClientAuthCookie) => { this.accessExpiration = r.token.access_expiration this.refreshExpiration = r.token.refresh_expiration this.initUser(r) @@ -256,6 +260,7 @@ export class AuthService { return true }), catchError((e: HttpErrorResponse) => { + console.debug(`${this.authDesktopClient.name} - ${e.error.message}`) if (e.error.message === CLIENT_TOKEN_EXPIRED_ERROR) { this.electron.send(EVENT.SERVER.AUTHENTICATION_TOKEN_EXPIRED) } else { @@ -270,6 +275,39 @@ export class AuthService { ) } + private authOIDCDesktopClient(): Observable { + return this.electron.authenticate().pipe( + switchMap((auth: SyncClientAuthDto) => { + if (!auth.clientId || auth.tokenHasExpired) { + // The client must be registered, or the token must be renewed + return this.http.post(API_SYNC_REGISTER_AUTH, auth).pipe( + switchMap((externalAuth: SyncClientAuthRegistration) => { + return this.electron.externalRegister(externalAuth).pipe( + switchMap((success: boolean) => { + if (success) { + console.debug(`${this.authOIDCDesktopClient.name} - ${auth.clientId ? 'client was registered' : 'client token renewed'}`) + return this.authDesktopClient() + } else { + this.logout(true, true) + return of(false) + } + }) + ) + }), + catchError((e: HttpErrorResponse) => { + console.error(`${this.authOIDCDesktopClient.name} - ${e}`) + this.logout(true) + return of(false) + }) + ) + } else { + // The client must be authenticated + return this.authDesktopClient() + } + }) + ) + } + private refreshTokenHasExpired(): boolean { return this.refreshExpiration === 0 || currentTimeStamp() >= this.refreshExpiration } diff --git a/frontend/src/app/electron/electron.service.ts b/frontend/src/app/electron/electron.service.ts index 7c27c3cd..dc9a5ede 100644 --- a/frontend/src/app/electron/electron.service.ts +++ b/frontend/src/app/electron/electron.service.ts @@ -8,6 +8,7 @@ import { effect, inject, Injectable, NgZone } from '@angular/core' import { toObservable } from '@angular/core/rxjs-interop' import { FileTask } from '@sync-in-server/backend/src/applications/files/models/file-task' import type { SyncClientAuthDto } from '@sync-in-server/backend/src/applications/sync/dtos/sync-client-auth.dto' +import type { SyncClientAuthRegistration } from '@sync-in-server/backend/src/applications/sync/interfaces/sync-client-auth.interface' import { combineLatest, from, map, Observable } from 'rxjs' import { NotificationModel } from '../applications/notifications/models/notification.model' import { CLIENT_APP_COUNTER, CLIENT_SCHEDULER_STATE } from '../applications/sync/constants/client' @@ -71,15 +72,27 @@ export class Electron { } authenticate(): Observable { + // Get information about client authentication return from(this.invoke(EVENT.SERVER.AUTHENTICATION)) } register(login: string, password: string, code?: string): Observable { + // The client handles the registration. return from(this.invoke(EVENT.SERVER.REGISTRATION, { login, password, code })).pipe( map((e: { ok: boolean; msg?: string }) => ({ success: e.ok, message: e.msg ?? null }) satisfies AuthResult) ) } + externalRegister(externalAuth: SyncClientAuthRegistration): Observable { + // The registration has already been completed on the server, and the client must be updated accordingly. + return from(this.invoke(EVENT.SERVER.REGISTRATION, null, externalAuth)).pipe( + map((e: { ok: boolean; msg?: string }) => { + if (!e.ok) console.error(`${this.externalRegister.name} - ${e.msg}`) + return e.ok + }) + ) + } + openPath(path: string) { this.send(EVENT.MISC.FILE_OPEN, path) } From 5195e95469b161c868fcea8e3b937a889fc4d5d8 Mon Sep 17 00:00:00 2001 From: johaven Date: Thu, 29 Jan 2026 10:41:16 +0100 Subject: [PATCH 13/18] chore(deps): update dependencies --- package-lock.json | 1046 +++++++++++++++++---------------------------- 1 file changed, 388 insertions(+), 658 deletions(-) diff --git a/package-lock.json b/package-lock.json index 395b5357..c5224aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -445,13 +445,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2003.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.14.tgz", - "integrity": "sha512-dVlWqaYu0PIgHTBu16uYUS6lJOIpXCpOYhPWuYwqdo7a4x2HcagPQ+omUZJTA6kukh7ROpKcRoiy/DsO/DgvUA==", + "version": "0.2003.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.15.tgz", + "integrity": "sha512-HmGnUTLVwpvOFilc3gTP6CL9o+UbkVyu9S4WENkQbInbW3zp54lkzY71uWJIP7QvuXPa+bS4WHEmoGNQtNvv1A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.14", + "@angular-devkit/core": "20.3.15", "rxjs": "7.8.2" }, "engines": { @@ -461,9 +461,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "20.3.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.14.tgz", - "integrity": "sha512-hWQVi73aGdIRInJqNia79Yi6SzqEThkfLug3AdZiNuNvYMaxAI347yPQz4f3Dr/i0QuiqRq/T8zfqbr46tfCqg==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.15.tgz", + "integrity": "sha512-s7sE4S5Hy62dLrtHwizbZaMcupAE8fPhm6rF+jBkhHZ75zXGhGzXP8WKFztYCAuGnis4pPnGSEKP/xVTc2lw6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -499,13 +499,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "20.3.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.14.tgz", - "integrity": "sha512-+Al9QojzTucccSUnJI+9x64Nnuev82eIgIlb1Ov9hLR572SNtjhV7zIXIalphFghEy+SPvynRuvOSc69Otp3Fg==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.15.tgz", + "integrity": "sha512-xMN1fyuhhP8Y5sNlmQvl4nMiOouHTKPkLR0zlhu5z6fHuwxxlverh31Gpq3eFzPHqmOzzb2TkgYCptCFXsXcrg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.14", + "@angular-devkit/core": "20.3.15", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -957,14 +957,14 @@ } }, "node_modules/@angular/build": { - "version": "20.3.14", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.14.tgz", - "integrity": "sha512-ajFJqTqyI2N9PYcWVxUfb6YEUQsZ13jsBzI/kDpeEZZCGadLJGSMZVNwkX7n9Csw7gzertpenGBXsSTxUjd8TA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.15.tgz", + "integrity": "sha512-DMp/wb3I9/izveXRuOkCTYEQlEzvNlJVnqA215tijOSiJGjYoUsQLazTCxtEx/trftOhVpnMP/2OvvMQVAJJoQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2003.14", + "@angular-devkit/architect": "0.2003.15", "@babel/core": "7.28.3", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -1006,7 +1006,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.3.14", + "@angular/ssr": "^20.3.15", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -1085,26 +1085,26 @@ } }, "node_modules/@angular/cli": { - "version": "20.3.14", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.14.tgz", - "integrity": "sha512-vlvnxyUtPnETl5az+creSPOrcnrZC5mhD5hSGl2WoqhYeyWdyUwsC9KLSy8/5gCH/4TNwtjqeX3Pw0KaAJUoCQ==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.15.tgz", + "integrity": "sha512-OgPMhXtNLXds0wIw6YU5/X3dU8TlAZbmPy6LYHs9ifF8K4pXpbm27vWGSZhUevSf66dMvfz8wB/aE2e0s2e5Ng==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2003.14", - "@angular-devkit/core": "20.3.14", - "@angular-devkit/schematics": "20.3.14", + "@angular-devkit/architect": "0.2003.15", + "@angular-devkit/core": "20.3.15", + "@angular-devkit/schematics": "20.3.15", "@inquirer/prompts": "7.8.2", "@listr2/prompt-adapter-inquirer": "3.0.1", "@modelcontextprotocol/sdk": "1.25.2", - "@schematics/angular": "20.3.14", + "@schematics/angular": "20.3.15", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.35.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", "listr2": "9.0.1", "npm-package-arg": "13.0.0", - "pacote": "21.0.0", + "pacote": "21.0.4", "resolve": "1.22.10", "semver": "7.7.2", "yargs": "18.0.0", @@ -8870,70 +8870,73 @@ } }, "node_modules/@npmcli/agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", - "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", "dev": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } }, "node_modules/@npmcli/fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", - "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/git": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", - "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-7.0.1.tgz", + "integrity": "sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/promise-spawn": "^8.0.0", - "ini": "^5.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^10.0.0", - "proc-log": "^5.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "ini": "^6.0.0", + "lru-cache": "^11.2.1", + "npm-pick-manifest": "^11.0.1", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^5.0.0" + "which": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/git/node_modules/ini": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", - "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/git/node_modules/isexe": { @@ -8947,16 +8950,29 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@npmcli/git/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/@npmcli/git/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -8966,170 +8982,109 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/installed-package-contents": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz", - "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-4.0.0.tgz", + "integrity": "sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==", "dev": true, "license": "ISC", "dependencies": { - "npm-bundled": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" + "npm-bundled": "^5.0.0", + "npm-normalize-package-bin": "^5.0.0" }, "bin": { "installed-package-contents": "bin/index.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/node-gyp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz", - "integrity": "sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-5.0.0.tgz", + "integrity": "sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/package-json": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.2.0.tgz", - "integrity": "sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-7.0.4.tgz", + "integrity": "sha512-0wInJG3j/K40OJt/33ax47WfWMzZTm6OQxB9cDhTt5huCP2a9g2GnlsxmfN+PulItNPIpPrZ+kfwwUil7eHcZQ==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^8.0.0", - "json-parse-even-better-errors": "^4.0.0", - "proc-log": "^5.0.0", + "@npmcli/git": "^7.0.0", + "glob": "^13.0.0", + "hosted-git-info": "^9.0.0", + "json-parse-even-better-errors": "^5.0.0", + "proc-log": "^6.0.0", "semver": "^7.5.3", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/package-json/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "dev": true, "license": "ISC", "dependencies": { - "lru-cache": "^10.0.1" + "lru-cache": "^11.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", - "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", "dev": true, "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/package-json/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "20 || >=22" } }, - "node_modules/@npmcli/package-json/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/@npmcli/package-json/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@npmcli/package-json/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/promise-spawn": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", - "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-9.0.1.tgz", + "integrity": "sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==", "dev": true, "license": "ISC", "dependencies": { - "which": "^5.0.0" + "which": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/promise-spawn/node_modules/isexe": { @@ -9143,9 +9098,9 @@ } }, "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -9155,35 +9110,35 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/redact": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-3.2.2.tgz", - "integrity": "sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/run-script": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-9.1.0.tgz", - "integrity": "sha512-aoNSbxtkePXUlbZB+anS1LqsJdctG5n3UVhfU47+CDdwMi6uNTBMF9gPcQRnqghQd2FGzcwwIFBruFMxjhBewg==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-10.0.3.tgz", + "integrity": "sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/node-gyp": "^4.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "node-gyp": "^11.0.0", - "proc-log": "^5.0.0", - "which": "^5.0.0" + "@npmcli/node-gyp": "^5.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "node-gyp": "^12.1.0", + "proc-log": "^6.0.0", + "which": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/run-script/node_modules/isexe": { @@ -9196,10 +9151,20 @@ "node": ">=16" } }, + "node_modules/@npmcli/run-script/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/@npmcli/run-script/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -9209,7 +9174,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@nuxt/opencollective": { @@ -9981,14 +9946,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "20.3.14", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.14.tgz", - "integrity": "sha512-JO37puMXFWN8YWqZZJ/URs8vPJNszZXcIyBnYdKDWTGaAnbOZMu0nzQlOC+h5NM7R5cPQtOpJv0wxEnY6EYI4A==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.15.tgz", + "integrity": "sha512-WkhW1HO8pA8JT8e27tvjQHQg8eO5KaOz+WsGkN00RyL5DwHgPSzu4a3eYug+b3rW7OGFub7jadXBuGSrzqgonA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.14", - "@angular-devkit/schematics": "20.3.14", + "@angular-devkit/core": "20.3.15", + "@angular-devkit/schematics": "20.3.15", "jsonc-parser": "3.3.1" }, "engines": { @@ -10011,32 +9976,32 @@ } }, "node_modules/@sigstore/bundle": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.1.0.tgz", - "integrity": "sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.0" + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-2.0.0.tgz", - "integrity": "sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.1.0.tgz", + "integrity": "sha512-o5cw1QYhNQ9IroioJxpzexmPjfCe7gzafd2RY3qnMpxr4ZEja+Jad/U8sgFpaue6bOaF+z7RVkyKVV44FN+N8A==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/protobuf-specs": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.4.3.tgz", - "integrity": "sha512-fk2zjD9117RL9BjqEwF7fwv7Q/P9yGsMV4MUJZ/DocaQJ6+3pKr+syBq1owU5Q5qGw5CUbXzm+4yJ2JVRDQeSA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.0.tgz", + "integrity": "sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10044,50 +10009,60 @@ } }, "node_modules/@sigstore/sign": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-3.1.0.tgz", - "integrity": "sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.0.tgz", + "integrity": "sha512-Vx1RmLxLGnSUqx/o5/VsCjkuN5L7y+vxEEwawvc7u+6WtX2W4GNa7b9HEjmcRWohw/d6BpATXmvOwc78m+Swdg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "make-fetch-happen": "^14.0.2", - "proc-log": "^5.0.0", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.3", + "proc-log": "^6.1.0", "promise-retry": "^2.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/tuf": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-3.1.1.tgz", - "integrity": "sha512-eFFvlcBIoGwVkkwmTi/vEQFSva3xs5Ot3WmBcjgjVdiaoelBLQaQ/ZBfhlG0MnG0cmTYScPpk7eDdGDWUcFUmg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.1.tgz", + "integrity": "sha512-OPZBg8y5Vc9yZjmWCHrlWPMBqW5yd8+wFNl+thMdtcWz3vjVSoJQutF8YkrzI0SLGnkuFof4HSsWUhXrf219Lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.4.1", - "tuf-js": "^3.0.1" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sigstore/verify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-2.1.1.tgz", - "integrity": "sha512-hVJD77oT67aowHxwT4+M6PGOp+E2LtLdTK3+FC0lBO9T7sYwItDMXZ7Z07IDCvR1M717a4axbIWckrW67KMP/w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", + "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.1" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@sinclair/typebox": { @@ -11297,40 +11272,30 @@ } }, "node_modules/@tufjs/models": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-3.0.1.tgz", - "integrity": "sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", "dev": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.5" + "minimatch": "^10.1.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12523,9 +12488,9 @@ } }, "node_modules/@vue/compiler-core/node_modules/entities": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", - "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -13173,13 +13138,13 @@ "license": "BSD-2-Clause" }, "node_modules/abbrev": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", - "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/abort-controller": { @@ -14081,9 +14046,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.9.16", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.16.tgz", - "integrity": "sha512-KeUZdBuxngy825i8xvzaK1Ncnkx0tBmb3k8DkEuqjKRkmtvNTjey2ZsNeh8Dw4lfKvbCOu9oeNx2TKm2vHqcRw==", + "version": "2.9.17", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", + "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -14364,81 +14329,36 @@ } }, "node_modules/cacache": { - "version": "19.0.1", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", - "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.3.tgz", + "integrity": "sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^4.0.0", + "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", - "ssri": "^12.0.0", - "tar": "^7.4.3", - "unique-filename": "^4.0.0" + "ssri": "^13.0.0", + "unique-filename": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "20 || >=22" } }, "node_modules/cacache/node_modules/minipass": { @@ -14451,23 +14371,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/cacache/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -15395,9 +15298,9 @@ "license": "MIT" }, "node_modules/core-js": { - "version": "3.47.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", - "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -18748,9 +18651,9 @@ } }, "node_modules/htmlparser2": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", - "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -18763,14 +18666,14 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.2.1", - "entities": "^6.0.0" + "domutils": "^3.2.2", + "entities": "^7.0.1" } }, "node_modules/htmlparser2/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -22419,26 +22322,26 @@ "license": "ISC" }, "node_modules/make-fetch-happen": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", - "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", + "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^3.0.0", - "cacache": "^19.0.1", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", + "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", - "proc-log": "^5.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "ssri": "^12.0.0" + "ssri": "^13.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/make-fetch-happen/node_modules/minipass": { @@ -22451,6 +22354,16 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/make-fetch-happen/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -22902,9 +22815,9 @@ } }, "node_modules/minipass-fetch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", - "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", + "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", "dev": true, "license": "MIT", "dependencies": { @@ -22913,7 +22826,7 @@ "minizlib": "^3.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -23028,19 +22941,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -23390,28 +23290,28 @@ } }, "node_modules/node-gyp": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", - "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.1.0.tgz", + "integrity": "sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^14.0.3", - "nopt": "^8.0.0", - "proc-log": "^5.0.0", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^7.4.3", + "tar": "^7.5.2", "tinyglobby": "^0.2.12", - "which": "^5.0.0" + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-gyp-build-optional-packages": { @@ -23440,10 +23340,20 @@ "node": ">=16" } }, + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/node-gyp/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -23453,7 +23363,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-int64": { @@ -23503,19 +23413,19 @@ } }, "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^3.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-package-data": { @@ -23563,39 +23473,39 @@ "license": "MIT" }, "node_modules/npm-bundled": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", - "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-5.0.0.tgz", + "integrity": "sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==", "dev": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^4.0.0" + "npm-normalize-package-bin": "^5.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-install-checks": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", - "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", + "integrity": "sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", - "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-5.0.0.tgz", + "integrity": "sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-package-arg": { @@ -23662,97 +23572,41 @@ } }, "node_modules/npm-pick-manifest": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", - "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-11.0.3.tgz", + "integrity": "sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==", "dev": true, "license": "ISC", "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", + "npm-install-checks": "^8.0.0", + "npm-normalize-package-bin": "^5.0.0", + "npm-package-arg": "^13.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-pick-manifest/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", - "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-registry-fetch": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz", - "integrity": "sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-19.1.1.tgz", + "integrity": "sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/redact": "^3.0.0", + "@npmcli/redact": "^4.0.0", "jsonparse": "^1.3.1", - "make-fetch-happen": "^14.0.0", + "make-fetch-happen": "^15.0.0", "minipass": "^7.0.2", - "minipass-fetch": "^4.0.0", + "minipass-fetch": "^5.0.0", "minizlib": "^3.0.1", - "npm-package-arg": "^12.0.0", - "proc-log": "^5.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" + "npm-package-arg": "^13.0.0", + "proc-log": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/npm-registry-fetch/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -23763,20 +23617,14 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", - "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", + "node_modules/npm-registry-fetch/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", - "dependencies": { - "hosted-git-info": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/npm-run-path": { @@ -24107,29 +23955,29 @@ "license": "BlueOak-1.0.0" }, "node_modules/pacote": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.0.tgz", - "integrity": "sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA==", + "version": "21.0.4", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.4.tgz", + "integrity": "sha512-RplP/pDW0NNNDh3pnaoIWYPvNenS7UqMbXyvMqJczosiFWTeGGwJC2NQBLqKf4rGLFfwCOnntw1aEp9Jiqm1MA==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^6.0.0", - "@npmcli/installed-package-contents": "^3.0.0", - "@npmcli/package-json": "^6.0.0", - "@npmcli/promise-spawn": "^8.0.0", - "@npmcli/run-script": "^9.0.0", - "cacache": "^19.0.0", + "@npmcli/git": "^7.0.0", + "@npmcli/installed-package-contents": "^4.0.0", + "@npmcli/package-json": "^7.0.0", + "@npmcli/promise-spawn": "^9.0.0", + "@npmcli/run-script": "^10.0.0", + "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", - "npm-package-arg": "^12.0.0", - "npm-packlist": "^10.0.0", - "npm-pick-manifest": "^10.0.0", - "npm-registry-fetch": "^18.0.0", - "proc-log": "^5.0.0", + "npm-package-arg": "^13.0.0", + "npm-packlist": "^10.0.1", + "npm-pick-manifest": "^11.0.1", + "npm-registry-fetch": "^19.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "sigstore": "^3.0.0", - "ssri": "^12.0.0", - "tar": "^6.1.11" + "sigstore": "^4.0.0", + "ssri": "^13.0.0", + "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" @@ -24138,36 +23986,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/pacote/node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/pacote/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/pacote/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -24178,102 +23996,14 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/pacote/node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/pacote/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pacote/node_modules/npm-package-arg": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", - "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", - "dev": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^8.0.0", - "proc-log": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^6.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/pacote/node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/pacote/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pacote/node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "node_modules/pacote/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", "dev": true, "license": "ISC", "engines": { - "node": ">=8" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/parent-module": { @@ -24945,9 +24675,9 @@ } }, "node_modules/prettier": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.0.tgz", - "integrity": "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", "peer": true, @@ -26257,21 +25987,21 @@ } }, "node_modules/sigstore": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-3.1.0.tgz", - "integrity": "sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-4.1.0.tgz", + "integrity": "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^3.1.0", - "@sigstore/core": "^2.0.0", - "@sigstore/protobuf-specs": "^0.4.0", - "@sigstore/sign": "^3.1.0", - "@sigstore/tuf": "^3.1.0", - "@sigstore/verify": "^2.1.0" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0", + "@sigstore/sign": "^4.1.0", + "@sigstore/tuf": "^4.0.1", + "@sigstore/verify": "^3.1.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/slash": { @@ -26608,16 +26338,16 @@ } }, "node_modules/ssri": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", - "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/ssri/node_modules/minipass": { @@ -27043,9 +26773,9 @@ } }, "node_modules/tar": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.4.tgz", - "integrity": "sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -27854,18 +27584,18 @@ "peer": true }, "node_modules/tuf-js": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.1.0.tgz", - "integrity": "sha512-3T3T04WzowbwV2FDiGXBbr81t64g1MUGGJRgT4x5o97N+8ArdhVCAF9IxFrxuSJmM3E5Asn7nKHkao0ibcZXAg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", "dev": true, "license": "MIT", "dependencies": { - "@tufjs/models": "3.0.1", - "debug": "^4.4.1", - "make-fetch-happen": "^14.0.3" + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/type-check": { @@ -28031,29 +27761,29 @@ "license": "MIT" }, "node_modules/unique-filename": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", - "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", + "integrity": "sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==", "dev": true, "license": "ISC", "dependencies": { - "unique-slug": "^5.0.0" + "unique-slug": "^6.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/unique-slug": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", - "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-6.0.0.tgz", + "integrity": "sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/universalify": { From 0d6963f95a86cae7e9283557a2e5961108ead65e Mon Sep 17 00:00:00 2001 From: johaven Date: Sun, 1 Feb 2026 13:42:55 +0100 Subject: [PATCH 14/18] feat(auth): support desktop app OIDC authentication flow --- .../sync/dtos/sync-client-auth.dto.ts | 11 +++-- .../sync/dtos/sync-client-info.dto.ts | 29 ++++++++++++ .../sync/dtos/sync-client-registration.dto.ts | 17 ++++--- .../src/authentication/constants/routes.ts | 1 + .../oidc/auth-oidc-desktop.constants.ts | 9 ++++ .../providers/oidc/auth-oidc.controller.ts | 5 +- .../oidc/auth-provider-oidc.service.ts | 33 +++++++++++-- environment/environment.dist.yaml | 9 ++++ frontend/src/app/auth/auth.component.html | 1 + frontend/src/app/auth/auth.component.ts | 47 +++++++++++++++++-- frontend/src/app/auth/auth.service.ts | 8 ++-- frontend/src/app/electron/constants/events.ts | 8 +++- frontend/src/app/electron/electron.service.ts | 20 ++++++++ 13 files changed, 174 insertions(+), 24 deletions(-) create mode 100644 backend/src/applications/sync/dtos/sync-client-info.dto.ts create mode 100644 backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts diff --git a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts index 174763be..0086b94b 100644 --- a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts @@ -4,8 +4,9 @@ * See the LICENSE file for licensing details */ -import { IsBoolean, IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID } from 'class-validator' -import type { SyncClientInfo } from '../interfaces/sync-client.interface' +import { Type } from 'class-transformer' +import { IsBoolean, IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID, ValidateNested } from 'class-validator' +import { SyncClientInfoDto } from './sync-client-info.dto' export class SyncClientAuthDto { @IsNotEmpty() @@ -19,9 +20,11 @@ export class SyncClientAuthDto { token: string @IsDefined() - @IsNotEmptyObject() @IsObject() - info: SyncClientInfo + @IsNotEmptyObject() + @ValidateNested() + @Type(() => SyncClientInfoDto) + info: SyncClientInfoDto @IsOptional() @IsBoolean() diff --git a/backend/src/applications/sync/dtos/sync-client-info.dto.ts b/backend/src/applications/sync/dtos/sync-client-info.dto.ts new file mode 100644 index 00000000..32c4a410 --- /dev/null +++ b/backend/src/applications/sync/dtos/sync-client-info.dto.ts @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +import { IsEnum, IsString } from 'class-validator' +import { SYNC_CLIENT_TYPE } from '../constants/sync' +import { SyncClientInfo } from '../interfaces/sync-client.interface' + +export class SyncClientInfoDto implements SyncClientInfo { + @IsString() + node: string + + @IsString() + os: string + + @IsString() + osRelease: string + + @IsString() + user: string + + @IsEnum(SYNC_CLIENT_TYPE) + type: SYNC_CLIENT_TYPE + + @IsString() + version: string +} diff --git a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts index 8598e738..0d8e17fb 100644 --- a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts @@ -4,8 +4,9 @@ * See the LICENSE file for licensing details */ -import { IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID } from 'class-validator' -import { SyncClientInfo } from '../interfaces/sync-client.interface' +import { Type } from 'class-transformer' +import { IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID, ValidateNested } from 'class-validator' +import { SyncClientInfoDto } from './sync-client-info.dto' export class SyncClientRegistrationDto { @IsNotEmpty() @@ -26,9 +27,11 @@ export class SyncClientRegistrationDto { clientId: string @IsDefined() - @IsNotEmptyObject() @IsObject() - info: SyncClientInfo // TODO: create a DTO for validation + @IsNotEmptyObject() + @ValidateNested() + @Type(() => SyncClientInfoDto) + info: SyncClientInfoDto } export class SyncClientAuthRegistrationDto { @@ -38,7 +41,9 @@ export class SyncClientAuthRegistrationDto { clientId?: string @IsDefined() - @IsNotEmptyObject() @IsObject() - info: SyncClientInfo // TODO: create a DTO for validation + @IsNotEmptyObject() + @ValidateNested() + @Type(() => SyncClientInfoDto) + info: SyncClientInfoDto } diff --git a/backend/src/authentication/constants/routes.ts b/backend/src/authentication/constants/routes.ts index d4e4adcf..a6617ccf 100644 --- a/backend/src/authentication/constants/routes.ts +++ b/backend/src/authentication/constants/routes.ts @@ -35,3 +35,4 @@ export const API_TWO_FA_DISABLE = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/ export const API_TWO_FA_LOGIN_VERIFY = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_LOGIN_VERIFY}` export const API_TWO_FA_ADMIN_RESET_USER = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_ADMIN_RESET_USER}` export const API_OIDC_LOGIN = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.OIDC_LOGIN}` +export const API_OIDC_CALLBACK = `${AUTH_ROUTE.BASE}/${AUTH_ROUTE.OIDC_CALLBACK}` diff --git a/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts b/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts new file mode 100644 index 00000000..fec106a7 --- /dev/null +++ b/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2012-2026 Johan Legrand + * This file is part of Sync-in | The open source file sync and share solution + * See the LICENSE file for licensing details + */ + +export const OAuthDesktopPortParam = 'desktop_port' as const +export const OAuthDesktopCallBackURI = '/oidc/callback' as const +export const OAuthDesktopLoopbackPorts = new Set([49152, 49153, 49154]) diff --git a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts index 3b775628..35f2b5ab 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts @@ -11,6 +11,7 @@ import { AuthManager } from '../../auth.service' import { AUTH_ROUTE } from '../../constants/routes' import { AuthTokenSkip } from '../../decorators/auth-token-skip.decorator' import type { LoginResponseDto } from '../../dto/login-response.dto' +import { OAuthDesktopPortParam } from './auth-oidc-desktop.constants' import { AuthProviderOIDC } from './auth-provider-oidc.service' @Controller(AUTH_ROUTE.BASE) @@ -22,8 +23,8 @@ export class AuthOIDCController { @Get(AUTH_ROUTE.OIDC_LOGIN) @AuthTokenSkip() - async oidcLogin(@Res() res: FastifyReply): Promise { - const url = await this.authProviderOIDC.getAuthorizationUrl(res) + async oidcLogin(@Query(OAuthDesktopPortParam) desktopPort: number, @Res() res: FastifyReply): Promise { + const url = await this.authProviderOIDC.getAuthorizationUrl(res, desktopPort) // Redirect to OIDC provider return res.redirect(url, HttpStatus.FOUND) } diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index 07359af8..b8f100bd 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -36,6 +36,7 @@ import type { AUTH_SCOPE } from '../../constants/scope' import { TOKEN_TYPE } from '../../interfaces/token.interface' import { AUTH_PROVIDER } from '../auth-providers.constants' import { AuthProvider } from '../auth-providers.models' +import { OAuthDesktopCallBackURI, OAuthDesktopLoopbackPorts, OAuthDesktopPortParam } from './auth-oidc-desktop.constants' import type { AuthMethodOIDCConfig } from './auth-oidc.config' import { OAuthCookie, OAuthCookieSettings, OAuthTokenEndpoint } from './auth-oidc.constants' @@ -78,7 +79,8 @@ export class AuthProviderOIDC implements AuthProvider { return this.config } - async getAuthorizationUrl(res: FastifyReply): Promise { + async getAuthorizationUrl(res: FastifyReply, desktopPort?: number): Promise { + const redirectURI = this.getRedirectURI(desktopPort) const config = await this.getConfig() // state: CSRF protection, nonce: binds the ID Token to this auth request (replay protection) @@ -90,7 +92,7 @@ export class AuthProviderOIDC implements AuthProvider { const authUrl = new URL(config.serverMetadata().authorization_endpoint!) authUrl.searchParams.set('client_id', this.oidcConfig.clientId!) - authUrl.searchParams.set('redirect_uri', this.oidcConfig.redirectUri) + authUrl.searchParams.set('redirect_uri', redirectURI) authUrl.searchParams.set('response_type', 'code') authUrl.searchParams.set('scope', this.oidcConfig.security.scope) authUrl.searchParams.set('state', state) @@ -139,8 +141,14 @@ export class AuthProviderOIDC implements AuthProvider { const pkceCodeVerifier = supportsPKCE ? codeVerifier : undefined const callbackParams = new URLSearchParams(query) + // Get Desktop Port if defined + const desktopPort: string | null = callbackParams.get(OAuthDesktopPortParam) + if (desktopPort) { + callbackParams.delete(OAuthDesktopPortParam) + } + // Exchange authorization code for tokens - const callbackUrl = new URL(this.oidcConfig.redirectUri) + const callbackUrl = new URL(this.getRedirectURI(desktopPort)) callbackUrl.search = callbackParams.toString() const tokens = await authorizationCodeGrant(config, callbackUrl, { expectedState, @@ -174,7 +182,10 @@ export class AuthProviderOIDC implements AuthProvider { throw new HttpException(error.error_description, HttpStatus.BAD_REQUEST) } else { this.logger.error(`${this.handleCallback.name} - OIDC callback error: ${error}`) - throw new HttpException('OIDC authentication failed', error instanceof HttpException ? error.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR) + throw new HttpException( + error.error_description ?? 'OIDC authentication failed', + error instanceof HttpException ? error.getStatus() : (error.status ?? HttpStatus.INTERNAL_SERVER_ERROR) + ) } } finally { // Always clear temporary OIDC cookies (success or failure) @@ -200,6 +211,20 @@ export class AuthProviderOIDC implements AuthProvider { return url.toString() } + getRedirectURI(desktopPort?: number | string): string { + // web / default callback + if (!desktopPort) return this.oidcConfig.redirectUri + // desktop app callback + if (typeof desktopPort === 'string') { + desktopPort = Number(desktopPort) + } + if (!Number.isInteger(desktopPort) || !OAuthDesktopLoopbackPorts.has(desktopPort)) { + throw new HttpException('Invalid desktop_port', HttpStatus.BAD_REQUEST) + } + // The redirect url must be known from provider + return `http://127.0.0.1:${desktopPort}${OAuthDesktopCallBackURI}` + } + private async initializeOIDCClient(): Promise { try { const issuerUrl = new URL(this.oidcConfig.issuerUrl) diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index bb2d7f66..c6021e1c 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -184,6 +184,15 @@ auth: # redirectUri: The callback URL where users are redirected after authentication # This URL must be registered in your OIDC provider's allowed redirect URIs # Example (API callback): https://sync-in.domain.com/api/auth/oidc/callback + # + # To allow authentication from the desktop application, the following redirect URLs must also be registered in your OIDC provider: + # - http://127.0.0.1:49152/oidc/callback + # - http://127.0.0.1:49153/oidc/callback + # - http://127.0.0.1:49154/oidc/callback + # + # If your OIDC provider supports wildcards or regular expressions, you may instead register a single entry such as: + # - http://127.0.0.1/* + # # required redirectUri: https://sync-in.domain.com/api/auth/oidc/callback options: diff --git a/frontend/src/app/auth/auth.component.html b/frontend/src/app/auth/auth.component.html index 93963a57..994edbae 100644 --- a/frontend/src/app/auth/auth.component.html +++ b/frontend/src/app/auth/auth.component.html @@ -80,6 +80,7 @@ diff --git a/frontend/src/app/auth/auth.component.ts b/frontend/src/app/auth/auth.component.ts index 3ff2f64e..07c0d28c 100644 --- a/frontend/src/app/auth/auth.component.ts +++ b/frontend/src/app/auth/auth.component.ts @@ -11,6 +11,8 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome' import { faKey, faLock, faQrcode, faUserAlt } from '@fortawesome/free-solid-svg-icons' import { USER_PASSWORD_MIN_LENGTH } from '@sync-in-server/backend/src/applications/users/constants/user' import { TWO_FA_CODE_LENGTH } from '@sync-in-server/backend/src/authentication/constants/auth' +import { API_OIDC_CALLBACK } from '@sync-in-server/backend/src/authentication/constants/routes' +import { OAuthDesktopPortParam } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants' import type { AuthOIDCSettings } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc.interfaces' import type { TwoFaResponseDto, TwoFaVerifyDto } from '@sync-in-server/backend/src/authentication/providers/two-fa/auth-two-fa.dtos' import { L10N_LOCALE, L10nLocale, L10nTranslateDirective, L10nTranslatePipe } from 'angular-l10n' @@ -33,6 +35,7 @@ export class AuthComponent { protected logoUrl = logoDarkUrl protected hasError: any = null protected submitted = false + protected OIDCSubmitted = false protected twoFaVerify = false private route = inject(ActivatedRoute) protected oidcSettings: AuthOIDCSettings | false = this.route.snapshot.data.authSettings @@ -51,7 +54,7 @@ export class AuthComponent { constructor() { if (this.oidcSettings && this.oidcSettings.autoRedirect) { - this.loginWithOIDC() + void this.loginWithOIDC() } } @@ -91,9 +94,45 @@ export class AuthComponent { this.hasError = null } - loginWithOIDC() { - if (this.oidcSettings) { - window.location.href = this.oidcSettings.loginUrl + async loginWithOIDC() { + if (!this.oidcSettings) return + if (!this.auth.electron.enabled) { + window.location.assign(this.oidcSettings.loginUrl) + return + } + this.OIDCSubmitted = true + try { + const desktopPort = await this.auth.electron.startOIDCDesktopAuth() + + if (!desktopPort) { + this.OIDCSubmitted = false + console.error('OIDC desktop auth failed') + return + } + + // Called when the OIDC provider redirects to desktop app + this.auth.electron + .waitOIDCDesktopCallbackParams() + .then((callbackParams: Record) => { + // Receive callback params from desktop app and send it to backend to be authenticated. + const params = new URLSearchParams(callbackParams) + // Indicates the desktop app port used to reconstruct the redirect URI expected by the backend + params.set(OAuthDesktopPortParam, String(desktopPort)) + window.location.assign(`${API_OIDC_CALLBACK}?${params.toString()}`) + // Show desktop app window + this.auth.electron.setActiveAndShow() + }) + .catch((e: Error) => { + this.OIDCSubmitted = false + console.error('Unavailable OIDC desktop callback params:', e) + }) + + // With the desktop app, navigation to the OIDC provider is intercepted and opened in the user's browser. + // The backend sends the oidc cookies to desktop app before the redirection. + window.location.assign(`${this.oidcSettings.loginUrl}?${this.auth.electron.genParamOIDCDesktopPort(desktopPort)}`) + } catch (e) { + this.OIDCSubmitted = false + console.error(e) } } diff --git a/frontend/src/app/auth/auth.service.ts b/frontend/src/app/auth/auth.service.ts index 30bb3a61..f340e97c 100644 --- a/frontend/src/app/auth/auth.service.ts +++ b/frontend/src/app/auth/auth.service.ts @@ -153,8 +153,7 @@ export class AuthService { checkUserAuthAndLoad(returnUrl: string, authFromOIDC?: AuthOIDCQueryParams): Observable { if (authFromOIDC) { - // At this point, the cookies are stored in the session. - console.debug(`${this.checkUserAuthAndLoad.name} - auth from OIDC`) + // At this point, the auth cookies are already stored in the session. this.accessExpiration = parseInt(authFromOIDC.access_expiration) this.refreshExpiration = parseInt(authFromOIDC.refresh_expiration) if (this.electron.enabled) { @@ -276,16 +275,19 @@ export class AuthService { } private authOIDCDesktopClient(): Observable { + // Retrieve authentication info from the desktop app return this.electron.authenticate().pipe( switchMap((auth: SyncClientAuthDto) => { if (!auth.clientId || auth.tokenHasExpired) { // The client must be registered, or the token must be renewed return this.http.post(API_SYNC_REGISTER_AUTH, auth).pipe( switchMap((externalAuth: SyncClientAuthRegistration) => { + // Store the clientId and the clientToken on the desktop app return this.electron.externalRegister(externalAuth).pipe( switchMap((success: boolean) => { if (success) { console.debug(`${this.authOIDCDesktopClient.name} - ${auth.clientId ? 'client was registered' : 'client token renewed'}`) + // Starts authentication return this.authDesktopClient() } else { this.logout(true, true) @@ -301,7 +303,7 @@ export class AuthService { }) ) } else { - // The client must be authenticated + // The client must be (re)authenticated return this.authDesktopClient() } }) diff --git a/frontend/src/app/electron/constants/events.ts b/frontend/src/app/electron/constants/events.ts index 5c0e3218..2503d81e 100644 --- a/frontend/src/app/electron/constants/events.ts +++ b/frontend/src/app/electron/constants/events.ts @@ -11,7 +11,13 @@ export const EVENT = { AUTHENTICATION: 'server-authentication', AUTHENTICATION_FAILED: 'server-authentication-failed', AUTHENTICATION_TOKEN_UPDATE: 'server-authentication-token-update', - AUTHENTICATION_TOKEN_EXPIRED: 'server-authentication-token-expired' + AUTHENTICATION_TOKEN_EXPIRED: 'server-authentication-token-expired', + SET_ACTIVE_AND_SHOW: 'server-set-active-and-show' + }, + // oidc authentication + OIDC: { + START_LOOPBACK: 'oidc-start-loopback', + WAIT_CALLBACK: 'oidc-wait-callback' }, // sync SYNC: { diff --git a/frontend/src/app/electron/electron.service.ts b/frontend/src/app/electron/electron.service.ts index dc9a5ede..e41705c9 100644 --- a/frontend/src/app/electron/electron.service.ts +++ b/frontend/src/app/electron/electron.service.ts @@ -9,6 +9,7 @@ import { toObservable } from '@angular/core/rxjs-interop' import { FileTask } from '@sync-in-server/backend/src/applications/files/models/file-task' import type { SyncClientAuthDto } from '@sync-in-server/backend/src/applications/sync/dtos/sync-client-auth.dto' import type { SyncClientAuthRegistration } from '@sync-in-server/backend/src/applications/sync/interfaces/sync-client-auth.interface' +import { OAuthDesktopPortParam } from '@sync-in-server/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants' import { combineLatest, from, map, Observable } from 'rxjs' import { NotificationModel } from '../applications/notifications/models/notification.model' import { CLIENT_APP_COUNTER, CLIENT_SCHEDULER_STATE } from '../applications/sync/constants/client' @@ -93,6 +94,21 @@ export class Electron { ) } + async startOIDCDesktopAuth(): Promise { + const desktop: { redirectPort: number } = await this.invoke(EVENT.OIDC.START_LOOPBACK) + console.debug(`Starting OIDC desktop auth with port ${desktop.redirectPort}`) + return desktop.redirectPort + } + + async waitOIDCDesktopCallbackParams(): Promise> { + console.debug('Waiting for OIDC desktop callback parameters') + return await this.invoke(EVENT.OIDC.WAIT_CALLBACK) + } + + genParamOIDCDesktopPort(desktopPort: number): string { + return `${OAuthDesktopPortParam}=${desktopPort}` + } + openPath(path: string) { this.send(EVENT.MISC.FILE_OPEN, path) } @@ -101,6 +117,10 @@ export class Electron { this.send(EVENT.MISC.URL_OPEN, url) } + setActiveAndShow() { + this.send(EVENT.SERVER.SET_ACTIVE_AND_SHOW) + } + private setSync(sync: SyncStatus) { if (sync.reportOnly) { this.store.clientSyncIsReporting.next(sync.state) From 9d187e06f848b6c56f0dfa6d904f22e71f485012 Mon Sep 17 00:00:00 2001 From: johaven Date: Mon, 2 Feb 2026 23:25:01 +0100 Subject: [PATCH 15/18] refactor(auth)!: rename method to provider in AuthConfig and replace authMethod with authProvider for naming consistency --- .../sync-clients-manager.service.spec.ts | 12 ++-- .../services/sync-clients-manager.service.ts | 10 +-- backend/src/authentication/auth.config.ts | 18 ++--- backend/src/authentication/auth.controller.ts | 12 ++-- backend/src/authentication/auth.module.ts | 6 +- backend/src/authentication/auth.service.ts | 2 +- .../guards/auth-basic.guard.spec.ts | 12 ++-- .../guards/auth-local.guard.spec.ts | 12 ++-- .../guards/auth-local.strategy.ts | 4 +- .../providers/auth-providers.utils.ts | 2 +- .../providers/ldap/auth-ldap.config.ts | 14 ++-- .../ldap/auth-provider-ldap.service.spec.ts | 68 +++++++++---------- .../ldap/auth-provider-ldap.service.ts | 4 +- .../providers/oidc/auth-oidc.config.ts | 14 ++-- ...module.ts => auth-provider-oidc.module.ts} | 2 +- .../oidc/auth-provider-oidc.service.ts | 4 +- .../src/configuration/config.environment.ts | 9 --- environment/environment.dist.yaml | 4 +- 18 files changed, 100 insertions(+), 109 deletions(-) rename backend/src/authentication/providers/oidc/{auth-method-oidc.module.ts => auth-provider-oidc.module.ts} (92%) diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts index 33bf2597..375ab4cd 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts @@ -54,7 +54,7 @@ describe(SyncClientsManager.name, () => { // Mocks let http: { axiosRef: jest.Mock } let authManager: { setCookies: jest.Mock; getTokens: jest.Mock } - let authMethod: { validateUser: jest.Mock } + let authProvider: { validateUser: jest.Mock } let usersManager: { fromUserId: jest.Mock; updateAccesses: jest.Mock } let syncQueries: { getOrCreateClient: jest.Mock @@ -94,7 +94,7 @@ describe(SyncClientsManager.name, () => { beforeAll(async () => { http = { axiosRef: jest.fn() } authManager = { setCookies: jest.fn(), getTokens: jest.fn() } - authMethod = { validateUser: jest.fn() } + authProvider = { validateUser: jest.fn() } usersManager = { fromUserId: jest.fn(), updateAccesses: jest.fn() } syncQueries = { getOrCreateClient: jest.fn(), @@ -119,7 +119,7 @@ describe(SyncClientsManager.name, () => { { provide: SyncQueries, useValue: syncQueries }, { provide: UsersManager, useValue: usersManager }, { provide: AuthManager, useValue: authManager }, - { provide: AuthProvider, useValue: authMethod }, + { provide: AuthProvider, useValue: authProvider }, { provide: AuthProvider2FA, useValue: {} } ] }).compile() @@ -162,12 +162,12 @@ describe(SyncClientsManager.name, () => { ['Unauthorized when credentials are invalid', null, HttpStatus.UNAUTHORIZED], ['Forbidden when user lacks DESKTOP_APP permission', { id: 10, login: 'john', havePermission: () => false }, HttpStatus.FORBIDDEN] ])('should throw %s', async (_label, user, status) => { - authMethod.validateUser.mockResolvedValue(user) + authProvider.validateUser.mockResolvedValue(user) await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status }) }) it('should return client token when registration succeeds', async () => { - authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true }) + authProvider.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true }) syncQueries.getOrCreateClient.mockResolvedValue('token-abc') const r = await service.register(baseDto as any, '1.2.3.4') @@ -176,7 +176,7 @@ describe(SyncClientsManager.name, () => { }) it('should throw Internal Server Error when persistence fails', async () => { - authMethod.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true }) + authProvider.validateUser.mockResolvedValue({ id: 10, login: 'john', havePermission: () => true }) syncQueries.getOrCreateClient.mockRejectedValue(new Error('db error')) await expect(service.register(baseDto as any, '1.2.3.4')).rejects.toMatchObject({ status: HttpStatus.INTERNAL_SERVER_ERROR }) }) diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.ts b/backend/src/applications/sync/services/sync-clients-manager.service.ts index 15f5222c..80506c9b 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.ts @@ -44,14 +44,14 @@ export class SyncClientsManager { constructor( private readonly http: HttpService, private readonly authManager: AuthManager, - private readonly authMethod: AuthProvider, - private readonly authMethod2Fa: AuthProvider2FA, + private readonly authProvider: AuthProvider, + private readonly authProvider2FA: AuthProvider2FA, private readonly usersManager: UsersManager, private readonly syncQueries: SyncQueries ) {} async register(clientRegistrationDto: SyncClientRegistrationDto, ip: string): Promise { - const user: UserModel = await this.authMethod.validateUser(clientRegistrationDto.login, clientRegistrationDto.password) + const user: UserModel = await this.authProvider.validateUser(clientRegistrationDto.login, clientRegistrationDto.password) if (!user) { this.logger.warn(`${this.register.name} - auth failed for user *${clientRegistrationDto.login}*`) throw new HttpException('Wrong login or password', HttpStatus.UNAUTHORIZED) @@ -66,10 +66,10 @@ export class SyncClientsManager { this.logger.warn(`${this.register.name} - missing two-fa code for user *${user.login}* (${user.id})`) throw new HttpException('Missing TWO-FA code', HttpStatus.UNAUTHORIZED) } - const authCode = this.authMethod2Fa.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret) + const authCode = this.authProvider2FA.validateTwoFactorCode(clientRegistrationDto.code, user.secrets.twoFaSecret) if (!authCode.success) { this.logger.warn(`${this.register.name} - two-fa code for *${user.login}* (${user.id}) - ${authCode.message}`) - const authRCode = await this.authMethod2Fa.validateRecoveryCode(user.id, clientRegistrationDto.code, user.secrets.recoveryCodes) + const authRCode = await this.authProvider2FA.validateRecoveryCode(user.id, clientRegistrationDto.code, user.secrets.recoveryCodes) if (!authRCode.success) { this.logger.warn(`${this.register.name} - two-fa recovery code for *${user.login}* (${user.id}) - ${authRCode.message}`) this.usersManager.updateAccesses(user, ip, false).catch((e: Error) => this.logger.error(`${this.register.name} - ${e}`)) diff --git a/backend/src/authentication/auth.config.ts b/backend/src/authentication/auth.config.ts index af21ab88..a89a576b 100644 --- a/backend/src/authentication/auth.config.ts +++ b/backend/src/authentication/auth.config.ts @@ -8,8 +8,8 @@ import { Exclude, Type } from 'class-transformer' import { IsDefined, IsEnum, IsIn, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { ACCESS_KEY, CSRF_KEY, REFRESH_KEY, WS_KEY } from './constants/auth' import { AUTH_PROVIDER } from './providers/auth-providers.constants' -import { AuthMethodLDAPConfig } from './providers/ldap/auth-ldap.config' -import { AuthMethodOIDCConfig } from './providers/oidc/auth-oidc.config' +import { AuthProviderLDAPConfig } from './providers/ldap/auth-ldap.config' +import { AuthProviderOIDCConfig } from './providers/oidc/auth-oidc.config' import { AuthMFAConfig } from './providers/two-fa/auth-two-fa.config' export class AuthTokenAccessConfig { @@ -85,7 +85,7 @@ export class AuthTokenConfig { export class AuthConfig { @IsString() @IsEnum(AUTH_PROVIDER) - method: AUTH_PROVIDER = AUTH_PROVIDER.MYSQL + provider: AUTH_PROVIDER = AUTH_PROVIDER.MYSQL @IsOptional() @IsString() @@ -109,17 +109,17 @@ export class AuthConfig { @Type(() => AuthTokenConfig) token: AuthTokenConfig - @ValidateIf((o: AuthConfig) => o.method === AUTH_PROVIDER.LDAP) + @ValidateIf((o: AuthConfig) => o.provider === AUTH_PROVIDER.LDAP) @IsDefined() @IsObject() @ValidateNested() - @Type(() => AuthMethodLDAPConfig) - ldap: AuthMethodLDAPConfig + @Type(() => AuthProviderLDAPConfig) + ldap: AuthProviderLDAPConfig - @ValidateIf((o: AuthConfig) => o.method === AUTH_PROVIDER.OIDC) + @ValidateIf((o: AuthConfig) => o.provider === AUTH_PROVIDER.OIDC) @IsDefined() @IsObject() @ValidateNested() - @Type(() => AuthMethodOIDCConfig) - oidc: AuthMethodOIDCConfig + @Type(() => AuthProviderOIDCConfig) + oidc: AuthProviderOIDCConfig } diff --git a/backend/src/authentication/auth.controller.ts b/backend/src/authentication/auth.controller.ts index 6eedc150..86bb1be3 100755 --- a/backend/src/authentication/auth.controller.ts +++ b/backend/src/authentication/auth.controller.ts @@ -31,7 +31,7 @@ import { TwoFaSetup, TwoFaVerifyResult } from './providers/two-fa/auth-two-fa.in export class AuthController { constructor( private readonly authManager: AuthManager, - private readonly authMethod2FA: AuthProvider2FA + private readonly authProvider2FA: AuthProvider2FA ) {} @Post(AUTH_ROUTE.LOGIN) @@ -80,21 +80,21 @@ export class AuthController { @UseGuards(UserRolesGuard) @UserHaveRole(USER_ROLE.USER) twoFaInit(@GetUser() user: UserModel): Promise { - return this.authMethod2FA.initTwoFactor(user) + return this.authProvider2FA.initTwoFactor(user) } @Post(`${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_ENABLE}`) @UseGuards(UserRolesGuard) @UserHaveRole(USER_ROLE.USER) twoFaEnable(@Body() body: TwoFaVerifyWithPasswordDto, @Req() req: FastifyAuthenticatedRequest): Promise { - return this.authMethod2FA.enableTwoFactor(body, req) + return this.authProvider2FA.enableTwoFactor(body, req) } @Post(`${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_DISABLE}`) @UseGuards(UserRolesGuard) @UserHaveRole(USER_ROLE.USER) twoFaDisable(@Body() body: TwoFaVerifyWithPasswordDto, @Req() req: FastifyAuthenticatedRequest): Promise { - return this.authMethod2FA.disableTwoFactor(body, req) + return this.authProvider2FA.disableTwoFactor(body, req) } @Post(`${AUTH_ROUTE.TWO_FA_BASE}/${AUTH_ROUTE.TWO_FA_LOGIN_VERIFY}`) @@ -105,7 +105,7 @@ export class AuthController { @Req() req: FastifyAuthenticatedRequest, @Res({ passthrough: true }) res: FastifyReply ): Promise { - const [authStatus, user] = await this.authMethod2FA.verify(body, req, true) + const [authStatus, user] = await this.authProvider2FA.verify(body, req, true) if (authStatus.success) { const loginResponseDto = await this.authManager.setCookies(user, res) // clear the temporary 2FA cookie @@ -119,6 +119,6 @@ export class AuthController { @UseGuards(UserRolesGuard, AuthTwoFaGuard) @UserHaveRole(USER_ROLE.ADMINISTRATOR) twoFaReset(@Param('id', ParseIntPipe) userId: number): Promise { - return this.authMethod2FA.adminResetUserTwoFa(userId) + return this.authProvider2FA.adminResetUserTwoFa(userId) } } diff --git a/backend/src/authentication/auth.module.ts b/backend/src/authentication/auth.module.ts index 89ed19fd..8d81b3f5 100755 --- a/backend/src/authentication/auth.module.ts +++ b/backend/src/authentication/auth.module.ts @@ -25,7 +25,7 @@ import { AuthTokenRefreshStrategy } from './guards/auth-token-refresh.strategy' import { AUTH_PROVIDER } from './providers/auth-providers.constants' import { AuthProvider } from './providers/auth-providers.models' import { selectAuthProvider } from './providers/auth-providers.utils' -import { AuthMethodOIDCModule } from './providers/oidc/auth-method-oidc.module' +import { AuthProviderOIDCModule } from './providers/oidc/auth-provider-oidc.module' import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service' @Global() @@ -34,7 +34,7 @@ import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service JwtModule.register({ global: true }), UsersModule, PassportModule, - ...(configuration.auth.method === AUTH_PROVIDER.OIDC ? [AuthMethodOIDCModule] : []) + ...(configuration.auth.provider === AUTH_PROVIDER.OIDC ? [AuthProviderOIDCModule] : []) ], controllers: [AuthController], providers: [ @@ -53,7 +53,7 @@ import { AuthProvider2FA } from './providers/two-fa/auth-provider-two-fa.service AuthAnonymousStrategy, AuthManager, AuthProvider2FA, - selectAuthProvider(configuration.auth.method) + selectAuthProvider(configuration.auth.provider) ], exports: [AuthManager, AuthProvider, AuthProvider2FA] }) diff --git a/backend/src/authentication/auth.service.ts b/backend/src/authentication/auth.service.ts index 35365420..b373dbfa 100755 --- a/backend/src/authentication/auth.service.ts +++ b/backend/src/authentication/auth.service.ts @@ -142,7 +142,7 @@ export class AuthManager { } authSettings(): AuthOIDCSettings | false { - if (configuration.auth.method !== AUTH_PROVIDER.OIDC) { + if (configuration.auth.provider !== AUTH_PROVIDER.OIDC) { return false } return { diff --git a/backend/src/authentication/guards/auth-basic.guard.spec.ts b/backend/src/authentication/guards/auth-basic.guard.spec.ts index 28d7d5a9..85967464 100644 --- a/backend/src/authentication/guards/auth-basic.guard.spec.ts +++ b/backend/src/authentication/guards/auth-basic.guard.spec.ts @@ -19,7 +19,7 @@ import { AuthBasicStrategy } from './auth-basic.strategy' describe(AuthBasicGuard.name, () => { let authBasicGuard: AuthBasicGuard let authBasicStrategy: AuthBasicStrategy - let authMethod: AuthProvider + let authProvider: AuthProvider let cache: Cache let userTest: UserModel let encodedAuth: string @@ -56,7 +56,7 @@ describe(AuthBasicGuard.name, () => { authBasicGuard = module.get(AuthBasicGuard) authBasicStrategy = module.get(AuthBasicStrategy) - authMethod = module.get(AuthProvider) + authProvider = module.get(AuthProvider) cache = module.get(Cache) userTest = new UserModel(generateUserTest(), false) encodedAuth = Buffer.from(`${userTest.login}:${userTest.password}`).toString('base64') @@ -66,7 +66,7 @@ describe(AuthBasicGuard.name, () => { it('should be defined', () => { expect(authBasicGuard).toBeDefined() expect(authBasicStrategy).toBeDefined() - expect(authMethod).toBeDefined() + expect(authProvider).toBeDefined() expect(cache).toBeDefined() expect(encodedAuth).toBeDefined() expect(userTest).toBeDefined() @@ -74,7 +74,7 @@ describe(AuthBasicGuard.name, () => { }) it('should validate the user authentication', async () => { - authMethod.validateUser = jest.fn().mockReturnValueOnce(userTest) + authProvider.validateUser = jest.fn().mockReturnValueOnce(userTest) context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, headers: { authorization: `Basic ${encodedAuth}` } @@ -88,7 +88,7 @@ describe(AuthBasicGuard.name, () => { const userWithColonPassword = new UserModel({ ...generateUserTest(), password: passwordWithColon }, false) const encodedAuthWithColon = Buffer.from(`${userWithColonPassword.login}:${passwordWithColon}`).toString('base64') - authMethod.validateUser = jest.fn().mockImplementation((login: string, password: string) => { + authProvider.validateUser = jest.fn().mockImplementation((login: string, password: string) => { expect(login).toBe(userWithColonPassword.login) expect(password).toBe(passwordWithColon) return userWithColonPassword @@ -121,7 +121,7 @@ describe(AuthBasicGuard.name, () => { it('should not validate the user authentication when cache returns undefined and database return null', async () => { cache.get = jest.fn().mockReturnValueOnce(undefined) - authMethod.validateUser = jest.fn().mockReturnValueOnce(null) + authProvider.validateUser = jest.fn().mockReturnValueOnce(null) jest.spyOn(cache, 'set').mockRejectedValueOnce(new Error('cache failed')) context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, diff --git a/backend/src/authentication/guards/auth-local.guard.spec.ts b/backend/src/authentication/guards/auth-local.guard.spec.ts index 443267a9..f8c6cf23 100644 --- a/backend/src/authentication/guards/auth-local.guard.spec.ts +++ b/backend/src/authentication/guards/auth-local.guard.spec.ts @@ -16,7 +16,7 @@ import { AuthLocalStrategy } from './auth-local.strategy' describe(AuthLocalGuard.name, () => { let authLocalGuard: AuthLocalGuard - let authMethod: AuthProvider + let authProvider: AuthProvider let userTest: UserModel let context: DeepMocked @@ -36,19 +36,19 @@ describe(AuthLocalGuard.name, () => { }).compile() authLocalGuard = module.get(AuthLocalGuard) - authMethod = module.get(AuthProvider) + authProvider = module.get(AuthProvider) userTest = new UserModel(generateUserTest(), false) context = createMock() }) it('should be defined', () => { expect(authLocalGuard).toBeDefined() - expect(authMethod).toBeDefined() + expect(authProvider).toBeDefined() expect(userTest).toBeDefined() }) it('should validate the user authentication', async () => { - authMethod.validateUser = jest.fn().mockReturnValueOnce(userTest) + authProvider.validateUser = jest.fn().mockReturnValueOnce(userTest) context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, body: { @@ -62,7 +62,7 @@ describe(AuthLocalGuard.name, () => { it('should not validate the user authentication', async () => { userTest.password = 'password' - authMethod.validateUser = jest.fn().mockReturnValueOnce(null) + authProvider.validateUser = jest.fn().mockReturnValueOnce(null) context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, body: { @@ -74,7 +74,7 @@ describe(AuthLocalGuard.name, () => { }) it('should throw error due to malformed body', async () => { - authMethod.validateUser = jest.fn().mockReturnValueOnce(null) + authProvider.validateUser = jest.fn().mockReturnValueOnce(null) context.switchToHttp().getRequest.mockReturnValue({ raw: { user: '' }, body: null diff --git a/backend/src/authentication/guards/auth-local.strategy.ts b/backend/src/authentication/guards/auth-local.strategy.ts index b083e720..2d11cc6d 100755 --- a/backend/src/authentication/guards/auth-local.strategy.ts +++ b/backend/src/authentication/guards/auth-local.strategy.ts @@ -15,7 +15,7 @@ import { AuthProvider } from '../providers/auth-providers.models' @Injectable() export class AuthLocalStrategy extends PassportStrategy(Strategy, 'local') implements AbstractStrategy { constructor( - private readonly authMethod: AuthProvider, + private readonly authProvider: AuthProvider, private readonly logger: PinoLogger ) { super({ usernameField: 'login', passwordField: 'password', passReqToCallback: true }) @@ -25,7 +25,7 @@ export class AuthLocalStrategy extends PassportStrategy(Strategy, 'local') imple loginOrEmail = loginOrEmail.trim() password = password.trim() this.logger.assign({ user: loginOrEmail }) - const user: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip) + const user: UserModel = await this.authProvider.validateUser(loginOrEmail, password, req.ip) if (user) { user.removePassword() return user diff --git a/backend/src/authentication/providers/auth-providers.utils.ts b/backend/src/authentication/providers/auth-providers.utils.ts index c52419bd..0404cc10 100644 --- a/backend/src/authentication/providers/auth-providers.utils.ts +++ b/backend/src/authentication/providers/auth-providers.utils.ts @@ -15,7 +15,7 @@ import { AuthProviderOIDC } from './oidc/auth-provider-oidc.service' export function selectAuthProvider(provider: AUTH_PROVIDER): Provider { switch (provider) { case AUTH_PROVIDER.OIDC: - // AuthMethodOIDC is already provided by AuthMethodOIDCModule + // `AuthProviderOIDC` is already provided by `AuthProviderOIDCModule` return { provide: AuthProvider, useExisting: AuthProviderOIDC } case AUTH_PROVIDER.LDAP: diff --git a/backend/src/authentication/providers/ldap/auth-ldap.config.ts b/backend/src/authentication/providers/ldap/auth-ldap.config.ts index 237a446f..fe2bb535 100644 --- a/backend/src/authentication/providers/ldap/auth-ldap.config.ts +++ b/backend/src/authentication/providers/ldap/auth-ldap.config.ts @@ -21,7 +21,7 @@ import { import { USER_PERMISSION } from '../../../applications/users/constants/user' import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' -export class AuthMethodLDAPAttributesConfig { +export class AuthProviderLDAPAttributesConfig { @IsOptional() @Transform(({ value }) => value || LDAP_LOGIN_ATTR.UID) @IsEnum(LDAP_LOGIN_ATTR) @@ -33,7 +33,7 @@ export class AuthMethodLDAPAttributesConfig { email: string = LDAP_COMMON_ATTR.MAIL } -export class AuthMethodLDAPOptionsConfig { +export class AuthProviderLDAPOptionsConfig { @IsOptional() @IsString() adminGroup?: string @@ -52,7 +52,7 @@ export class AuthMethodLDAPOptionsConfig { enablePasswordAuthFallback? = true } -export class AuthMethodLDAPConfig { +export class AuthProviderLDAPConfig { @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value)) @ArrayNotEmpty() @IsArray() @@ -71,8 +71,8 @@ export class AuthMethodLDAPConfig { @IsNotEmptyObject() @IsObject() @ValidateNested() - @Type(() => AuthMethodLDAPAttributesConfig) - attributes: AuthMethodLDAPAttributesConfig = new AuthMethodLDAPAttributesConfig() + @Type(() => AuthProviderLDAPAttributesConfig) + attributes: AuthProviderLDAPAttributesConfig = new AuthProviderLDAPAttributesConfig() @IsOptional() @IsString() @@ -86,6 +86,6 @@ export class AuthMethodLDAPConfig { @IsNotEmptyObject() @IsObject() @ValidateNested() - @Type(() => AuthMethodLDAPOptionsConfig) - options: AuthMethodLDAPOptionsConfig = new AuthMethodLDAPOptionsConfig() + @Type(() => AuthProviderLDAPOptionsConfig) + options: AuthProviderLDAPOptionsConfig = new AuthProviderLDAPOptionsConfig() } diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts index 2d6e67da..5f189d61 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts @@ -14,7 +14,7 @@ import { AdminUsersManager } from '../../../applications/users/services/admin-us import { UsersManager } from '../../../applications/users/services/users-manager.service' import * as commonFunctions from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' -import type { AuthMethodLDAPConfig } from './auth-ldap.config' +import type { AuthProviderLDAPConfig } from './auth-ldap.config' import { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' import { AuthProviderLDAP } from './auth-provider-ldap.service' @@ -52,17 +52,17 @@ const ldapClient = { ;(Client as Mocked).mockImplementation(() => ldapClient) describe(AuthProviderLDAP.name, () => { - let authMethodLdapService: AuthProviderLDAP + let authProviderLDAP: AuthProviderLDAP let usersManager: Mocked let adminUsersManager: Mocked - type LdapConfigOverrides = Omit, 'attributes' | 'options'> & { - attributes?: Partial - options?: Partial + type LdapConfigOverrides = Omit, 'attributes' | 'options'> & { + attributes?: Partial + options?: Partial } const setLdapConfig = (overrides: LdapConfigOverrides = {}) => { - const base: AuthMethodLDAPConfig = { + const base: AuthProviderLDAPConfig = { servers: ['ldap://localhost:389'], attributes: { login: LDAP_LOGIN_ATTR.UID, email: LDAP_COMMON_ATTR.MAIL }, baseDN: 'ou=people,dc=example,dc=org', @@ -73,15 +73,15 @@ describe(AuthProviderLDAP.name, () => { enablePasswordAuthFallback: true } } - const next: AuthMethodLDAPConfig = { + const next: AuthProviderLDAPConfig = { ...base, ...overrides, attributes: { ...base.attributes, ...(overrides.attributes || {}) }, options: { ...base.options, ...(overrides.options || {}) } } configuration.auth.ldap = next - ;(authMethodLdapService as any).ldapConfig = next - ;(authMethodLdapService as any).isAD = [LDAP_LOGIN_ATTR.SAM, LDAP_LOGIN_ATTR.UPN].includes(next.attributes.login) + ;(authProviderLDAP as any).ldapConfig = next + ;(authProviderLDAP as any).isAD = [LDAP_LOGIN_ATTR.SAM, LDAP_LOGIN_ATTR.UPN].includes(next.attributes.login) } const mockBindResolve = () => { @@ -123,7 +123,7 @@ describe(AuthProviderLDAP.name, () => { }).compile() module.useLogger(['fatal']) - authMethodLdapService = module.get(AuthProviderLDAP) + authProviderLDAP = module.get(AuthProviderLDAP) adminUsersManager = module.get>(AdminUsersManager) usersManager = module.get>(UsersManager) }) @@ -139,7 +139,7 @@ describe(AuthProviderLDAP.name, () => { }) it('should be defined', () => { - expect(authMethodLdapService).toBeDefined() + expect(authProviderLDAP).toBeDefined() expect(usersManager).toBeDefined() expect(adminUsersManager).toBeDefined() expect(ldapClient).toBeDefined() @@ -151,7 +151,7 @@ describe(AuthProviderLDAP.name, () => { const dbAuthResult: any = { ...guestUser, token: 'jwt' } usersManager.logUser.mockResolvedValue(dbAuthResult) - const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1') + const res = await authProviderLDAP.validateUser('guest1', 'pass', '127.0.0.1') expect(res).toEqual(dbAuthResult) expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1', undefined) @@ -163,7 +163,7 @@ describe(AuthProviderLDAP.name, () => { usersManager.findUser.mockResolvedValue(user) usersManager.logUser.mockResolvedValue(user) - const res = await authMethodLdapService.validateUser('john', 'app-password', '10.0.0.2', 'webdav' as any) + const res = await authProviderLDAP.validateUser('john', 'app-password', '10.0.0.2', 'webdav' as any) expect(res).toBe(user) expect(usersManager.logUser).toHaveBeenCalledWith(user, 'app-password', '10.0.0.2', 'webdav') @@ -172,9 +172,9 @@ describe(AuthProviderLDAP.name, () => { it('should throw FORBIDDEN for locked account', async () => { usersManager.findUser.mockResolvedValue({ login: 'john', isGuest: false, isActive: false } as UserModel) - const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) + const loggerErrorSpy = jest.spyOn(authProviderLDAP['logger'], 'error').mockImplementation(() => undefined as any) - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i) + await expect(authProviderLDAP.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i) expect(loggerErrorSpy).toHaveBeenCalled() }) @@ -183,7 +183,7 @@ describe(AuthProviderLDAP.name, () => { usersManager.findUser.mockResolvedValue(existingUser) mockBindRejectInvalid('invalid credentials') - const res = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1') + const res = await authProviderLDAP.validateUser('john', 'badpwd', '10.0.0.1') expect(res).toBeNull() expect(usersManager.logUser).not.toHaveBeenCalled() @@ -196,12 +196,12 @@ describe(AuthProviderLDAP.name, () => { mockBindResolve() mockSearchEntries([]) - const resA = await authMethodLdapService.validateUser('john', 'pwd') + const resA = await authProviderLDAP.validateUser('john', 'pwd') expect(resA).toBeNull() ldapClient.search.mockRejectedValue(new Error('search failed')) - const resB = await authMethodLdapService.validateUser('john', 'pwd') + const resB = await authProviderLDAP.validateUser('john', 'pwd') expect(resB).toBeNull() expect(usersManager.updateAccesses).not.toHaveBeenCalled() @@ -215,7 +215,7 @@ describe(AuthProviderLDAP.name, () => { ldapClient.bind.mockRejectedValue({ errors: [err] }) ldapClient.unbind.mockResolvedValue(undefined) - const res = await authMethodLdapService.validateUser('john', 'pwd', '10.0.0.3') + const res = await authProviderLDAP.validateUser('john', 'pwd', '10.0.0.3') expect(res).toBe(existingUser) expect(usersManager.logUser).toHaveBeenCalledWith(existingUser, 'pwd', '10.0.0.3') @@ -229,7 +229,7 @@ describe(AuthProviderLDAP.name, () => { ldapClient.bind.mockRejectedValue({ errors: [err] }) ldapClient.unbind.mockResolvedValue(undefined) - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service error/i) + await expect(authProviderLDAP.validateUser('john', 'pwd')).rejects.toThrow(/authentication service error/i) }) it('should allow admin local fallback when LDAP is unavailable even if fallback is disabled', async () => { @@ -241,7 +241,7 @@ describe(AuthProviderLDAP.name, () => { ldapClient.bind.mockRejectedValue({ errors: [err] }) ldapClient.unbind.mockResolvedValue(undefined) - const res = await authMethodLdapService.validateUser('john', 'pwd') + const res = await authProviderLDAP.validateUser('john', 'pwd') expect(res).toBe(existingUser) expect(usersManager.logUser).toHaveBeenCalledWith(existingUser, 'pwd', undefined) @@ -251,9 +251,9 @@ describe(AuthProviderLDAP.name, () => { usersManager.findUser.mockResolvedValue(null) mockBindResolve() mockSearchEntries([{ uid: 'jane', cn: 'Jane Doe', mail: undefined }]) - const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any) + const loggerErrorSpy = jest.spyOn(authProviderLDAP['logger'], 'error').mockImplementation(() => undefined as any) - const res = await authMethodLdapService.validateUser('jane', 'pwd') + const res = await authProviderLDAP.validateUser('jane', 'pwd') expect(res).toBeNull() expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled() @@ -263,12 +263,12 @@ describe(AuthProviderLDAP.name, () => { it('should throw UNAUTHORIZED when autoCreateUser is disabled', async () => { setLdapConfig({ options: { autoCreateUser: false } }) usersManager.findUser.mockResolvedValue(null) - const checkAuthSpy = jest.spyOn(authMethodLdapService as any, 'checkAuth').mockResolvedValue({ + const checkAuthSpy = jest.spyOn(authProviderLDAP as any, 'checkAuth').mockResolvedValue({ uid: 'john', mail: 'john@example.org' }) - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/user not found/i) + await expect(authProviderLDAP.validateUser('john', 'pwd')).rejects.toThrow(/user not found/i) checkAuthSpy.mockRestore() }) @@ -294,7 +294,7 @@ describe(AuthProviderLDAP.name, () => { adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser) usersManager.fromUserId.mockResolvedValue(createdUser) - const res = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10') + const res = await authProviderLDAP.validateUser('john', 'pwd', '192.168.1.10') expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith( { @@ -320,7 +320,7 @@ describe(AuthProviderLDAP.name, () => { mockSearchEntries([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }]) jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(true) - await authMethodLdapService.validateUser('john', 'pwd') + await authProviderLDAP.validateUser('john', 'pwd') expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled() const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0][1] @@ -336,7 +336,7 @@ describe(AuthProviderLDAP.name, () => { const compareSpy = jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false) const splitSpy = jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'Jane', lastName: 'Doe' }) - const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2') + const res = await authProviderLDAP.validateUser('john', 'new-plain-password', '127.0.0.2') expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith( 6, @@ -362,21 +362,21 @@ describe(AuthProviderLDAP.name, () => { mockBindResolve() mockSearchEntries([{ uid: 'jane', cn: 'Jane Doe', mail: 'jane@example.org' }]) - await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i) + await expect(authProviderLDAP.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i) }) it('should build LDAP logins and filters for AD and standard LDAP', () => { setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.UPN }, upnSuffix: 'sync-in.com', filter: '(memberOf=cn=staff)' }) - const adLogin = (authMethodLdapService as any).buildLdapLogin('john') + const adLogin = (authProviderLDAP as any).buildLdapLogin('john') expect(adLogin).toBe('john@sync-in.com') - const adFilter = (authMethodLdapService as any).buildUserFilter('SYNC-IN\\john', '(memberOf=cn=staff)') + const adFilter = (authProviderLDAP as any).buildUserFilter('SYNC-IN\\john', '(memberOf=cn=staff)') expect(adFilter).toContain('(sAMAccountName=john)') expect(adFilter).toContain('(userPrincipalName=john)') expect(adFilter).toContain('(mail=john)') expect(adFilter).toContain('(memberOf=cn=staff)') setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.UID }, filter: '(department=IT)' }) - const ldapFilter = (authMethodLdapService as any).buildUserFilter('john', '(department=IT)') + const ldapFilter = (authProviderLDAP as any).buildUserFilter('john', '(department=IT)') expect(ldapFilter).toContain('(uid=john)') expect(ldapFilter).toContain('(cn=john)') expect(ldapFilter).toContain('(mail=john)') @@ -390,7 +390,7 @@ describe(AuthProviderLDAP.name, () => { memberOf: ['CN=Admins,OU=Groups,DC=example,DC=org', 'CN=Staff,OU=Groups,DC=example,DC=org'] } - const normalized = (authMethodLdapService as any).convertToLdapUserEntry(entry) + const normalized = (authProviderLDAP as any).convertToLdapUserEntry(entry) expect(normalized.uid).toBe('john') expect(normalized.mail).toBe('john@example.org') @@ -399,7 +399,7 @@ describe(AuthProviderLDAP.name, () => { it('should build LDAP logins for SAM account name when netbiosName is set', () => { setLdapConfig({ attributes: { login: LDAP_LOGIN_ATTR.SAM }, netbiosName: 'SYNC' }) - const samLogin = (authMethodLdapService as any).buildLdapLogin('john') + const samLogin = (authProviderLDAP as any).buildLdapLogin('john') expect(samLogin).toBe('SYNC\\john') }) }) diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts index 58ceaa38..cd012f79 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts @@ -16,7 +16,7 @@ import { comparePassword, splitFullName } from '../../../common/functions' import { configuration } from '../../../configuration/config.environment' import type { AUTH_SCOPE } from '../../constants/scope' import { AuthProvider } from '../auth-providers.models' -import type { AuthMethodLDAPConfig } from './auth-ldap.config' +import type { AuthProviderLDAPConfig } from './auth-ldap.config' import { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './auth-ldap.constants' type LdapUserEntry = Entry & Record @@ -24,7 +24,7 @@ type LdapUserEntry = Entry & Record AuthMethodOIDCOptionsConfig) - options: AuthMethodOIDCOptionsConfig = new AuthMethodOIDCOptionsConfig() + @Type(() => AuthProviderOIDCOptionsConfig) + options: AuthProviderOIDCOptionsConfig = new AuthProviderOIDCOptionsConfig() @IsDefined() @IsNotEmptyObject() @IsObject() @ValidateNested() - @Type(() => AuthMethodOIDCSecurityConfig) - security: AuthMethodOIDCSecurityConfig = new AuthMethodOIDCSecurityConfig() + @Type(() => AuthProviderOIDCSecurityConfig) + security: AuthProviderOIDCSecurityConfig = new AuthProviderOIDCSecurityConfig() } diff --git a/backend/src/authentication/providers/oidc/auth-method-oidc.module.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts similarity index 92% rename from backend/src/authentication/providers/oidc/auth-method-oidc.module.ts rename to backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts index d08660bf..c3bdd210 100644 --- a/backend/src/authentication/providers/oidc/auth-method-oidc.module.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts @@ -13,4 +13,4 @@ import { AuthProviderOIDC } from './auth-provider-oidc.service' providers: [AuthProviderOIDC], exports: [AuthProviderOIDC] }) -export class AuthMethodOIDCModule {} +export class AuthProviderOIDCModule {} diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index b8f100bd..c74a2c44 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -37,13 +37,13 @@ import { TOKEN_TYPE } from '../../interfaces/token.interface' import { AUTH_PROVIDER } from '../auth-providers.constants' import { AuthProvider } from '../auth-providers.models' import { OAuthDesktopCallBackURI, OAuthDesktopLoopbackPorts, OAuthDesktopPortParam } from './auth-oidc-desktop.constants' -import type { AuthMethodOIDCConfig } from './auth-oidc.config' +import type { AuthProviderOIDCConfig } from './auth-oidc.config' import { OAuthCookie, OAuthCookieSettings, OAuthTokenEndpoint } from './auth-oidc.constants' @Injectable() export class AuthProviderOIDC implements AuthProvider { private readonly logger = new Logger(AuthProviderOIDC.name) - private readonly oidcConfig: AuthMethodOIDCConfig = configuration.auth.oidc + private readonly oidcConfig: AuthProviderOIDCConfig = configuration.auth.oidc private frontendBaseUrl: string private config: Configuration = null diff --git a/backend/src/configuration/config.environment.ts b/backend/src/configuration/config.environment.ts index e7398c5b..4c9a535e 100755 --- a/backend/src/configuration/config.environment.ts +++ b/backend/src/configuration/config.environment.ts @@ -47,14 +47,5 @@ function loadConfiguration(): GlobalConfig { config.applications.files.usersPath = join(config.applications.files.dataPath, 'users') config.applications.files.spacesPath = join(config.applications.files.dataPath, 'spaces') config.applications.files.tmpPath = join(config.applications.files.dataPath, 'tmp') - // DEPRECATIONS - // ldap.adminGroup → ldap.options.adminGroup - if (typeof config.auth.ldap['adminGroup'] === 'string' && config.auth.ldap['adminGroup'].length > 0) { - config.auth.ldap.options.adminGroup = config.auth.ldap['adminGroup'] - console.warn( - '[DEPRECATED][CONFIGURATION] auth.ldap.adminGroup is deprecated and will be removed in a future version. ' + - 'Please use auth.ldap.options.adminGroup instead.' - ) - } return transformAndValidate(GlobalConfig, config, { exposeDefaultValues: true }, { skipMissingProperties: false }) } diff --git a/environment/environment.dist.yaml b/environment/environment.dist.yaml index c6021e1c..d0a5a01b 100755 --- a/environment/environment.dist.yaml +++ b/environment/environment.dist.yaml @@ -77,9 +77,9 @@ mail: # default: `false` debug: false auth: - # adapter : `mysql` | `ldap` | `oidc` + # provider : `mysql` | `ldap` | `oidc` # default: `mysql` - method: mysql + provider: mysql # Key used to encrypt user secret keys in the database # Optional but strongly recommended # Warning: do not change or remove the encryption key after MFA activation, or the codes will become invalid From 1460223a963b56568b2064ee5f4e588ffeda6a08 Mon Sep 17 00:00:00 2001 From: johaven Date: Mon, 2 Feb 2026 23:42:48 +0100 Subject: [PATCH 16/18] chore(docker): update image versions for sync-in services --- .../docker-compose.sync-in-desktop-releases.yaml | 2 +- docker/docker-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/config/sync-in-desktop-releases/docker-compose.sync-in-desktop-releases.yaml b/docker/config/sync-in-desktop-releases/docker-compose.sync-in-desktop-releases.yaml index a252f439..97c2cb1e 100644 --- a/docker/config/sync-in-desktop-releases/docker-compose.sync-in-desktop-releases.yaml +++ b/docker/config/sync-in-desktop-releases/docker-compose.sync-in-desktop-releases.yaml @@ -1,7 +1,7 @@ services: sync_in_desktop_releases: profiles: [ "releases" ] - image: syncin/desktop-releases:1 + image: syncin/desktop-releases:2 container_name: sync-in-desktop-releases user: "${PUID:-8888}:${PGID:-8888}" volumes: diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 42c2bd33..f8d9eda3 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -7,7 +7,7 @@ name: sync-in services: sync_in: - image: syncin/server:1 + image: syncin/server:2 container_name: sync-in restart: always environment: From 1fe9454cd2ad3e7e6ed0c175d889b466e89d0a4a Mon Sep 17 00:00:00 2001 From: johaven Date: Wed, 4 Feb 2026 00:09:13 +0100 Subject: [PATCH 17/18] chore: remove redundant license --- LICENSE | 2 +- backend/eslint.config.mjs | 6 ------ backend/src/app.bootstrap.ts | 6 ------ backend/src/app.constants.ts | 6 ------ backend/src/app.e2e-spec.ts | 6 ------ backend/src/app.functions.ts | 6 ------ backend/src/app.module.ts | 6 ------ backend/src/app.service.spec.ts | 9 ++------- backend/src/app.service.ts | 6 ------ backend/src/applications/admin/admin.module.ts | 6 ------ backend/src/applications/admin/constants/routes.ts | 6 ------ .../admin/interfaces/check-update.interfaces.ts | 6 ------ .../admin/services/admin-scheduler.service.ts | 6 ------ .../applications/admin/services/admin.service.spec.ts | 6 ------ .../src/applications/admin/services/admin.service.ts | 6 ------ backend/src/applications/admin/utils/check-update.ts | 6 ------ backend/src/applications/applications.config.ts | 6 ------ backend/src/applications/applications.constants.ts | 6 ------ backend/src/applications/applications.module.ts | 6 ------ .../applications/comments/comments.controller.spec.ts | 6 ------ .../src/applications/comments/comments.controller.ts | 6 ------ backend/src/applications/comments/comments.module.ts | 6 ------ backend/src/applications/comments/constants/routes.ts | 5 ----- backend/src/applications/comments/dto/comment.dto.ts | 6 ------ .../comments/interfaces/comment-recent.interface.ts | 6 ------ .../comments/schemas/comment.interface.ts | 6 ------ .../applications/comments/schemas/comments.schema.ts | 6 ------ .../services/comments-manager.service.spec.ts | 6 ------ .../comments/services/comments-manager.service.ts | 6 ------ .../comments/services/comments-queries.service.ts | 6 ------ .../adapters/files-indexer-mysql.service.spec.ts | 6 ------ .../files/adapters/files-indexer-mysql.service.ts | 6 ------ backend/src/applications/files/constants/cache.ts | 6 ------ backend/src/applications/files/constants/compress.ts | 6 ------ backend/src/applications/files/constants/files.ts | 6 ------ backend/src/applications/files/constants/indexing.ts | 6 ------ .../src/applications/files/constants/operations.ts | 6 ------ backend/src/applications/files/constants/routes.ts | 6 ------ backend/src/applications/files/constants/samples.ts | 6 ------ .../src/applications/files/dto/file-operations.dto.ts | 6 ------ .../src/applications/files/events/file-task-event.ts | 6 ------ .../applications/files/files-tasks.controller.spec.ts | 6 ------ .../src/applications/files/files-tasks.controller.ts | 6 ------ backend/src/applications/files/files.config.ts | 6 ------ .../src/applications/files/files.controller.spec.ts | 6 ------ backend/src/applications/files/files.controller.ts | 6 ------ backend/src/applications/files/files.module.ts | 6 ------ .../files/interfaces/file-db-props.interface.ts | 6 ------ .../files/interfaces/file-lock.interface.ts | 6 ------ .../applications/files/interfaces/file-parse-index.ts | 6 ------ .../files/interfaces/file-props.interface.ts | 6 ------ .../interfaces/file-recent-location.interface.ts | 6 ------ .../files/interfaces/file-space.interface.ts | 6 ------ .../files/interfaces/file-tree.interface.ts | 6 ------ backend/src/applications/files/models/file-error.ts | 6 ------ .../src/applications/files/models/file-lock-error.ts | 6 ------ backend/src/applications/files/models/file-task.ts | 6 ------ .../src/applications/files/models/files-indexer.ts | 6 ------ .../collabora-online-environment.decorator.ts | 6 ------ .../collabora-online-manager.service.spec.ts | 6 ------ .../collabora-online-manager.service.ts | 6 ------ .../collabora-online/collabora-online.config.ts | 6 ------ .../collabora-online/collabora-online.constants.ts | 6 ------ .../collabora-online.controller.spec.ts | 6 ------ .../collabora-online/collabora-online.controller.ts | 6 ------ .../modules/collabora-online/collabora-online.dtos.ts | 6 ------ .../collabora-online/collabora-online.guard.spec.ts | 6 ------ .../collabora-online/collabora-online.guard.ts | 6 ------ .../collabora-online/collabora-online.interface.ts | 6 ------ .../collabora-online/collabora-online.module.ts | 6 ------ .../collabora-online/collabora-online.routes.ts | 6 ------ .../collabora-online/collabora-online.strategy.ts | 6 ------ .../collabora-online/collabora-online.utils.ts | 6 ------ .../only-office/only-office-environment.decorator.ts | 6 ------ .../only-office/only-office-manager.service.spec.ts | 6 ------ .../only-office/only-office-manager.service.ts | 6 ------ .../files/modules/only-office/only-office.config.ts | 6 ------ .../modules/only-office/only-office.constants.ts | 6 ------ .../only-office/only-office.controller.spec.ts | 6 ------ .../modules/only-office/only-office.controller.ts | 6 ------ .../files/modules/only-office/only-office.dtos.ts | 6 ------ .../modules/only-office/only-office.guard.spec.ts | 6 ------ .../files/modules/only-office/only-office.guard.ts | 6 ------ .../modules/only-office/only-office.interface.ts | 6 ------ .../files/modules/only-office/only-office.module.ts | 6 ------ .../files/modules/only-office/only-office.routes.ts | 6 ------ .../files/modules/only-office/only-office.strategy.ts | 6 ------ .../files/schemas/file-content.interface.ts | 6 ------ .../files/schemas/file-recent.interface.ts | 6 ------ .../src/applications/files/schemas/file.interface.ts | 6 ------ .../files/schemas/files-content.schema.ts | 6 ------ .../files/schemas/files-recents.schema.ts | 6 ------ .../src/applications/files/schemas/files.schema.ts | 6 ------ .../files/services/files-content-manager.service.ts | 6 ------ .../files/services/files-lock-manager.service.spec.ts | 6 ------ .../files/services/files-lock-manager.service.ts | 6 ------ .../files/services/files-manager.service.spec.ts | 6 ------ .../files/services/files-manager.service.ts | 6 ------ .../files/services/files-methods.service.spec.ts | 6 ------ .../files/services/files-methods.service.ts | 6 ------ .../files/services/files-parser.service.spec.ts | 6 ------ .../files/services/files-parser.service.ts | 6 ------ .../files/services/files-queries.service.ts | 6 ------ .../files/services/files-recents.service.spec.ts | 6 ------ .../files/services/files-recents.service.ts | 6 ------ .../files/services/files-scheduler.service.ts | 6 ------ .../services/files-search-manager.service.spec.ts | 6 ------ .../files/services/files-search-manager.service.ts | 6 ------ .../services/files-tasks-manager.service.spec.ts | 6 ------ .../files/services/files-tasks-manager.service.ts | 6 ------ .../files/utils/doc-textify/adapters/excel.ts | 6 ------ .../files/utils/doc-textify/adapters/html.ts | 6 ------ .../files/utils/doc-textify/adapters/open-office.ts | 5 ----- .../files/utils/doc-textify/adapters/pdf.ts | 5 ----- .../files/utils/doc-textify/adapters/power-point.ts | 6 ------ .../files/utils/doc-textify/adapters/text.ts | 6 ------ .../files/utils/doc-textify/adapters/word.ts | 6 ------ .../files/utils/doc-textify/doc-textify.ts | 6 ------ .../doc-textify/interfaces/doc-textify.interfaces.ts | 6 ------ .../files/utils/doc-textify/utils/clean.ts | 6 ------ backend/src/applications/files/utils/files-search.ts | 6 ------ backend/src/applications/files/utils/files-tree.ts | 6 ------ backend/src/applications/files/utils/files.ts | 6 ------ backend/src/applications/files/utils/send-file.ts | 6 ------ backend/src/applications/files/utils/unzip-file.ts | 6 ------ backend/src/applications/files/utils/url-file.ts | 6 ------ backend/src/applications/links/constants/cache.ts | 6 ------ backend/src/applications/links/constants/links.ts | 6 ------ backend/src/applications/links/constants/routes.ts | 6 ------ .../links/dto/create-or-update-link.dto.ts | 6 ------ .../links/interfaces/link-guest.interface.ts | 6 ------ .../links/interfaces/link-space.interface.ts | 6 ------ .../src/applications/links/links.controller.spec.ts | 6 ------ backend/src/applications/links/links.controller.ts | 6 ------ .../src/applications/links/schemas/link.interface.ts | 6 ------ .../src/applications/links/schemas/links.schema.ts | 6 ------ .../links/services/links-manager.service.spec.ts | 6 ------ .../links/services/links-manager.service.ts | 6 ------ .../links/services/links-queries.service.ts | 6 ------ .../notifications/constants/notifications.ts | 6 ------ .../applications/notifications/constants/routes.ts | 6 ------ .../applications/notifications/constants/websocket.ts | 6 ------ backend/src/applications/notifications/i18n/de.ts | 11 ----------- backend/src/applications/notifications/i18n/es.ts | 6 ------ backend/src/applications/notifications/i18n/fr.ts | 6 ------ backend/src/applications/notifications/i18n/hi.ts | 6 ------ backend/src/applications/notifications/i18n/index.ts | 6 ------ backend/src/applications/notifications/i18n/it.ts | 6 ------ backend/src/applications/notifications/i18n/ja.ts | 6 ------ backend/src/applications/notifications/i18n/ko.ts | 6 ------ backend/src/applications/notifications/i18n/pl.ts | 6 ------ backend/src/applications/notifications/i18n/pt.ts | 6 ------ backend/src/applications/notifications/i18n/pt_br.ts | 6 ------ backend/src/applications/notifications/i18n/ru.ts | 5 ----- backend/src/applications/notifications/i18n/tr.ts | 6 ------ backend/src/applications/notifications/i18n/zh.ts | 6 ------ .../interfaces/notification-properties.interface.ts | 6 ------ .../interfaces/user-mail-notification.interface.ts | 6 ------ .../src/applications/notifications/mails/models.ts | 6 ------ .../src/applications/notifications/mails/templates.ts | 6 ------ backend/src/applications/notifications/mails/urls.ts | 6 ------ .../notifications/notifications.controller.spec.ts | 6 ------ .../notifications/notifications.controller.ts | 6 ------ .../notifications/notifications.gateway.ts | 6 ------ .../notifications/notifications.module.ts | 6 ------ .../notifications/schemas/notification.interface.ts | 6 ------ .../notifications/schemas/notifications.schema.ts | 6 ------ .../services/notifications-manager.service.spec.ts | 6 ------ .../services/notifications-manager.service.ts | 6 ------ .../services/notifications-queries.service.ts | 6 ------ backend/src/applications/shares/constants/routes.ts | 6 ------ backend/src/applications/shares/constants/shares.ts | 6 ------ .../shares/dto/create-or-update-share.dto.ts | 6 ------ .../shares/interfaces/share-child.interface.ts | 6 ------ .../shares/interfaces/share-env.interface.ts | 6 ------ .../shares/interfaces/share-file.interface.ts | 6 ------ .../shares/interfaces/share-link.interface.ts | 6 ------ .../shares/interfaces/share-props.interface.ts | 5 ----- .../applications/shares/models/share-child.model.ts | 6 ------ .../shares/schemas/share-members.interface.ts | 6 ------ .../applications/shares/schemas/share.interface.ts | 6 ------ .../shares/schemas/shares-members.schema.ts | 6 ------ .../src/applications/shares/schemas/shares.schema.ts | 6 ------ .../shares/services/shares-manager.service.spec.ts | 6 ------ .../shares/services/shares-manager.service.ts | 6 ------ .../shares/services/shares-queries.service.ts | 6 ------ .../src/applications/shares/shares.controller.spec.ts | 6 ------ backend/src/applications/shares/shares.controller.ts | 6 ------ backend/src/applications/shares/shares.module.ts | 6 ------ backend/src/applications/spaces/constants/cache.ts | 6 ------ backend/src/applications/spaces/constants/routes.ts | 6 ------ backend/src/applications/spaces/constants/spaces.ts | 6 ------ .../decorators/space-override-permission.decorator.ts | 6 ------ .../spaces/decorators/space-skip-guard.decorator.ts | 6 ------ .../decorators/space-skip-permissions.decorator.ts | 6 ------ .../applications/spaces/decorators/space.decorator.ts | 6 ------ .../spaces/dto/create-or-update-space.dto.ts | 6 ------ .../src/applications/spaces/dto/delete-space.dto.ts | 6 ------ .../src/applications/spaces/dto/search-space.dto.ts | 6 ------ .../src/applications/spaces/dto/space-roots.dto.ts | 6 ------ .../applications/spaces/guards/space.guard.spec.ts | 6 ------ backend/src/applications/spaces/guards/space.guard.ts | 6 ------ .../spaces/interfaces/space-diff.interface.ts | 6 ------ .../spaces/interfaces/space-files.interface.ts | 6 ------ .../spaces/interfaces/space-request.interface.ts | 6 ------ .../spaces/interfaces/space-trash.interface.ts | 6 ------ .../src/applications/spaces/models/space-env.model.ts | 6 ------ .../applications/spaces/models/space-props.model.ts | 6 ------ .../spaces/models/space-root-props.model.ts | 6 ------ backend/src/applications/spaces/models/space.model.ts | 6 ------ .../spaces/schemas/space-members.interface.ts | 6 ------ .../spaces/schemas/space-root.interface.ts | 6 ------ .../applications/spaces/schemas/space.interface.ts | 6 ------ .../spaces/schemas/spaces-members.schema.ts | 6 ------ .../spaces/schemas/spaces-roots.schema.ts | 6 ------ .../src/applications/spaces/schemas/spaces.schema.ts | 6 ------ .../spaces/services/spaces-browser.service.spec.ts | 6 ------ .../spaces/services/spaces-browser.service.ts | 6 ------ .../spaces/services/spaces-manager.service.spec.ts | 6 ------ .../spaces/services/spaces-manager.service.ts | 6 ------ .../spaces/services/spaces-queries.service.ts | 6 ------ .../spaces/services/spaces-scheduler.service.ts | 6 ------ .../src/applications/spaces/spaces.controller.spec.ts | 6 ------ backend/src/applications/spaces/spaces.controller.ts | 6 ------ backend/src/applications/spaces/spaces.module.ts | 6 ------ backend/src/applications/spaces/utils/paths.ts | 6 ------ backend/src/applications/spaces/utils/permissions.ts | 6 ------ backend/src/applications/spaces/utils/routes.ts | 6 ------ backend/src/applications/sync/constants/auth.ts | 6 ------ backend/src/applications/sync/constants/routes.ts | 6 ------ backend/src/applications/sync/constants/store.ts | 6 ------ backend/src/applications/sync/constants/sync.ts | 6 ------ .../sync/decorators/sync-context.decorator.ts | 6 ------ .../sync/decorators/sync-environment.decorator.ts | 6 ------ .../applications/sync/dtos/sync-client-auth.dto.ts | 6 ------ .../applications/sync/dtos/sync-client-info.dto.ts | 6 ------ .../sync/dtos/sync-client-registration.dto.ts | 6 ------ .../src/applications/sync/dtos/sync-operations.dto.ts | 6 ------ backend/src/applications/sync/dtos/sync-path.dto.ts | 6 ------ backend/src/applications/sync/dtos/sync-upload.dto.ts | 6 ------ .../sync-diff-gzip-body.interceptor.spec.ts | 6 ------ .../interceptors/sync-diff-gzip-body.interceptor.ts | 6 ------ .../sync/interfaces/store-manifest.interface.ts | 6 ------ .../sync/interfaces/sync-client-auth.interface.ts | 6 ------ .../sync/interfaces/sync-client-paths.interface.ts | 6 ------ .../sync/interfaces/sync-client.interface.ts | 6 ------ .../sync/interfaces/sync-diff.interface.ts | 6 ------ .../sync/interfaces/sync-path.interface.ts | 6 ------ .../sync/schemas/sync-client.interface.ts | 6 ------ .../applications/sync/schemas/sync-clients.schema.ts | 6 ------ .../applications/sync/schemas/sync-path.interface.ts | 6 ------ .../applications/sync/schemas/sync-paths.schema.ts | 6 ------ .../services/sync-clients-manager.service.spec.ts | 6 ------ .../sync/services/sync-clients-manager.service.ts | 6 ------ .../sync/services/sync-manager.service.spec.ts | 6 ------ .../sync/services/sync-manager.service.ts | 6 ------ .../sync/services/sync-paths-manager.service.spec.ts | 6 ------ .../sync/services/sync-paths-manager.service.ts | 6 ------ .../sync/services/sync-queries.service.ts | 6 ------ backend/src/applications/sync/sync.config.ts | 6 ------ backend/src/applications/sync/sync.controller.spec.ts | 6 ------ backend/src/applications/sync/sync.controller.ts | 6 ------ backend/src/applications/sync/sync.module.ts | 6 ------ backend/src/applications/sync/utils/functions.ts | 6 ------ backend/src/applications/sync/utils/normalizedMap.ts | 6 ------ backend/src/applications/sync/utils/routes.ts | 6 ------ .../applications/users/admin-users.controller.spec.ts | 6 ------ .../src/applications/users/admin-users.controller.ts | 6 ------ backend/src/applications/users/constants/group.ts | 6 ------ backend/src/applications/users/constants/member.ts | 6 ------ backend/src/applications/users/constants/routes.ts | 6 ------ backend/src/applications/users/constants/user.ts | 6 ------ backend/src/applications/users/constants/websocket.ts | 6 ------ .../users/decorators/permissions.decorator.ts | 6 ------ .../applications/users/decorators/roles.decorator.ts | 6 ------ .../applications/users/decorators/user.decorator.ts | 6 ------ .../users/dto/create-or-update-group.dto.ts | 6 ------ .../users/dto/create-or-update-user.dto.ts | 6 ------ backend/src/applications/users/dto/delete-user.dto.ts | 6 ------ .../src/applications/users/dto/search-members.dto.ts | 6 ------ .../src/applications/users/dto/user-properties.dto.ts | 6 ------ .../users/guards/permissions.guard.spec.ts | 6 ------ .../applications/users/guards/permissions.guard.ts | 6 ------ .../src/applications/users/guards/roles.guard.spec.ts | 6 ------ backend/src/applications/users/guards/roles.guard.ts | 6 ------ .../users/interfaces/admin-group.interface.ts | 6 ------ .../users/interfaces/admin-user.interface.ts | 6 ------ .../users/interfaces/group-browse.interface.ts | 6 ------ .../src/applications/users/interfaces/group-member.ts | 6 ------ .../users/interfaces/guest-user.interface.ts | 6 ------ .../applications/users/interfaces/member.interface.ts | 6 ------ .../applications/users/interfaces/owner.interface.ts | 6 ------ .../users/interfaces/user-secrets.interface.ts | 6 ------ .../users/interfaces/websocket.interface.ts | 6 ------ backend/src/applications/users/models/user.model.ts | 6 ------ .../src/applications/users/schemas/group.interface.ts | 6 ------ .../src/applications/users/schemas/groups.schema.ts | 6 ------ .../users/schemas/user-group.interface.ts | 6 ------ .../src/applications/users/schemas/user.interface.ts | 6 ------ .../applications/users/schemas/users-groups.schema.ts | 6 ------ .../applications/users/schemas/users-guests.schema.ts | 6 ------ .../src/applications/users/schemas/users.schema.ts | 6 ------ .../services/admin-users-manager.service.spec.ts | 6 ------ .../users/services/admin-users-manager.service.ts | 6 ------ .../users/services/admin-users-queries.service.ts | 6 ------ .../users/services/users-manager.service.spec.ts | 6 ------ .../users/services/users-manager.service.ts | 6 ------ .../users/services/users-queries.service.ts | 6 ------ .../src/applications/users/users.controller.spec.ts | 6 ------ backend/src/applications/users/users.controller.ts | 6 ------ backend/src/applications/users/users.e2e-spec.ts | 6 ------ backend/src/applications/users/users.gateway.spec.ts | 6 ------ backend/src/applications/users/users.gateway.ts | 6 ------ backend/src/applications/users/users.module.ts | 6 ------ backend/src/applications/users/utils/avatar.ts | 6 ------ backend/src/applications/users/utils/test.ts | 6 ------ backend/src/applications/webdav/constants/routes.ts | 6 ------ backend/src/applications/webdav/constants/webdav.ts | 6 ------ .../webdav/decorators/if-header.decorator.ts | 6 ------ .../webdav/decorators/webdav-context.decorator.ts | 6 ------ .../applications/webdav/filters/webdav.filter.spec.ts | 5 ----- .../src/applications/webdav/filters/webdav.filter.ts | 6 ------ .../webdav/guards/webdav-protocol.guard.ts | 6 ------ .../webdav/interfaces/if-header.interface.ts | 6 ------ .../webdav/interfaces/webdav.interface.ts | 6 ------ .../applications/webdav/models/webdav-file.model.ts | 6 ------ .../webdav/services/webdav-methods.service.spec.ts | 6 ------ .../webdav/services/webdav-methods.service.ts | 6 ------ .../webdav/services/webdav-spaces.service.spec.ts | 6 ------ .../webdav/services/webdav-spaces.service.ts | 6 ------ backend/src/applications/webdav/utils/bootstrap.ts | 6 ------ backend/src/applications/webdav/utils/if-header.ts | 6 ------ backend/src/applications/webdav/utils/routes.ts | 6 ------ backend/src/applications/webdav/utils/webdav.ts | 6 ------ backend/src/applications/webdav/utils/xml.ts | 6 ------ .../src/applications/webdav/webdav.controller.spec.ts | 6 ------ backend/src/applications/webdav/webdav.controller.ts | 6 ------ backend/src/applications/webdav/webdav.e2e-spec.ts | 6 ------ backend/src/applications/webdav/webdav.module.ts | 6 ------ backend/src/authentication/auth.config.ts | 6 ------ backend/src/authentication/auth.controller.spec.ts | 6 ------ backend/src/authentication/auth.controller.ts | 6 ------ backend/src/authentication/auth.e2e-spec.ts | 6 ------ backend/src/authentication/auth.module.ts | 6 ------ backend/src/authentication/auth.service.spec.ts | 6 ------ backend/src/authentication/auth.service.ts | 5 ----- backend/src/authentication/constants/auth.ts | 6 ------ backend/src/authentication/constants/routes.ts | 6 ------ backend/src/authentication/constants/scope.ts | 6 ------ .../decorators/auth-token-optional.decorator.ts | 6 ------ .../decorators/auth-token-skip.decorator.ts | 6 ------ backend/src/authentication/dto/login-response.dto.ts | 6 ------ backend/src/authentication/dto/token-response.dto.ts | 6 ------ .../guards/auth-anonymous.guard.spec.ts | 6 ------ .../src/authentication/guards/auth-anonymous.guard.ts | 6 ------ .../authentication/guards/auth-anonymous.strategy.ts | 6 ------ .../authentication/guards/auth-basic.guard.spec.ts | 6 ------ backend/src/authentication/guards/auth-basic.guard.ts | 6 ------ .../src/authentication/guards/auth-basic.strategy.ts | 6 ------ .../src/authentication/guards/auth-digest.guard.ts | 6 ------ .../src/authentication/guards/auth-digest.strategy.ts | 6 ------ .../authentication/guards/auth-local.guard.spec.ts | 6 ------ backend/src/authentication/guards/auth-local.guard.ts | 6 ------ .../src/authentication/guards/auth-local.strategy.ts | 6 ------ .../guards/auth-token-access.guard.spec.ts | 6 ------ .../authentication/guards/auth-token-access.guard.ts | 6 ------ .../guards/auth-token-access.strategy.ts | 6 ------ .../guards/auth-token-refresh.guard.spec.ts | 6 ------ .../authentication/guards/auth-token-refresh.guard.ts | 6 ------ .../guards/auth-token-refresh.strategy.ts | 6 ------ .../guards/implementations/http-basic.strategy.ts | 6 ------ .../guards/implementations/http-digest.strategy.ts | 6 ------ .../interfaces/auth-request.interface.ts | 6 ------ .../interfaces/jwt-payload.interface.ts | 6 ------ .../src/authentication/interfaces/token.interface.ts | 6 ------ .../providers/auth-providers.constants.ts | 6 ------ .../authentication/providers/auth-providers.models.ts | 6 ------ .../authentication/providers/auth-providers.utils.ts | 6 ------ .../authentication/providers/ldap/auth-ldap.config.ts | 6 ------ .../providers/ldap/auth-ldap.constants.ts | 6 ------ .../providers/ldap/auth-provider-ldap.service.spec.ts | 6 ------ .../providers/ldap/auth-provider-ldap.service.ts | 6 ------ .../mysql/auth-provider-mysql.service.spec.ts | 6 ------ .../providers/mysql/auth-provider-mysql.service.ts | 6 ------ .../providers/oidc/auth-oidc-desktop.constants.ts | 6 ------ .../authentication/providers/oidc/auth-oidc.config.ts | 6 ------ .../providers/oidc/auth-oidc.constants.ts | 6 ------ .../providers/oidc/auth-oidc.controller.ts | 6 ------ .../providers/oidc/auth-oidc.interfaces.ts | 6 ------ .../providers/oidc/auth-provider-oidc.module.ts | 6 ------ .../providers/oidc/auth-provider-oidc.service.spec.ts | 6 ------ .../providers/oidc/auth-provider-oidc.service.ts | 6 ------ .../two-fa/auth-provider-two-fa.service.spec.ts | 6 ------ .../providers/two-fa/auth-provider-two-fa.service.ts | 6 ------ .../providers/two-fa/auth-two-fa-guard.ts | 6 ------ .../providers/two-fa/auth-two-fa.config.ts | 6 ------ .../providers/two-fa/auth-two-fa.dtos.ts | 6 ------ .../providers/two-fa/auth-two-fa.interfaces.ts | 6 ------ backend/src/authentication/utils/crypt-secret.ts | 6 ------ backend/src/common/constants.ts | 6 ------ backend/src/common/decorators.ts | 6 ------ backend/src/common/functions.ts | 6 ------ backend/src/common/i18n.ts | 6 ------ backend/src/common/image.ts | 6 ------ backend/src/common/interfaces.ts | 6 ------ backend/src/common/qrcode.ts | 6 ------ backend/src/common/shared.ts | 6 ------ backend/src/configuration/config.constants.ts | 6 ------ backend/src/configuration/config.environment.ts | 6 ------ backend/src/configuration/config.interfaces.ts | 6 ------ backend/src/configuration/config.loader.ts | 6 ------ backend/src/configuration/config.logger.ts | 6 ------ backend/src/configuration/config.validation.ts | 6 ------ .../cache/adapters/mysql-cache.adapter.ts | 6 ------ .../cache/adapters/redis-cache.adapter.ts | 6 ------ backend/src/infrastructure/cache/cache.config.ts | 6 ------ backend/src/infrastructure/cache/cache.decorator.ts | 6 ------ backend/src/infrastructure/cache/cache.e2e-spec.ts | 6 ------ backend/src/infrastructure/cache/cache.module.ts | 6 ------ .../cache/schemas/mysql-cache.interface.ts | 6 ------ .../cache/schemas/mysql-cache.schema.ts | 6 ------ .../infrastructure/cache/services/cache.service.ts | 6 ------ backend/src/infrastructure/context/context.module.ts | 6 ------ .../context/interceptors/context.interceptor.spec.ts | 10 ++-------- .../context/interceptors/context.interceptor.ts | 6 ------ .../context/interfaces/context-store.interface.ts | 6 ------ .../context/services/context-manager.service.spec.ts | 8 +------- .../context/services/context-manager.service.ts | 6 ------ backend/src/infrastructure/database/columns.ts | 6 ------ backend/src/infrastructure/database/configuration.ts | 6 ------ backend/src/infrastructure/database/constants.ts | 8 +------- .../src/infrastructure/database/database.config.ts | 6 ------ .../src/infrastructure/database/database.logger.ts | 6 ------ .../src/infrastructure/database/database.module.ts | 6 ------ .../database/interfaces/database.interface.ts | 6 ------ backend/src/infrastructure/database/schema.ts | 6 ------ .../infrastructure/database/scripts/create-user.ts | 6 ------ backend/src/infrastructure/database/scripts/db.ts | 6 ------ .../src/infrastructure/database/scripts/seed/main.ts | 6 ------ .../database/scripts/seed/usersgroups.ts | 6 ------ backend/src/infrastructure/database/utils.ts | 6 ------ .../mailer/interfaces/mail.interface.ts | 6 ------ backend/src/infrastructure/mailer/mailer.config.ts | 6 ------ backend/src/infrastructure/mailer/mailer.module.ts | 6 ------ .../src/infrastructure/mailer/mailer.service.spec.ts | 10 ++-------- backend/src/infrastructure/mailer/mailer.service.ts | 6 ------ .../infrastructure/scheduler/scheduler.constants.ts | 6 ------ .../src/infrastructure/scheduler/scheduler.module.ts | 6 ------ .../websocket/adapters/cluster.adapter.ts | 6 ------ .../websocket/adapters/redis.adapter.ts | 6 ------ .../websocket/adapters/web-socket.adapter.ts | 6 ------ .../websocket/decorators/web-socket-user.decorator.ts | 6 ------ .../websocket/interfaces/auth-socket-io.interface.ts | 6 ------ backend/src/infrastructure/websocket/utils.ts | 6 ------ .../src/infrastructure/websocket/web-socket.config.ts | 6 ------ backend/src/main.ts | 6 ------ frontend/eslint.config.js | 6 ------ frontend/scripts/build-assets.mjs | 6 ------ frontend/scripts/pdfjs.mjs | 6 ------ frontend/src/app/app.component.ts | 6 ------ frontend/src/app/app.config.ts | 6 ------ frontend/src/app/app.constants.ts | 6 ------ frontend/src/app/app.routes.ts | 6 ------ .../src/app/applications/admin/admin.constants.ts | 6 ------ frontend/src/app/applications/admin/admin.guard.ts | 5 ----- frontend/src/app/applications/admin/admin.routes.ts | 6 ------ frontend/src/app/applications/admin/admin.service.ts | 6 ------ .../admin/components/admin-groups.component.html | 6 ------ .../admin/components/admin-groups.component.ts | 6 ------ .../admin/components/admin-users.component.html | 6 ------ .../admin/components/admin-users.component.ts | 6 ------ .../admin-group-add-users-dialog.component.html | 6 ------ .../dialogs/admin-group-add-users-dialog.component.ts | 6 ------ .../dialogs/admin-group-delete-dialog.component.html | 6 ------ .../dialogs/admin-group-delete-dialog.component.ts | 6 ------ .../dialogs/admin-group-dialog.component.html | 5 ----- .../dialogs/admin-group-dialog.component.ts | 6 ------ .../admin-group-edit-user-dialog.component.html | 6 ------ .../dialogs/admin-group-edit-user-dialog.component.ts | 6 ------ .../dialogs/admin-guest-dialog.component.ts | 6 ------ .../dialogs/admin-user-delete-dialog.component.html | 6 ------ .../dialogs/admin-user-delete-dialog.component.ts | 6 ------ .../dialogs/admin-user-dialog.component.html | 6 ------ .../components/dialogs/admin-user-dialog.component.ts | 6 ------ .../components/utils/admin-permissions.component.ts | 6 ------ .../applications/admin/models/admin-group.model.ts | 6 ------ .../app/applications/admin/models/admin-user.model.ts | 6 ------ .../app/applications/comments/comments.constants.ts | 6 ------ .../sidebar/comments-selection.component.html | 6 ------ .../sidebar/comments-selection.component.ts | 6 ------ .../widgets/comments-recents-widget.component.html | 6 ------ .../widgets/comments-recents-widget.component.ts | 6 ------ .../comments/models/comment-recent.model.ts | 6 ------ .../app/applications/comments/models/comment.model.ts | 6 ------ .../comments/services/comments.service.ts | 6 ------ .../dialogs/files-compression-dialog.component.html | 6 ------ .../dialogs/files-compression-dialog.component.ts | 6 ------ .../dialogs/files-lock-dialog.component.html | 6 ------ .../components/dialogs/files-lock-dialog.component.ts | 6 ------ .../dialogs/files-new-dialog.component.html | 6 ------ .../components/dialogs/files-new-dialog.component.ts | 6 ------ .../dialogs/files-overwrite-dialog.component.html | 6 ------ .../dialogs/files-overwrite-dialog.component.ts | 6 ------ .../dialogs/files-trash-dialog.component.html | 6 ------ .../dialogs/files-trash-dialog.component.ts | 6 ------ .../dialogs/files-trash-empty-dialog.component.html | 6 ------ .../dialogs/files-trash-empty-dialog.component.ts | 6 ------ .../dialogs/files-tree-dialog.component.html | 6 ------ .../components/dialogs/files-tree-dialog.component.ts | 6 ------ .../dialogs/files-viewer-dialog.component.html | 6 ------ .../dialogs/files-viewer-dialog.component.ts | 6 ------ .../dialogs/files-viewer-select-dialog.component.html | 6 ------ .../dialogs/files-viewer-select-dialog.component.ts | 6 ------ .../components/sidebar/files-clipboard.component.html | 6 ------ .../components/sidebar/files-clipboard.component.ts | 6 ------ .../components/sidebar/files-selection.component.html | 6 ------ .../components/sidebar/files-selection.component.ts | 6 ------ .../components/sidebar/files-tasks.component.html | 6 ------ .../files/components/sidebar/files-tasks.component.ts | 6 ------ .../components/sidebar/files-tree.component.html | 5 ----- .../files/components/sidebar/files-tree.component.ts | 6 ------ .../files/components/utils/file-lock.utils.ts | 6 ------ .../components/utils/file-permissions.component.ts | 6 ------ .../files/components/utils/only-office.component.ts | 6 ------ .../files/components/utils/only-office.utils.ts | 6 ------ .../files-viewer-collabora-online.component.ts | 6 ------ .../components/viewers/files-viewer-html.component.ts | 6 ------ .../viewers/files-viewer-image.component.html | 6 ------ .../viewers/files-viewer-image.component.ts | 6 ------ .../viewers/files-viewer-media.component.ts | 6 ------ .../viewers/files-viewer-only-office.component.ts | 6 ------ .../components/viewers/files-viewer-pdf.component.ts | 6 ------ .../viewers/files-viewer-text.component.html | 6 ------ .../components/viewers/files-viewer-text.component.ts | 6 ------ .../widgets/files-recents-widget.component.html | 6 ------ .../widgets/files-recents-widget.component.ts | 6 ------ .../src/app/applications/files/files.constants.ts | 6 ------ .../files/interfaces/file-event.interface.ts | 6 ------ .../files/interfaces/file-upload.interface.ts | 6 ------ .../applications/files/models/file-content.model.ts | 6 ------ .../applications/files/models/file-recent.model.ts | 6 ------ .../src/app/applications/files/models/file.model.ts | 6 ------ .../files/services/files-tasks.service.ts | 6 ------ .../files/services/files-upload.service.ts | 6 ------ .../app/applications/files/services/files.service.ts | 6 ------ .../components/dialogs/link-dialog.component.html | 5 ----- .../links/components/dialogs/link-dialog.component.ts | 6 ------ .../links/components/links.component.html | 6 ------ .../applications/links/components/links.component.ts | 6 ------ .../components/public/public-link-auth.component.html | 6 ------ .../components/public/public-link-auth.component.ts | 6 ------ .../public/public-link-error.component.html | 6 ------ .../components/public/public-link-error.component.ts | 6 ------ .../components/public/public-link.component.html | 6 ------ .../links/components/public/public-link.component.ts | 6 ------ .../components/sidebar/link-selection.component.html | 6 ------ .../components/sidebar/link-selection.component.ts | 6 ------ .../src/app/applications/links/links.constants.ts | 6 ------ frontend/src/app/applications/links/links.guard.ts | 6 ------ frontend/src/app/applications/links/links.routes.ts | 6 ------ .../app/applications/links/models/share-link.model.ts | 6 ------ .../app/applications/links/services/links.service.ts | 6 ------ .../components/sidebar/notifications.component.html | 6 ------ .../components/sidebar/notifications.component.ts | 6 ------ .../notifications/models/notification.model.ts | 6 ------ .../notifications/notifications.constants.ts | 6 ------ .../notifications/notifications.service.ts | 6 ------ .../recents/components/recents.component.html | 6 ------ .../recents/components/recents.component.ts | 6 ------ .../widgets/recents-apps-widget.component.html | 6 ------ .../widgets/recents-apps-widget.component.ts | 6 ------ .../src/app/applications/recents/recents.constants.ts | 6 ------ .../src/app/applications/recents/recents.routes.ts | 6 ------ .../search/components/search.component.html | 6 ------ .../search/components/search.component.ts | 6 ------ .../src/app/applications/search/search.constants.ts | 6 ------ frontend/src/app/applications/search/search.routes.ts | 6 ------ .../components/dialogs/share-dialog.component.html | 6 ------ .../components/dialogs/share-dialog.component.ts | 6 ------ .../dialogs/shared-children-dialog.component.html | 6 ------ .../dialogs/shared-children-dialog.component.scss | 6 ------ .../dialogs/shared-children-dialog.component.ts | 6 ------ .../shares/components/shared.component.html | 6 ------ .../shares/components/shared.component.ts | 6 ------ .../components/sidebar/share-selection.component.html | 6 ------ .../components/sidebar/share-selection.component.ts | 6 ------ .../components/utils/share-file-name.component.ts | 6 ------ .../components/utils/share-repository.component.ts | 6 ------ .../applications/shares/models/share-child.model.ts | 6 ------ .../applications/shares/models/share-file.model.ts | 6 ------ .../src/app/applications/shares/models/share.model.ts | 6 ------ .../applications/shares/services/shares.service.ts | 6 ------ .../src/app/applications/shares/shares.functions.ts | 6 ------ .../dialogs/space-anchor-file-dialog.component.html | 6 ------ .../dialogs/space-anchor-file-dialog.component.ts | 6 ------ .../components/dialogs/space-dialog.component.html | 5 ----- .../components/dialogs/space-dialog.component.ts | 6 ------ .../dialogs/space-root-path-dialog.component.html | 6 ------ .../dialogs/space-root-path-dialog.component.ts | 6 ------ .../dialogs/space-user-anchors-dialog.component.html | 6 ------ .../dialogs/space-user-anchors-dialog.component.ts | 6 ------ .../components/sidebar/space-selection.component.html | 6 ------ .../components/sidebar/space-selection.component.ts | 6 ------ .../components/sidebar/trash-selection.component.html | 6 ------ .../components/sidebar/trash-selection.component.ts | 6 ------ .../spaces/components/spaces-browser.component.html | 6 ------ .../spaces/components/spaces-browser.component.ts | 6 ------ .../spaces/components/spaces-nav.component.ts | 6 ------ .../spaces/components/spaces.component.html | 6 ------ .../spaces/components/spaces.component.ts | 6 ------ .../spaces/components/trash.component.html | 6 ------ .../applications/spaces/components/trash.component.ts | 6 ------ .../utils/space-manage-roots.component.html | 6 ------ .../components/utils/space-manage-roots.component.ts | 6 ------ .../src/app/applications/spaces/models/space.model.ts | 6 ------ .../src/app/applications/spaces/models/trash.model.ts | 6 ------ .../spaces/services/spaces-browser.service.ts | 6 ------ .../applications/spaces/services/spaces.service.ts | 6 ------ .../src/app/applications/spaces/spaces.constants.ts | 6 ------ .../src/app/applications/spaces/spaces.functions.ts | 6 ------ .../src/app/applications/spaces/spaces.resolvers.ts | 6 ------ frontend/src/app/applications/spaces/spaces.routes.ts | 6 ------ .../dialogs/sync-client-delete.dialog.component.html | 6 ------ .../dialogs/sync-client-delete.dialog.component.ts | 5 ----- .../dialogs/sync-path-report.dialog.component.html | 6 ------ .../dialogs/sync-path-report.dialog.component.ts | 6 ------ .../dialogs/sync-path-settings.dialog.component.html | 6 ------ .../dialogs/sync-path-settings.dialog.component.ts | 5 ----- .../sync-transfers-delete.dialog.component.html | 6 ------ .../dialogs/sync-transfers-delete.dialog.component.ts | 6 ------ .../shared/sync-path-settings.component.html | 6 ------ .../components/shared/sync-path-settings.component.ts | 6 ------ .../sync/components/sync-clients.component.html | 5 ----- .../sync/components/sync-clients.component.scss | 6 ------ .../sync/components/sync-clients.component.ts | 6 ------ .../sync/components/sync-paths.component.html | 5 ----- .../sync/components/sync-paths.component.ts | 6 ------ .../sync/components/sync-transfers.component.html | 6 ------ .../sync/components/sync-transfers.component.ts | 5 ----- .../utils/sync-path-direction-icon.component.ts | 6 ------ .../components/utils/sync-path-scheduler.component.ts | 6 ------ .../wizard/sync-wizard-client.component.html | 6 ------ .../components/wizard/sync-wizard-client.component.ts | 5 ----- .../wizard/sync-wizard-server.component.html | 6 ------ .../components/wizard/sync-wizard-server.component.ts | 5 ----- .../wizard/sync-wizard-settings.component.html | 6 ------ .../wizard/sync-wizard-settings.component.ts | 6 ------ .../src/app/applications/sync/constants/client.ts | 6 ------ frontend/src/app/applications/sync/constants/path.ts | 6 ------ .../src/app/applications/sync/constants/transfer.ts | 6 ------ .../sync/interfaces/sync-status.interface.ts | 6 ------ .../sync/interfaces/sync-task.interface.ts | 6 ------ .../sync/interfaces/sync-transfer.interface.ts | 6 ------ .../app/applications/sync/models/sync-client.model.ts | 6 ------ .../app/applications/sync/models/sync-path.model.ts | 6 ------ .../applications/sync/models/sync-transfer.model.ts | 6 ------ .../sync/models/sync-wizard-path.model.ts | 6 ------ .../app/applications/sync/services/sync.service.ts | 6 ------ frontend/src/app/applications/sync/sync.constants.ts | 6 ------ frontend/src/app/applications/sync/sync.guards.ts | 6 ------ frontend/src/app/applications/sync/sync.resolvers.ts | 6 ------ frontend/src/app/applications/sync/sync.routes.ts | 6 ------ frontend/src/app/applications/sync/sync.utils.ts | 6 ------ .../user-auth-2fa-enable-dialog.component.html | 5 ----- .../dialogs/user-auth-2fa-enable-dialog.component.ts | 6 ------ .../user-auth-2fa-verify-dialog.component.html | 5 ----- .../dialogs/user-auth-2fa-verify-dialog.component.ts | 6 ------ ...er-auth-manage-app-passwords-dialog.component.html | 6 ------ ...user-auth-manage-app-passwords-dialog.component.ts | 6 ------ .../user-group-add-users-dialog.component.html | 6 ------ .../dialogs/user-group-add-users-dialog.component.ts | 6 ------ .../dialogs/user-group-delete-dialog.component.html | 6 ------ .../dialogs/user-group-delete-dialog.component.ts | 6 ------ .../dialogs/user-group-dialog.component.html | 6 ------ .../components/dialogs/user-group-dialog.component.ts | 6 ------ .../dialogs/user-guest-dialog.component.html | 6 ------ .../components/dialogs/user-guest-dialog.component.ts | 6 ------ ...ser-personal-group-edit-user-dialog.component.html | 6 ------ .../user-personal-group-edit-user-dialog.component.ts | 6 ------ .../user-personal-group-leave-dialog.component.html | 6 ------ .../user-personal-group-leave-dialog.component.ts | 6 ------ .../components/sidebar/user-onlines.component.html | 6 ------ .../components/sidebar/user-onlines.component.ts | 6 ------ .../components/sidebar/user-profile.component.html | 6 ------ .../components/sidebar/user-profile.component.ts | 6 ------ .../users/components/user-account.component.html | 6 ------ .../users/components/user-account.component.ts | 6 ------ .../users/components/user-applications.component.html | 5 ----- .../users/components/user-applications.component.ts | 6 ------ .../users/components/user-groups.component.html | 6 ------ .../users/components/user-groups.component.ts | 6 ------ .../users/components/user-guests.component.html | 6 ------ .../users/components/user-guests.component.ts | 6 ------ .../users/components/utils/user-avatar.component.ts | 6 ------ .../users/components/utils/user-search.component.ts | 6 ------ .../components/utils/users-search.component.html | 6 ------ .../applications/users/interfaces/group.interface.ts | 6 ------ .../applications/users/interfaces/owner.interface.ts | 6 ------ .../applications/users/interfaces/user.interface.ts | 6 ------ .../applications/users/models/group-browse.model.ts | 6 ------ .../src/app/applications/users/models/guest.model.ts | 6 ------ .../src/app/applications/users/models/member.model.ts | 6 ------ .../applications/users/models/user-online.model.ts | 6 ------ frontend/src/app/applications/users/user.constants.ts | 6 ------ frontend/src/app/applications/users/user.functions.ts | 6 ------ frontend/src/app/applications/users/user.guards.ts | 6 ------ frontend/src/app/applications/users/user.routes.ts | 6 ------ frontend/src/app/applications/users/user.service.ts | 6 ------ frontend/src/app/auth/auth-resolvers.ts | 6 ------ frontend/src/app/auth/auth.component.html | 6 ------ frontend/src/app/auth/auth.component.ts | 6 ------ frontend/src/app/auth/auth.constants.ts | 6 ------ frontend/src/app/auth/auth.guards.ts | 6 ------ frontend/src/app/auth/auth.interceptor.ts | 6 ------ frontend/src/app/auth/auth.interface.ts | 6 ------ frontend/src/app/auth/auth.routes.ts | 6 ------ frontend/src/app/auth/auth.service.ts | 6 ------ .../src/app/common/components/filter.component.ts | 6 ------ .../app/common/components/input-password.component.ts | 6 ------ .../navigation-view/navigation-view.component.html | 6 ------ .../navigation-view/navigation-view.component.ts | 6 ------ .../app/common/components/only-office.component.ts | 6 ------ .../components/password-strength-bar.component.ts | 6 ------ .../common/components/select/select.component.html | 6 ------ .../common/components/select/select.component.scss | 6 ------ .../app/common/components/select/select.component.ts | 6 ------ .../src/app/common/components/select/select.model.ts | 6 ------ .../app/common/components/storage-quota.component.ts | 6 ------ .../app/common/components/storage-usage.component.ts | 6 ------ .../app/common/components/virtual-scroll.component.ts | 6 ------ .../src/app/common/directives/auto-focus.directive.ts | 6 ------ .../app/common/directives/auto-resize.directive.ts | 6 ------ .../src/app/common/directives/input-edit.directive.ts | 6 ------ .../src/app/common/directives/off-click.directive.ts | 6 ------ frontend/src/app/common/directives/tap.directive.ts | 6 ------ .../app/common/directives/upload-files.directive.ts | 6 ------ frontend/src/app/common/interfaces/table.interface.ts | 6 ------ frontend/src/app/common/pipes/capitalize.pipe.ts | 6 ------ frontend/src/app/common/pipes/highlight.pipe.ts | 6 ------ frontend/src/app/common/pipes/join-counts.pipe.ts | 6 ------ frontend/src/app/common/pipes/join.pipe.ts | 6 ------ frontend/src/app/common/pipes/linkify.pipe.ts | 6 ------ frontend/src/app/common/pipes/paginate.pipe.ts | 6 ------ frontend/src/app/common/pipes/path-slice.ts | 6 ------ frontend/src/app/common/pipes/search.pipe.ts | 6 ------ frontend/src/app/common/pipes/split.pipe.ts | 6 ------ frontend/src/app/common/pipes/time-add.pipe.ts | 6 ------ frontend/src/app/common/pipes/time-ago.pipe.ts | 6 ------ .../src/app/common/pipes/time-date-format.pipe.ts | 6 ------ frontend/src/app/common/pipes/time-duration.pipe.ts | 6 ------ frontend/src/app/common/pipes/to-bytes.pipe.ts | 6 ------ frontend/src/app/common/pipes/truncate-text.pipe.ts | 6 ------ frontend/src/app/common/resolvers/route.resolver.ts | 6 ------ frontend/src/app/common/utils/functions.ts | 6 ------ frontend/src/app/common/utils/regexp.ts | 6 ------ frontend/src/app/common/utils/sort-table.ts | 6 ------ frontend/src/app/common/utils/sort.ts | 6 ------ frontend/src/app/common/utils/time.ts | 6 ------ frontend/src/app/electron/constants/dialogs.ts | 6 ------ frontend/src/app/electron/constants/events.ts | 6 ------ frontend/src/app/electron/electron.service.ts | 6 ------ frontend/src/app/electron/interface.ts | 6 ------ frontend/src/app/electron/utils.ts | 6 ------ .../app/layout/breadcrumb/breadcrumb.component.html | 6 ------ .../src/app/layout/breadcrumb/breadcrumb.component.ts | 6 ------ .../app/layout/breadcrumb/breadcrumb.interfaces.ts | 6 ------ frontend/src/app/layout/layout.component.html | 6 ------ frontend/src/app/layout/layout.component.ts | 6 ------ frontend/src/app/layout/layout.constants.ts | 6 ------ frontend/src/app/layout/layout.interfaces.ts | 6 ------ frontend/src/app/layout/layout.service.ts | 6 ------ frontend/src/app/layout/layout.tooltip.config.ts | 6 ------ frontend/src/app/layout/navbar/navbar.component.html | 6 ------ frontend/src/app/layout/navbar/navbar.component.ts | 6 ------ .../sidebar/components/selection.component.html | 5 ----- .../layout/sidebar/components/selection.component.ts | 6 ------ .../layout/sidebar/components/windows.component.html | 6 ------ .../layout/sidebar/components/windows.component.ts | 6 ------ .../app/layout/sidebar/sidebar.left.component.html | 6 ------ .../src/app/layout/sidebar/sidebar.left.component.ts | 6 ------ .../app/layout/sidebar/sidebar.right.component.html | 6 ------ .../src/app/layout/sidebar/sidebar.right.component.ts | 6 ------ frontend/src/app/store/store.service.ts | 6 ------ frontend/src/app/websocket/websocket.constants.ts | 6 ------ frontend/src/i18n/l10n.ts | 6 ------ frontend/src/i18n/lib/bs.i18n.ts | 6 ------ frontend/src/i18n/lib/dayjs.i18n.ts | 6 ------ frontend/src/index.html | 2 +- frontend/src/main.ts | 6 ------ frontend/src/styles/components/_app.scss | 6 ------ frontend/src/styles/components/_avatar.scss | 6 ------ frontend/src/styles/components/_backgrounds.scss | 6 ------ frontend/src/styles/components/_boxes.scss | 6 ------ frontend/src/styles/components/_buttons.scss | 6 ------ frontend/src/styles/components/_chats.scss | 6 ------ frontend/src/styles/components/_circles.scss | 6 ------ frontend/src/styles/components/_contextmenu.scss | 6 ------ frontend/src/styles/components/_core.scss | 6 ------ frontend/src/styles/components/_dropdowns.scss | 6 ------ frontend/src/styles/components/_fixes.scss | 6 ------ frontend/src/styles/components/_forms.scss | 6 ------ frontend/src/styles/components/_header.scss | 6 ------ frontend/src/styles/components/_links.scss | 6 ------ frontend/src/styles/components/_miscellaneous.scss | 6 ------ frontend/src/styles/components/_mixins.scss | 6 ------ frontend/src/styles/components/_modal.scss | 6 ------ frontend/src/styles/components/_notifications.scss | 6 ------ frontend/src/styles/components/_products.scss | 6 ------ frontend/src/styles/components/_profile.scss | 6 ------ frontend/src/styles/components/_sidebar_left.scss | 6 ------ .../src/styles/components/_sidebar_left_collapse.scss | 6 ------ frontend/src/styles/components/_sidebar_right.scss | 6 ------ frontend/src/styles/components/_tasks.scss | 6 ------ frontend/src/styles/components/_texts.scss | 6 ------ frontend/src/styles/components/_theme_dark.scss | 6 ------ frontend/src/styles/components/_theme_light.scss | 6 ------ frontend/src/styles/components/_themes.scss | 6 ------ frontend/src/styles/components/_tree.scss | 6 ------ frontend/src/styles/components/_variables.scss | 6 ------ frontend/src/styles/components/_wizard.scss | 6 ------ frontend/src/styles/components/index.scss | 6 ------ frontend/src/styles/index.scss | 6 ------ frontend/src/styles/variables.scss | 6 ------ scripts/unused-translations.js | 5 ----- 824 files changed, 10 insertions(+), 4922 deletions(-) diff --git a/LICENSE b/LICENSE index 40b88506..bb61842c 100644 --- a/LICENSE +++ b/LICENSE @@ -629,7 +629,7 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Sync-in® Copyright (C) 2012-2025 Johan Legrand + Sync-in® Copyright (C) 2012-2026 Johan Legrand This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published diff --git a/backend/eslint.config.mjs b/backend/eslint.config.mjs index abe182b2..0e46da18 100644 --- a/backend/eslint.config.mjs +++ b/backend/eslint.config.mjs @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // @ts-check import eslint from '@eslint/js' import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' diff --git a/backend/src/app.bootstrap.ts b/backend/src/app.bootstrap.ts index 98ca7145..97fa98b1 100644 --- a/backend/src/app.bootstrap.ts +++ b/backend/src/app.bootstrap.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fastifyCookie from '@fastify/cookie' import fastifyHelmet from '@fastify/helmet' import multipart from '@fastify/multipart' diff --git a/backend/src/app.constants.ts b/backend/src/app.constants.ts index 53640026..92cf253b 100644 --- a/backend/src/app.constants.ts +++ b/backend/src/app.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { loadVersion } from './app.functions' export const VERSION = loadVersion() diff --git a/backend/src/app.e2e-spec.ts b/backend/src/app.e2e-spec.ts index 2178b53f..eaa979bf 100644 --- a/backend/src/app.e2e-spec.ts +++ b/backend/src/app.e2e-spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { appBootstrap } from './app.bootstrap' import { dbCloseConnection } from './infrastructure/database/utils' diff --git a/backend/src/app.functions.ts b/backend/src/app.functions.ts index bdfd1257..6369b229 100644 --- a/backend/src/app.functions.ts +++ b/backend/src/app.functions.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { existsSync, readFileSync } from 'fs' import { join, resolve } from 'node:path' import { IS_TEST_ENV } from './configuration/config.constants' diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index d9e67c97..067f4321 100755 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpModule } from '@nestjs/axios' import { Module } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' diff --git a/backend/src/app.service.spec.ts b/backend/src/app.service.spec.ts index 0e1b130c..7cc40cb4 100644 --- a/backend/src/app.service.spec.ts +++ b/backend/src/app.service.spec.ts @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Logger } from '@nestjs/common' +import { setupPrimary } from '@socket.io/cluster-adapter' import cluster from 'node:cluster' import fs from 'node:fs' import os from 'node:os' @@ -13,10 +8,10 @@ import process from 'node:process' import { AppService } from './app.service' import { ENVIRONMENT_PREFIX } from './configuration/config.constants' import { configuration, exportConfiguration } from './configuration/config.environment' + jest.mock('@socket.io/cluster-adapter', () => ({ setupPrimary: jest.fn() })) -import { setupPrimary } from '@socket.io/cluster-adapter' describe(AppService.name, () => { let appService: AppService diff --git a/backend/src/app.service.ts b/backend/src/app.service.ts index 3b7819d2..9588898f 100755 --- a/backend/src/app.service.ts +++ b/backend/src/app.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import { setupPrimary } from '@socket.io/cluster-adapter' import cluster, { Worker } from 'node:cluster' diff --git a/backend/src/applications/admin/admin.module.ts b/backend/src/applications/admin/admin.module.ts index 63055619..39b7c454 100644 --- a/backend/src/applications/admin/admin.module.ts +++ b/backend/src/applications/admin/admin.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { AdminSchedulerService } from './services/admin-scheduler.service' import { AdminService } from './services/admin.service' diff --git a/backend/src/applications/admin/constants/routes.ts b/backend/src/applications/admin/constants/routes.ts index 74e9f4be..ca7d956a 100644 --- a/backend/src/applications/admin/constants/routes.ts +++ b/backend/src/applications/admin/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const ADMIN_ROUTE = { BASE: '/api/admin' } diff --git a/backend/src/applications/admin/interfaces/check-update.interfaces.ts b/backend/src/applications/admin/interfaces/check-update.interfaces.ts index e7b11c83..b81eb609 100644 --- a/backend/src/applications/admin/interfaces/check-update.interfaces.ts +++ b/backend/src/applications/admin/interfaces/check-update.interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface ServerReleaseVersionManifest { // tag_name: `v1.0.0` tag_name: string diff --git a/backend/src/applications/admin/services/admin-scheduler.service.ts b/backend/src/applications/admin/services/admin-scheduler.service.ts index c74ecef6..a054429f 100644 --- a/backend/src/applications/admin/services/admin-scheduler.service.ts +++ b/backend/src/applications/admin/services/admin-scheduler.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { Cron, CronExpression } from '@nestjs/schedule' import { setTimeout } from 'node:timers/promises' diff --git a/backend/src/applications/admin/services/admin.service.spec.ts b/backend/src/applications/admin/services/admin.service.spec.ts index 44c650a2..b1d7ae04 100644 --- a/backend/src/applications/admin/services/admin.service.spec.ts +++ b/backend/src/applications/admin/services/admin.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../../infrastructure/cache/services/cache.service' diff --git a/backend/src/applications/admin/services/admin.service.ts b/backend/src/applications/admin/services/admin.service.ts index ed727296..c9332680 100644 --- a/backend/src/applications/admin/services/admin.service.ts +++ b/backend/src/applications/admin/services/admin.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { Injectable, Logger } from '@nestjs/common' import type { AxiosResponse } from 'axios' diff --git a/backend/src/applications/admin/utils/check-update.ts b/backend/src/applications/admin/utils/check-update.ts index a2356fa8..7d714e36 100644 --- a/backend/src/applications/admin/utils/check-update.ts +++ b/backend/src/applications/admin/utils/check-update.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export function isServerUpdateAvailable(current: string, latest: string) { const c = current.split('.').map(Number) const l = latest.split('.').map(Number) diff --git a/backend/src/applications/applications.config.ts b/backend/src/applications/applications.config.ts index 59f2a279..278c20de 100644 --- a/backend/src/applications/applications.config.ts +++ b/backend/src/applications/applications.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsDefined, IsNotEmptyObject, IsObject, ValidateNested } from 'class-validator' import { FilesConfig } from './files/files.config' diff --git a/backend/src/applications/applications.constants.ts b/backend/src/applications/applications.constants.ts index 6d709f03..d8193b0c 100644 --- a/backend/src/applications/applications.constants.ts +++ b/backend/src/applications/applications.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const APP_BASE_ROUTE = '/api/app' export const HTTP_WEBDAV_METHOD = { diff --git a/backend/src/applications/applications.module.ts b/backend/src/applications/applications.module.ts index 849be375..d4a580b7 100644 --- a/backend/src/applications/applications.module.ts +++ b/backend/src/applications/applications.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { AdminModule } from './admin/admin.module' import { CommentsModule } from './comments/comments.module' diff --git a/backend/src/applications/comments/comments.controller.spec.ts b/backend/src/applications/comments/comments.controller.spec.ts index c81713a8..fc9c98d3 100644 --- a/backend/src/applications/comments/comments.controller.spec.ts +++ b/backend/src/applications/comments/comments.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../infrastructure/cache/services/cache.service' import { ContextManager } from '../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/comments/comments.controller.ts b/backend/src/applications/comments/comments.controller.ts index 581ec11e..28786170 100644 --- a/backend/src/applications/comments/comments.controller.ts +++ b/backend/src/applications/comments/comments.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Delete, Get, Patch, Post, Query, UseGuards, UseInterceptors } from '@nestjs/common' import { ContextInterceptor } from '../../infrastructure/context/interceptors/context.interceptor' import { SkipSpaceGuard } from '../spaces/decorators/space-skip-guard.decorator' diff --git a/backend/src/applications/comments/comments.module.ts b/backend/src/applications/comments/comments.module.ts index 88ee3f61..90268e8a 100644 --- a/backend/src/applications/comments/comments.module.ts +++ b/backend/src/applications/comments/comments.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { CommentsController } from './comments.controller' import { CommentsManager } from './services/comments-manager.service' diff --git a/backend/src/applications/comments/constants/routes.ts b/backend/src/applications/comments/constants/routes.ts index 578da38a..88a68290 100644 --- a/backend/src/applications/comments/constants/routes.ts +++ b/backend/src/applications/comments/constants/routes.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { APP_BASE_ROUTE } from '../../applications.constants' export const COMMENTS_ROUTE = { diff --git a/backend/src/applications/comments/dto/comment.dto.ts b/backend/src/applications/comments/dto/comment.dto.ts index f43bb15c..8e394424 100644 --- a/backend/src/applications/comments/dto/comment.dto.ts +++ b/backend/src/applications/comments/dto/comment.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator' export class CreateOrUpdateCommentDto { diff --git a/backend/src/applications/comments/interfaces/comment-recent.interface.ts b/backend/src/applications/comments/interfaces/comment-recent.interface.ts index e2d34d3a..ee1ede1c 100644 --- a/backend/src/applications/comments/interfaces/comment-recent.interface.ts +++ b/backend/src/applications/comments/interfaces/comment-recent.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { Owner } from '../../users/interfaces/owner.interface' export interface CommentRecent { diff --git a/backend/src/applications/comments/schemas/comment.interface.ts b/backend/src/applications/comments/schemas/comment.interface.ts index d22f414c..64beb407 100644 --- a/backend/src/applications/comments/schemas/comment.interface.ts +++ b/backend/src/applications/comments/schemas/comment.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Owner } from '../../users/interfaces/owner.interface' import type { comments } from './comments.schema' diff --git a/backend/src/applications/comments/schemas/comments.schema.ts b/backend/src/applications/comments/schemas/comments.schema.ts index da2c0166..6e79c524 100644 --- a/backend/src/applications/comments/schemas/comments.schema.ts +++ b/backend/src/applications/comments/schemas/comments.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Column, SQL, sql } from 'drizzle-orm' import { bigint, datetime, index, mysqlTable, text } from 'drizzle-orm/mysql-core' import { files } from '../../files/schemas/files.schema' diff --git a/backend/src/applications/comments/services/comments-manager.service.spec.ts b/backend/src/applications/comments/services/comments-manager.service.spec.ts index 9fb9897a..b535dfc1 100644 --- a/backend/src/applications/comments/services/comments-manager.service.spec.ts +++ b/backend/src/applications/comments/services/comments-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../../infrastructure/cache/services/cache.service' diff --git a/backend/src/applications/comments/services/comments-manager.service.ts b/backend/src/applications/comments/services/comments-manager.service.ts index c27a6c8b..fba718de 100644 --- a/backend/src/applications/comments/services/comments-manager.service.ts +++ b/backend/src/applications/comments/services/comments-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { ContextManager } from '../../../infrastructure/context/services/context-manager.service' import type { FileProps } from '../../files/interfaces/file-props.interface' diff --git a/backend/src/applications/comments/services/comments-queries.service.ts b/backend/src/applications/comments/services/comments-queries.service.ts index 2fe37790..46c50b51 100644 --- a/backend/src/applications/comments/services/comments-queries.service.ts +++ b/backend/src/applications/comments/services/comments-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable } from '@nestjs/common' import { and, desc, eq, getTableColumns, inArray, isNotNull, isNull, ne, or, SelectedFields, SQL, sql } from 'drizzle-orm' import { alias, union } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/files/adapters/files-indexer-mysql.service.spec.ts b/backend/src/applications/files/adapters/files-indexer-mysql.service.spec.ts index 96d8cba6..22954976 100644 --- a/backend/src/applications/files/adapters/files-indexer-mysql.service.spec.ts +++ b/backend/src/applications/files/adapters/files-indexer-mysql.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' diff --git a/backend/src/applications/files/adapters/files-indexer-mysql.service.ts b/backend/src/applications/files/adapters/files-indexer-mysql.service.ts index d775f9c4..45cdc0e9 100644 --- a/backend/src/applications/files/adapters/files-indexer-mysql.service.ts +++ b/backend/src/applications/files/adapters/files-indexer-mysql.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { SQL, sql } from 'drizzle-orm' import { MySqlQueryResult } from 'drizzle-orm/mysql2' diff --git a/backend/src/applications/files/constants/cache.ts b/backend/src/applications/files/constants/cache.ts index 2308b9b9..cd06483a 100644 --- a/backend/src/applications/files/constants/cache.ts +++ b/backend/src/applications/files/constants/cache.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // cache task key = `ftask-$(userId}-${taskId}` => FileTask export const CACHE_TASK_PREFIX = 'ftask' as const export const CACHE_TASK_TTL = 86400 as const // one day diff --git a/backend/src/applications/files/constants/compress.ts b/backend/src/applications/files/constants/compress.ts index 6bec6fad..48e2beea 100644 --- a/backend/src/applications/files/constants/compress.ts +++ b/backend/src/applications/files/constants/compress.ts @@ -1,8 +1,2 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const tarExtension = 'tar' export const tarGzExtension = 'tgz' diff --git a/backend/src/applications/files/constants/files.ts b/backend/src/applications/files/constants/files.ts index 15eb29fa..00b391d0 100644 --- a/backend/src/applications/files/constants/files.ts +++ b/backend/src/applications/files/constants/files.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const DEFAULT_CHECKSUM_ALGORITHM = 'sha512-256' export const DEFAULT_HIGH_WATER_MARK = 1024 * 1024 export const DEFAULT_MIME_TYPE = 'application/octet-stream' diff --git a/backend/src/applications/files/constants/indexing.ts b/backend/src/applications/files/constants/indexing.ts index d56dab26..aba74315 100644 --- a/backend/src/applications/files/constants/indexing.ts +++ b/backend/src/applications/files/constants/indexing.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const userIndexPrefix = 'user_' export const spaceIndexPrefix = 'space_' export const shareIndexPrefix = 'share_' diff --git a/backend/src/applications/files/constants/operations.ts b/backend/src/applications/files/constants/operations.ts index 8ba0d8bc..ce800d45 100644 --- a/backend/src/applications/files/constants/operations.ts +++ b/backend/src/applications/files/constants/operations.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum FILE_OPERATION { MAKE = 'make', COPY = 'copy', diff --git a/backend/src/applications/files/constants/routes.ts b/backend/src/applications/files/constants/routes.ts index ff8c7222..1e98a069 100644 --- a/backend/src/applications/files/constants/routes.ts +++ b/backend/src/applications/files/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SPACES_ROUTE } from '../../spaces/constants/routes' import { FILE_OPERATION } from './operations' diff --git a/backend/src/applications/files/constants/samples.ts b/backend/src/applications/files/constants/samples.ts index f7f70cde..b15c22db 100644 --- a/backend/src/applications/files/constants/samples.ts +++ b/backend/src/applications/files/constants/samples.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const SAMPLE_PATH_WITHOUT_EXT = '../assets/samples/sample' export const DOCUMENT_TYPE = { 'Microsoft Word': '.docx', diff --git a/backend/src/applications/files/dto/file-operations.dto.ts b/backend/src/applications/files/dto/file-operations.dto.ts index 4da50717..50f986bd 100644 --- a/backend/src/applications/files/dto/file-operations.dto.ts +++ b/backend/src/applications/files/dto/file-operations.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { ArrayMinSize, IsArray, IsBoolean, IsDefined, IsIn, IsInt, IsNotEmpty, IsOptional, IsString, IsUrl } from 'class-validator' import { RejectIfMatch } from '../../../common/decorators' diff --git a/backend/src/applications/files/events/file-task-event.ts b/backend/src/applications/files/events/file-task-event.ts index f8f562ff..806d688b 100644 --- a/backend/src/applications/files/events/file-task-event.ts +++ b/backend/src/applications/files/events/file-task-event.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import EventEmitter from 'node:events' export const FileTaskEvent: EventEmitter = new EventEmitter() diff --git a/backend/src/applications/files/files-tasks.controller.spec.ts b/backend/src/applications/files/files-tasks.controller.spec.ts index 084e464f..04b86f7e 100644 --- a/backend/src/applications/files/files-tasks.controller.spec.ts +++ b/backend/src/applications/files/files-tasks.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../infrastructure/cache/services/cache.service' import { FilesTasksController } from './files-tasks.controller' diff --git a/backend/src/applications/files/files-tasks.controller.ts b/backend/src/applications/files/files-tasks.controller.ts index 6dbb3b7e..4190946d 100644 --- a/backend/src/applications/files/files-tasks.controller.ts +++ b/backend/src/applications/files/files-tasks.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Controller, Delete, Get, Param, Req, Res, StreamableFile } from '@nestjs/common' import { FastifyReply } from 'fastify' import { FastifySpaceRequest } from '../spaces/interfaces/space-request.interface' diff --git a/backend/src/applications/files/files.config.ts b/backend/src/applications/files/files.config.ts index a7ab6280..f6f7feae 100644 --- a/backend/src/applications/files/files.config.ts +++ b/backend/src/applications/files/files.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsBoolean, IsInt, IsNotEmpty, IsNotEmptyObject, IsString, ValidateNested } from 'class-validator' import { CollaboraOnlineConfig } from './modules/collabora-online/collabora-online.config' diff --git a/backend/src/applications/files/files.controller.spec.ts b/backend/src/applications/files/files.controller.spec.ts index cf819d94..b06719f4 100644 --- a/backend/src/applications/files/files.controller.spec.ts +++ b/backend/src/applications/files/files.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { ContextInterceptor } from '../../infrastructure/context/interceptors/context.interceptor' import { ContextManager } from '../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/files/files.controller.ts b/backend/src/applications/files/files.controller.ts index 94d8647c..fc2c0503 100644 --- a/backend/src/applications/files/files.controller.ts +++ b/backend/src/applications/files/files.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, diff --git a/backend/src/applications/files/files.module.ts b/backend/src/applications/files/files.module.ts index 47ef8306..e10f9b7a 100644 --- a/backend/src/applications/files/files.module.ts +++ b/backend/src/applications/files/files.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { configuration } from '../../configuration/config.environment' import { FilesIndexerMySQL } from './adapters/files-indexer-mysql.service' diff --git a/backend/src/applications/files/interfaces/file-db-props.interface.ts b/backend/src/applications/files/interfaces/file-db-props.interface.ts index dc291b09..22541134 100644 --- a/backend/src/applications/files/interfaces/file-db-props.interface.ts +++ b/backend/src/applications/files/interfaces/file-db-props.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { File } from '../schemas/file.interface' export interface FileDBProps extends Partial> { diff --git a/backend/src/applications/files/interfaces/file-lock.interface.ts b/backend/src/applications/files/interfaces/file-lock.interface.ts index 9d8312d4..0d863ac7 100644 --- a/backend/src/applications/files/interfaces/file-lock.interface.ts +++ b/backend/src/applications/files/interfaces/file-lock.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SERVER_NAME } from '../../../common/shared' import { Owner } from '../../users/interfaces/owner.interface' import { LOCK_DEPTH, LOCK_SCOPE, WEBDAV_APP_LOCK } from '../../webdav/constants/webdav' diff --git a/backend/src/applications/files/interfaces/file-parse-index.ts b/backend/src/applications/files/interfaces/file-parse-index.ts index 8f72a899..eb825908 100644 --- a/backend/src/applications/files/interfaces/file-parse-index.ts +++ b/backend/src/applications/files/interfaces/file-parse-index.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export type FileParseType = 'user' | 'space' | 'share' export interface FileParseContext { diff --git a/backend/src/applications/files/interfaces/file-props.interface.ts b/backend/src/applications/files/interfaces/file-props.interface.ts index bedfda14..25573a8a 100644 --- a/backend/src/applications/files/interfaces/file-props.interface.ts +++ b/backend/src/applications/files/interfaces/file-props.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { Share } from '../../shares/schemas/share.interface' import type { SpaceRoot } from '../../spaces/schemas/space-root.interface' import type { Space } from '../../spaces/schemas/space.interface' diff --git a/backend/src/applications/files/interfaces/file-recent-location.interface.ts b/backend/src/applications/files/interfaces/file-recent-location.interface.ts index 18159d60..2d4acc9e 100644 --- a/backend/src/applications/files/interfaces/file-recent-location.interface.ts +++ b/backend/src/applications/files/interfaces/file-recent-location.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface FileRecentLocation { ownerId?: number spaceId?: number diff --git a/backend/src/applications/files/interfaces/file-space.interface.ts b/backend/src/applications/files/interfaces/file-space.interface.ts index 660db7fa..e61303ef 100644 --- a/backend/src/applications/files/interfaces/file-space.interface.ts +++ b/backend/src/applications/files/interfaces/file-space.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { File } from '../schemas/file.interface' export class FileSpace implements Pick { diff --git a/backend/src/applications/files/interfaces/file-tree.interface.ts b/backend/src/applications/files/interfaces/file-tree.interface.ts index bb7848e9..f632aa24 100644 --- a/backend/src/applications/files/interfaces/file-tree.interface.ts +++ b/backend/src/applications/files/interfaces/file-tree.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileProps } from './file-props.interface' export interface FileTree extends Pick { diff --git a/backend/src/applications/files/models/file-error.ts b/backend/src/applications/files/models/file-error.ts index 58021101..0df437d1 100644 --- a/backend/src/applications/files/models/file-error.ts +++ b/backend/src/applications/files/models/file-error.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export class FileError extends Error { httpCode: number diff --git a/backend/src/applications/files/models/file-lock-error.ts b/backend/src/applications/files/models/file-lock-error.ts index 3b50194a..25186fa2 100644 --- a/backend/src/applications/files/models/file-lock-error.ts +++ b/backend/src/applications/files/models/file-lock-error.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FileLock } from '../interfaces/file-lock.interface' export class LockConflict extends Error { diff --git a/backend/src/applications/files/models/file-task.ts b/backend/src/applications/files/models/file-task.ts index e62f2940..96b90db7 100644 --- a/backend/src/applications/files/models/file-task.ts +++ b/backend/src/applications/files/models/file-task.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FILE_OPERATION } from '../constants/operations' export enum FileTaskStatus { diff --git a/backend/src/applications/files/models/files-indexer.ts b/backend/src/applications/files/models/files-indexer.ts index f2299db9..68bbb78c 100644 --- a/backend/src/applications/files/models/files-indexer.ts +++ b/backend/src/applications/files/models/files-indexer.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FileContent } from '../schemas/file-content.interface' export abstract class FilesIndexer { diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online-environment.decorator.ts b/backend/src/applications/files/modules/collabora-online/collabora-online-environment.decorator.ts index dde035e5..4ca5865b 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online-environment.decorator.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online-environment.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common' import { SpaceGuard } from '../../../spaces/guards/space.guard' import { COLLABORA_CONTEXT } from './collabora-online.constants' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.spec.ts b/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.spec.ts index 855d2111..dd4a778d 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.spec.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.ts b/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.ts index 937f1a01..dcdb7df2 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' import { FastifyReply } from 'fastify' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.config.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.config.ts index bab3fc50..e5a891d2 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.config.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsBoolean, IsOptional, IsString } from 'class-validator' export class CollaboraOnlineConfig { diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.constants.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.constants.ts index 8e220f8f..3a4abedd 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.constants.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const COLLABORA_URI = 'browser/dist/cool.html' export const COLLABORA_CONTEXT = 'CollaboraOnlineEnvironment' as const export const COLLABORA_WOPI_SRC_QUERY_PARAM_NAME = 'WOPISrc' as const diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.controller.spec.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.controller.spec.ts index de6b0bc9..756a31ab 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.controller.spec.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { ContextInterceptor } from '../../../../infrastructure/context/interceptors/context.interceptor' import { ContextManager } from '../../../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.controller.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.controller.ts index 0dfa1af4..df51f08d 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.controller.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Controller, Get, HttpCode, HttpStatus, Post, Request, Res, StreamableFile, UseInterceptors } from '@nestjs/common' import { FastifyReply } from 'fastify' import { ContextInterceptor } from '../../../../infrastructure/context/interceptors/context.interceptor' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.dtos.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.dtos.ts index 7d246439..a4904102 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.dtos.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.dtos.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FILE_MODE } from '../../constants/operations' import type { FileLockProps } from '../../interfaces/file-props.interface' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.guard.spec.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.guard.spec.ts index 1e1f1fdb..da43ddd0 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.guard.spec.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' import { JwtModule, JwtService } from '@nestjs/jwt' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.guard.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.guard.ts index d1c5b38f..a2b721ce 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.guard.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.interface.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.interface.ts index 4274d4bc..89304623 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.interface.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { JwtIdentityPayload } from '../../../../authentication/interfaces/jwt-payload.interface' import type { FastifySpaceRequest } from '../../../spaces/interfaces/space-request.interface' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.module.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.module.ts index 0630e69a..feb822ad 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.module.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { CollaboraOnlineManager } from './collabora-online-manager.service' import { CollaboraOnlineController } from './collabora-online.controller' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.routes.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.routes.ts index 6a4c263f..f7e0a184 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.routes.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const COLLABORA_ONLINE_ROUTE = { BASE: '/wopi', FILES: 'files', diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.strategy.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.strategy.ts index c6b27584..c6b47b54 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.strategy.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import { PinoLogger } from 'nestjs-pino' diff --git a/backend/src/applications/files/modules/collabora-online/collabora-online.utils.ts b/backend/src/applications/files/modules/collabora-online/collabora-online.utils.ts index 4b2acfa0..0e2cd179 100644 --- a/backend/src/applications/files/modules/collabora-online/collabora-online.utils.ts +++ b/backend/src/applications/files/modules/collabora-online/collabora-online.utils.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { PATH_TO_SPACE_SEGMENTS } from '../../../spaces/utils/routes' import type { FastifyCollaboraOnlineSpaceRequest } from './collabora-online.interface' diff --git a/backend/src/applications/files/modules/only-office/only-office-environment.decorator.ts b/backend/src/applications/files/modules/only-office/only-office-environment.decorator.ts index 5017a0f5..190ef0bc 100644 --- a/backend/src/applications/files/modules/only-office/only-office-environment.decorator.ts +++ b/backend/src/applications/files/modules/only-office/only-office-environment.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common' import { SpaceGuard } from '../../../spaces/guards/space.guard' import { ONLY_OFFICE_CONTEXT } from './only-office.constants' diff --git a/backend/src/applications/files/modules/only-office/only-office-manager.service.spec.ts b/backend/src/applications/files/modules/only-office/only-office-manager.service.spec.ts index 6484a90b..149a25d8 100644 --- a/backend/src/applications/files/modules/only-office/only-office-manager.service.spec.ts +++ b/backend/src/applications/files/modules/only-office/only-office-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { HttpException, HttpStatus } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' diff --git a/backend/src/applications/files/modules/only-office/only-office-manager.service.ts b/backend/src/applications/files/modules/only-office/only-office-manager.service.ts index e7779373..49db9a3d 100644 --- a/backend/src/applications/files/modules/only-office/only-office-manager.service.ts +++ b/backend/src/applications/files/modules/only-office/only-office-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' diff --git a/backend/src/applications/files/modules/only-office/only-office.config.ts b/backend/src/applications/files/modules/only-office/only-office.config.ts index 7c7a754d..111c73a4 100644 --- a/backend/src/applications/files/modules/only-office/only-office.config.ts +++ b/backend/src/applications/files/modules/only-office/only-office.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsBoolean, IsNotEmpty, IsOptional, IsString, ValidateIf } from 'class-validator' export class OnlyOfficeConfig { diff --git a/backend/src/applications/files/modules/only-office/only-office.constants.ts b/backend/src/applications/files/modules/only-office/only-office.constants.ts index 9b08f187..0b970a82 100644 --- a/backend/src/applications/files/modules/only-office/only-office.constants.ts +++ b/backend/src/applications/files/modules/only-office/only-office.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const ONLY_OFFICE_INTERNAL_URI = '/onlyoffice' // used by nginx as a proxy export const ONLY_OFFICE_CONTEXT = 'OnlyOfficeEnvironment' as const export const ONLY_OFFICE_TOKEN_QUERY_PARAM_NAME = 'token' as const diff --git a/backend/src/applications/files/modules/only-office/only-office.controller.spec.ts b/backend/src/applications/files/modules/only-office/only-office.controller.spec.ts index a419e9ee..0311b72d 100644 --- a/backend/src/applications/files/modules/only-office/only-office.controller.spec.ts +++ b/backend/src/applications/files/modules/only-office/only-office.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { ContextInterceptor } from '../../../../infrastructure/context/interceptors/context.interceptor' import { ContextManager } from '../../../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/files/modules/only-office/only-office.controller.ts b/backend/src/applications/files/modules/only-office/only-office.controller.ts index b2260b5a..815d0757 100644 --- a/backend/src/applications/files/modules/only-office/only-office.controller.ts +++ b/backend/src/applications/files/modules/only-office/only-office.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Get, HttpCode, HttpStatus, Post, Request, Res, StreamableFile, UseInterceptors } from '@nestjs/common' import { FastifyReply } from 'fastify' import { ContextInterceptor } from '../../../../infrastructure/context/interceptors/context.interceptor' diff --git a/backend/src/applications/files/modules/only-office/only-office.dtos.ts b/backend/src/applications/files/modules/only-office/only-office.dtos.ts index 622f3079..2263b80f 100644 --- a/backend/src/applications/files/modules/only-office/only-office.dtos.ts +++ b/backend/src/applications/files/modules/only-office/only-office.dtos.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileLockProps } from '../../interfaces/file-props.interface' import type { OnlyOfficeConfig } from './only-office.interface' diff --git a/backend/src/applications/files/modules/only-office/only-office.guard.spec.ts b/backend/src/applications/files/modules/only-office/only-office.guard.spec.ts index fd6b9556..9eda9893 100644 --- a/backend/src/applications/files/modules/only-office/only-office.guard.spec.ts +++ b/backend/src/applications/files/modules/only-office/only-office.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' import { JwtModule, JwtService } from '@nestjs/jwt' diff --git a/backend/src/applications/files/modules/only-office/only-office.guard.ts b/backend/src/applications/files/modules/only-office/only-office.guard.ts index 77c518f5..d4c8fc80 100644 --- a/backend/src/applications/files/modules/only-office/only-office.guard.ts +++ b/backend/src/applications/files/modules/only-office/only-office.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/applications/files/modules/only-office/only-office.interface.ts b/backend/src/applications/files/modules/only-office/only-office.interface.ts index 826a67e7..87801c87 100644 --- a/backend/src/applications/files/modules/only-office/only-office.interface.ts +++ b/backend/src/applications/files/modules/only-office/only-office.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FILE_MODE } from '../../constants/operations' export interface OnlyOfficeConvertForm { diff --git a/backend/src/applications/files/modules/only-office/only-office.module.ts b/backend/src/applications/files/modules/only-office/only-office.module.ts index 855d907f..339d64ff 100644 --- a/backend/src/applications/files/modules/only-office/only-office.module.ts +++ b/backend/src/applications/files/modules/only-office/only-office.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { OnlyOfficeManager } from './only-office-manager.service' import { OnlyOfficeController } from './only-office.controller' diff --git a/backend/src/applications/files/modules/only-office/only-office.routes.ts b/backend/src/applications/files/modules/only-office/only-office.routes.ts index abef1aa6..26465f3c 100644 --- a/backend/src/applications/files/modules/only-office/only-office.routes.ts +++ b/backend/src/applications/files/modules/only-office/only-office.routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SPACES_ROUTE } from '../../../spaces/constants/routes' export const ONLY_OFFICE_ROUTE = { diff --git a/backend/src/applications/files/modules/only-office/only-office.strategy.ts b/backend/src/applications/files/modules/only-office/only-office.strategy.ts index c933ce2c..b85fb83c 100644 --- a/backend/src/applications/files/modules/only-office/only-office.strategy.ts +++ b/backend/src/applications/files/modules/only-office/only-office.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import { PinoLogger } from 'nestjs-pino' diff --git a/backend/src/applications/files/schemas/file-content.interface.ts b/backend/src/applications/files/schemas/file-content.interface.ts index e7772276..695927d5 100644 --- a/backend/src/applications/files/schemas/file-content.interface.ts +++ b/backend/src/applications/files/schemas/file-content.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface FileContent { id: number path: string diff --git a/backend/src/applications/files/schemas/file-recent.interface.ts b/backend/src/applications/files/schemas/file-recent.interface.ts index 28380c72..f165716f 100644 --- a/backend/src/applications/files/schemas/file-recent.interface.ts +++ b/backend/src/applications/files/schemas/file-recent.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { filesRecents } from './files-recents.schema' type FileRecentSchema = typeof filesRecents.$inferSelect diff --git a/backend/src/applications/files/schemas/file.interface.ts b/backend/src/applications/files/schemas/file.interface.ts index 4a7210f6..3d7a4bb7 100644 --- a/backend/src/applications/files/schemas/file.interface.ts +++ b/backend/src/applications/files/schemas/file.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { files } from './files.schema' type FileSchema = typeof files.$inferSelect diff --git a/backend/src/applications/files/schemas/files-content.schema.ts b/backend/src/applications/files/schemas/files-content.schema.ts index 0d0b07c2..ce2952f3 100644 --- a/backend/src/applications/files/schemas/files-content.schema.ts +++ b/backend/src/applications/files/schemas/files-content.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const FILES_CONTENT_TABLE_PREFIX = 'files_content_' as const // The utf8mb4_uca1400_ai_ci COLLATE is better for precision but slower diff --git a/backend/src/applications/files/schemas/files-recents.schema.ts b/backend/src/applications/files/schemas/files-recents.schema.ts index 0672de40..66cb1502 100644 --- a/backend/src/applications/files/schemas/files-recents.schema.ts +++ b/backend/src/applications/files/schemas/files-recents.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { bigint, index, mysqlTable, varchar } from 'drizzle-orm/mysql-core' import { shares } from '../../shares/schemas/shares.schema' import { spaces } from '../../spaces/schemas/spaces.schema' diff --git a/backend/src/applications/files/schemas/files.schema.ts b/backend/src/applications/files/schemas/files.schema.ts index edf7cceb..2b930a9f 100644 --- a/backend/src/applications/files/schemas/files.schema.ts +++ b/backend/src/applications/files/schemas/files.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SQL, sql } from 'drizzle-orm' import { AnyMySqlColumn, bigint, boolean, index, mysqlTable, varchar } from 'drizzle-orm/mysql-core' import { escapeSQLRegexp } from '../../../common/functions' diff --git a/backend/src/applications/files/services/files-content-manager.service.ts b/backend/src/applications/files/services/files-content-manager.service.ts index 8f34ed44..79e66346 100644 --- a/backend/src/applications/files/services/files-content-manager.service.ts +++ b/backend/src/applications/files/services/files-content-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import fs from 'fs/promises' import { Stats } from 'node:fs' diff --git a/backend/src/applications/files/services/files-lock-manager.service.spec.ts b/backend/src/applications/files/services/files-lock-manager.service.spec.ts index e21c3f2a..b4570e59 100644 --- a/backend/src/applications/files/services/files-lock-manager.service.spec.ts +++ b/backend/src/applications/files/services/files-lock-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' diff --git a/backend/src/applications/files/services/files-lock-manager.service.ts b/backend/src/applications/files/services/files-lock-manager.service.ts index 9698d313..30d0cdd8 100644 --- a/backend/src/applications/files/services/files-lock-manager.service.ts +++ b/backend/src/applications/files/services/files-lock-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import crypto from 'node:crypto' diff --git a/backend/src/applications/files/services/files-manager.service.spec.ts b/backend/src/applications/files/services/files-manager.service.spec.ts index 7a86f5c4..eef29ebe 100644 --- a/backend/src/applications/files/services/files-manager.service.spec.ts +++ b/backend/src/applications/files/services/files-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { Test, TestingModule } from '@nestjs/testing' import { ContextManager } from '../../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/files/services/files-manager.service.ts b/backend/src/applications/files/services/files-manager.service.ts index f6b16041..883c2d11 100644 --- a/backend/src/applications/files/services/files-manager.service.ts +++ b/backend/src/applications/files/services/files-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { HttpStatus, Injectable, Logger } from '@nestjs/common' import archiver, { Archiver, ArchiverError } from 'archiver' diff --git a/backend/src/applications/files/services/files-methods.service.spec.ts b/backend/src/applications/files/services/files-methods.service.spec.ts index 5d4c2a62..5c32debb 100644 --- a/backend/src/applications/files/services/files-methods.service.spec.ts +++ b/backend/src/applications/files/services/files-methods.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import path from 'node:path' import { transformAndValidate } from '../../../common/functions' diff --git a/backend/src/applications/files/services/files-methods.service.ts b/backend/src/applications/files/services/files-methods.service.ts index 9adc7b1c..2d33ee18 100644 --- a/backend/src/applications/files/services/files-methods.service.ts +++ b/backend/src/applications/files/services/files-methods.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply } from 'fastify' import path from 'node:path' diff --git a/backend/src/applications/files/services/files-parser.service.spec.ts b/backend/src/applications/files/services/files-parser.service.spec.ts index eec00511..be77d6a4 100644 --- a/backend/src/applications/files/services/files-parser.service.spec.ts +++ b/backend/src/applications/files/services/files-parser.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' import { FilesParser } from './files-parser.service' diff --git a/backend/src/applications/files/services/files-parser.service.ts b/backend/src/applications/files/services/files-parser.service.ts index d0df7d9f..8afab095 100644 --- a/backend/src/applications/files/services/files-parser.service.ts +++ b/backend/src/applications/files/services/files-parser.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, eq, inArray, isNotNull, isNull, lte, or, sql } from 'drizzle-orm' import path from 'node:path' diff --git a/backend/src/applications/files/services/files-queries.service.ts b/backend/src/applications/files/services/files-queries.service.ts index ad51c187..9eb35bde 100644 --- a/backend/src/applications/files/services/files-queries.service.ts +++ b/backend/src/applications/files/services/files-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, desc, eq, getTableColumns, inArray, isNull, or, SelectedFields, sql, SQL } from 'drizzle-orm' import { popFromObject } from '../../../common/shared' diff --git a/backend/src/applications/files/services/files-recents.service.spec.ts b/backend/src/applications/files/services/files-recents.service.spec.ts index 5f3aba25..0174d559 100644 --- a/backend/src/applications/files/services/files-recents.service.spec.ts +++ b/backend/src/applications/files/services/files-recents.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { SharesQueries } from '../../shares/services/shares-queries.service' import { SpacesQueries } from '../../spaces/services/spaces-queries.service' diff --git a/backend/src/applications/files/services/files-recents.service.ts b/backend/src/applications/files/services/files-recents.service.ts index fd7e5837..d41a7346 100644 --- a/backend/src/applications/files/services/files-recents.service.ts +++ b/backend/src/applications/files/services/files-recents.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { convertDiffUpdate, convertHumanTimeToMs, diffCollection } from '../../../common/functions' import { currentTimeStamp } from '../../../common/shared' diff --git a/backend/src/applications/files/services/files-scheduler.service.ts b/backend/src/applications/files/services/files-scheduler.service.ts index 174b2897..8153e1c1 100644 --- a/backend/src/applications/files/services/files-scheduler.service.ts +++ b/backend/src/applications/files/services/files-scheduler.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { Cron, CronExpression, Timeout } from '@nestjs/schedule' import { isNotNull, sql } from 'drizzle-orm' diff --git a/backend/src/applications/files/services/files-search-manager.service.spec.ts b/backend/src/applications/files/services/files-search-manager.service.spec.ts index 7b77cbfe..95b99383 100644 --- a/backend/src/applications/files/services/files-search-manager.service.spec.ts +++ b/backend/src/applications/files/services/files-search-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { SharesQueries } from '../../shares/services/shares-queries.service' import { SpacesQueries } from '../../spaces/services/spaces-queries.service' diff --git a/backend/src/applications/files/services/files-search-manager.service.ts b/backend/src/applications/files/services/files-search-manager.service.ts index 904093b5..e6ac10b0 100644 --- a/backend/src/applications/files/services/files-search-manager.service.ts +++ b/backend/src/applications/files/services/files-search-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import fs from 'fs/promises' import { Stats } from 'node:fs' diff --git a/backend/src/applications/files/services/files-tasks-manager.service.spec.ts b/backend/src/applications/files/services/files-tasks-manager.service.spec.ts index e49e488a..947a957c 100644 --- a/backend/src/applications/files/services/files-tasks-manager.service.spec.ts +++ b/backend/src/applications/files/services/files-tasks-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../../infrastructure/cache/services/cache.service' import { SpacesManager } from '../../spaces/services/spaces-manager.service' diff --git a/backend/src/applications/files/services/files-tasks-manager.service.ts b/backend/src/applications/files/services/files-tasks-manager.service.ts index 3d93a0fc..6c5b9d80 100644 --- a/backend/src/applications/files/services/files-tasks-manager.service.ts +++ b/backend/src/applications/files/services/files-tasks-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply } from 'fastify' import crypto from 'node:crypto' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/excel.ts b/backend/src/applications/files/utils/doc-textify/adapters/excel.ts index 8e26887b..aeb0a89b 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/excel.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/excel.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { promisify } from 'node:util' import sax from 'sax' import { open as openZip, Options, ZipFile } from 'yauzl' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/html.ts b/backend/src/applications/files/utils/doc-textify/adapters/html.ts index a9d787c0..df913dac 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/html.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/html.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'fs/promises' import { compile } from 'html-to-text' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/open-office.ts b/backend/src/applications/files/utils/doc-textify/adapters/open-office.ts index 056553ce..125e5823 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/open-office.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/open-office.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { promisify } from 'node:util' import sax from 'sax' import { Entry, open as openZip, Options, ZipFile } from 'yauzl' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/pdf.ts b/backend/src/applications/files/utils/doc-textify/adapters/pdf.ts index a2d7196a..b97c960e 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/pdf.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/pdf.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { readFile } from 'node:fs/promises' import { getDocumentProxy } from 'unpdf' import type { PDFDocumentProxy } from 'unpdf/pdfjs' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/power-point.ts b/backend/src/applications/files/utils/doc-textify/adapters/power-point.ts index 95c6a2b8..bdcde674 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/power-point.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/power-point.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { promisify } from 'node:util' import sax from 'sax' import { Entry, open as openZip, Options, ZipFile } from 'yauzl' diff --git a/backend/src/applications/files/utils/doc-textify/adapters/text.ts b/backend/src/applications/files/utils/doc-textify/adapters/text.ts index 3033d3ae..959cac14 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/text.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/text.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'fs/promises' export async function parseText(filePath: string): Promise { diff --git a/backend/src/applications/files/utils/doc-textify/adapters/word.ts b/backend/src/applications/files/utils/doc-textify/adapters/word.ts index b93154b2..34fcfb92 100644 --- a/backend/src/applications/files/utils/doc-textify/adapters/word.ts +++ b/backend/src/applications/files/utils/doc-textify/adapters/word.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { promisify } from 'node:util' import sax from 'sax' import { Entry, open as openZip, Options, ZipFile } from 'yauzl' diff --git a/backend/src/applications/files/utils/doc-textify/doc-textify.ts b/backend/src/applications/files/utils/doc-textify/doc-textify.ts index 314d38b0..473c7ae5 100644 --- a/backend/src/applications/files/utils/doc-textify/doc-textify.ts +++ b/backend/src/applications/files/utils/doc-textify/doc-textify.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'node:fs/promises' import path from 'node:path' import { parseExcel } from './adapters/excel' diff --git a/backend/src/applications/files/utils/doc-textify/interfaces/doc-textify.interfaces.ts b/backend/src/applications/files/utils/doc-textify/interfaces/doc-textify.interfaces.ts index d3560f91..9a298f83 100644 --- a/backend/src/applications/files/utils/doc-textify/interfaces/doc-textify.interfaces.ts +++ b/backend/src/applications/files/utils/doc-textify/interfaces/doc-textify.interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface DocTextifyOptions { outputErrorToConsole?: boolean newlineDelimiter: string diff --git a/backend/src/applications/files/utils/doc-textify/utils/clean.ts b/backend/src/applications/files/utils/doc-textify/utils/clean.ts index 2a3f24da..0fdf4523 100644 --- a/backend/src/applications/files/utils/doc-textify/utils/clean.ts +++ b/backend/src/applications/files/utils/doc-textify/utils/clean.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { DocTextifyOptions } from '../interfaces/doc-textify.interfaces' const regexAlphanumeric = /[a-zA-Z0-9]/ diff --git a/backend/src/applications/files/utils/files-search.ts b/backend/src/applications/files/utils/files-search.ts index 6833dc83..b9dab0ee 100644 --- a/backend/src/applications/files/utils/files-search.ts +++ b/backend/src/applications/files/utils/files-search.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { escapeString } from '../../../common/functions' import { minCharsToSearch } from '../constants/indexing' diff --git a/backend/src/applications/files/utils/files-tree.ts b/backend/src/applications/files/utils/files-tree.ts index 501a3f1f..8e306afe 100644 --- a/backend/src/applications/files/utils/files-tree.ts +++ b/backend/src/applications/files/utils/files-tree.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import path from 'node:path' import { sortObjByName } from '../../../common/functions' import { SHARE_ALL_OPERATIONS } from '../../shares/constants/shares' diff --git a/backend/src/applications/files/utils/files.ts b/backend/src/applications/files/utils/files.ts index 8f478138..1c832389 100644 --- a/backend/src/applications/files/utils/files.ts +++ b/backend/src/applications/files/utils/files.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpStatus } from '@nestjs/common' import { WriteStream } from 'fs' import fse from 'fs-extra' diff --git a/backend/src/applications/files/utils/send-file.ts b/backend/src/applications/files/utils/send-file.ts index 3e4a095f..2776fc65 100644 --- a/backend/src/applications/files/utils/send-file.ts +++ b/backend/src/applications/files/utils/send-file.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { send, SendOptions, SendResult } from '@fastify/send' import { HttpStatus, StreamableFile } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' diff --git a/backend/src/applications/files/utils/unzip-file.ts b/backend/src/applications/files/utils/unzip-file.ts index 33da35a8..6fe65e63 100644 --- a/backend/src/applications/files/utils/unzip-file.ts +++ b/backend/src/applications/files/utils/unzip-file.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'node:fs' import path from 'node:path' import { promisify } from 'node:util' diff --git a/backend/src/applications/files/utils/url-file.ts b/backend/src/applications/files/utils/url-file.ts index f4fe8569..f613a8f2 100644 --- a/backend/src/applications/files/utils/url-file.ts +++ b/backend/src/applications/files/utils/url-file.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - const parts = [ // IPv4 loopback (127.0.0.0/8) '127\\.(?:\\d{1,3}\\.){2}\\d{1,3}', diff --git a/backend/src/applications/links/constants/cache.ts b/backend/src/applications/links/constants/cache.ts index 4b4dd192..f4d1cd2b 100644 --- a/backend/src/applications/links/constants/cache.ts +++ b/backend/src/applications/links/constants/cache.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // cache link key = `link-uuid-${userId}-${uuid}` => ${uuid} export const CACHE_LINK_UUID_PREFIX = 'link-uuid' export const CACHE_LINK_UUID_TTL = 900 // 15 minutes diff --git a/backend/src/applications/links/constants/links.ts b/backend/src/applications/links/constants/links.ts index 955e14cd..2a797114 100644 --- a/backend/src/applications/links/constants/links.ts +++ b/backend/src/applications/links/constants/links.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const LINK_ERROR = { UNAUTHORIZED: 'unauthorized', DISABLED: 'disabled', diff --git a/backend/src/applications/links/constants/routes.ts b/backend/src/applications/links/constants/routes.ts index f71c624d..26238ccb 100644 --- a/backend/src/applications/links/constants/routes.ts +++ b/backend/src/applications/links/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_BASE_ROUTE } from '../../applications.constants' export const PUBLIC_LINKS_ROUTE = { diff --git a/backend/src/applications/links/dto/create-or-update-link.dto.ts b/backend/src/applications/links/dto/create-or-update-link.dto.ts index e99a6a03..84fc5b95 100644 --- a/backend/src/applications/links/dto/create-or-update-link.dto.ts +++ b/backend/src/applications/links/dto/create-or-update-link.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsDate, IsOptional, IsString, MinLength } from 'class-validator' import { currentDate } from '../../../common/shared' diff --git a/backend/src/applications/links/interfaces/link-guest.interface.ts b/backend/src/applications/links/interfaces/link-guest.interface.ts index 147a8871..4d888c63 100644 --- a/backend/src/applications/links/interfaces/link-guest.interface.ts +++ b/backend/src/applications/links/interfaces/link-guest.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { ShareMembers } from '../../shares/schemas/share-members.interface' import type { User } from '../../users/schemas/user.interface' import type { Link } from '../schemas/link.interface' diff --git a/backend/src/applications/links/interfaces/link-space.interface.ts b/backend/src/applications/links/interfaces/link-space.interface.ts index bd904bd6..aa361100 100644 --- a/backend/src/applications/links/interfaces/link-space.interface.ts +++ b/backend/src/applications/links/interfaces/link-space.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileEditorProvider } from '../../../configuration/config.interfaces' export interface SpaceLink { diff --git a/backend/src/applications/links/links.controller.spec.ts b/backend/src/applications/links/links.controller.spec.ts index 02bf85d3..3ecf0f1f 100644 --- a/backend/src/applications/links/links.controller.spec.ts +++ b/backend/src/applications/links/links.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { StreamableFile } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { LinksController } from './links.controller' diff --git a/backend/src/applications/links/links.controller.ts b/backend/src/applications/links/links.controller.ts index 945ced35..06ed9d1f 100644 --- a/backend/src/applications/links/links.controller.ts +++ b/backend/src/applications/links/links.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Get, Param, Post, Req, Res, StreamableFile } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' import { AuthTokenOptional } from '../../authentication/decorators/auth-token-optional.decorator' diff --git a/backend/src/applications/links/schemas/link.interface.ts b/backend/src/applications/links/schemas/link.interface.ts index 455d43ea..9344353b 100644 --- a/backend/src/applications/links/schemas/link.interface.ts +++ b/backend/src/applications/links/schemas/link.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { links } from './links.schema' type LinkSchema = typeof links.$inferSelect diff --git a/backend/src/applications/links/schemas/links.schema.ts b/backend/src/applications/links/schemas/links.schema.ts index 433eb24e..536f9746 100644 --- a/backend/src/applications/links/schemas/links.schema.ts +++ b/backend/src/applications/links/schemas/links.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, boolean, date, datetime, index, int, mysqlTable, uniqueIndex, varchar } from 'drizzle-orm/mysql-core' import { users } from '../../users/schemas/users.schema' diff --git a/backend/src/applications/links/services/links-manager.service.spec.ts b/backend/src/applications/links/services/links-manager.service.spec.ts index 9073f8a5..32c3cbc5 100644 --- a/backend/src/applications/links/services/links-manager.service.spec.ts +++ b/backend/src/applications/links/services/links-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' diff --git a/backend/src/applications/links/services/links-manager.service.ts b/backend/src/applications/links/services/links-manager.service.ts index 4538af48..e9fe29f9 100644 --- a/backend/src/applications/links/services/links-manager.service.ts +++ b/backend/src/applications/links/services/links-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' import { AuthManager } from '../../../authentication/auth.service' diff --git a/backend/src/applications/links/services/links-queries.service.ts b/backend/src/applications/links/services/links-queries.service.ts index 103a33ba..2a91f683 100644 --- a/backend/src/applications/links/services/links-queries.service.ts +++ b/backend/src/applications/links/services/links-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable } from '@nestjs/common' import { and, eq, getTableColumns, isNotNull, isNull, or, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/notifications/constants/notifications.ts b/backend/src/applications/notifications/constants/notifications.ts index e79dae27..bb1eb39d 100644 --- a/backend/src/applications/notifications/constants/notifications.ts +++ b/backend/src/applications/notifications/constants/notifications.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ACTION } from '../../../common/constants' export enum NOTIFICATION_APP { diff --git a/backend/src/applications/notifications/constants/routes.ts b/backend/src/applications/notifications/constants/routes.ts index 81dd9fa2..3ee2218e 100644 --- a/backend/src/applications/notifications/constants/routes.ts +++ b/backend/src/applications/notifications/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_BASE_ROUTE } from '../../applications.constants' export const NOTIFICATIONS_ROUTE = { diff --git a/backend/src/applications/notifications/constants/websocket.ts b/backend/src/applications/notifications/constants/websocket.ts index 6dacfae1..7af3596a 100644 --- a/backend/src/applications/notifications/constants/websocket.ts +++ b/backend/src/applications/notifications/constants/websocket.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const NOTIFICATIONS_WS = { NAME_SPACE: '/', EVENTS: { NOTIFICATION: 'notification' } diff --git a/backend/src/applications/notifications/i18n/de.ts b/backend/src/applications/notifications/i18n/de.ts index 76e4f012..d75be0ef 100644 --- a/backend/src/applications/notifications/i18n/de.ts +++ b/backend/src/applications/notifications/i18n/de.ts @@ -1,14 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const de = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Wenn Sie keine Benachrichtigungen mehr erhalten möchten, ändern Sie Ihre Einstellungen direkt in Ihrem Benutzerbereich.', diff --git a/backend/src/applications/notifications/i18n/es.ts b/backend/src/applications/notifications/i18n/es.ts index d174ded6..33ea5315 100644 --- a/backend/src/applications/notifications/i18n/es.ts +++ b/backend/src/applications/notifications/i18n/es.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const es = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Si ya no desea recibir notificaciones, cambie sus preferencias directamente desde su espacio de usuario.', diff --git a/backend/src/applications/notifications/i18n/fr.ts b/backend/src/applications/notifications/i18n/fr.ts index f5bb0f3a..2e17e384 100644 --- a/backend/src/applications/notifications/i18n/fr.ts +++ b/backend/src/applications/notifications/i18n/fr.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const fr = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Si vous ne souhaitez plus recevoir de notifications, modifiez vos préferences directement depuis votre espace utilisateur.', diff --git a/backend/src/applications/notifications/i18n/hi.ts b/backend/src/applications/notifications/i18n/hi.ts index 0c12d16f..9162538d 100644 --- a/backend/src/applications/notifications/i18n/hi.ts +++ b/backend/src/applications/notifications/i18n/hi.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const hi = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'यदि आप अब सूचनाएँ प्राप्त नहीं करना चाहते हैं, तो अपने उपयोगकर्ता क्षेत्र से सीधे अपनी प्राथमिकताएँ बदलें।', diff --git a/backend/src/applications/notifications/i18n/index.ts b/backend/src/applications/notifications/i18n/index.ts index 7b6e728b..841ab442 100644 --- a/backend/src/applications/notifications/i18n/index.ts +++ b/backend/src/applications/notifications/i18n/index.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { i18nLocale } from '../../../common/i18n' import { de } from './de' import { es } from './es' diff --git a/backend/src/applications/notifications/i18n/it.ts b/backend/src/applications/notifications/i18n/it.ts index 229b2298..740eb587 100644 --- a/backend/src/applications/notifications/i18n/it.ts +++ b/backend/src/applications/notifications/i18n/it.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const it = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Se non desideri più ricevere notifiche, modifica le tue preferenze direttamente dalla tua area utente.', diff --git a/backend/src/applications/notifications/i18n/ja.ts b/backend/src/applications/notifications/i18n/ja.ts index 2ff0ba78..32f8a636 100644 --- a/backend/src/applications/notifications/i18n/ja.ts +++ b/backend/src/applications/notifications/i18n/ja.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const ja = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': '通知の受信を希望しない場合は、ユーザー用スペースから直接設定を変更してください。', diff --git a/backend/src/applications/notifications/i18n/ko.ts b/backend/src/applications/notifications/i18n/ko.ts index 2ead493a..b45a23b2 100644 --- a/backend/src/applications/notifications/i18n/ko.ts +++ b/backend/src/applications/notifications/i18n/ko.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const ko = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': '더 이상 알림을 받고 싶지 않다면, 사용자 공간에서 바로 환경설정을 변경하세요.', diff --git a/backend/src/applications/notifications/i18n/pl.ts b/backend/src/applications/notifications/i18n/pl.ts index ff658fdd..125f482c 100644 --- a/backend/src/applications/notifications/i18n/pl.ts +++ b/backend/src/applications/notifications/i18n/pl.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const pl = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Jeśli nie chcesz już otrzymywać powiadomień, zmień swoje preferencje bezpośrednio w przestrzeni użytkownika.', diff --git a/backend/src/applications/notifications/i18n/pt.ts b/backend/src/applications/notifications/i18n/pt.ts index 4347e9fe..8e51f73a 100644 --- a/backend/src/applications/notifications/i18n/pt.ts +++ b/backend/src/applications/notifications/i18n/pt.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const pt = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Se já não deseja receber notificações, altere as suas preferências diretamente no seu espaço de utilizador.', diff --git a/backend/src/applications/notifications/i18n/pt_br.ts b/backend/src/applications/notifications/i18n/pt_br.ts index 63f8b774..6f3b66fd 100644 --- a/backend/src/applications/notifications/i18n/pt_br.ts +++ b/backend/src/applications/notifications/i18n/pt_br.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const pt_BR = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Se você não deseja mais receber notificações, altere suas preferências diretamente no seu espaço do usuário.', diff --git a/backend/src/applications/notifications/i18n/ru.ts b/backend/src/applications/notifications/i18n/ru.ts index f311c7a9..2eaf6bd5 100644 --- a/backend/src/applications/notifications/i18n/ru.ts +++ b/backend/src/applications/notifications/i18n/ru.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ export const ru = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Если вы больше не хотите получать уведомления, измените настройки прямо в вашем пользовательском пространстве.', diff --git a/backend/src/applications/notifications/i18n/tr.ts b/backend/src/applications/notifications/i18n/tr.ts index fe8fb6d7..97a36209 100644 --- a/backend/src/applications/notifications/i18n/tr.ts +++ b/backend/src/applications/notifications/i18n/tr.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const tr = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': 'Artık bildirim almak istemiyorsanız, kullanıcı alanınızdan doğrudan tercihlerinizi değiştirin.', diff --git a/backend/src/applications/notifications/i18n/zh.ts b/backend/src/applications/notifications/i18n/zh.ts index 3f0b5b5e..15cc8594 100644 --- a/backend/src/applications/notifications/i18n/zh.ts +++ b/backend/src/applications/notifications/i18n/zh.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const zh = { 'If you no longer wish to receive notifications, change your preferences directly from your user space.': '如果您不再希望接收通知,请在您的用户空间中直接更改偏好设置。', diff --git a/backend/src/applications/notifications/interfaces/notification-properties.interface.ts b/backend/src/applications/notifications/interfaces/notification-properties.interface.ts index 9d29bb66..9194303e 100644 --- a/backend/src/applications/notifications/interfaces/notification-properties.interface.ts +++ b/backend/src/applications/notifications/interfaces/notification-properties.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { ACTION } from '../../../common/constants' import type { Owner } from '../../users/interfaces/owner.interface' import type { UserModel } from '../../users/models/user.model' diff --git a/backend/src/applications/notifications/interfaces/user-mail-notification.interface.ts b/backend/src/applications/notifications/interfaces/user-mail-notification.interface.ts index 499cb556..5b645b5f 100644 --- a/backend/src/applications/notifications/interfaces/user-mail-notification.interface.ts +++ b/backend/src/applications/notifications/interfaces/user-mail-notification.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { USER_NOTIFICATION } from '../../users/constants/user' export interface UserMailNotification { diff --git a/backend/src/applications/notifications/mails/models.ts b/backend/src/applications/notifications/mails/models.ts index cc8c4a8a..0480522a 100644 --- a/backend/src/applications/notifications/mails/models.ts +++ b/backend/src/applications/notifications/mails/models.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ACTION } from '../../../common/constants' import { i18nLocale } from '../../../common/i18n' import { capitalizeString, SERVER_NAME } from '../../../common/shared' diff --git a/backend/src/applications/notifications/mails/templates.ts b/backend/src/applications/notifications/mails/templates.ts index f78c9691..bb38a408 100644 --- a/backend/src/applications/notifications/mails/templates.ts +++ b/backend/src/applications/notifications/mails/templates.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { capitalizeString } from '../../../common/shared' import { UserModel } from '../../users/models/user.model' diff --git a/backend/src/applications/notifications/mails/urls.ts b/backend/src/applications/notifications/mails/urls.ts index e97b932e..25a5da92 100644 --- a/backend/src/applications/notifications/mails/urls.ts +++ b/backend/src/applications/notifications/mails/urls.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { PUBLIC_LINKS_ROUTE } from '../../links/constants/routes' import { SPACES_BASE_ROUTE } from '../../spaces/constants/routes' import { SYNC_BASE_ROUTE } from '../../sync/constants/routes' diff --git a/backend/src/applications/notifications/notifications.controller.spec.ts b/backend/src/applications/notifications/notifications.controller.spec.ts index c52c63fa..ab0780ca 100644 --- a/backend/src/applications/notifications/notifications.controller.spec.ts +++ b/backend/src/applications/notifications/notifications.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { NotificationsController } from './notifications.controller' import { NotificationsManager } from './services/notifications-manager.service' diff --git a/backend/src/applications/notifications/notifications.controller.ts b/backend/src/applications/notifications/notifications.controller.ts index 6029e57b..7a8565a9 100644 --- a/backend/src/applications/notifications/notifications.controller.ts +++ b/backend/src/applications/notifications/notifications.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Controller, Delete, Get, Param, ParseIntPipe, Patch } from '@nestjs/common' import { GetUser } from '../users/decorators/user.decorator' import type { UserModel } from '../users/models/user.model' diff --git a/backend/src/applications/notifications/notifications.gateway.ts b/backend/src/applications/notifications/notifications.gateway.ts index 6b5681d8..c3a748cc 100644 --- a/backend/src/applications/notifications/notifications.gateway.ts +++ b/backend/src/applications/notifications/notifications.gateway.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets' import { Server } from 'socket.io' import { USER_ROOM_PREFIX } from '../users/constants/websocket' diff --git a/backend/src/applications/notifications/notifications.module.ts b/backend/src/applications/notifications/notifications.module.ts index bddf1b3d..b971999e 100644 --- a/backend/src/applications/notifications/notifications.module.ts +++ b/backend/src/applications/notifications/notifications.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { NotificationsController } from './notifications.controller' import { WebSocketNotifications } from './notifications.gateway' diff --git a/backend/src/applications/notifications/schemas/notification.interface.ts b/backend/src/applications/notifications/schemas/notification.interface.ts index ce53b641..4f496263 100644 --- a/backend/src/applications/notifications/schemas/notification.interface.ts +++ b/backend/src/applications/notifications/schemas/notification.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NotificationContent } from '../interfaces/notification-properties.interface' import { notifications } from './notifications.schema' diff --git a/backend/src/applications/notifications/schemas/notifications.schema.ts b/backend/src/applications/notifications/schemas/notifications.schema.ts index f9e4cd52..a3787aa1 100644 --- a/backend/src/applications/notifications/schemas/notifications.schema.ts +++ b/backend/src/applications/notifications/schemas/notifications.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, boolean, datetime, index, mysqlTable } from 'drizzle-orm/mysql-core' import { jsonColumn } from '../../../infrastructure/database/columns' diff --git a/backend/src/applications/notifications/services/notifications-manager.service.spec.ts b/backend/src/applications/notifications/services/notifications-manager.service.spec.ts index d253cda5..6a7ae101 100644 --- a/backend/src/applications/notifications/services/notifications-manager.service.spec.ts +++ b/backend/src/applications/notifications/services/notifications-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Mailer } from '../../../infrastructure/mailer/mailer.service' import { USER_NOTIFICATION } from '../../users/constants/user' diff --git a/backend/src/applications/notifications/services/notifications-manager.service.ts b/backend/src/applications/notifications/services/notifications-manager.service.ts index 638e5728..2b3d1369 100644 --- a/backend/src/applications/notifications/services/notifications-manager.service.ts +++ b/backend/src/applications/notifications/services/notifications-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import { i18nLocale } from '../../../common/i18n' import { MailProps } from '../../../infrastructure/mailer/interfaces/mail.interface' diff --git a/backend/src/applications/notifications/services/notifications-queries.service.ts b/backend/src/applications/notifications/services/notifications-queries.service.ts index 7b97991b..82de6db8 100644 --- a/backend/src/applications/notifications/services/notifications-queries.service.ts +++ b/backend/src/applications/notifications/services/notifications-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable } from '@nestjs/common' import { and, desc, eq, inArray, SelectedFields, SQL } from 'drizzle-orm' import { DB_TOKEN_PROVIDER } from '../../../infrastructure/database/constants' diff --git a/backend/src/applications/shares/constants/routes.ts b/backend/src/applications/shares/constants/routes.ts index 81bee5fe..6de4e10e 100644 --- a/backend/src/applications/shares/constants/routes.ts +++ b/backend/src/applications/shares/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_BASE_ROUTE } from '../../applications.constants' export const SHARES_ROUTE = { diff --git a/backend/src/applications/shares/constants/shares.ts b/backend/src/applications/shares/constants/shares.ts index faa4c8a6..bfd38bde 100644 --- a/backend/src/applications/shares/constants/shares.ts +++ b/backend/src/applications/shares/constants/shares.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SPACE_OPERATION, SPACE_PERMS_SEP } from '../../spaces/constants/spaces' export enum SHARE_TYPE { diff --git a/backend/src/applications/shares/dto/create-or-update-share.dto.ts b/backend/src/applications/shares/dto/create-or-update-share.dto.ts index 5914a24d..8ffb5479 100644 --- a/backend/src/applications/shares/dto/create-or-update-share.dto.ts +++ b/backend/src/applications/shares/dto/create-or-update-share.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { IsArray, IsBoolean, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { FileSpace } from '../../files/interfaces/file-space.interface' diff --git a/backend/src/applications/shares/interfaces/share-child.interface.ts b/backend/src/applications/shares/interfaces/share-child.interface.ts index cb2ad03f..a2338964 100644 --- a/backend/src/applications/shares/interfaces/share-child.interface.ts +++ b/backend/src/applications/shares/interfaces/share-child.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { Share } from '../schemas/share.interface' export interface ShareChildQuery extends Pick { diff --git a/backend/src/applications/shares/interfaces/share-env.interface.ts b/backend/src/applications/shares/interfaces/share-env.interface.ts index 7980f113..b8352c69 100644 --- a/backend/src/applications/shares/interfaces/share-env.interface.ts +++ b/backend/src/applications/shares/interfaces/share-env.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SpaceEnv } from '../../spaces/models/space-env.model' export interface ShareEnv extends Partial { diff --git a/backend/src/applications/shares/interfaces/share-file.interface.ts b/backend/src/applications/shares/interfaces/share-file.interface.ts index 518611f7..c66e6427 100644 --- a/backend/src/applications/shares/interfaces/share-file.interface.ts +++ b/backend/src/applications/shares/interfaces/share-file.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileSpace } from '../../files/interfaces/file-space.interface' import type { Share } from '../schemas/share.interface' diff --git a/backend/src/applications/shares/interfaces/share-link.interface.ts b/backend/src/applications/shares/interfaces/share-link.interface.ts index 5d63fd6c..c3541896 100644 --- a/backend/src/applications/shares/interfaces/share-link.interface.ts +++ b/backend/src/applications/shares/interfaces/share-link.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileSpace } from '../../files/interfaces/file-space.interface' import type { LinkGuest } from '../../links/interfaces/link-guest.interface' import type { Share } from '../schemas/share.interface' diff --git a/backend/src/applications/shares/interfaces/share-props.interface.ts b/backend/src/applications/shares/interfaces/share-props.interface.ts index 950a4f4f..819b839f 100644 --- a/backend/src/applications/shares/interfaces/share-props.interface.ts +++ b/backend/src/applications/shares/interfaces/share-props.interface.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import type { FileSpace } from '../../files/interfaces/file-space.interface' import type { Member } from '../../users/interfaces/member.interface' import type { Share } from '../schemas/share.interface' diff --git a/backend/src/applications/shares/models/share-child.model.ts b/backend/src/applications/shares/models/share-child.model.ts index 2cdbb43e..9a98e8b7 100644 --- a/backend/src/applications/shares/models/share-child.model.ts +++ b/backend/src/applications/shares/models/share-child.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { popFromObject } from '../../../common/shared' import type { Owner } from '../../users/interfaces/owner.interface' import type { ShareChildQuery } from '../interfaces/share-child.interface' diff --git a/backend/src/applications/shares/schemas/share-members.interface.ts b/backend/src/applications/shares/schemas/share-members.interface.ts index 00114f52..3fad0124 100644 --- a/backend/src/applications/shares/schemas/share-members.interface.ts +++ b/backend/src/applications/shares/schemas/share-members.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { sharesMembers } from './shares-members.schema' type ShareMembersSchema = typeof sharesMembers.$inferSelect diff --git a/backend/src/applications/shares/schemas/share.interface.ts b/backend/src/applications/shares/schemas/share.interface.ts index 7b94d3e9..f831900e 100644 --- a/backend/src/applications/shares/schemas/share.interface.ts +++ b/backend/src/applications/shares/schemas/share.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { shares } from './shares.schema' type ShareSchema = typeof shares.$inferSelect diff --git a/backend/src/applications/shares/schemas/shares-members.schema.ts b/backend/src/applications/shares/schemas/shares-members.schema.ts index df4681e1..29aa9038 100644 --- a/backend/src/applications/shares/schemas/shares-members.schema.ts +++ b/backend/src/applications/shares/schemas/shares-members.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, datetime, index, mysqlTable, unique, varchar } from 'drizzle-orm/mysql-core' import { links } from '../../links/schemas/links.schema' diff --git a/backend/src/applications/shares/schemas/shares.schema.ts b/backend/src/applications/shares/schemas/shares.schema.ts index c4ded410..69827eb4 100644 --- a/backend/src/applications/shares/schemas/shares.schema.ts +++ b/backend/src/applications/shares/schemas/shares.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { AnyMySqlColumn, bigint, boolean, datetime, index, mysqlTable, tinyint, uniqueIndex, varchar } from 'drizzle-orm/mysql-core' import { files } from '../../files/schemas/files.schema' diff --git a/backend/src/applications/shares/services/shares-manager.service.spec.ts b/backend/src/applications/shares/services/shares-manager.service.spec.ts index a7a974fe..3686b8ad 100644 --- a/backend/src/applications/shares/services/shares-manager.service.spec.ts +++ b/backend/src/applications/shares/services/shares-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import * as commonFunctions from '../../../common/functions' diff --git a/backend/src/applications/shares/services/shares-manager.service.ts b/backend/src/applications/shares/services/shares-manager.service.ts index 9d8f85f0..f237c103 100644 --- a/backend/src/applications/shares/services/shares-manager.service.ts +++ b/backend/src/applications/shares/services/shares-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import path from 'node:path' import { ACTION } from '../../../common/constants' diff --git a/backend/src/applications/shares/services/shares-queries.service.ts b/backend/src/applications/shares/services/shares-queries.service.ts index 3a3c127a..501c706b 100644 --- a/backend/src/applications/shares/services/shares-queries.service.ts +++ b/backend/src/applications/shares/services/shares-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, count, eq, inArray, isNotNull, isNull, ne, or, SelectedFields, SQL, sql } from 'drizzle-orm' import { alias, MySqlSelectDynamic, union } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/shares/shares.controller.spec.ts b/backend/src/applications/shares/shares.controller.spec.ts index 44ec04b1..45aa0011 100644 --- a/backend/src/applications/shares/shares.controller.spec.ts +++ b/backend/src/applications/shares/shares.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../infrastructure/cache/services/cache.service' import { ContextManager } from '../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/shares/shares.controller.ts b/backend/src/applications/shares/shares.controller.ts index 64d7e63c..fde363e1 100644 --- a/backend/src/applications/shares/shares.controller.ts +++ b/backend/src/applications/shares/shares.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, UseGuards, UseInterceptors } from '@nestjs/common' import { ContextInterceptor } from '../../infrastructure/context/interceptors/context.interceptor' import { LINK_TYPE } from '../links/constants/links' diff --git a/backend/src/applications/shares/shares.module.ts b/backend/src/applications/shares/shares.module.ts index 24a689aa..5021dd95 100644 --- a/backend/src/applications/shares/shares.module.ts +++ b/backend/src/applications/shares/shares.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { LinksController } from '../links/links.controller' import { LinksManager } from '../links/services/links-manager.service' diff --git a/backend/src/applications/spaces/constants/cache.ts b/backend/src/applications/spaces/constants/cache.ts index 02eaf6c3..bf57bff2 100644 --- a/backend/src/applications/spaces/constants/cache.ts +++ b/backend/src/applications/spaces/constants/cache.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // cache quota key = `(quota-user|quota-space)-${id}` => number export const CACHE_QUOTA_USER_PREFIX = 'quota-user' export const CACHE_QUOTA_SPACE_PREFIX = 'quota-space' diff --git a/backend/src/applications/spaces/constants/routes.ts b/backend/src/applications/spaces/constants/routes.ts index fb6b31ea..9cb21f7c 100644 --- a/backend/src/applications/spaces/constants/routes.ts +++ b/backend/src/applications/spaces/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_BASE_ROUTE } from '../../applications.constants' import { SPACE_REPOSITORY } from './spaces' diff --git a/backend/src/applications/spaces/constants/spaces.ts b/backend/src/applications/spaces/constants/spaces.ts index d3bc3684..917bef06 100644 --- a/backend/src/applications/spaces/constants/spaces.ts +++ b/backend/src/applications/spaces/constants/spaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const SPACE_MAX_DISABLED_DAYS = 30 //days export const SPACE_PERMS_SEP = ':' diff --git a/backend/src/applications/spaces/decorators/space-override-permission.decorator.ts b/backend/src/applications/spaces/decorators/space-override-permission.decorator.ts index bd97b3b4..63326594 100644 --- a/backend/src/applications/spaces/decorators/space-override-permission.decorator.ts +++ b/backend/src/applications/spaces/decorators/space-override-permission.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Reflector } from '@nestjs/core' import { SPACE_OPERATION } from '../constants/spaces' diff --git a/backend/src/applications/spaces/decorators/space-skip-guard.decorator.ts b/backend/src/applications/spaces/decorators/space-skip-guard.decorator.ts index d1103902..028ee461 100644 --- a/backend/src/applications/spaces/decorators/space-skip-guard.decorator.ts +++ b/backend/src/applications/spaces/decorators/space-skip-guard.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SetMetadata } from '@nestjs/common' export const SKIP_SPACE_GUARD = 'skipSpaceGuard' diff --git a/backend/src/applications/spaces/decorators/space-skip-permissions.decorator.ts b/backend/src/applications/spaces/decorators/space-skip-permissions.decorator.ts index 76a73096..edf32dae 100644 --- a/backend/src/applications/spaces/decorators/space-skip-permissions.decorator.ts +++ b/backend/src/applications/spaces/decorators/space-skip-permissions.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SetMetadata } from '@nestjs/common' export const SKIP_SPACE_PERMISSIONS_CHECK = 'skipSpacePermissionsCheck' diff --git a/backend/src/applications/spaces/decorators/space.decorator.ts b/backend/src/applications/spaces/decorators/space.decorator.ts index bfd2088b..f235ce29 100644 --- a/backend/src/applications/spaces/decorators/space.decorator.ts +++ b/backend/src/applications/spaces/decorators/space.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createParamDecorator, ExecutionContext } from '@nestjs/common' import { SpaceEnv } from '../models/space-env.model' diff --git a/backend/src/applications/spaces/dto/create-or-update-space.dto.ts b/backend/src/applications/spaces/dto/create-or-update-space.dto.ts index be2e0333..2db50e71 100644 --- a/backend/src/applications/spaces/dto/create-or-update-space.dto.ts +++ b/backend/src/applications/spaces/dto/create-or-update-space.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { ArrayMinSize, IsArray, IsBoolean, IsInt, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator' import { sanitizeName } from '../../files/utils/files' diff --git a/backend/src/applications/spaces/dto/delete-space.dto.ts b/backend/src/applications/spaces/dto/delete-space.dto.ts index 577f3e04..4c7d8114 100644 --- a/backend/src/applications/spaces/dto/delete-space.dto.ts +++ b/backend/src/applications/spaces/dto/delete-space.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsBoolean, IsOptional } from 'class-validator' export class DeleteSpaceDto { diff --git a/backend/src/applications/spaces/dto/search-space.dto.ts b/backend/src/applications/spaces/dto/search-space.dto.ts index 5baff8e2..96634f79 100644 --- a/backend/src/applications/spaces/dto/search-space.dto.ts +++ b/backend/src/applications/spaces/dto/search-space.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' diff --git a/backend/src/applications/spaces/dto/space-roots.dto.ts b/backend/src/applications/spaces/dto/space-roots.dto.ts index 9ba6b1c9..e7b6a9d4 100644 --- a/backend/src/applications/spaces/dto/space-roots.dto.ts +++ b/backend/src/applications/spaces/dto/space-roots.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { IsDefined, IsInt, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { sanitizeName, sanitizePath } from '../../files/utils/files' diff --git a/backend/src/applications/spaces/guards/space.guard.spec.ts b/backend/src/applications/spaces/guards/space.guard.spec.ts index abe3b765..ec39d9e7 100644 --- a/backend/src/applications/spaces/guards/space.guard.spec.ts +++ b/backend/src/applications/spaces/guards/space.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext, HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/applications/spaces/guards/space.guard.ts b/backend/src/applications/spaces/guards/space.guard.ts index 8908525b..51af1a5d 100644 --- a/backend/src/applications/spaces/guards/space.guard.ts +++ b/backend/src/applications/spaces/guards/space.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' import { HTTP_METHOD } from '../../applications.constants' diff --git a/backend/src/applications/spaces/interfaces/space-diff.interface.ts b/backend/src/applications/spaces/interfaces/space-diff.interface.ts index cfa88d38..25674485 100644 --- a/backend/src/applications/spaces/interfaces/space-diff.interface.ts +++ b/backend/src/applications/spaces/interfaces/space-diff.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SyncDiffDto } from '../../sync/dtos/sync-operations.dto' export interface ParseDiffContext { diff --git a/backend/src/applications/spaces/interfaces/space-files.interface.ts b/backend/src/applications/spaces/interfaces/space-files.interface.ts index 7b45eb5b..752f347a 100644 --- a/backend/src/applications/spaces/interfaces/space-files.interface.ts +++ b/backend/src/applications/spaces/interfaces/space-files.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileProps } from '../../files/interfaces/file-props.interface' export interface SpaceFiles { diff --git a/backend/src/applications/spaces/interfaces/space-request.interface.ts b/backend/src/applications/spaces/interfaces/space-request.interface.ts index d258245e..68706299 100644 --- a/backend/src/applications/spaces/interfaces/space-request.interface.ts +++ b/backend/src/applications/spaces/interfaces/space-request.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface' import { SpaceEnv } from '../models/space-env.model' diff --git a/backend/src/applications/spaces/interfaces/space-trash.interface.ts b/backend/src/applications/spaces/interfaces/space-trash.interface.ts index 54f44451..a220978d 100644 --- a/backend/src/applications/spaces/interfaces/space-trash.interface.ts +++ b/backend/src/applications/spaces/interfaces/space-trash.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface SpaceTrash { id: number name: string diff --git a/backend/src/applications/spaces/models/space-env.model.ts b/backend/src/applications/spaces/models/space-env.model.ts index 37384313..869a3017 100644 --- a/backend/src/applications/spaces/models/space-env.model.ts +++ b/backend/src/applications/spaces/models/space-env.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { uniquePermissions } from '../../../common/functions' import { FileDBProps } from '../../files/interfaces/file-db-props.interface' import { FileTaskProps } from '../../files/models/file-task' diff --git a/backend/src/applications/spaces/models/space-props.model.ts b/backend/src/applications/spaces/models/space-props.model.ts index 59163054..5b3df975 100644 --- a/backend/src/applications/spaces/models/space-props.model.ts +++ b/backend/src/applications/spaces/models/space-props.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { uniquePermissions } from '../../../common/functions' import { MEMBER_TYPE } from '../../users/constants/member' import { Member } from '../../users/interfaces/member.interface' diff --git a/backend/src/applications/spaces/models/space-root-props.model.ts b/backend/src/applications/spaces/models/space-root-props.model.ts index 838ee3f3..9b1361d0 100644 --- a/backend/src/applications/spaces/models/space-root-props.model.ts +++ b/backend/src/applications/spaces/models/space-root-props.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { FileProps } from '../../files/interfaces/file-props.interface' import type { Owner } from '../../users/interfaces/owner.interface' import type { SpaceRoot } from '../schemas/space-root.interface' diff --git a/backend/src/applications/spaces/models/space.model.ts b/backend/src/applications/spaces/models/space.model.ts index a61f20f3..55a85085 100644 --- a/backend/src/applications/spaces/models/space.model.ts +++ b/backend/src/applications/spaces/models/space.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'node:fs/promises' import path from 'node:path' import { configuration } from '../../../configuration/config.environment' diff --git a/backend/src/applications/spaces/schemas/space-members.interface.ts b/backend/src/applications/spaces/schemas/space-members.interface.ts index dd0f9e7f..c07d24eb 100644 --- a/backend/src/applications/spaces/schemas/space-members.interface.ts +++ b/backend/src/applications/spaces/schemas/space-members.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { spacesMembers } from './spaces-members.schema' type SpaceMembersSchema = typeof spacesMembers.$inferSelect diff --git a/backend/src/applications/spaces/schemas/space-root.interface.ts b/backend/src/applications/spaces/schemas/space-root.interface.ts index 49803ad4..a405fd7f 100644 --- a/backend/src/applications/spaces/schemas/space-root.interface.ts +++ b/backend/src/applications/spaces/schemas/space-root.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { spacesRoots } from './spaces-roots.schema' type SpaceRootSchema = typeof spacesRoots.$inferSelect diff --git a/backend/src/applications/spaces/schemas/space.interface.ts b/backend/src/applications/spaces/schemas/space.interface.ts index f3ac4119..b340bb07 100644 --- a/backend/src/applications/spaces/schemas/space.interface.ts +++ b/backend/src/applications/spaces/schemas/space.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { spaces } from './spaces.schema' type SpaceSchema = typeof spaces.$inferSelect diff --git a/backend/src/applications/spaces/schemas/spaces-members.schema.ts b/backend/src/applications/spaces/schemas/spaces-members.schema.ts index 7cb4345c..5878ba67 100644 --- a/backend/src/applications/spaces/schemas/spaces-members.schema.ts +++ b/backend/src/applications/spaces/schemas/spaces-members.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, datetime, index, mysqlTable, tinyint, unique, varchar } from 'drizzle-orm/mysql-core' import { links } from '../../links/schemas/links.schema' diff --git a/backend/src/applications/spaces/schemas/spaces-roots.schema.ts b/backend/src/applications/spaces/schemas/spaces-roots.schema.ts index 0c7cdefb..e97d278e 100644 --- a/backend/src/applications/spaces/schemas/spaces-roots.schema.ts +++ b/backend/src/applications/spaces/schemas/spaces-roots.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, datetime, index, mysqlTable, unique, varchar } from 'drizzle-orm/mysql-core' import { files } from '../../files/schemas/files.schema' diff --git a/backend/src/applications/spaces/schemas/spaces.schema.ts b/backend/src/applications/spaces/schemas/spaces.schema.ts index d38fda49..0c3e86ca 100644 --- a/backend/src/applications/spaces/schemas/spaces.schema.ts +++ b/backend/src/applications/spaces/schemas/spaces.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SQL, sql } from 'drizzle-orm' import { bigint, boolean, datetime, mysqlTable, uniqueIndex, varchar } from 'drizzle-orm/mysql-core' import { SPACE_PERMS_SEP } from '../constants/spaces' diff --git a/backend/src/applications/spaces/services/spaces-browser.service.spec.ts b/backend/src/applications/spaces/services/spaces-browser.service.spec.ts index 53ef5330..726fdb32 100644 --- a/backend/src/applications/spaces/services/spaces-browser.service.spec.ts +++ b/backend/src/applications/spaces/services/spaces-browser.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' import { exportConfiguration } from '../../../configuration/config.environment' diff --git a/backend/src/applications/spaces/services/spaces-browser.service.ts b/backend/src/applications/spaces/services/spaces-browser.service.ts index f6e45478..b6f1170d 100644 --- a/backend/src/applications/spaces/services/spaces-browser.service.ts +++ b/backend/src/applications/spaces/services/spaces-browser.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, Injectable, Logger } from '@nestjs/common' import fs from 'node:fs/promises' import path from 'node:path' diff --git a/backend/src/applications/spaces/services/spaces-manager.service.spec.ts b/backend/src/applications/spaces/services/spaces-manager.service.spec.ts index 800290e0..069118fc 100644 --- a/backend/src/applications/spaces/services/spaces-manager.service.spec.ts +++ b/backend/src/applications/spaces/services/spaces-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigModule, ConfigService } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' import fs from 'node:fs/promises' diff --git a/backend/src/applications/spaces/services/spaces-manager.service.ts b/backend/src/applications/spaces/services/spaces-manager.service.ts index f34c89af..0012af35 100644 --- a/backend/src/applications/spaces/services/spaces-manager.service.ts +++ b/backend/src/applications/spaces/services/spaces-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { eq, isNotNull, lte } from 'drizzle-orm' import fs from 'node:fs/promises' diff --git a/backend/src/applications/spaces/services/spaces-queries.service.ts b/backend/src/applications/spaces/services/spaces-queries.service.ts index bd738324..d5b6bc08 100644 --- a/backend/src/applications/spaces/services/spaces-queries.service.ts +++ b/backend/src/applications/spaces/services/spaces-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, countDistinct, eq, isNotNull, isNull, max, SelectedFields, SQL, sql } from 'drizzle-orm' import { alias, union } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/spaces/services/spaces-scheduler.service.ts b/backend/src/applications/spaces/services/spaces-scheduler.service.ts index 29b774a0..9ea22e38 100644 --- a/backend/src/applications/spaces/services/spaces-scheduler.service.ts +++ b/backend/src/applications/spaces/services/spaces-scheduler.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import { Cron, CronExpression, Timeout } from '@nestjs/schedule' import { SharesManager } from '../../shares/services/shares-manager.service' diff --git a/backend/src/applications/spaces/spaces.controller.spec.ts b/backend/src/applications/spaces/spaces.controller.spec.ts index cf7dfeb0..89f88126 100644 --- a/backend/src/applications/spaces/spaces.controller.spec.ts +++ b/backend/src/applications/spaces/spaces.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Cache } from '../../infrastructure/cache/services/cache.service' import { ContextManager } from '../../infrastructure/context/services/context-manager.service' diff --git a/backend/src/applications/spaces/spaces.controller.ts b/backend/src/applications/spaces/spaces.controller.ts index 95496adb..52a7c7f3 100644 --- a/backend/src/applications/spaces/spaces.controller.ts +++ b/backend/src/applications/spaces/spaces.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, diff --git a/backend/src/applications/spaces/spaces.module.ts b/backend/src/applications/spaces/spaces.module.ts index 1f793abd..cad464af 100644 --- a/backend/src/applications/spaces/spaces.module.ts +++ b/backend/src/applications/spaces/spaces.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { SpaceGuard } from './guards/space.guard' import { SpacesBrowser } from './services/spaces-browser.service' diff --git a/backend/src/applications/spaces/utils/paths.ts b/backend/src/applications/spaces/utils/paths.ts index ee6e2f7f..fb59c18a 100644 --- a/backend/src/applications/spaces/utils/paths.ts +++ b/backend/src/applications/spaces/utils/paths.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpStatus } from '@nestjs/common' import fs from 'fs/promises' import path from 'node:path' diff --git a/backend/src/applications/spaces/utils/permissions.ts b/backend/src/applications/spaces/utils/permissions.ts index 49564f72..ffa27e8d 100644 --- a/backend/src/applications/spaces/utils/permissions.ts +++ b/backend/src/applications/spaces/utils/permissions.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { intersectPermissions } from '../../../common/shared' import { USER_PERMISSION } from '../../users/constants/user' import type { UserModel } from '../../users/models/user.model' diff --git a/backend/src/applications/spaces/utils/routes.ts b/backend/src/applications/spaces/utils/routes.ts index adcc9776..ea172675 100644 --- a/backend/src/applications/spaces/utils/routes.ts +++ b/backend/src/applications/spaces/utils/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sanitizePath } from '../../files/utils/files' export function PATH_TO_SPACE_SEGMENTS(path: string): string[] { diff --git a/backend/src/applications/sync/constants/auth.ts b/backend/src/applications/sync/constants/auth.ts index e4c351dd..bcd9dc7b 100644 --- a/backend/src/applications/sync/constants/auth.ts +++ b/backend/src/applications/sync/constants/auth.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum CLIENT_AUTH_TYPE { COOKIE = 'cookie', TOKEN = 'token' diff --git a/backend/src/applications/sync/constants/routes.ts b/backend/src/applications/sync/constants/routes.ts index a0d85eb6..f00552aa 100644 --- a/backend/src/applications/sync/constants/routes.ts +++ b/backend/src/applications/sync/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_BASE_ROUTE } from '../../applications.constants' export const SYNC_BASE_ROUTE = 'sync' diff --git a/backend/src/applications/sync/constants/store.ts b/backend/src/applications/sync/constants/store.ts index cdb92fac..b4e97159 100644 --- a/backend/src/applications/sync/constants/store.ts +++ b/backend/src/applications/sync/constants/store.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const APP_STORE_URL = 'https://updates.sync-in.org' export const APP_STORE_DIRNAME = 'releases' export const APP_STORE_MANIFEST_FILE = 'latest.json' diff --git a/backend/src/applications/sync/constants/sync.ts b/backend/src/applications/sync/constants/sync.ts index 41473982..d24be33e 100644 --- a/backend/src/applications/sync/constants/sync.ts +++ b/backend/src/applications/sync/constants/sync.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SPACE_ALIAS, SPACE_REPOSITORY } from '../../spaces/constants/spaces' export const SYNC_IN_SERVER_AGENT = 'sync-in' as const diff --git a/backend/src/applications/sync/decorators/sync-context.decorator.ts b/backend/src/applications/sync/decorators/sync-context.decorator.ts index 6df5b4f8..f435523b 100644 --- a/backend/src/applications/sync/decorators/sync-context.decorator.ts +++ b/backend/src/applications/sync/decorators/sync-context.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SetMetadata } from '@nestjs/common' export const SYNC_CONTEXT = 'SyncContext' diff --git a/backend/src/applications/sync/decorators/sync-environment.decorator.ts b/backend/src/applications/sync/decorators/sync-environment.decorator.ts index 51e20420..09385140 100644 --- a/backend/src/applications/sync/decorators/sync-environment.decorator.ts +++ b/backend/src/applications/sync/decorators/sync-environment.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { applyDecorators, UseGuards } from '@nestjs/common' import { SpaceGuard } from '../../spaces/guards/space.guard' import { USER_PERMISSION } from '../../users/constants/user' diff --git a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts index 0086b94b..fc92f358 100644 --- a/backend/src/applications/sync/dtos/sync-client-auth.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-auth.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsBoolean, IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID, ValidateNested } from 'class-validator' import { SyncClientInfoDto } from './sync-client-info.dto' diff --git a/backend/src/applications/sync/dtos/sync-client-info.dto.ts b/backend/src/applications/sync/dtos/sync-client-info.dto.ts index 32c4a410..ce218890 100644 --- a/backend/src/applications/sync/dtos/sync-client-info.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-info.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsEnum, IsString } from 'class-validator' import { SYNC_CLIENT_TYPE } from '../constants/sync' import { SyncClientInfo } from '../interfaces/sync-client.interface' diff --git a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts index 0d8e17fb..9fe477c4 100644 --- a/backend/src/applications/sync/dtos/sync-client-registration.dto.ts +++ b/backend/src/applications/sync/dtos/sync-client-registration.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsDefined, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, IsUUID, ValidateNested } from 'class-validator' import { SyncClientInfoDto } from './sync-client-info.dto' diff --git a/backend/src/applications/sync/dtos/sync-operations.dto.ts b/backend/src/applications/sync/dtos/sync-operations.dto.ts index 2e6cc075..d30448e7 100644 --- a/backend/src/applications/sync/dtos/sync-operations.dto.ts +++ b/backend/src/applications/sync/dtos/sync-operations.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsDefined, IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator' import { MakeFileDto } from '../../files/dto/file-operations.dto' diff --git a/backend/src/applications/sync/dtos/sync-path.dto.ts b/backend/src/applications/sync/dtos/sync-path.dto.ts index b6c5b081..d9f42e96 100644 --- a/backend/src/applications/sync/dtos/sync-path.dto.ts +++ b/backend/src/applications/sync/dtos/sync-path.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { IsArray, IsBoolean, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator' import { SYNC_PATH_CONFLICT_MODE, SYNC_PATH_DIFF_MODE, SYNC_PATH_MODE, SYNC_PATH_SCHEDULER_UNIT } from '../constants/sync' diff --git a/backend/src/applications/sync/dtos/sync-upload.dto.ts b/backend/src/applications/sync/dtos/sync-upload.dto.ts index de0d551c..f3e54fe4 100644 --- a/backend/src/applications/sync/dtos/sync-upload.dto.ts +++ b/backend/src/applications/sync/dtos/sync-upload.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsDefined, IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator' diff --git a/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.ts b/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.ts index a83fd19c..98da1c11 100644 --- a/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.ts +++ b/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CallHandler, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { lastValueFrom, of } from 'rxjs' diff --git a/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.ts b/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.ts index 18672da6..b4f63eae 100644 --- a/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.ts +++ b/backend/src/applications/sync/interceptors/sync-diff-gzip-body.interceptor.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CallHandler, ExecutionContext, HttpException, HttpStatus, Injectable, NestInterceptor } from '@nestjs/common' import { FastifyRequest } from 'fastify' import { buffer } from 'node:stream/consumers' diff --git a/backend/src/applications/sync/interfaces/store-manifest.interface.ts b/backend/src/applications/sync/interfaces/store-manifest.interface.ts index df6c5c31..a349c809 100644 --- a/backend/src/applications/sync/interfaces/store-manifest.interface.ts +++ b/backend/src/applications/sync/interfaces/store-manifest.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { APP_STORE_PLATFORM, APP_STORE_REPOSITORY } from '../constants/store' interface PackageManifest { diff --git a/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts b/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts index 4e65cd8a..b7570bdf 100644 --- a/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-client-auth.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { LoginResponseDto } from '../../../authentication/dto/login-response.dto' import type { TokenResponseDto } from '../../../authentication/dto/token-response.dto' diff --git a/backend/src/applications/sync/interfaces/sync-client-paths.interface.ts b/backend/src/applications/sync/interfaces/sync-client-paths.interface.ts index ed0f5386..1ec28dfd 100644 --- a/backend/src/applications/sync/interfaces/sync-client-paths.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-client-paths.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SyncClient } from '../schemas/sync-client.interface' import { SyncPath } from '../schemas/sync-path.interface' import { SyncClientInfo } from './sync-client.interface' diff --git a/backend/src/applications/sync/interfaces/sync-client.interface.ts b/backend/src/applications/sync/interfaces/sync-client.interface.ts index 3b6454a1..8565d7c3 100644 --- a/backend/src/applications/sync/interfaces/sync-client.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-client.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { SYNC_CLIENT_TYPE } from '../constants/sync' export interface SyncClientInfo { diff --git a/backend/src/applications/sync/interfaces/sync-diff.interface.ts b/backend/src/applications/sync/interfaces/sync-diff.interface.ts index 45667012..3cd25960 100644 --- a/backend/src/applications/sync/interfaces/sync-diff.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-diff.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // use FSTAT positions for stats array import { F_SPECIAL_STAT } from '../constants/sync' diff --git a/backend/src/applications/sync/interfaces/sync-path.interface.ts b/backend/src/applications/sync/interfaces/sync-path.interface.ts index b98dabd6..d638a3c3 100644 --- a/backend/src/applications/sync/interfaces/sync-path.interface.ts +++ b/backend/src/applications/sync/interfaces/sync-path.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { SYNC_PATH_CONFLICT_MODE, SYNC_PATH_DIFF_MODE, SYNC_PATH_MODE } from '../constants/sync' import type { SyncPath } from '../schemas/sync-path.interface' diff --git a/backend/src/applications/sync/schemas/sync-client.interface.ts b/backend/src/applications/sync/schemas/sync-client.interface.ts index e0967c7e..109ab67b 100644 --- a/backend/src/applications/sync/schemas/sync-client.interface.ts +++ b/backend/src/applications/sync/schemas/sync-client.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SyncClientInfo } from '../interfaces/sync-client.interface' import type { syncClients } from './sync-clients.schema' diff --git a/backend/src/applications/sync/schemas/sync-clients.schema.ts b/backend/src/applications/sync/schemas/sync-clients.schema.ts index d0726c48..251b231c 100644 --- a/backend/src/applications/sync/schemas/sync-clients.schema.ts +++ b/backend/src/applications/sync/schemas/sync-clients.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, boolean, char, datetime, index, mysqlTable, varchar } from 'drizzle-orm/mysql-core' import { jsonColumn } from '../../../infrastructure/database/columns' diff --git a/backend/src/applications/sync/schemas/sync-path.interface.ts b/backend/src/applications/sync/schemas/sync-path.interface.ts index dc50aa09..2aae640a 100644 --- a/backend/src/applications/sync/schemas/sync-path.interface.ts +++ b/backend/src/applications/sync/schemas/sync-path.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { SyncPathSettings } from '../interfaces/sync-path.interface' import { syncPaths } from './sync-paths.schema' diff --git a/backend/src/applications/sync/schemas/sync-paths.schema.ts b/backend/src/applications/sync/schemas/sync-paths.schema.ts index 293b0f8a..2b4aee6a 100644 --- a/backend/src/applications/sync/schemas/sync-paths.schema.ts +++ b/backend/src/applications/sync/schemas/sync-paths.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, char, datetime, index, mysqlTable } from 'drizzle-orm/mysql-core' import { jsonColumn } from '../../../infrastructure/database/columns' diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts index 375ab4cd..43743f4d 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/applications/sync/services/sync-clients-manager.service.ts b/backend/src/applications/sync/services/sync-clients-manager.service.ts index 80506c9b..6ef5ea69 100644 --- a/backend/src/applications/sync/services/sync-clients-manager.service.ts +++ b/backend/src/applications/sync/services/sync-clients-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpService } from '@nestjs/axios' import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { AxiosResponse } from 'axios' diff --git a/backend/src/applications/sync/services/sync-manager.service.spec.ts b/backend/src/applications/sync/services/sync-manager.service.spec.ts index 2b84dd94..3acb161b 100644 --- a/backend/src/applications/sync/services/sync-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, StreamableFile } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' // Helpers to access mocked fs/promises diff --git a/backend/src/applications/sync/services/sync-manager.service.ts b/backend/src/applications/sync/services/sync-manager.service.ts index 6829bbc2..dfbc226c 100644 --- a/backend/src/applications/sync/services/sync-manager.service.ts +++ b/backend/src/applications/sync/services/sync-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply } from 'fastify' import fs from 'fs/promises' diff --git a/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts b/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts index 94b29732..2da77d6d 100644 --- a/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts +++ b/backend/src/applications/sync/services/sync-paths-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { currentTimeStamp } from '../../../common/shared' diff --git a/backend/src/applications/sync/services/sync-paths-manager.service.ts b/backend/src/applications/sync/services/sync-paths-manager.service.ts index 0439324c..a4bcfd00 100644 --- a/backend/src/applications/sync/services/sync-paths-manager.service.ts +++ b/backend/src/applications/sync/services/sync-paths-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { ACTION } from '../../../common/constants' import { currentTimeStamp } from '../../../common/shared' diff --git a/backend/src/applications/sync/services/sync-queries.service.ts b/backend/src/applications/sync/services/sync-queries.service.ts index afb43143..bf5a047b 100644 --- a/backend/src/applications/sync/services/sync-queries.service.ts +++ b/backend/src/applications/sync/services/sync-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable } from '@nestjs/common' import { and, desc, eq, SQL, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/sync/sync.config.ts b/backend/src/applications/sync/sync.config.ts index 583a3776..f997f658 100644 --- a/backend/src/applications/sync/sync.config.ts +++ b/backend/src/applications/sync/sync.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsEnum } from 'class-validator' import { APP_STORE_REPOSITORY } from './constants/store' diff --git a/backend/src/applications/sync/sync.controller.spec.ts b/backend/src/applications/sync/sync.controller.spec.ts index 9677556b..5237d7db 100644 --- a/backend/src/applications/sync/sync.controller.spec.ts +++ b/backend/src/applications/sync/sync.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { ContextManager } from '../../infrastructure/context/services/context-manager.service' import { SpacesManager } from '../spaces/services/spaces-manager.service' diff --git a/backend/src/applications/sync/sync.controller.ts b/backend/src/applications/sync/sync.controller.ts index 3a6064ee..31d41b1b 100644 --- a/backend/src/applications/sync/sync.controller.ts +++ b/backend/src/applications/sync/sync.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, diff --git a/backend/src/applications/sync/sync.module.ts b/backend/src/applications/sync/sync.module.ts index 3d5a8a52..e3f39030 100644 --- a/backend/src/applications/sync/sync.module.ts +++ b/backend/src/applications/sync/sync.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { SyncDiffGzipBodyInterceptor } from './interceptors/sync-diff-gzip-body.interceptor' import { SyncClientsManager } from './services/sync-clients-manager.service' diff --git a/backend/src/applications/sync/utils/functions.ts b/backend/src/applications/sync/utils/functions.ts index e33225ca..5f1ecc0d 100644 --- a/backend/src/applications/sync/utils/functions.ts +++ b/backend/src/applications/sync/utils/functions.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import path from 'node:path' import { SYNC_FILE_NAME_PREFIX } from '../constants/sync' diff --git a/backend/src/applications/sync/utils/normalizedMap.ts b/backend/src/applications/sync/utils/normalizedMap.ts index 32e37693..86c05033 100644 --- a/backend/src/applications/sync/utils/normalizedMap.ts +++ b/backend/src/applications/sync/utils/normalizedMap.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export class NormalizedMap extends Map { // NFC-normalized path → actual key private index = new Map() diff --git a/backend/src/applications/sync/utils/routes.ts b/backend/src/applications/sync/utils/routes.ts index 95edf5f1..22e0883e 100644 --- a/backend/src/applications/sync/utils/routes.ts +++ b/backend/src/applications/sync/utils/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { PATH_TO_SPACE_SEGMENTS } from '../../spaces/utils/routes' import { SYNC_PATH_REPOSITORY } from '../constants/sync' diff --git a/backend/src/applications/users/admin-users.controller.spec.ts b/backend/src/applications/users/admin-users.controller.spec.ts index 91c3af91..35e8776a 100644 --- a/backend/src/applications/users/admin-users.controller.spec.ts +++ b/backend/src/applications/users/admin-users.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigModule } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/applications/users/admin-users.controller.ts b/backend/src/applications/users/admin-users.controller.ts index 90c196dd..fb23d901 100644 --- a/backend/src/applications/users/admin-users.controller.ts +++ b/backend/src/applications/users/admin-users.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Put, Res, Search, UseGuards } from '@nestjs/common' import { FastifyReply } from 'fastify' import { LoginResponseDto } from '../../authentication/dto/login-response.dto' diff --git a/backend/src/applications/users/constants/group.ts b/backend/src/applications/users/constants/group.ts index 5e0db76d..c1eb2e95 100644 --- a/backend/src/applications/users/constants/group.ts +++ b/backend/src/applications/users/constants/group.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum GROUP_TYPE { USER, PERSONAL diff --git a/backend/src/applications/users/constants/member.ts b/backend/src/applications/users/constants/member.ts index f1b19a8f..f5651a30 100644 --- a/backend/src/applications/users/constants/member.ts +++ b/backend/src/applications/users/constants/member.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum MEMBER_TYPE { USER = 'user', GUEST = 'guest', diff --git a/backend/src/applications/users/constants/routes.ts b/backend/src/applications/users/constants/routes.ts index c4968828..c78e156b 100644 --- a/backend/src/applications/users/constants/routes.ts +++ b/backend/src/applications/users/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ADMIN_ROUTE } from '../../admin/constants/routes' export const USERS_ROUTE = { diff --git a/backend/src/applications/users/constants/user.ts b/backend/src/applications/users/constants/user.ts index 7d200c06..f3baa2c7 100644 --- a/backend/src/applications/users/constants/user.ts +++ b/backend/src/applications/users/constants/user.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const USER_PASSWORD_MIN_LENGTH = 8 export const USER_MAX_PASSWORD_ATTEMPTS = 10 export const USER_LOGIN_VALIDATION = /^(?! )(?!.* $)[a-zA-Z0-9@\-\\._ ]{2,255}$/ diff --git a/backend/src/applications/users/constants/websocket.ts b/backend/src/applications/users/constants/websocket.ts index 09576b6b..2204be32 100644 --- a/backend/src/applications/users/constants/websocket.ts +++ b/backend/src/applications/users/constants/websocket.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const USER_ROOM_PREFIX = 'uid_' as const export const USERS_WS = { NAME_SPACE: '/', diff --git a/backend/src/applications/users/decorators/permissions.decorator.ts b/backend/src/applications/users/decorators/permissions.decorator.ts index 4f29d7e8..82a74570 100644 --- a/backend/src/applications/users/decorators/permissions.decorator.ts +++ b/backend/src/applications/users/decorators/permissions.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Reflector } from '@nestjs/core' import type { USER_PERMISSION } from '../constants/user' diff --git a/backend/src/applications/users/decorators/roles.decorator.ts b/backend/src/applications/users/decorators/roles.decorator.ts index da22be24..a2de3c98 100644 --- a/backend/src/applications/users/decorators/roles.decorator.ts +++ b/backend/src/applications/users/decorators/roles.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Reflector } from '@nestjs/core' import { USER_ROLE } from '../constants/user' diff --git a/backend/src/applications/users/decorators/user.decorator.ts b/backend/src/applications/users/decorators/user.decorator.ts index 04a106ed..657583b2 100644 --- a/backend/src/applications/users/decorators/user.decorator.ts +++ b/backend/src/applications/users/decorators/user.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createParamDecorator, ExecutionContext } from '@nestjs/common' import { UserModel } from '../models/user.model' diff --git a/backend/src/applications/users/dto/create-or-update-group.dto.ts b/backend/src/applications/users/dto/create-or-update-group.dto.ts index 72e6d02a..17c14dd8 100644 --- a/backend/src/applications/users/dto/create-or-update-group.dto.ts +++ b/backend/src/applications/users/dto/create-or-update-group.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsEnum, IsInt, IsOptional, IsString } from 'class-validator' import { GROUP_VISIBILITY } from '../constants/group' diff --git a/backend/src/applications/users/dto/create-or-update-user.dto.ts b/backend/src/applications/users/dto/create-or-update-user.dto.ts index 8b493c87..a6acf23f 100644 --- a/backend/src/applications/users/dto/create-or-update-user.dto.ts +++ b/backend/src/applications/users/dto/create-or-update-user.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsArray, diff --git a/backend/src/applications/users/dto/delete-user.dto.ts b/backend/src/applications/users/dto/delete-user.dto.ts index d8d87b7b..9b3ce975 100644 --- a/backend/src/applications/users/dto/delete-user.dto.ts +++ b/backend/src/applications/users/dto/delete-user.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsBoolean } from 'class-validator' export class DeleteUserDto { diff --git a/backend/src/applications/users/dto/search-members.dto.ts b/backend/src/applications/users/dto/search-members.dto.ts index 86ebad41..34e9923a 100644 --- a/backend/src/applications/users/dto/search-members.dto.ts +++ b/backend/src/applications/users/dto/search-members.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsInt, IsOptional, IsString } from 'class-validator' diff --git a/backend/src/applications/users/dto/user-properties.dto.ts b/backend/src/applications/users/dto/user-properties.dto.ts index f9da2c68..9627c4f5 100644 --- a/backend/src/applications/users/dto/user-properties.dto.ts +++ b/backend/src/applications/users/dto/user-properties.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsDate, IsDefined, IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, MinLength, ValidateIf } from 'class-validator' import { AUTH_SCOPE } from '../../../authentication/constants/scope' diff --git a/backend/src/applications/users/guards/permissions.guard.spec.ts b/backend/src/applications/users/guards/permissions.guard.spec.ts index 273f7fe6..5c244c76 100644 --- a/backend/src/applications/users/guards/permissions.guard.spec.ts +++ b/backend/src/applications/users/guards/permissions.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext, HttpException, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' diff --git a/backend/src/applications/users/guards/permissions.guard.ts b/backend/src/applications/users/guards/permissions.guard.ts index a7cbed64..082810ce 100644 --- a/backend/src/applications/users/guards/permissions.guard.ts +++ b/backend/src/applications/users/guards/permissions.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface' diff --git a/backend/src/applications/users/guards/roles.guard.spec.ts b/backend/src/applications/users/guards/roles.guard.spec.ts index d06d8bcc..7cfa1d16 100644 --- a/backend/src/applications/users/guards/roles.guard.spec.ts +++ b/backend/src/applications/users/guards/roles.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' diff --git a/backend/src/applications/users/guards/roles.guard.ts b/backend/src/applications/users/guards/roles.guard.ts index e5312cc9..341c8699 100644 --- a/backend/src/applications/users/guards/roles.guard.ts +++ b/backend/src/applications/users/guards/roles.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface' diff --git a/backend/src/applications/users/interfaces/admin-group.interface.ts b/backend/src/applications/users/interfaces/admin-group.interface.ts index 225a694a..cc21e4e9 100644 --- a/backend/src/applications/users/interfaces/admin-group.interface.ts +++ b/backend/src/applications/users/interfaces/admin-group.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { GROUP_TYPE, GROUP_VISIBILITY } from '../constants/group' import type { Group } from '../schemas/group.interface' diff --git a/backend/src/applications/users/interfaces/admin-user.interface.ts b/backend/src/applications/users/interfaces/admin-user.interface.ts index 5e3cf7de..874d9b5c 100644 --- a/backend/src/applications/users/interfaces/admin-user.interface.ts +++ b/backend/src/applications/users/interfaces/admin-user.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { User } from '../schemas/user.interface' import type { Member } from './member.interface' diff --git a/backend/src/applications/users/interfaces/group-browse.interface.ts b/backend/src/applications/users/interfaces/group-browse.interface.ts index 5404f661..b6c6bb73 100644 --- a/backend/src/applications/users/interfaces/group-browse.interface.ts +++ b/backend/src/applications/users/interfaces/group-browse.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { GROUP_TYPE } from '../constants/group' import { USER_GROUP_ROLE } from '../constants/user' import type { Member } from './member.interface' diff --git a/backend/src/applications/users/interfaces/group-member.ts b/backend/src/applications/users/interfaces/group-member.ts index 9d3bbf65..296841c5 100644 --- a/backend/src/applications/users/interfaces/group-member.ts +++ b/backend/src/applications/users/interfaces/group-member.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { MEMBER_TYPE } from '../constants/member' import type { Group } from '../schemas/group.interface' import type { Member } from './member.interface' diff --git a/backend/src/applications/users/interfaces/guest-user.interface.ts b/backend/src/applications/users/interfaces/guest-user.interface.ts index 21a0e210..d8cf4270 100644 --- a/backend/src/applications/users/interfaces/guest-user.interface.ts +++ b/backend/src/applications/users/interfaces/guest-user.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { User } from '../schemas/user.interface' import type { Member } from './member.interface' diff --git a/backend/src/applications/users/interfaces/member.interface.ts b/backend/src/applications/users/interfaces/member.interface.ts index 8ca13d0a..d4291010 100644 --- a/backend/src/applications/users/interfaces/member.interface.ts +++ b/backend/src/applications/users/interfaces/member.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { CreateOrUpdateLinkDto } from '../../links/dto/create-or-update-link.dto' import type { SPACE_ROLE } from '../../spaces/constants/spaces' import type { MEMBER_TYPE } from '../constants/member' diff --git a/backend/src/applications/users/interfaces/owner.interface.ts b/backend/src/applications/users/interfaces/owner.interface.ts index c9a30226..0758827d 100644 --- a/backend/src/applications/users/interfaces/owner.interface.ts +++ b/backend/src/applications/users/interfaces/owner.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface Owner { id?: number login: string diff --git a/backend/src/applications/users/interfaces/user-secrets.interface.ts b/backend/src/applications/users/interfaces/user-secrets.interface.ts index f947c57b..949f6981 100644 --- a/backend/src/applications/users/interfaces/user-secrets.interface.ts +++ b/backend/src/applications/users/interfaces/user-secrets.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { AUTH_SCOPE } from '../../../authentication/constants/scope' import { USER_SECRET } from '../constants/user' diff --git a/backend/src/applications/users/interfaces/websocket.interface.ts b/backend/src/applications/users/interfaces/websocket.interface.ts index 4c71f9fd..0e48146a 100644 --- a/backend/src/applications/users/interfaces/websocket.interface.ts +++ b/backend/src/applications/users/interfaces/websocket.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { USER_ONLINE_STATUS } from '../constants/user' export interface UserOnline { diff --git a/backend/src/applications/users/models/user.model.ts b/backend/src/applications/users/models/user.model.ts index 7b8b6d70..c2589992 100644 --- a/backend/src/applications/users/models/user.model.ts +++ b/backend/src/applications/users/models/user.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Exclude, Expose } from 'class-transformer' import fs from 'node:fs/promises' import path from 'node:path' diff --git a/backend/src/applications/users/schemas/group.interface.ts b/backend/src/applications/users/schemas/group.interface.ts index 55c9b1f7..473ced12 100644 --- a/backend/src/applications/users/schemas/group.interface.ts +++ b/backend/src/applications/users/schemas/group.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { groups } from './groups.schema' type GroupSchema = typeof groups.$inferSelect diff --git a/backend/src/applications/users/schemas/groups.schema.ts b/backend/src/applications/users/schemas/groups.schema.ts index b9074677..7fc40b05 100644 --- a/backend/src/applications/users/schemas/groups.schema.ts +++ b/backend/src/applications/users/schemas/groups.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { AnyMySqlColumn, bigint, datetime, index, mysqlTable, tinyint, uniqueIndex, varchar } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/users/schemas/user-group.interface.ts b/backend/src/applications/users/schemas/user-group.interface.ts index 244f3a1b..1272aaa5 100644 --- a/backend/src/applications/users/schemas/user-group.interface.ts +++ b/backend/src/applications/users/schemas/user-group.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { usersGroups } from './users-groups.schema' type UserGroupSchema = typeof usersGroups.$inferSelect diff --git a/backend/src/applications/users/schemas/user.interface.ts b/backend/src/applications/users/schemas/user.interface.ts index a1c338d9..a83ea3df 100644 --- a/backend/src/applications/users/schemas/user.interface.ts +++ b/backend/src/applications/users/schemas/user.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { UserSecrets } from '../interfaces/user-secrets.interface' import type { users } from './users.schema' diff --git a/backend/src/applications/users/schemas/users-groups.schema.ts b/backend/src/applications/users/schemas/users-groups.schema.ts index 5c80be0b..3bf651a8 100644 --- a/backend/src/applications/users/schemas/users-groups.schema.ts +++ b/backend/src/applications/users/schemas/users-groups.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, datetime, index, mysqlTable, primaryKey, tinyint } from 'drizzle-orm/mysql-core' import { groups } from './groups.schema' diff --git a/backend/src/applications/users/schemas/users-guests.schema.ts b/backend/src/applications/users/schemas/users-guests.schema.ts index 0dd0106f..4881eb37 100644 --- a/backend/src/applications/users/schemas/users-guests.schema.ts +++ b/backend/src/applications/users/schemas/users-guests.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sql } from 'drizzle-orm' import { bigint, check, datetime, index, mysqlTable, primaryKey } from 'drizzle-orm/mysql-core' import { users } from './users.schema' diff --git a/backend/src/applications/users/schemas/users.schema.ts b/backend/src/applications/users/schemas/users.schema.ts index 9f84a000..d582fa49 100644 --- a/backend/src/applications/users/schemas/users.schema.ts +++ b/backend/src/applications/users/schemas/users.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SQL, sql } from 'drizzle-orm' import { bigint, boolean, datetime, index, mysqlTable, tinyint, uniqueIndex, varchar } from 'drizzle-orm/mysql-core' import { jsonColumn } from '../../../infrastructure/database/columns' diff --git a/backend/src/applications/users/services/admin-users-manager.service.spec.ts b/backend/src/applications/users/services/admin-users-manager.service.spec.ts index d2e39219..4998b5a4 100644 --- a/backend/src/applications/users/services/admin-users-manager.service.spec.ts +++ b/backend/src/applications/users/services/admin-users-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { AuthManager } from '../../../authentication/auth.service' diff --git a/backend/src/applications/users/services/admin-users-manager.service.ts b/backend/src/applications/users/services/admin-users-manager.service.ts index 70ef7ddf..6ac7e316 100644 --- a/backend/src/applications/users/services/admin-users-manager.service.ts +++ b/backend/src/applications/users/services/admin-users-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { FastifyReply } from 'fastify' import { AuthManager } from '../../../authentication/auth.service' diff --git a/backend/src/applications/users/services/admin-users-queries.service.ts b/backend/src/applications/users/services/admin-users-queries.service.ts index 851c4f64..f461b0f2 100644 --- a/backend/src/applications/users/services/admin-users-queries.service.ts +++ b/backend/src/applications/users/services/admin-users-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, count, countDistinct, eq, inArray, isNotNull, isNull, lte, SelectedFields, SQL, sql } from 'drizzle-orm' import { alias, union } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/users/services/users-manager.service.spec.ts b/backend/src/applications/users/services/users-manager.service.spec.ts index 49409318..9fb9c7f5 100755 --- a/backend/src/applications/users/services/users-manager.service.spec.ts +++ b/backend/src/applications/users/services/users-manager.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import bcrypt from 'bcryptjs' import path from 'node:path' diff --git a/backend/src/applications/users/services/users-manager.service.ts b/backend/src/applications/users/services/users-manager.service.ts index 37f802f6..a1fc56d5 100755 --- a/backend/src/applications/users/services/users-manager.service.ts +++ b/backend/src/applications/users/services/users-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { MultipartFile } from '@fastify/multipart' import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import bcrypt from 'bcryptjs' diff --git a/backend/src/applications/users/services/users-queries.service.ts b/backend/src/applications/users/services/users-queries.service.ts index 6eef0f90..9f5c19d6 100644 --- a/backend/src/applications/users/services/users-queries.service.ts +++ b/backend/src/applications/users/services/users-queries.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { and, countDistinct, eq, inArray, isNotNull, like, lte, ne, notInArray, or, SelectedFields, SQL, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/mysql-core' diff --git a/backend/src/applications/users/users.controller.spec.ts b/backend/src/applications/users/users.controller.spec.ts index 62a3e78a..1147f510 100755 --- a/backend/src/applications/users/users.controller.spec.ts +++ b/backend/src/applications/users/users.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigModule } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/applications/users/users.controller.ts b/backend/src/applications/users/users.controller.ts index 59b90d12..9b245803 100755 --- a/backend/src/applications/users/users.controller.ts +++ b/backend/src/applications/users/users.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Delete, Get, Header, Param, ParseIntPipe, Patch, Post, Put, Req, Search, StreamableFile, UseGuards } from '@nestjs/common' import { createReadStream } from 'fs' import { LoginResponseDto } from '../../authentication/dto/login-response.dto' diff --git a/backend/src/applications/users/users.e2e-spec.ts b/backend/src/applications/users/users.e2e-spec.ts index fd62d6ac..2e829312 100644 --- a/backend/src/applications/users/users.e2e-spec.ts +++ b/backend/src/applications/users/users.e2e-spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { appBootstrap } from '../../app.bootstrap' import { AuthManager } from '../../authentication/auth.service' diff --git a/backend/src/applications/users/users.gateway.spec.ts b/backend/src/applications/users/users.gateway.spec.ts index 89212a35..d54fdb23 100644 --- a/backend/src/applications/users/users.gateway.spec.ts +++ b/backend/src/applications/users/users.gateway.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { UsersManager } from './services/users-manager.service' import { WebSocketUsers } from './users.gateway' diff --git a/backend/src/applications/users/users.gateway.ts b/backend/src/applications/users/users.gateway.ts index c3a90b9d..bd5deff2 100644 --- a/backend/src/applications/users/users.gateway.ts +++ b/backend/src/applications/users/users.gateway.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { BeforeApplicationShutdown, Logger } from '@nestjs/common' import { MessageBody, diff --git a/backend/src/applications/users/users.module.ts b/backend/src/applications/users/users.module.ts index 67bf24cb..1a0b678f 100755 --- a/backend/src/applications/users/users.module.ts +++ b/backend/src/applications/users/users.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { AdminUsersController } from './admin-users.controller' import { UserPermissionsGuard } from './guards/permissions.guard' diff --git a/backend/src/applications/users/utils/avatar.ts b/backend/src/applications/users/utils/avatar.ts index de5ee802..eeb635e9 100644 --- a/backend/src/applications/users/utils/avatar.ts +++ b/backend/src/applications/users/utils/avatar.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import path from 'node:path' import { convertImageToBase64 } from '../../../common/image' import { STATIC_ASSETS_PATH } from '../../../configuration/config.constants' diff --git a/backend/src/applications/users/utils/test.ts b/backend/src/applications/users/utils/test.ts index 7b614934..83a90c40 100644 --- a/backend/src/applications/users/utils/test.ts +++ b/backend/src/applications/users/utils/test.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { faker } from '@faker-js/faker' import { SERVER_NAME } from '../../../common/shared' import { USER_PERMISSION, USER_ROLE } from '../constants/user' diff --git a/backend/src/applications/webdav/constants/routes.ts b/backend/src/applications/webdav/constants/routes.ts index f76bdeca..0f1379dd 100644 --- a/backend/src/applications/webdav/constants/routes.ts +++ b/backend/src/applications/webdav/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SERVER_NAME } from '../../../common/shared' import { SPACE_ALIAS, SPACE_REPOSITORY } from '../../spaces/constants/spaces' diff --git a/backend/src/applications/webdav/constants/webdav.ts b/backend/src/applications/webdav/constants/webdav.ts index ab22001c..80444451 100644 --- a/backend/src/applications/webdav/constants/webdav.ts +++ b/backend/src/applications/webdav/constants/webdav.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SERVER_NAME } from '../../../common/shared' import { HTTP_STANDARD_METHOD, HTTP_WEBDAV_METHOD } from '../../applications.constants' import { WEBDAV_BASE_PATH } from './routes' diff --git a/backend/src/applications/webdav/decorators/if-header.decorator.ts b/backend/src/applications/webdav/decorators/if-header.decorator.ts index cb7a66f7..4c4dfa89 100644 --- a/backend/src/applications/webdav/decorators/if-header.decorator.ts +++ b/backend/src/applications/webdav/decorators/if-header.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FastifyReply } from 'fastify' import { FastifyDAVRequest } from '../interfaces/webdav.interface' diff --git a/backend/src/applications/webdav/decorators/webdav-context.decorator.ts b/backend/src/applications/webdav/decorators/webdav-context.decorator.ts index 958c0ebb..c6be16e2 100644 --- a/backend/src/applications/webdav/decorators/webdav-context.decorator.ts +++ b/backend/src/applications/webdav/decorators/webdav-context.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { applyDecorators, SetMetadata, UseFilters, UseGuards } from '@nestjs/common' import { AuthBasicGuard } from '../../../authentication/guards/auth-basic.guard' import { WebDAVExceptionsFilter } from '../filters/webdav.filter' diff --git a/backend/src/applications/webdav/filters/webdav.filter.spec.ts b/backend/src/applications/webdav/filters/webdav.filter.spec.ts index 6322a6f0..766e1446 100644 --- a/backend/src/applications/webdav/filters/webdav.filter.spec.ts +++ b/backend/src/applications/webdav/filters/webdav.filter.spec.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { ArgumentsHost, HttpException } from '@nestjs/common' import { Test } from '@nestjs/testing' import { SERVER_NAME } from '../../../common/shared' diff --git a/backend/src/applications/webdav/filters/webdav.filter.ts b/backend/src/applications/webdav/filters/webdav.filter.ts index b7bf1afa..9e3e6a42 100644 --- a/backend/src/applications/webdav/filters/webdav.filter.ts +++ b/backend/src/applications/webdav/filters/webdav.filter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common' import { SERVER_NAME } from '../../../common/shared' import { XML_CONTENT_TYPE } from '../constants/webdav' diff --git a/backend/src/applications/webdav/guards/webdav-protocol.guard.ts b/backend/src/applications/webdav/guards/webdav-protocol.guard.ts index 943711e1..a5c33e52 100644 --- a/backend/src/applications/webdav/guards/webdav-protocol.guard.ts +++ b/backend/src/applications/webdav/guards/webdav-protocol.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { ValidationError } from 'fast-xml-parser' import { FastifyReply } from 'fastify' diff --git a/backend/src/applications/webdav/interfaces/if-header.interface.ts b/backend/src/applications/webdav/interfaces/if-header.interface.ts index 6e615f0e..d109efd2 100644 --- a/backend/src/applications/webdav/interfaces/if-header.interface.ts +++ b/backend/src/applications/webdav/interfaces/if-header.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface IfHeader { path?: string token?: { mustMatch: boolean; value: string } diff --git a/backend/src/applications/webdav/interfaces/webdav.interface.ts b/backend/src/applications/webdav/interfaces/webdav.interface.ts index 41dca8a3..c2f84a9c 100644 --- a/backend/src/applications/webdav/interfaces/webdav.interface.ts +++ b/backend/src/applications/webdav/interfaces/webdav.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FastifyAuthenticatedRequest } from '../../../authentication/interfaces/auth-request.interface' import { SpaceEnv } from '../../spaces/models/space-env.model' import { DEPTH, LOCK_SCOPE, PROPSTAT } from '../constants/webdav' diff --git a/backend/src/applications/webdav/models/webdav-file.model.ts b/backend/src/applications/webdav/models/webdav-file.model.ts index 8ea92ec3..48154b13 100644 --- a/backend/src/applications/webdav/models/webdav-file.model.ts +++ b/backend/src/applications/webdav/models/webdav-file.model.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import path from 'node:path' import { encodeUrl } from '../../../common/shared' diff --git a/backend/src/applications/webdav/services/webdav-methods.service.spec.ts b/backend/src/applications/webdav/services/webdav-methods.service.spec.ts index 994d3446..2ccb604a 100644 --- a/backend/src/applications/webdav/services/webdav-methods.service.spec.ts +++ b/backend/src/applications/webdav/services/webdav-methods.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { HTTP_VERSION } from '../../applications.constants' diff --git a/backend/src/applications/webdav/services/webdav-methods.service.ts b/backend/src/applications/webdav/services/webdav-methods.service.ts index 4a4229cb..dd976c63 100644 --- a/backend/src/applications/webdav/services/webdav-methods.service.ts +++ b/backend/src/applications/webdav/services/webdav-methods.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger, StreamableFile } from '@nestjs/common' import { FastifyReply } from 'fastify' import { currentTimeStamp, encodeUrl } from '../../../common/shared' diff --git a/backend/src/applications/webdav/services/webdav-spaces.service.spec.ts b/backend/src/applications/webdav/services/webdav-spaces.service.spec.ts index c457c380..e9a2412a 100644 --- a/backend/src/applications/webdav/services/webdav-spaces.service.spec.ts +++ b/backend/src/applications/webdav/services/webdav-spaces.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { getProps, isPathExists, isPathIsDir } from '../../files/utils/files' diff --git a/backend/src/applications/webdav/services/webdav-spaces.service.ts b/backend/src/applications/webdav/services/webdav-spaces.service.ts index 245a5be9..1cf52831 100644 --- a/backend/src/applications/webdav/services/webdav-spaces.service.ts +++ b/backend/src/applications/webdav/services/webdav-spaces.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { getProps, isPathExists, isPathIsDir } from '../../files/utils/files' import { SPACE_REPOSITORY } from '../../spaces/constants/spaces' diff --git a/backend/src/applications/webdav/utils/bootstrap.ts b/backend/src/applications/webdav/utils/bootstrap.ts index f39eb1d9..c9e1e2a6 100644 --- a/backend/src/applications/webdav/utils/bootstrap.ts +++ b/backend/src/applications/webdav/utils/bootstrap.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { FastifyInstance } from 'fastify' import { HTTP_METHOD, HTTP_WEBDAV_METHOD } from '../../applications.constants' diff --git a/backend/src/applications/webdav/utils/if-header.ts b/backend/src/applications/webdav/utils/if-header.ts index d4a1a1af..d2d6edae 100644 --- a/backend/src/applications/webdav/utils/if-header.ts +++ b/backend/src/applications/webdav/utils/if-header.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { urlToPath } from '../../../common/functions' import { IfHeader } from '../interfaces/if-header.interface' diff --git a/backend/src/applications/webdav/utils/routes.ts b/backend/src/applications/webdav/utils/routes.ts index d32c4f37..4403d725 100644 --- a/backend/src/applications/webdav/utils/routes.ts +++ b/backend/src/applications/webdav/utils/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { PATH_TO_SPACE_SEGMENTS } from '../../spaces/utils/routes' import { WEBDAV_BASE_PATH, WEBDAV_SPACES } from '../constants/routes' diff --git a/backend/src/applications/webdav/utils/webdav.ts b/backend/src/applications/webdav/utils/webdav.ts index 181c023d..f969e128 100644 --- a/backend/src/applications/webdav/utils/webdav.ts +++ b/backend/src/applications/webdav/utils/webdav.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FastifyReply } from 'fastify' import http from 'node:http' import { currentTimeStamp, encodeUrl, SERVER_NAME } from '../../../common/shared' diff --git a/backend/src/applications/webdav/utils/xml.ts b/backend/src/applications/webdav/utils/xml.ts index 79bf3339..d1ea7293 100644 --- a/backend/src/applications/webdav/utils/xml.ts +++ b/backend/src/applications/webdav/utils/xml.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { XMLBuilder, XMLParser, XMLValidator } from 'fast-xml-parser' export const XML_NS_PREFIX = '@_' diff --git a/backend/src/applications/webdav/webdav.controller.spec.ts b/backend/src/applications/webdav/webdav.controller.spec.ts index 3deef035..4ccbb7ea 100644 --- a/backend/src/applications/webdav/webdav.controller.spec.ts +++ b/backend/src/applications/webdav/webdav.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { SpacesManager } from '../spaces/services/spaces-manager.service' import { WebDAVMethods } from './services/webdav-methods.service' diff --git a/backend/src/applications/webdav/webdav.controller.ts b/backend/src/applications/webdav/webdav.controller.ts index 83d371bd..46b7e29a 100644 --- a/backend/src/applications/webdav/webdav.controller.ts +++ b/backend/src/applications/webdav/webdav.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { All, Controller, HttpStatus, Options, Param, Propfind, Req, Res, StreamableFile, UseGuards } from '@nestjs/common' import { FastifyReply } from 'fastify' import { HTTP_METHOD } from '../applications.constants' diff --git a/backend/src/applications/webdav/webdav.e2e-spec.ts b/backend/src/applications/webdav/webdav.e2e-spec.ts index e169ffcb..af1e45fc 100644 --- a/backend/src/applications/webdav/webdav.e2e-spec.ts +++ b/backend/src/applications/webdav/webdav.e2e-spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { appBootstrap } from '../../app.bootstrap' import { XML_CONTENT_TYPE } from './constants/webdav' diff --git a/backend/src/applications/webdav/webdav.module.ts b/backend/src/applications/webdav/webdav.module.ts index 57e5e04d..94a949f0 100644 --- a/backend/src/applications/webdav/webdav.module.ts +++ b/backend/src/applications/webdav/webdav.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { WebDAVProtocolGuard } from './guards/webdav-protocol.guard' import { WebDAVMethods } from './services/webdav-methods.service' diff --git a/backend/src/authentication/auth.config.ts b/backend/src/authentication/auth.config.ts index a89a576b..f8c195d8 100644 --- a/backend/src/authentication/auth.config.ts +++ b/backend/src/authentication/auth.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Exclude, Type } from 'class-transformer' import { IsDefined, IsEnum, IsIn, IsNotEmpty, IsNotEmptyObject, IsObject, IsOptional, IsString, ValidateIf, ValidateNested } from 'class-validator' import { ACCESS_KEY, CSRF_KEY, REFRESH_KEY, WS_KEY } from './constants/auth' diff --git a/backend/src/authentication/auth.controller.spec.ts b/backend/src/authentication/auth.controller.spec.ts index 6764edfb..63341d81 100755 --- a/backend/src/authentication/auth.controller.spec.ts +++ b/backend/src/authentication/auth.controller.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigModule, ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { PassportModule } from '@nestjs/passport' diff --git a/backend/src/authentication/auth.controller.ts b/backend/src/authentication/auth.controller.ts index 86bb1be3..546722b1 100755 --- a/backend/src/authentication/auth.controller.ts +++ b/backend/src/authentication/auth.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Body, Controller, Get, Param, ParseIntPipe, Post, Req, Res, UseGuards } from '@nestjs/common' import { FastifyReply } from 'fastify' import { USER_ROLE } from '../applications/users/constants/user' diff --git a/backend/src/authentication/auth.e2e-spec.ts b/backend/src/authentication/auth.e2e-spec.ts index 20509ffb..4f00412a 100644 --- a/backend/src/authentication/auth.e2e-spec.ts +++ b/backend/src/authentication/auth.e2e-spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { NestFastifyApplication } from '@nestjs/platform-fastify' diff --git a/backend/src/authentication/auth.module.ts b/backend/src/authentication/auth.module.ts index 8d81b3f5..4919e7cc 100755 --- a/backend/src/authentication/auth.module.ts +++ b/backend/src/authentication/auth.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { APP_GUARD } from '@nestjs/core' import { JwtModule } from '@nestjs/jwt' diff --git a/backend/src/authentication/auth.service.spec.ts b/backend/src/authentication/auth.service.spec.ts index aa7d301b..18c183fd 100755 --- a/backend/src/authentication/auth.service.spec.ts +++ b/backend/src/authentication/auth.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ConfigService } from '@nestjs/config' import { JwtService } from '@nestjs/jwt' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/authentication/auth.service.ts b/backend/src/authentication/auth.service.ts index b373dbfa..d30903b4 100755 --- a/backend/src/authentication/auth.service.ts +++ b/backend/src/authentication/auth.service.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { unsign, UnsignResult } from '@fastify/cookie' import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' diff --git a/backend/src/authentication/constants/auth.ts b/backend/src/authentication/constants/auth.ts index 93055652..8df68348 100644 --- a/backend/src/authentication/constants/auth.ts +++ b/backend/src/authentication/constants/auth.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { TOKEN_TYPE } from '../interfaces/token.interface' import { API_AUTH_REFRESH, API_AUTH_WS, API_TWO_FA_LOGIN_VERIFY } from './routes' diff --git a/backend/src/authentication/constants/routes.ts b/backend/src/authentication/constants/routes.ts index a6617ccf..a5ab22aa 100644 --- a/backend/src/authentication/constants/routes.ts +++ b/backend/src/authentication/constants/routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const AUTH_ROUTE = { BASE: '/api/auth', LOGIN: 'login', diff --git a/backend/src/authentication/constants/scope.ts b/backend/src/authentication/constants/scope.ts index c9817ad3..38b43aae 100644 --- a/backend/src/authentication/constants/scope.ts +++ b/backend/src/authentication/constants/scope.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum AUTH_SCOPE { WEBDAV = 'webdav' } diff --git a/backend/src/authentication/decorators/auth-token-optional.decorator.ts b/backend/src/authentication/decorators/auth-token-optional.decorator.ts index b6c417f1..1bb2cf98 100644 --- a/backend/src/authentication/decorators/auth-token-optional.decorator.ts +++ b/backend/src/authentication/decorators/auth-token-optional.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { applyDecorators, UseGuards } from '@nestjs/common' import { AuthGuard } from '@nestjs/passport' import { AuthTokenSkip } from './auth-token-skip.decorator' diff --git a/backend/src/authentication/decorators/auth-token-skip.decorator.ts b/backend/src/authentication/decorators/auth-token-skip.decorator.ts index cee7f372..e3060886 100755 --- a/backend/src/authentication/decorators/auth-token-skip.decorator.ts +++ b/backend/src/authentication/decorators/auth-token-skip.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { SetMetadata } from '@nestjs/common' export const AUTH_TOKEN_SKIP = 'authTokenSkip' diff --git a/backend/src/authentication/dto/login-response.dto.ts b/backend/src/authentication/dto/login-response.dto.ts index 29848550..31140c08 100644 --- a/backend/src/authentication/dto/login-response.dto.ts +++ b/backend/src/authentication/dto/login-response.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { UserModel } from '../../applications/users/models/user.model' import { ServerConfig } from '../../configuration/config.interfaces' import { TokenResponseDto } from './token-response.dto' diff --git a/backend/src/authentication/dto/token-response.dto.ts b/backend/src/authentication/dto/token-response.dto.ts index a2a89093..4160b3a1 100644 --- a/backend/src/authentication/dto/token-response.dto.ts +++ b/backend/src/authentication/dto/token-response.dto.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsInt, IsOptional, IsString } from 'class-validator' export class TokenResponseDto { diff --git a/backend/src/authentication/guards/auth-anonymous.guard.spec.ts b/backend/src/authentication/guards/auth-anonymous.guard.spec.ts index 270cda28..8f1f1e9f 100644 --- a/backend/src/authentication/guards/auth-anonymous.guard.spec.ts +++ b/backend/src/authentication/guards/auth-anonymous.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' import { ConfigModule } from '@nestjs/config' diff --git a/backend/src/authentication/guards/auth-anonymous.guard.ts b/backend/src/authentication/guards/auth-anonymous.guard.ts index e50f47c5..894a1355 100644 --- a/backend/src/authentication/guards/auth-anonymous.guard.ts +++ b/backend/src/authentication/guards/auth-anonymous.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/authentication/guards/auth-anonymous.strategy.ts b/backend/src/authentication/guards/auth-anonymous.strategy.ts index 29c1be5c..25ca9ce2 100644 --- a/backend/src/authentication/guards/auth-anonymous.strategy.ts +++ b/backend/src/authentication/guards/auth-anonymous.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { PassportStrategy } from '@nestjs/passport' import { Strategy } from 'passport-strategy' diff --git a/backend/src/authentication/guards/auth-basic.guard.spec.ts b/backend/src/authentication/guards/auth-basic.guard.spec.ts index 85967464..b54e4554 100644 --- a/backend/src/authentication/guards/auth-basic.guard.spec.ts +++ b/backend/src/authentication/guards/auth-basic.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/authentication/guards/auth-basic.guard.ts b/backend/src/authentication/guards/auth-basic.guard.ts index 2db8ed47..6d7df57b 100644 --- a/backend/src/authentication/guards/auth-basic.guard.ts +++ b/backend/src/authentication/guards/auth-basic.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' import { FastifyRequest } from 'fastify' diff --git a/backend/src/authentication/guards/auth-basic.strategy.ts b/backend/src/authentication/guards/auth-basic.strategy.ts index 3367ae7a..53b789f9 100644 --- a/backend/src/authentication/guards/auth-basic.strategy.ts +++ b/backend/src/authentication/guards/auth-basic.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import { instanceToPlain, plainToInstance } from 'class-transformer' diff --git a/backend/src/authentication/guards/auth-digest.guard.ts b/backend/src/authentication/guards/auth-digest.guard.ts index b8bfa837..f211167c 100644 --- a/backend/src/authentication/guards/auth-digest.guard.ts +++ b/backend/src/authentication/guards/auth-digest.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // import { ExecutionContext, Injectable, Logger } from '@nestjs/common' // import { AuthGuard, IAuthGuard } from '@nestjs/passport' // diff --git a/backend/src/authentication/guards/auth-digest.strategy.ts b/backend/src/authentication/guards/auth-digest.strategy.ts index 28872a40..7c9331ac 100644 --- a/backend/src/authentication/guards/auth-digest.strategy.ts +++ b/backend/src/authentication/guards/auth-digest.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // import { Injectable } from '@nestjs/common' // import { PassportStrategy } from '@nestjs/passport' // import { PinoLogger } from 'nestjs-pino' diff --git a/backend/src/authentication/guards/auth-local.guard.spec.ts b/backend/src/authentication/guards/auth-local.guard.spec.ts index f8c6cf23..2bdcf62a 100644 --- a/backend/src/authentication/guards/auth-local.guard.spec.ts +++ b/backend/src/authentication/guards/auth-local.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' diff --git a/backend/src/authentication/guards/auth-local.guard.ts b/backend/src/authentication/guards/auth-local.guard.ts index a7a6988b..10dbab99 100755 --- a/backend/src/authentication/guards/auth-local.guard.ts +++ b/backend/src/authentication/guards/auth-local.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/authentication/guards/auth-local.strategy.ts b/backend/src/authentication/guards/auth-local.strategy.ts index 2d11cc6d..ccc98037 100755 --- a/backend/src/authentication/guards/auth-local.strategy.ts +++ b/backend/src/authentication/guards/auth-local.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, UnauthorizedException } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import type { FastifyRequest } from 'fastify' diff --git a/backend/src/authentication/guards/auth-token-access.guard.spec.ts b/backend/src/authentication/guards/auth-token-access.guard.spec.ts index ce21d492..f4de9d1b 100644 --- a/backend/src/authentication/guards/auth-token-access.guard.spec.ts +++ b/backend/src/authentication/guards/auth-token-access.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sign } from '@fastify/cookie' import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' diff --git a/backend/src/authentication/guards/auth-token-access.guard.ts b/backend/src/authentication/guards/auth-token-access.guard.ts index f145f241..bd991ad1 100755 --- a/backend/src/authentication/guards/auth-token-access.guard.ts +++ b/backend/src/authentication/guards/auth-token-access.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { Reflector } from '@nestjs/core' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/authentication/guards/auth-token-access.strategy.ts b/backend/src/authentication/guards/auth-token-access.strategy.ts index 3f1f56e6..3abed9a7 100755 --- a/backend/src/authentication/guards/auth-token-access.strategy.ts +++ b/backend/src/authentication/guards/auth-token-access.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import { FastifyRequest } from 'fastify' diff --git a/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts b/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts index d93da5b0..4660e06b 100644 --- a/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts +++ b/backend/src/authentication/guards/auth-token-refresh.guard.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { sign } from '@fastify/cookie' import { createMock, DeepMocked } from '@golevelup/ts-jest' import { ExecutionContext } from '@nestjs/common' diff --git a/backend/src/authentication/guards/auth-token-refresh.guard.ts b/backend/src/authentication/guards/auth-token-refresh.guard.ts index a89997b5..696430fd 100755 --- a/backend/src/authentication/guards/auth-token-refresh.guard.ts +++ b/backend/src/authentication/guards/auth-token-refresh.guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { ExecutionContext, Injectable, Logger } from '@nestjs/common' import { AuthGuard, IAuthGuard } from '@nestjs/passport' diff --git a/backend/src/authentication/guards/auth-token-refresh.strategy.ts b/backend/src/authentication/guards/auth-token-refresh.strategy.ts index 48a523ba..0184fb36 100755 --- a/backend/src/authentication/guards/auth-token-refresh.strategy.ts +++ b/backend/src/authentication/guards/auth-token-refresh.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AbstractStrategy, PassportStrategy } from '@nestjs/passport' import { FastifyRequest } from 'fastify' diff --git a/backend/src/authentication/guards/implementations/http-basic.strategy.ts b/backend/src/authentication/guards/implementations/http-basic.strategy.ts index 1a9ab7b9..140d20f9 100644 --- a/backend/src/authentication/guards/implementations/http-basic.strategy.ts +++ b/backend/src/authentication/guards/implementations/http-basic.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Strategy as PassportStrategy } from 'passport-strategy' export type BasicVerifyCallback = (err?: any, user?: any) => void diff --git a/backend/src/authentication/guards/implementations/http-digest.strategy.ts b/backend/src/authentication/guards/implementations/http-digest.strategy.ts index ecc85cfd..6b3002b7 100644 --- a/backend/src/authentication/guards/implementations/http-digest.strategy.ts +++ b/backend/src/authentication/guards/implementations/http-digest.strategy.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import crypto from 'node:crypto' import { Strategy as PassportStrategy } from 'passport-strategy' diff --git a/backend/src/authentication/interfaces/auth-request.interface.ts b/backend/src/authentication/interfaces/auth-request.interface.ts index d5059681..f43b399c 100644 --- a/backend/src/authentication/interfaces/auth-request.interface.ts +++ b/backend/src/authentication/interfaces/auth-request.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { FastifyRequest } from 'fastify' import { UserModel } from '../../applications/users/models/user.model' diff --git a/backend/src/authentication/interfaces/jwt-payload.interface.ts b/backend/src/authentication/interfaces/jwt-payload.interface.ts index 8e2b856a..46fe796e 100644 --- a/backend/src/authentication/interfaces/jwt-payload.interface.ts +++ b/backend/src/authentication/interfaces/jwt-payload.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export class JwtIdentityPayload { id: number login: string diff --git a/backend/src/authentication/interfaces/token.interface.ts b/backend/src/authentication/interfaces/token.interface.ts index b1c363ea..39cae539 100644 --- a/backend/src/authentication/interfaces/token.interface.ts +++ b/backend/src/authentication/interfaces/token.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum TOKEN_TYPE { ACCESS = 'access', ACCESS_2FA = 'access_2fa', diff --git a/backend/src/authentication/providers/auth-providers.constants.ts b/backend/src/authentication/providers/auth-providers.constants.ts index 72d5a506..7566e175 100644 --- a/backend/src/authentication/providers/auth-providers.constants.ts +++ b/backend/src/authentication/providers/auth-providers.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum AUTH_PROVIDER { MYSQL = 'mysql', LDAP = 'ldap', diff --git a/backend/src/authentication/providers/auth-providers.models.ts b/backend/src/authentication/providers/auth-providers.models.ts index c42479e3..e9b8a61f 100644 --- a/backend/src/authentication/providers/auth-providers.models.ts +++ b/backend/src/authentication/providers/auth-providers.models.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { UserModel } from '../../applications/users/models/user.model' import type { AUTH_SCOPE } from '../constants/scope' diff --git a/backend/src/authentication/providers/auth-providers.utils.ts b/backend/src/authentication/providers/auth-providers.utils.ts index 0404cc10..ea6398c2 100644 --- a/backend/src/authentication/providers/auth-providers.utils.ts +++ b/backend/src/authentication/providers/auth-providers.utils.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Provider } from '@nestjs/common' import { AUTH_PROVIDER } from './auth-providers.constants' diff --git a/backend/src/authentication/providers/ldap/auth-ldap.config.ts b/backend/src/authentication/providers/ldap/auth-ldap.config.ts index fe2bb535..6a840aaa 100644 --- a/backend/src/authentication/providers/ldap/auth-ldap.config.ts +++ b/backend/src/authentication/providers/ldap/auth-ldap.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { ArrayNotEmpty, diff --git a/backend/src/authentication/providers/ldap/auth-ldap.constants.ts b/backend/src/authentication/providers/ldap/auth-ldap.constants.ts index aab414cd..112e1041 100644 --- a/backend/src/authentication/providers/ldap/auth-ldap.constants.ts +++ b/backend/src/authentication/providers/ldap/auth-ldap.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum LDAP_LOGIN_ATTR { UID = 'uid', CN = 'cn', diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts index 5f189d61..a3cadafe 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { Mocked } from 'jest-mock' import { Client, InvalidCredentialsError } from 'ldapts' diff --git a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts index cd012f79..69f7426d 100644 --- a/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts +++ b/backend/src/authentication/providers/ldap/auth-provider-ldap.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { AndFilter, Client, ClientOptions, Entry, EqualityFilter, InvalidCredentialsError, OrFilter } from 'ldapts' import { CONNECT_ERROR_CODE } from '../../../app.constants' diff --git a/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts index e222f931..967873de 100644 --- a/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts +++ b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { CONNECT_ERROR_CODE } from '../../../app.constants' import { NotificationsManager } from '../../../applications/notifications/services/notifications-manager.service' diff --git a/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts index c993062d..d47246ab 100644 --- a/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts +++ b/backend/src/authentication/providers/mysql/auth-provider-mysql.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { CONNECT_ERROR_CODE } from '../../../app.constants' import { UserModel } from '../../../applications/users/models/user.model' diff --git a/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts b/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts index fec106a7..726104a3 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc-desktop.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const OAuthDesktopPortParam = 'desktop_port' as const export const OAuthDesktopCallBackURI = '/oidc/callback' as const export const OAuthDesktopLoopbackPorts = new Set([49152, 49153, 49154]) diff --git a/backend/src/authentication/providers/oidc/auth-oidc.config.ts b/backend/src/authentication/providers/oidc/auth-oidc.config.ts index 2bf75b78..9eafddfa 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.config.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { IsArray, diff --git a/backend/src/authentication/providers/oidc/auth-oidc.constants.ts b/backend/src/authentication/providers/oidc/auth-oidc.constants.ts index 05f95a63..249c66de 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.constants.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum OAuthTokenEndpoint { ClientSecretPost = 'client_secret_post', ClientSecretBasic = 'client_secret_basic' diff --git a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts index 35f2b5ab..cb1c6a68 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.controller.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.controller.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Controller, Get, HttpStatus, Query, Req, Res } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' import type { UserModel } from '../../../applications/users/models/user.model' diff --git a/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts index 9e5c1b7c..4cbf9092 100644 --- a/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts +++ b/backend/src/authentication/providers/oidc/auth-oidc.interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface AuthOIDCSettings { loginUrl: string autoRedirect: boolean diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts index c3bdd210..c971f5c2 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Module } from '@nestjs/common' import { AuthOIDCController } from './auth-oidc.controller' import { AuthProviderOIDC } from './auth-provider-oidc.service' diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts index 78eee58b..430cac4d 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { diff --git a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts index c74a2c44..3f764cd2 100644 --- a/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts +++ b/backend/src/authentication/providers/oidc/auth-provider-oidc.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { FastifyReply, FastifyRequest } from 'fastify' import { diff --git a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts index 2c5c48f2..c77bee98 100644 --- a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts +++ b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing' import { Totp } from 'time2fa' diff --git a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts index 75181f1e..ed07a0f1 100644 --- a/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts +++ b/backend/src/authentication/providers/two-fa/auth-provider-two-fa.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common' import { Totp } from 'time2fa' import { NOTIFICATION_APP, NOTIFICATION_APP_EVENT } from '../../../applications/notifications/constants/notifications' diff --git a/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts b/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts index c6ac2d05..f36e57d3 100644 --- a/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa-guard.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, mixin, Type } from '@nestjs/common' import { configuration } from '../../../configuration/config.environment' import { TWO_FA_HEADER_CODE, TWO_FA_HEADER_PASSWORD } from '../../constants/auth' diff --git a/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts index 8491350f..bc472ff7 100644 --- a/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2026 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsBoolean, IsDefined, IsNotEmptyObject, IsObject, IsString, ValidateNested } from 'class-validator' import { SERVER_NAME } from '../../../common/shared' diff --git a/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts index 33beec39..da8e30b0 100644 --- a/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa.dtos.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator' import { LoginResponseDto } from '../../dto/login-response.dto' diff --git a/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts b/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts index e2a3961d..9f73d3e0 100644 --- a/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts +++ b/backend/src/authentication/providers/two-fa/auth-two-fa.interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface TwoFaSetup { secret: string qrDataUrl: string diff --git a/backend/src/authentication/utils/crypt-secret.ts b/backend/src/authentication/utils/crypt-secret.ts index 81479c2f..ab1ea666 100644 --- a/backend/src/authentication/utils/crypt-secret.ts +++ b/backend/src/authentication/utils/crypt-secret.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import crypto from 'crypto' // Encrypt a plaintext string with a passphrase. Returns a compact string. diff --git a/backend/src/common/constants.ts b/backend/src/common/constants.ts index 8e5518cc..4b843e99 100644 --- a/backend/src/common/constants.ts +++ b/backend/src/common/constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export enum ACTION { ADD = 'add', UPDATE = 'update', diff --git a/backend/src/common/decorators.ts b/backend/src/common/decorators.ts index 46aaaf33..235fa7bc 100644 --- a/backend/src/common/decorators.ts +++ b/backend/src/common/decorators.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator' export function RejectIfMatch(regex: RegExp, validationOptions?: ValidationOptions) { diff --git a/backend/src/common/functions.ts b/backend/src/common/functions.ts index 7058aaba..12900fce 100755 --- a/backend/src/common/functions.ts +++ b/backend/src/common/functions.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { parse as parseMs } from '@lukeed/ms' import bcrypt from 'bcryptjs' import { ClassTransformOptions, plainToInstance } from 'class-transformer' diff --git a/backend/src/common/i18n.ts b/backend/src/common/i18n.ts index 3d0144cd..d1ee16ce 100644 --- a/backend/src/common/i18n.ts +++ b/backend/src/common/i18n.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const LANG_DEFAULT = 'en' as const export type i18nLocaleSupported = 'de' | 'en' | 'es' | 'fr' | 'hi' | 'it' | 'ja' | 'ko' | 'pl' | 'pt' | 'pt-BR' | 'ru' | 'tr' | 'zh' export const LANG_SUPPORTED = new Set(['de', 'en', 'es', 'fr', 'hi', 'it', 'ja', 'ko', 'pl', 'pt', 'pt-BR', 'ru', 'tr', 'zh']) diff --git a/backend/src/common/image.ts b/backend/src/common/image.ts index 850784b5..0a4adf7f 100644 --- a/backend/src/common/image.ts +++ b/backend/src/common/image.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import fs from 'node:fs/promises' import os from 'node:os' import path from 'node:path' diff --git a/backend/src/common/interfaces.ts b/backend/src/common/interfaces.ts index 4cd45594..411291a2 100644 --- a/backend/src/common/interfaces.ts +++ b/backend/src/common/interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export type Entries = { [K in keyof T]: [K, T[K]] }[keyof T][] export interface StorageQuota { diff --git a/backend/src/common/qrcode.ts b/backend/src/common/qrcode.ts index c8828fb5..501222cb 100644 --- a/backend/src/common/qrcode.ts +++ b/backend/src/common/qrcode.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import qrcode from 'qrcode-generator' export function qrcodeToDataURL(text: string) { diff --git a/backend/src/common/shared.ts b/backend/src/common/shared.ts index 11cbed8b..806bbf89 100644 --- a/backend/src/common/shared.ts +++ b/backend/src/common/shared.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - /* THIS FILE IS SHARED WITH THE FRONTEND PACKAGE */ import { SPACE_PERMS_SEP } from '../applications/spaces/constants/spaces' diff --git a/backend/src/configuration/config.constants.ts b/backend/src/configuration/config.constants.ts index 4bbe9ad4..b5b9705b 100644 --- a/backend/src/configuration/config.constants.ts +++ b/backend/src/configuration/config.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import path from 'node:path' import process from 'node:process' diff --git a/backend/src/configuration/config.environment.ts b/backend/src/configuration/config.environment.ts index 4c9a535e..143a96f3 100755 --- a/backend/src/configuration/config.environment.ts +++ b/backend/src/configuration/config.environment.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { join } from 'node:path' import { AuthTokenAccessConfig, AuthTokenRefreshConfig } from '../authentication/auth.config' import { ACCESS_KEY, CSRF_KEY, TWO_FA_VERIFY_EXPIRATION, WS_KEY } from '../authentication/constants/auth' diff --git a/backend/src/configuration/config.interfaces.ts b/backend/src/configuration/config.interfaces.ts index b8fe4326..a2f778a0 100644 --- a/backend/src/configuration/config.interfaces.ts +++ b/backend/src/configuration/config.interfaces.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface FileEditorProvider { collabora: boolean onlyoffice: boolean diff --git a/backend/src/configuration/config.loader.ts b/backend/src/configuration/config.loader.ts index e46ba535..a8b8aad5 100644 --- a/backend/src/configuration/config.loader.ts +++ b/backend/src/configuration/config.loader.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import deepmerge from 'deepmerge' import * as yaml from 'js-yaml' import fs from 'node:fs' diff --git a/backend/src/configuration/config.logger.ts b/backend/src/configuration/config.logger.ts index 0ece6aeb..bad055f4 100644 --- a/backend/src/configuration/config.logger.ts +++ b/backend/src/configuration/config.logger.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import type { Options } from 'pino-http' import type { LoggerConfig } from './config.validation' diff --git a/backend/src/configuration/config.validation.ts b/backend/src/configuration/config.validation.ts index c137bf1b..0f477f01 100644 --- a/backend/src/configuration/config.validation.ts +++ b/backend/src/configuration/config.validation.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform, Type } from 'class-transformer' import { IsBoolean, diff --git a/backend/src/infrastructure/cache/adapters/mysql-cache.adapter.ts b/backend/src/infrastructure/cache/adapters/mysql-cache.adapter.ts index dc1e5adc..66788e8a 100644 --- a/backend/src/infrastructure/cache/adapters/mysql-cache.adapter.ts +++ b/backend/src/infrastructure/cache/adapters/mysql-cache.adapter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject, Injectable, Logger } from '@nestjs/common' import { SchedulerRegistry } from '@nestjs/schedule' import { CronJob } from 'cron' diff --git a/backend/src/infrastructure/cache/adapters/redis-cache.adapter.ts b/backend/src/infrastructure/cache/adapters/redis-cache.adapter.ts index 8c71d43b..3a3b39fa 100644 --- a/backend/src/infrastructure/cache/adapters/redis-cache.adapter.ts +++ b/backend/src/infrastructure/cache/adapters/redis-cache.adapter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import { RedisClientOptions } from '@redis/client' import { createClient, RedisClientType } from 'redis' diff --git a/backend/src/infrastructure/cache/cache.config.ts b/backend/src/infrastructure/cache/cache.config.ts index 60f18b8c..f6c45653 100644 --- a/backend/src/infrastructure/cache/cache.config.ts +++ b/backend/src/infrastructure/cache/cache.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsIn, IsInt, IsNotEmpty, IsString, Min, ValidateIf } from 'class-validator' export class CacheConfig { diff --git a/backend/src/infrastructure/cache/cache.decorator.ts b/backend/src/infrastructure/cache/cache.decorator.ts index 7f8c448f..0204d5bf 100644 --- a/backend/src/infrastructure/cache/cache.decorator.ts +++ b/backend/src/infrastructure/cache/cache.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Inject } from '@nestjs/common' import { Cache } from './services/cache.service' diff --git a/backend/src/infrastructure/cache/cache.e2e-spec.ts b/backend/src/infrastructure/cache/cache.e2e-spec.ts index e713d3ad..ed97333c 100644 --- a/backend/src/infrastructure/cache/cache.e2e-spec.ts +++ b/backend/src/infrastructure/cache/cache.e2e-spec.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' import { LoggerModule } from 'nestjs-pino' import { setTimeout } from 'node:timers/promises' diff --git a/backend/src/infrastructure/cache/cache.module.ts b/backend/src/infrastructure/cache/cache.module.ts index aa68e739..293a2067 100644 --- a/backend/src/infrastructure/cache/cache.module.ts +++ b/backend/src/infrastructure/cache/cache.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { SchedulerRegistry } from '@nestjs/schedule' import { configuration } from '../../configuration/config.environment' diff --git a/backend/src/infrastructure/cache/schemas/mysql-cache.interface.ts b/backend/src/infrastructure/cache/schemas/mysql-cache.interface.ts index 3ceab2da..c21a698a 100644 --- a/backend/src/infrastructure/cache/schemas/mysql-cache.interface.ts +++ b/backend/src/infrastructure/cache/schemas/mysql-cache.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { cache } from './mysql-cache.schema' type MysqlCacheSchema = typeof cache.$inferSelect diff --git a/backend/src/infrastructure/cache/schemas/mysql-cache.schema.ts b/backend/src/infrastructure/cache/schemas/mysql-cache.schema.ts index 538914a0..4d4d3178 100644 --- a/backend/src/infrastructure/cache/schemas/mysql-cache.schema.ts +++ b/backend/src/infrastructure/cache/schemas/mysql-cache.schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { index, int, mysqlTable, varchar } from 'drizzle-orm/mysql-core' import { jsonColumn } from '../../database/columns' diff --git a/backend/src/infrastructure/cache/services/cache.service.ts b/backend/src/infrastructure/cache/services/cache.service.ts index 53116fc8..747fb90b 100644 --- a/backend/src/infrastructure/cache/services/cache.service.ts +++ b/backend/src/infrastructure/cache/services/cache.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { OnModuleDestroy, OnModuleInit } from '@nestjs/common' export abstract class Cache implements OnModuleInit, OnModuleDestroy { diff --git a/backend/src/infrastructure/context/context.module.ts b/backend/src/infrastructure/context/context.module.ts index 4ec4a1fe..71039462 100644 --- a/backend/src/infrastructure/context/context.module.ts +++ b/backend/src/infrastructure/context/context.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { ContextInterceptor } from './interceptors/context.interceptor' import { ContextManager } from './services/context-manager.service' diff --git a/backend/src/infrastructure/context/interceptors/context.interceptor.spec.ts b/backend/src/infrastructure/context/interceptors/context.interceptor.spec.ts index c3b4b065..7d65d324 100644 --- a/backend/src/infrastructure/context/interceptors/context.interceptor.spec.ts +++ b/backend/src/infrastructure/context/interceptors/context.interceptor.spec.ts @@ -1,14 +1,8 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - -import { Test, TestingModule } from '@nestjs/testing' import { CallHandler, ExecutionContext } from '@nestjs/common' +import { Test, TestingModule } from '@nestjs/testing' import { of } from 'rxjs' -import { ContextInterceptor } from './context.interceptor' import { ContextManager } from '../services/context-manager.service' +import { ContextInterceptor } from './context.interceptor' // Helper to create a minimal ExecutionContext with Fastify-like request function createHttpExecutionContext(request: any): ExecutionContext { diff --git a/backend/src/infrastructure/context/interceptors/context.interceptor.ts b/backend/src/infrastructure/context/interceptors/context.interceptor.ts index 0ae3a1cc..cbf351a1 100644 --- a/backend/src/infrastructure/context/interceptors/context.interceptor.ts +++ b/backend/src/infrastructure/context/interceptors/context.interceptor.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common' import { FastifyRequest } from 'fastify' import type { Observable } from 'rxjs' diff --git a/backend/src/infrastructure/context/interfaces/context-store.interface.ts b/backend/src/infrastructure/context/interfaces/context-store.interface.ts index 444d0adc..e494843d 100644 --- a/backend/src/infrastructure/context/interfaces/context-store.interface.ts +++ b/backend/src/infrastructure/context/interfaces/context-store.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface ContextStore { headerOriginUrl: string } diff --git a/backend/src/infrastructure/context/services/context-manager.service.spec.ts b/backend/src/infrastructure/context/services/context-manager.service.spec.ts index 40642067..400931dd 100644 --- a/backend/src/infrastructure/context/services/context-manager.service.spec.ts +++ b/backend/src/infrastructure/context/services/context-manager.service.spec.ts @@ -1,12 +1,6 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Test, TestingModule } from '@nestjs/testing' -import { ContextManager } from './context-manager.service' import type { ContextStore } from '../interfaces/context-store.interface' +import { ContextManager } from './context-manager.service' describe(ContextManager.name, () => { let contextManager: ContextManager diff --git a/backend/src/infrastructure/context/services/context-manager.service.ts b/backend/src/infrastructure/context/services/context-manager.service.ts index d7098833..c552a7dc 100644 --- a/backend/src/infrastructure/context/services/context-manager.service.ts +++ b/backend/src/infrastructure/context/services/context-manager.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { AsyncLocalStorage } from 'node:async_hooks' import type { Observable } from 'rxjs' diff --git a/backend/src/infrastructure/database/columns.ts b/backend/src/infrastructure/database/columns.ts index 24142a2b..8be76259 100644 --- a/backend/src/infrastructure/database/columns.ts +++ b/backend/src/infrastructure/database/columns.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { customType } from 'drizzle-orm/mysql-core' export const jsonColumn = () => diff --git a/backend/src/infrastructure/database/configuration.ts b/backend/src/infrastructure/database/configuration.ts index 4e7c5683..f26f0a51 100644 --- a/backend/src/infrastructure/database/configuration.ts +++ b/backend/src/infrastructure/database/configuration.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Config, defineConfig } from 'drizzle-kit' import { configLoader } from '../../configuration/config.loader' import { getSchemaPath, MIGRATIONS_PATH } from './constants' diff --git a/backend/src/infrastructure/database/constants.ts b/backend/src/infrastructure/database/constants.ts index 00c95c83..3dae7400 100644 --- a/backend/src/infrastructure/database/constants.ts +++ b/backend/src/infrastructure/database/constants.ts @@ -1,11 +1,5 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - -import path from 'node:path' import fs from 'node:fs' +import path from 'node:path' export const DB_CHARSET = 'utf8mb4' export const DB_TOKEN_PROVIDER = 'DB' diff --git a/backend/src/infrastructure/database/database.config.ts b/backend/src/infrastructure/database/database.config.ts index 81d61b83..753d3aeb 100644 --- a/backend/src/infrastructure/database/database.config.ts +++ b/backend/src/infrastructure/database/database.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Transform } from 'class-transformer' import { IsBoolean, IsNotEmpty, IsString } from 'class-validator' import { DB_CHARSET } from './constants' diff --git a/backend/src/infrastructure/database/database.logger.ts b/backend/src/infrastructure/database/database.logger.ts index 443f5768..390ea94c 100644 --- a/backend/src/infrastructure/database/database.logger.ts +++ b/backend/src/infrastructure/database/database.logger.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Logger } from '@nestjs/common' import { Logger as DrizzleLogger } from 'drizzle-orm/logger.js' diff --git a/backend/src/infrastructure/database/database.module.ts b/backend/src/infrastructure/database/database.module.ts index 1217e7f4..e48ea11f 100644 --- a/backend/src/infrastructure/database/database.module.ts +++ b/backend/src/infrastructure/database/database.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { DrizzleMySqlConfig, DrizzleMySqlModule } from '@knaadh/nestjs-drizzle-mysql2' import { BeforeApplicationShutdown, Global, Inject, Module } from '@nestjs/common' import { MySql2Client } from 'drizzle-orm/mysql2' diff --git a/backend/src/infrastructure/database/interfaces/database.interface.ts b/backend/src/infrastructure/database/interfaces/database.interface.ts index 92c87a65..c82619ed 100644 --- a/backend/src/infrastructure/database/interfaces/database.interface.ts +++ b/backend/src/infrastructure/database/interfaces/database.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { MySql2Database } from 'drizzle-orm/mysql2' import * as schema from '../schema' diff --git a/backend/src/infrastructure/database/schema.ts b/backend/src/infrastructure/database/schema.ts index dbbdab7d..95cc1599 100644 --- a/backend/src/infrastructure/database/schema.ts +++ b/backend/src/infrastructure/database/schema.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export * from '../cache/schemas/mysql-cache.schema' export * from '../../applications/users/schemas/users.schema' export * from '../../applications/users/schemas/groups.schema' diff --git a/backend/src/infrastructure/database/scripts/create-user.ts b/backend/src/infrastructure/database/scripts/create-user.ts index 827de917..dc6afbcc 100644 --- a/backend/src/infrastructure/database/scripts/create-user.ts +++ b/backend/src/infrastructure/database/scripts/create-user.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { count, eq, or } from 'drizzle-orm' import { USER_PERMISSION, USER_ROLE } from '../../../applications/users/constants/user' import { User } from '../../../applications/users/schemas/user.interface' diff --git a/backend/src/infrastructure/database/scripts/db.ts b/backend/src/infrastructure/database/scripts/db.ts index 413df79c..9d75c741 100644 --- a/backend/src/infrastructure/database/scripts/db.ts +++ b/backend/src/infrastructure/database/scripts/db.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { drizzle } from 'drizzle-orm/mysql2' import { configLoader } from '../../../configuration/config.loader' import * as schema from '../schema' diff --git a/backend/src/infrastructure/database/scripts/seed/main.ts b/backend/src/infrastructure/database/scripts/seed/main.ts index b98a5520..36aca441 100644 --- a/backend/src/infrastructure/database/scripts/seed/main.ts +++ b/backend/src/infrastructure/database/scripts/seed/main.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { usersAndGroups } from './usersgroups' async function main() { diff --git a/backend/src/infrastructure/database/scripts/seed/usersgroups.ts b/backend/src/infrastructure/database/scripts/seed/usersgroups.ts index 552c4289..a1b1246a 100644 --- a/backend/src/infrastructure/database/scripts/seed/usersgroups.ts +++ b/backend/src/infrastructure/database/scripts/seed/usersgroups.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { faker } from '@faker-js/faker' import { ResultSetHeader } from 'mysql2/promise' import { USER_PERMISSION, USER_ROLE } from '../../../../applications/users/constants/user' diff --git a/backend/src/infrastructure/database/utils.ts b/backend/src/infrastructure/database/utils.ts index ff20ad15..8b7400e2 100644 --- a/backend/src/infrastructure/database/utils.ts +++ b/backend/src/infrastructure/database/utils.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { TestingModule } from '@nestjs/testing' import { Column, eq, inArray, isNull, SQL, sql } from 'drizzle-orm' diff --git a/backend/src/infrastructure/mailer/interfaces/mail.interface.ts b/backend/src/infrastructure/mailer/interfaces/mail.interface.ts index b716c669..3ab6e066 100644 --- a/backend/src/infrastructure/mailer/interfaces/mail.interface.ts +++ b/backend/src/infrastructure/mailer/interfaces/mail.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export interface MailTransport { host: string port: number diff --git a/backend/src/infrastructure/mailer/mailer.config.ts b/backend/src/infrastructure/mailer/mailer.config.ts index 13a06454..0c72c902 100644 --- a/backend/src/infrastructure/mailer/mailer.config.ts +++ b/backend/src/infrastructure/mailer/mailer.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Type } from 'class-transformer' import { IsBoolean, IsInt, IsNotEmpty, IsObject, IsOptional, IsString, Max, Min, ValidateNested } from 'class-validator' diff --git a/backend/src/infrastructure/mailer/mailer.module.ts b/backend/src/infrastructure/mailer/mailer.module.ts index 30a2670e..61d1c94b 100644 --- a/backend/src/infrastructure/mailer/mailer.module.ts +++ b/backend/src/infrastructure/mailer/mailer.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Global, Module } from '@nestjs/common' import { Mailer } from './mailer.service' diff --git a/backend/src/infrastructure/mailer/mailer.service.spec.ts b/backend/src/infrastructure/mailer/mailer.service.spec.ts index 42a4d1b1..2897d854 100644 --- a/backend/src/infrastructure/mailer/mailer.service.spec.ts +++ b/backend/src/infrastructure/mailer/mailer.service.spec.ts @@ -1,15 +1,9 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - -import { Test, TestingModule } from '@nestjs/testing' import { ConfigService } from '@nestjs/config' +import { Test, TestingModule } from '@nestjs/testing' import { PinoLogger } from 'nestjs-pino' import nodemailer from 'nodemailer' -import { Mailer } from './mailer.service' import { MailerConfig } from './mailer.config' +import { Mailer } from './mailer.service' // Mocks jest.mock('nodemailer') diff --git a/backend/src/infrastructure/mailer/mailer.service.ts b/backend/src/infrastructure/mailer/mailer.service.ts index 53d1ebaa..6db06e97 100644 --- a/backend/src/infrastructure/mailer/mailer.service.ts +++ b/backend/src/infrastructure/mailer/mailer.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { PinoLogger } from 'nestjs-pino' diff --git a/backend/src/infrastructure/scheduler/scheduler.constants.ts b/backend/src/infrastructure/scheduler/scheduler.constants.ts index 035e3141..ce63a698 100644 --- a/backend/src/infrastructure/scheduler/scheduler.constants.ts +++ b/backend/src/infrastructure/scheduler/scheduler.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - export const SCHEDULER_ENV = 'SCHEDULER' export enum SCHEDULER_STATE { diff --git a/backend/src/infrastructure/scheduler/scheduler.module.ts b/backend/src/infrastructure/scheduler/scheduler.module.ts index 8978fd8a..0081f060 100644 --- a/backend/src/infrastructure/scheduler/scheduler.module.ts +++ b/backend/src/infrastructure/scheduler/scheduler.module.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { DynamicModule, Module } from '@nestjs/common' import { ScheduleModule as NestScheduleModule } from '@nestjs/schedule' import cluster from 'node:cluster' diff --git a/backend/src/infrastructure/websocket/adapters/cluster.adapter.ts b/backend/src/infrastructure/websocket/adapters/cluster.adapter.ts index d450b15c..f55e236c 100644 --- a/backend/src/infrastructure/websocket/adapters/cluster.adapter.ts +++ b/backend/src/infrastructure/websocket/adapters/cluster.adapter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable } from '@nestjs/common' import { IoAdapter } from '@nestjs/platform-socket.io' import { createAdapter } from '@socket.io/cluster-adapter' diff --git a/backend/src/infrastructure/websocket/adapters/redis.adapter.ts b/backend/src/infrastructure/websocket/adapters/redis.adapter.ts index 0cd0ce1a..2bd46578 100644 --- a/backend/src/infrastructure/websocket/adapters/redis.adapter.ts +++ b/backend/src/infrastructure/websocket/adapters/redis.adapter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Logger } from '@nestjs/common' import { IoAdapter } from '@nestjs/platform-socket.io' import { ServerOptions } from 'socket.io' diff --git a/backend/src/infrastructure/websocket/adapters/web-socket.adapter.ts b/backend/src/infrastructure/websocket/adapters/web-socket.adapter.ts index 9e5a0f02..5c67289e 100644 --- a/backend/src/infrastructure/websocket/adapters/web-socket.adapter.ts +++ b/backend/src/infrastructure/websocket/adapters/web-socket.adapter.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Injectable, Logger } from '@nestjs/common' import { JwtService } from '@nestjs/jwt' import { NestFastifyApplication } from '@nestjs/platform-fastify' diff --git a/backend/src/infrastructure/websocket/decorators/web-socket-user.decorator.ts b/backend/src/infrastructure/websocket/decorators/web-socket-user.decorator.ts index 3fad55ec..cd496bed 100644 --- a/backend/src/infrastructure/websocket/decorators/web-socket-user.decorator.ts +++ b/backend/src/infrastructure/websocket/decorators/web-socket-user.decorator.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { createParamDecorator, ExecutionContext } from '@nestjs/common' import type { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface' diff --git a/backend/src/infrastructure/websocket/interfaces/auth-socket-io.interface.ts b/backend/src/infrastructure/websocket/interfaces/auth-socket-io.interface.ts index fbe28abf..ca8556f5 100644 --- a/backend/src/infrastructure/websocket/interfaces/auth-socket-io.interface.ts +++ b/backend/src/infrastructure/websocket/interfaces/auth-socket-io.interface.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Socket } from 'socket.io' import { JwtIdentityPayload } from '../../../authentication/interfaces/jwt-payload.interface' diff --git a/backend/src/infrastructure/websocket/utils.ts b/backend/src/infrastructure/websocket/utils.ts index ac581362..57df1ff0 100644 --- a/backend/src/infrastructure/websocket/utils.ts +++ b/backend/src/infrastructure/websocket/utils.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Socket } from 'socket.io' import { configuration } from '../../configuration/config.environment' diff --git a/backend/src/infrastructure/websocket/web-socket.config.ts b/backend/src/infrastructure/websocket/web-socket.config.ts index 580089c8..091b45b8 100644 --- a/backend/src/infrastructure/websocket/web-socket.config.ts +++ b/backend/src/infrastructure/websocket/web-socket.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { IsIn, IsNotEmpty, IsString, ValidateIf } from 'class-validator' export class WebSocketConfig { diff --git a/backend/src/main.ts b/backend/src/main.ts index d3b6dd23..66443472 100755 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,10 +1,4 @@ #!/usr/bin/env node -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { NestFastifyApplication } from '@nestjs/platform-fastify' import { Logger } from 'nestjs-pino' import { appBootstrap } from './app.bootstrap' diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index 813818ae..1cab7966 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - // @ts-check const eslint = require('@eslint/js') const tseslint = require('typescript-eslint') diff --git a/frontend/scripts/build-assets.mjs b/frontend/scripts/build-assets.mjs index 6b8bd502..bbab8326 100644 --- a/frontend/scripts/build-assets.mjs +++ b/frontend/scripts/build-assets.mjs @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { checkPdfjs } from './pdfjs.mjs' if (process.env.NODE_ENV !== 'development') { diff --git a/frontend/scripts/pdfjs.mjs b/frontend/scripts/pdfjs.mjs index 8e5f6790..04e0f947 100644 --- a/frontend/scripts/pdfjs.mjs +++ b/frontend/scripts/pdfjs.mjs @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { fileURLToPath } from 'url' import fs from 'node:fs/promises' import path from 'node:path' diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 9c3db6a6..563533c3 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Component } from '@angular/core' import { RouterOutlet } from '@angular/router' diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index c9fbac6a..0c5fc18c 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HashLocationStrategy, LocationStrategy } from '@angular/common' import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi, withXsrfConfiguration } from '@angular/common/http' import { ApplicationConfig, importProvidersFrom, isDevMode, provideZoneChangeDetection } from '@angular/core' diff --git a/frontend/src/app/app.constants.ts b/frontend/src/app/app.constants.ts index 992a16b3..2ca72dd4 100644 --- a/frontend/src/app/app.constants.ts +++ b/frontend/src/app/app.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { faCircleHalfStroke } from '@fortawesome/free-solid-svg-icons' import { productName, version } from '../../../package.json' import { AppMenu } from './layout/layout.interfaces' diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 342914db..7a0ead30 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Routes } from '@angular/router' import { APP_PATH } from './app.constants' import { adminRoutes } from './applications/admin/admin.routes' diff --git a/frontend/src/app/applications/admin/admin.constants.ts b/frontend/src/app/applications/admin/admin.constants.ts index 60a99431..8cef5c1b 100644 --- a/frontend/src/app/applications/admin/admin.constants.ts +++ b/frontend/src/app/applications/admin/admin.constants.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { faGear, faUserGear, faUsersGear } from '@fortawesome/free-solid-svg-icons' import { AppMenu } from '../../layout/layout.interfaces' diff --git a/frontend/src/app/applications/admin/admin.guard.ts b/frontend/src/app/applications/admin/admin.guard.ts index bea81f6d..fec3f0e5 100644 --- a/frontend/src/app/applications/admin/admin.guard.ts +++ b/frontend/src/app/applications/admin/admin.guard.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ import { inject } from '@angular/core' import { CanActivateFn } from '@angular/router' import { UserService } from '../users/user.service' diff --git a/frontend/src/app/applications/admin/admin.routes.ts b/frontend/src/app/applications/admin/admin.routes.ts index 7f24cd3a..ecf12b14 100644 --- a/frontend/src/app/applications/admin/admin.routes.ts +++ b/frontend/src/app/applications/admin/admin.routes.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { Routes } from '@angular/router' import { GROUP_TYPE } from '@sync-in-server/backend/src/applications/users/constants/group' import { USER_ROLE } from '@sync-in-server/backend/src/applications/users/constants/user' diff --git a/frontend/src/app/applications/admin/admin.service.ts b/frontend/src/app/applications/admin/admin.service.ts index 65074c8e..9c0aacaf 100644 --- a/frontend/src/app/applications/admin/admin.service.ts +++ b/frontend/src/app/applications/admin/admin.service.ts @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2012-2025 Johan Legrand - * This file is part of Sync-in | The open source file sync and share solution - * See the LICENSE file for licensing details - */ - import { HttpClient, HttpHeaders } from '@angular/common/http' import { inject, Injectable } from '@angular/core' import { Router } from '@angular/router' diff --git a/frontend/src/app/applications/admin/components/admin-groups.component.html b/frontend/src/app/applications/admin/components/admin-groups.component.html index 113b8f8a..23418bf4 100644 --- a/frontend/src/app/applications/admin/components/admin-groups.component.html +++ b/frontend/src/app/applications/admin/components/admin-groups.component.html @@ -1,9 +1,3 @@ - -