feat(workspace): add support for custom domain status toggle (#10114)

Introduce isCustomDomainEnabled field in Workspace entity to manage
custom domain activation. Update related services, types, and logic to
validate and toggle the custom domain's status dynamically based on its
current state. This ensures accurate domain configurations are reflected
across the system.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Antoine Moreaux
2025-02-13 16:01:33 +01:00
committed by GitHub
parent b67e850011
commit 8a425456f2
45 changed files with 1320 additions and 352 deletions

View File

@ -124,6 +124,7 @@ export class GoogleAPIsAuthController {
err,
workspace ?? {
subdomain: this.environmentService.get('DEFAULT_SUBDOMAIN'),
customDomain: null,
},
),
);

View File

@ -20,6 +20,7 @@ import { GoogleRequest } from 'src/engine/core-modules/auth/strategies/google.au
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Controller('auth/google')
@UseFilters(AuthRestApiExceptionFilter)
@ -28,6 +29,7 @@ export class GoogleAuthController {
private readonly loginTokenService: LoginTokenService,
private readonly authService: AuthService,
private readonly guardRedirectService: GuardRedirectService,
private readonly domainManagerService: DomainManagerService,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
) {}
@ -118,7 +120,7 @@ export class GoogleAuthController {
return res.redirect(
this.guardRedirectService.getRedirectErrorUrlAndCaptureExceptions(
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
currentWorkspace,
),
),

View File

@ -131,6 +131,7 @@ export class MicrosoftAPIsAuthController {
err,
workspace ?? {
subdomain: this.environmentService.get('DEFAULT_SUBDOMAIN'),
customDomain: null,
},
),
);

View File

