@ -38,7 +38,6 @@ import {
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator';
|
||||
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
|
||||
|
||||
import { ChallengeInput } from './dto/challenge.input';
|
||||
@ -129,13 +128,7 @@ export class AuthResolver {
|
||||
targetWorkspaceSubdomain:
|
||||
this.domainManagerService.getWorkspaceSubdomainByOrigin(origin),
|
||||
fromSSO: false,
|
||||
isAuthEnabled: workspaceValidator.isAuthEnabled(
|
||||
'password',
|
||||
new AuthException(
|
||||
'Password auth is not enabled for this workspace',
|
||||
AuthExceptionCode.OAUTH_ACCESS_DENIED,
|
||||
),
|
||||
),
|
||||
authProvider: 'password',
|
||||
});
|
||||
|
||||
const loginToken = await this.loginTokenService.generateLoginToken(
|
||||
|
||||
@ -18,13 +18,9 @@ import { GoogleProviderEnabledGuard } from 'src/engine/core-modules/auth/guards/
|
||||
import { AuthService } from 'src/engine/core-modules/auth/services/auth.service';
|
||||
import { GoogleRequest } from 'src/engine/core-modules/auth/strategies/google.auth.strategy';
|
||||
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
@Controller('auth/google')
|
||||
@ -70,13 +66,7 @@ export class GoogleAuthController {
|
||||
workspacePersonalInviteToken,
|
||||
targetWorkspaceSubdomain,
|
||||
fromSSO: true,
|
||||
isAuthEnabled: workspaceValidator.isAuthEnabled(
|
||||
'google',
|
||||
new AuthException(
|
||||
'Google auth is not enabled for this workspace',
|
||||
AuthExceptionCode.OAUTH_ACCESS_DENIED,
|
||||
),
|
||||
),
|
||||
isAuthEnabled: 'google',
|
||||
};
|
||||
|
||||
if (
|
||||
|
||||
@ -15,12 +15,8 @@ import { MicrosoftProviderEnabledGuard } from 'src/engine/core-modules/auth/guar
|
||||
import { AuthService } from 'src/engine/core-modules/auth/services/auth.service';
|
||||
import { MicrosoftRequest } from 'src/engine/core-modules/auth/strategies/microsoft.auth.strategy';
|
||||
import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service';
|
||||
import {
|
||||
AuthException,
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
|
||||
|
||||
@Controller('auth/microsoft')
|
||||
@ -66,13 +62,7 @@ export class MicrosoftAuthController {
|
||||
workspacePersonalInviteToken,
|
||||
targetWorkspaceSubdomain,
|
||||
fromSSO: true,
|
||||
isAuthEnabled: workspaceValidator.isAuthEnabled(
|
||||
'microsoft',
|
||||
new AuthException(
|
||||
'Microsoft auth is not enabled for this workspace',
|
||||
AuthExceptionCode.OAUTH_ACCESS_DENIED,
|
||||
),
|
||||
),
|
||||
authProvider: 'microsoft',
|
||||
});
|
||||
|
||||
const loginToken = await this.loginTokenService.generateLoginToken(
|
||||
|
||||
@ -45,9 +45,9 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use
|
||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
|
||||
import { UserService } from 'src/engine/core-modules/user/services/user.service';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
|
||||
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
|
||||
|
||||
@Injectable()
|
||||
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
|
||||
@ -161,7 +161,7 @@ export class AuthService {
|
||||
lastName,
|
||||
picture,
|
||||
fromSSO,
|
||||
isAuthEnabled,
|
||||
authProvider,
|
||||
}: {
|
||||
email: string;
|
||||
password?: string;
|
||||
@ -172,7 +172,7 @@ export class AuthService {
|
||||
picture?: string | null;
|
||||
fromSSO: boolean;
|
||||
targetWorkspaceSubdomain?: string;
|
||||
isAuthEnabled?: ReturnType<(typeof workspaceValidator)['isAuthEnabled']>;
|
||||
authProvider?: WorkspaceAuthProvider;
|
||||
}) {
|
||||
return await this.signInUpService.signInUp({
|
||||
email,
|
||||
@ -184,7 +184,7 @@ export class AuthService {
|
||||
targetWorkspaceSubdomain,
|
||||
picture,
|
||||
fromSSO,
|
||||
isAuthEnabled,
|
||||
authProvider,
|
||||
});
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ export class AuthService {
|
||||
where: { email },
|
||||
});
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
userWithIdAndDefaultWorkspaceId,
|
||||
new AuthException('User not found', AuthExceptionCode.USER_NOT_FOUND),
|
||||
);
|
||||
@ -210,7 +210,7 @@ export class AuthService {
|
||||
workspaceId &&
|
||||
userWithIdAndDefaultWorkspaceId.defaultWorkspaceId !== workspaceId
|
||||
) {
|
||||
await this.userService.saveDefaultWorkspace(
|
||||
await this.userService.saveDefaultWorkspaceIfUserHasAccessOrThrow(
|
||||
userWithIdAndDefaultWorkspaceId.id,
|
||||
workspaceId,
|
||||
);
|
||||
@ -223,7 +223,7 @@ export class AuthService {
|
||||
relations: ['defaultWorkspace', 'workspaces', 'workspaces.workspace'],
|
||||
});
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
user,
|
||||
new AuthException('User not found', AuthExceptionCode.USER_NOT_FOUND),
|
||||
);
|
||||
@ -254,7 +254,7 @@ export class AuthService {
|
||||
email,
|
||||
});
|
||||
|
||||
if (userValidator.isExist(user)) {
|
||||
if (userValidator.isDefined(user)) {
|
||||
return {
|
||||
exists: true,
|
||||
availableWorkspaces: await this.findAvailableWorkspacesByEmail(email),
|
||||
@ -460,7 +460,7 @@ export class AuthService {
|
||||
],
|
||||
});
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
user,
|
||||
new AuthException('User not found', AuthExceptionCode.USER_NOT_FOUND),
|
||||
);
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
} from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
|
||||
|
||||
export type SignInUpServiceInput = {
|
||||
email: string;
|
||||
@ -43,7 +44,7 @@ export type SignInUpServiceInput = {
|
||||
picture?: string | null;
|
||||
fromSSO: boolean;
|
||||
targetWorkspaceSubdomain?: string;
|
||||
isAuthEnabled?: ReturnType<(typeof workspaceValidator)['isAuthEnabled']>;
|
||||
authProvider?: WorkspaceAuthProvider;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@ -73,7 +74,7 @@ export class SignInUpService {
|
||||
picture,
|
||||
fromSSO,
|
||||
targetWorkspaceSubdomain,
|
||||
isAuthEnabled,
|
||||
authProvider,
|
||||
}: SignInUpServiceInput) {
|
||||
if (!firstName) firstName = '';
|
||||
if (!lastName) lastName = '';
|
||||
@ -154,7 +155,7 @@ export class SignInUpService {
|
||||
lastName,
|
||||
picture,
|
||||
existingUser,
|
||||
isAuthEnabled,
|
||||
authProvider,
|
||||
});
|
||||
|
||||
await this.workspaceInvitationService.invalidateWorkspaceInvitation(
|
||||
@ -187,7 +188,7 @@ export class SignInUpService {
|
||||
lastName,
|
||||
picture,
|
||||
existingUser,
|
||||
isAuthEnabled,
|
||||
authProvider,
|
||||
}: {
|
||||
email: string;
|
||||
passwordHash: string | undefined;
|
||||
@ -196,7 +197,7 @@ export class SignInUpService {
|
||||
lastName: string;
|
||||
picture: SignInUpServiceInput['picture'];
|
||||
existingUser: User | null;
|
||||
isAuthEnabled?: ReturnType<(typeof workspaceValidator)['isAuthEnabled']>;
|
||||
authProvider?: WorkspaceAuthProvider;
|
||||
}) {
|
||||
const isNewUser = !isDefined(existingUser);
|
||||
let user = existingUser;
|
||||
@ -217,8 +218,16 @@ export class SignInUpService {
|
||||
),
|
||||
);
|
||||
|
||||
if (isAuthEnabled)
|
||||
workspaceValidator.validateAuth(isAuthEnabled, workspace);
|
||||
if (authProvider) {
|
||||
workspaceValidator.isAuthEnabledOrThrow(
|
||||
authProvider,
|
||||
workspace,
|
||||
new AuthException(
|
||||
`${authProvider} auth is not enabled for this workspace`,
|
||||
AuthExceptionCode.OAUTH_ACCESS_DENIED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isNewUser) {
|
||||
const imagePath = await this.uploadPicture(picture, workspace.id);
|
||||
@ -236,7 +245,7 @@ export class SignInUpService {
|
||||
user = await this.userRepository.save(userToCreate);
|
||||
}
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
user,
|
||||
new AuthException(
|
||||
'User not found',
|
||||
|
||||
@ -47,7 +47,7 @@ describe('SwitchWorkspaceService', () => {
|
||||
{
|
||||
provide: UserService,
|
||||
useValue: {
|
||||
saveDefaultWorkspace: jest.fn(),
|
||||
saveDefaultWorkspaceIfUserHasAccessOrThrow: jest.fn(),
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -211,10 +211,9 @@ describe('SwitchWorkspaceService', () => {
|
||||
refreshToken: mockRefreshToken,
|
||||
},
|
||||
});
|
||||
expect(userService.saveDefaultWorkspace).toHaveBeenCalledWith(
|
||||
mockUser.id,
|
||||
mockWorkspace.id,
|
||||
);
|
||||
expect(
|
||||
userService.saveDefaultWorkspaceIfUserHasAccessOrThrow,
|
||||
).toHaveBeenCalledWith(mockUser.id, mockWorkspace.id);
|
||||
expect(accessTokenService.generateAccessToken).toHaveBeenCalledWith(
|
||||
mockUser.id,
|
||||
mockWorkspace.id,
|
||||
|
||||
@ -78,7 +78,10 @@ export class SwitchWorkspaceService {
|
||||
user: User,
|
||||
workspace: Workspace,
|
||||
): Promise<AuthTokens> {
|
||||
await this.userService.saveDefaultWorkspace(user.id, workspace.id);
|
||||
await this.userService.saveDefaultWorkspaceIfUserHasAccessOrThrow(
|
||||
user.id,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
const token = await this.accessTokenService.generateAccessToken(
|
||||
user.id,
|
||||
|
||||
@ -135,7 +135,7 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
return user;
|
||||
}
|
||||
|
||||
async saveDefaultWorkspace(userId: string, workspaceId: string) {
|
||||
async hasUserAccessToWorkspaceOrThrow(userId: string, workspaceId: string) {
|
||||
const user = await this.userRepository.findOne({
|
||||
where: {
|
||||
id: userId,
|
||||
@ -146,13 +146,20 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
relations: ['workspaces'],
|
||||
});
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
user,
|
||||
new AuthException(
|
||||
'User does not have access to this workspace',
|
||||
AuthExceptionCode.FORBIDDEN_EXCEPTION,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async saveDefaultWorkspaceIfUserHasAccessOrThrow(
|
||||
userId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
await this.hasUserAccessToWorkspaceOrThrow(userId, workspaceId);
|
||||
|
||||
return await this.userRepository.save({
|
||||
id: userId,
|
||||
|
||||
@ -77,7 +77,10 @@ export class UserResolver {
|
||||
this.environmentService.get('IS_MULTIWORKSPACE_ENABLED') &&
|
||||
workspaceId
|
||||
) {
|
||||
await this.userService.saveDefaultWorkspace(userId, workspaceId);
|
||||
await this.userService.saveDefaultWorkspaceIfUserHasAccessOrThrow(
|
||||
userId,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
const user = await this.userRepository.findOne({
|
||||
@ -87,7 +90,7 @@ export class UserResolver {
|
||||
relations: ['defaultWorkspace', 'workspaces', 'workspaces.workspace'],
|
||||
});
|
||||
|
||||
userValidator.assertIsExist(
|
||||
userValidator.assertIsDefinedOrThrow(
|
||||
user,
|
||||
new AuthException('User not found', AuthExceptionCode.USER_NOT_FOUND),
|
||||
);
|
||||
|
||||
@ -1,34 +1,24 @@
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
|
||||
const assertIsExist = (
|
||||
const assertIsDefinedOrThrow = (
|
||||
user: User | undefined | null,
|
||||
exceptionToThrow: CustomException,
|
||||
): asserts user is User => {
|
||||
if (!user) {
|
||||
if (!isDefined(user)) {
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
};
|
||||
|
||||
const isExist = (user: User | undefined | null): user is User => {
|
||||
return !!user;
|
||||
};
|
||||
|
||||
const assertHasDefaultWorkspace = (
|
||||
user: User,
|
||||
exceptionToThrow?: CustomException,
|
||||
): asserts user is User & { defaultWorkspaceId: string } => {
|
||||
if (!user.defaultWorkspaceId) {
|
||||
throw exceptionToThrow;
|
||||
}
|
||||
const isUserDefined = (user: User | undefined | null): user is User => {
|
||||
return isDefined(user);
|
||||
};
|
||||
|
||||
export const userValidator: {
|
||||
assertIsExist: typeof assertIsExist;
|
||||
assertHasDefaultWorkspace: typeof assertHasDefaultWorkspace;
|
||||
isExist: typeof isExist;
|
||||
assertIsDefinedOrThrow: typeof assertIsDefinedOrThrow;
|
||||
isDefined: typeof isUserDefined;
|
||||
} = {
|
||||
assertIsExist,
|
||||
assertHasDefaultWorkspace,
|
||||
isExist,
|
||||
assertIsDefinedOrThrow,
|
||||
isDefined: isUserDefined,
|
||||
};
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export type WorkspaceAuthProvider = 'google' | 'microsoft' | 'password';
|
||||
@ -3,8 +3,8 @@ import {
|
||||
WorkspaceActivationStatus,
|
||||
} from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
type WorkspaceAuthProvider = 'google' | 'microsoft' | 'password';
|
||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
|
||||
|
||||
const assertIsExist = (
|
||||
workspace: Workspace | undefined | null,
|
||||
@ -25,40 +25,24 @@ const assertIsActive = (
|
||||
throw exceptionToThrow;
|
||||
};
|
||||
|
||||
type IsAuthEnabled = <P extends WorkspaceAuthProvider>(
|
||||
provider: P,
|
||||
exceptionToThrow: CustomException,
|
||||
) => (
|
||||
const isAuthEnabledOrThrow = (
|
||||
provider: WorkspaceAuthProvider,
|
||||
workspace: Workspace,
|
||||
exceptionToThrowCustom?: CustomException,
|
||||
) => boolean;
|
||||
exceptionToThrowCustom: AuthException,
|
||||
) => {
|
||||
if (provider === 'google' && workspace.isGoogleAuthEnabled) return true;
|
||||
if (provider === 'microsoft' && workspace.isMicrosoftAuthEnabled) return true;
|
||||
if (provider === 'password' && workspace.isPasswordAuthEnabled) return true;
|
||||
|
||||
const isAuthEnabled: IsAuthEnabled = (provider, exceptionToThrow) => {
|
||||
return (workspace, exceptionToThrowCustom = exceptionToThrow) => {
|
||||
if (provider === 'google' && workspace.isGoogleAuthEnabled) return true;
|
||||
if (provider === 'microsoft' && workspace.isMicrosoftAuthEnabled)
|
||||
return true;
|
||||
if (provider === 'password' && workspace.isPasswordAuthEnabled) return true;
|
||||
|
||||
if (exceptionToThrowCustom) {
|
||||
throw exceptionToThrowCustom;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
throw exceptionToThrowCustom;
|
||||
};
|
||||
|
||||
const validateAuth = (fn: ReturnType<IsAuthEnabled>, workspace: Workspace) =>
|
||||
fn(workspace);
|
||||
|
||||
export const workspaceValidator: {
|
||||
assertIsExist: typeof assertIsExist;
|
||||
assertIsActive: typeof assertIsActive;
|
||||
isAuthEnabled: IsAuthEnabled;
|
||||
validateAuth: typeof validateAuth;
|
||||
isAuthEnabledOrThrow: typeof isAuthEnabledOrThrow;
|
||||
} = {
|
||||
assertIsExist: assertIsExist,
|
||||
assertIsActive: assertIsActive,
|
||||
isAuthEnabled,
|
||||
validateAuth,
|
||||
isAuthEnabledOrThrow: isAuthEnabledOrThrow,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user