[settings/security] Prevent toggle if only 1 authentication option (#10169)

fix https://github.com/twentyhq/core-team-issues/issues/387
This commit is contained in:
Guillim
2025-02-13 16:54:37 +01:00
committed by GitHub
parent 8a425456f2
commit 58a62ec6f0
4 changed files with 57 additions and 3 deletions

View File

@ -161,6 +161,31 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
customDomainRegistered = true;
}
const authProvidersBySystem = {
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
password: this.environmentService.get('AUTH_PASSWORD_ENABLED'),
microsoft: this.environmentService.get('AUTH_MICROSOFT_ENABLED'),
};
if (payload.isGoogleAuthEnabled && !authProvidersBySystem.google) {
throw new WorkspaceException(
'Google auth is not enabled in the system.',
WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED,
);
}
if (payload.isMicrosoftAuthEnabled && !authProvidersBySystem.microsoft) {
throw new WorkspaceException(
'Microsoft auth is not enabled in the system.',
WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED,
);
}
if (payload.isPasswordAuthEnabled && !authProvidersBySystem.password) {
throw new WorkspaceException(
'Password auth is not enabled in the system.',
WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED,
);
}
const permissionsEnabled = await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IsPermissionsEnabled,
workspace.id,

View File

@ -1,5 +1,6 @@
import {
ConflictError,
ForbiddenError,
InternalServerError,
NotFoundError,
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
@ -16,6 +17,8 @@ export const workspaceGraphqlApiExceptionHandler = (error: Error) => {
throw new NotFoundError(error.message);
case WorkspaceExceptionCode.SUBDOMAIN_ALREADY_TAKEN:
throw new ConflictError(error.message);
case WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED:
throw new ForbiddenError(error.message);
default:
throw new InternalServerError(error.message);
}

View File

@ -12,4 +12,5 @@ export enum WorkspaceExceptionCode {
DOMAIN_ALREADY_TAKEN = 'DOMAIN_ALREADY_TAKEN',
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
WORKSPACE_CUSTOM_DOMAIN_DISABLED = 'WORKSPACE_CUSTOM_DOMAIN_DISABLED',
ENVIRONMENT_VAR_NOT_ENABLED = 'ENVIRONMENT_VAR_NOT_ENABLED',
}

View File

@ -9,6 +9,8 @@ import {
} from '@nestjs/graphql';
import { InjectRepository } from '@nestjs/typeorm';
import assert from 'assert';
import { FileUpload, GraphQLUpload } from 'graphql-upload';
import { isDefined } from 'twenty-shared';
import { Repository } from 'typeorm';
@ -17,6 +19,7 @@ import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { CustomDomainValidRecords } from 'src/engine/core-modules/domain-manager/dtos/custom-domain-valid-records';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
@ -44,9 +47,7 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
import { GraphqlValidationExceptionFilter } from 'src/filters/graphql-validation-exception.filter';
import { assert } from 'src/utils/assert';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import { CustomDomainValidRecords } from 'src/engine/core-modules/domain-manager/dtos/custom-domain-valid-records';
import { Workspace } from './workspace.entity';
@ -76,7 +77,7 @@ export class WorkspaceResolver {
async currentWorkspace(@AuthWorkspace() { id }: Workspace) {
const workspace = await this.workspaceService.findById(id);
assert(workspace, 'User not found');
assert(workspace, 'Workspace not found');
return workspace;
}
@ -229,6 +230,30 @@ export class WorkspaceResolver {
return this.domainManagerService.getWorkspaceUrls(workspace);
}
@ResolveField(() => Boolean)
isGoogleAuthEnabled(@Parent() workspace: Workspace) {
return (
workspace.isGoogleAuthEnabled &&
this.environmentService.get('AUTH_GOOGLE_ENABLED')
);
}
@ResolveField(() => Boolean)
isMicrosoftAuthEnabled(@Parent() workspace: Workspace) {
return (
workspace.isMicrosoftAuthEnabled &&
this.environmentService.get('AUTH_MICROSOFT_ENABLED')
);
}
@ResolveField(() => Boolean)
isPasswordAuthEnabled(@Parent() workspace: Workspace) {
return (
workspace.isPasswordAuthEnabled &&
this.environmentService.get('AUTH_PASSWORD_ENABLED')
);
}
@Mutation(() => CustomDomainValidRecords, { nullable: true })
@UseGuards(WorkspaceAuthGuard)
async checkCustomDomainValidRecords(