@ -19,6 +19,7 @@ import { MicrosoftRequest } from 'src/engine/core-modules/auth/strategies/micros
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Controller('auth/microsoft')
@UseFilters(AuthRestApiExceptionFilter)
@ -29,6 +30,7 @@ export class MicrosoftAuthController {
private readonly guardRedirectService: GuardRedirectService,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
private readonly domainManagerService: DomainManagerService,
) {}
@Get()
@ -119,7 +121,7 @@ export class MicrosoftAuthController {
return res.redirect(
this.guardRedirectService.getRedirectErrorUrlAndCaptureExceptions(
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
currentWorkspace,
),
),

View File

@ -37,6 +37,7 @@ import { SAMLRequest } from 'src/engine/core-modules/auth/strategies/saml.auth.s
import { OIDCRequest } from 'src/engine/core-modules/auth/strategies/oidc.auth.strategy';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Controller('auth')
export class SSOAuthController {
@ -44,6 +45,8 @@ export class SSOAuthController {
private readonly loginTokenService: LoginTokenService,
private readonly authService: AuthService,
private readonly guardRedirectService: GuardRedirectService,
private readonly domainManagerService: DomainManagerService,
private readonly sSOService: SSOService,
@InjectRepository(User, 'core')
private readonly userRepository: Repository<User>,
@ -157,7 +160,7 @@ export class SSOAuthController {
return res.redirect(
this.guardRedirectService.getRedirectErrorUrlAndCaptureExceptions(
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
workspaceIdentityProvider?.workspace,
),
),

View File

@ -14,6 +14,7 @@ import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
@ -23,6 +24,7 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
private readonly guardRedirectService: GuardRedirectService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private readonly domainManagerService: DomainManagerService,
) {
super({
prompt: 'select_account',
@ -71,7 +73,7 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
workspace,
),
);

View File

@ -11,6 +11,7 @@ import {
} from 'src/engine/core-modules/auth/auth.exception';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class GoogleOauthGuard extends AuthGuard('google') {
@ -18,6 +19,7 @@ export class GoogleOauthGuard extends AuthGuard('google') {
private readonly guardRedirectService: GuardRedirectService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private readonly domainManagerService: DomainManagerService,
) {
super({
prompt: 'select_account',
@ -51,7 +53,7 @@ export class GoogleOauthGuard extends AuthGuard('google') {
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
workspace,
),
);

View File

@ -12,9 +12,9 @@ import { MicrosoftAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/a
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class MicrosoftAPIsOauthRequestCodeGuard extends AuthGuard(
@ -22,11 +22,11 @@ export class MicrosoftAPIsOauthRequestCodeGuard extends AuthGuard(
) {
constructor(
private readonly environmentService: EnvironmentService,
private readonly featureFlagService: FeatureFlagService,
private readonly transientTokenService: TransientTokenService,
private readonly guardRedirectService: GuardRedirectService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private readonly domainManagerService: DomainManagerService,
) {
super({
prompt: 'select_account',
@ -72,7 +72,7 @@ export class MicrosoftAPIsOauthRequestCodeGuard extends AuthGuard(
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
workspace,
),
);

View File

@ -6,6 +6,7 @@ import { Repository } from 'typeorm';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class MicrosoftOAuthGuard extends AuthGuard('microsoft') {
@ -13,6 +14,7 @@ export class MicrosoftOAuthGuard extends AuthGuard('microsoft') {
private readonly guardRedirectService: GuardRedirectService,
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private readonly domainManagerService: DomainManagerService,
) {
super({
prompt: 'select_account',
@ -39,7 +41,7 @@ export class MicrosoftOAuthGuard extends AuthGuard('microsoft') {
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
workspace,
),
);

View File

@ -14,12 +14,14 @@ import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { SSOConfiguration } from 'src/engine/core-modules/sso/types/SSOConfigurations.type';
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class OIDCAuthGuard extends AuthGuard('openidconnect') {
constructor(
private readonly sSOService: SSOService,
private readonly guardRedirectService: GuardRedirectService,
private readonly domainManagerService: DomainManagerService,
) {
super();
}
@ -88,7 +90,7 @@ export class OIDCAuthGuard extends AuthGuard('openidconnect') {
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
identityProvider?.workspace,
),
);

View File

@ -9,6 +9,7 @@ import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/ser
import { OIDCAuthGuard } from 'src/engine/core-modules/auth/guards/oidc-auth.guard';
import { SSOConfiguration } from 'src/engine/core-modules/sso/types/SSOConfigurations.type';
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
const createMockExecutionContext = (mockedRequest: any): ExecutionContext => {
return {
@ -58,6 +59,13 @@ describe('OIDCAuthGuard', () => {
getSubdomainAndCustomDomainFromWorkspace: jest.fn(),
},
},
{
provide: DomainManagerService,
useValue: {
getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain:
jest.fn(),
},
},
],
}).compile();

View File

@ -14,12 +14,14 @@ import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { SSOConfiguration } from 'src/engine/core-modules/sso/types/SSOConfigurations.type';
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
@Injectable()
export class SAMLAuthGuard extends AuthGuard('saml') {
constructor(
private readonly sSOService: SSOService,
private readonly guardRedirectService: GuardRedirectService,
private readonly domainManagerService: DomainManagerService,
) {
super();
}
@ -49,7 +51,7 @@ export class SAMLAuthGuard extends AuthGuard('saml') {
this.guardRedirectService.dispatchErrorFromGuard(
context,
err,
this.guardRedirectService.getSubdomainAndCustomDomainFromWorkspace(
this.domainManagerService.getSubdomainAndCustomDomainFromWorkspaceFallbackOnDefaultSubdomain(
identityProvider?.workspace,
),
);

View File

@ -56,6 +56,7 @@ import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-in
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { WorkspaceSubdomainCustomDomainAndIsCustomDomainEnabledType } from 'src/engine/core-modules/domain-manager/domain-manager.type';
@Injectable()
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
@ -459,7 +460,7 @@ export class AuthService {
billingCheckoutSessionState,
}: {
loginToken: string;
workspace: Pick<Workspace, 'subdomain' | 'customDomain'>;
workspace: WorkspaceSubdomainCustomDomainAndIsCustomDomainEnabledType;
billingCheckoutSessionState?: string;
}) {
const url = this.domainManagerService.buildWorkspaceURL({