Skip to content

Commit 83f0f97

Browse files
authored
Merge pull request #1205 from rocket-admin/backend_saml_sso
Backend saml sso
2 parents c46af31 + 00be02b commit 83f0f97

12 files changed

Lines changed: 161 additions & 1 deletion

backend/src/common/data-injection.tokens.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export enum UseCaseType {
108108
SAAS_GET_USERS_COUNT_IN_COMPANY = 'SAAS_GET_USERS_COUNT_IN_COMPANY',
109109
FREEZE_CONNECTIONS_IN_COMPANY = 'FREEZE_CONNECTIONS_IN_COMPANY',
110110
UNFREEZE_CONNECTIONS_IN_COMPANY = 'UNFREEZE_CONNECTIONS_IN_COMPANY',
111+
SAAS_REGISTER_USER_WITH_SAML = 'SAAS_REGISTER_USER_WITH_SAML',
111112

112113
INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP = 'INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP',
113114
VERIFY_INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP = 'VERIFY_INVITE_USER_IN_COMPANY_AND_CONNECTION_GROUP',

backend/src/entities/user/application/data-structures/register-user-ds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export class RegisterUserDs {
77
isActive: boolean;
88
name: string;
99
role?: UserRoleEnum;
10+
samlNameId?: string;
1011
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export enum ExternalRegistrationProviderEnum {
22
GOOGLE = 'GOOGLE',
33
GITHUB = 'GITHUB',
4+
SAML = 'SAML',
45
}

backend/src/entities/user/repository/user-custom-repository-extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const userCustomRepositoryExtension: IUserRepository = {
4747
async findOneUserByEmail(
4848
email: string,
4949
externalRegistrationProvider: ExternalRegistrationProviderEnum = null,
50+
samlNameId: string = null,
5051
): Promise<UserEntity | null> {
5152
const userQb = this.createQueryBuilder('user').where('user.email = :userEmail', {
5253
userEmail: email?.toLowerCase(),
@@ -56,6 +57,9 @@ export const userCustomRepositoryExtension: IUserRepository = {
5657
externalRegistrationProvider: externalRegistrationProvider,
5758
});
5859
}
60+
if (samlNameId && externalRegistrationProvider === ExternalRegistrationProviderEnum.SAML) {
61+
userQb.andWhere('user.samlNameId = :samlNameId', { samlNameId: samlNameId });
62+
}
5963
return userQb.getOne();
6064
},
6165

backend/src/entities/user/repository/user.repository.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface IUserRepository {
1313
findOneUserByEmail(
1414
email: string,
1515
externalRegistrationProvider?: ExternalRegistrationProviderEnum,
16+
samlNameId?: string,
1617
): Promise<UserEntity>;
1718

1819
findUserWithConnections(userId: string): Promise<UserEntity>;

backend/src/entities/user/user.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ export class UserEntity {
136136
})
137137
externalRegistrationProvider: ExternalRegistrationProviderEnum;
138138

139+
@Column({ default: null })
140+
samlNameId: string;
141+
139142
@Column({ default: true })
140143
showTestConnections: boolean;
141144

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
export class SaasSAMLUserRegisterDS {
4+
@ApiProperty()
5+
email: string;
6+
7+
@ApiProperty()
8+
name: string;
9+
10+
@ApiProperty()
11+
companyId: string;
12+
13+
@ApiProperty()
14+
samlConfigId: string;
15+
16+
@ApiProperty()
17+
samlNameId: string;
18+
19+
@ApiProperty({ required: false })
20+
samlAttributes?: Record<string, any>;
21+
}

backend/src/microservices/saas-microservice/saas.controller.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { SaasUsualUserRegisterDS } from '../../entities/user/application/data-st
66
import { FoundUserDto } from '../../entities/user/dto/found-user.dto.js';
77
import { ExternalRegistrationProviderEnum } from '../../entities/user/enums/external-registration-provider.enum.js';
88
import { UserEntity } from '../../entities/user/user.entity.js';
9+
import { InTransactionEnum } from '../../enums/in-transaction.enum.js';
910
import { SentryInterceptor } from '../../interceptors/sentry.interceptor.js';
1011
import { SuccessResponse } from './data-structures/common-responce.ds.js';
1112
import { RegisterCompanyWebhookDS } from './data-structures/register-company.ds.js';
1213
import { RegisteredCompanyDS } from './data-structures/registered-company.ds.js';
1314
import { SaasRegisterUserWithGithub } from './data-structures/saas-register-user-with-github.js';
15+
import { SaasSAMLUserRegisterDS } from './data-structures/saas-saml-user-register.ds.js';
1416
import { SaasRegisterUserWithGoogleDS } from './data-structures/sass-register-user-with-google.js';
1517
import {
1618
ICompanyRegistration,
@@ -23,9 +25,9 @@ import {
2325
ISaaSGetUsersCountInCompany,
2426
ISaasGetUsersInfosByEmail,
2527
ISaasRegisterUser,
28+
ISaasSAMLRegisterUser,
2629
ISuspendUsers,
2730
} from './use-cases/saas-use-cases.interface.js';
28-
import { InTransactionEnum } from '../../enums/in-transaction.enum.js';
2931

3032
@UseInterceptors(SentryInterceptor)
3133
@Controller('saas')
@@ -48,6 +50,8 @@ export class SaasController {
4850
private readonly loginUserWithGoogleUseCase: ILoginUserWithGoogle,
4951
@Inject(UseCaseType.SAAS_LOGIN_USER_WITH_GITHUB)
5052
private readonly loginUserWithGithubUseCase: ILoginUserWithGitHub,
53+
@Inject(UseCaseType.SAAS_REGISTER_USER_WITH_SAML)
54+
private readonly registerUserWithSamlUseCase: ISaasSAMLRegisterUser,
5155
@Inject(UseCaseType.SAAS_SUSPEND_USERS)
5256
private readonly suspendUsersUseCase: ISuspendUsers,
5357
@Inject(UseCaseType.SAAS_GET_COMPANY_INFO_BY_USER_ID)
@@ -203,4 +207,28 @@ export class SaasController {
203207
async unfreezeConnectionsInCompany(@Body('companyIds') companyIds: Array<string>) {
204208
return await this.unfreezeConnectionsInCompanyUseCase.execute({ companyIds });
205209
}
210+
211+
@ApiOperation({ summary: 'Register user with SAML' })
212+
@ApiBody({ type: SaasSAMLUserRegisterDS })
213+
@ApiResponse({
214+
status: 201,
215+
})
216+
@Post('user/saml/login')
217+
async registerUserWithSaml(
218+
@Body('email') email: string,
219+
@Body('name') name: string,
220+
@Body('companyId') companyId: string,
221+
@Body('samlConfigId') samlConfigId: string,
222+
@Body('samlNameId') samlNameId: string,
223+
@Body('samlAttributes') samlAttributes: Record<string, any>,
224+
): Promise<UserEntity> {
225+
return await this.registerUserWithSamlUseCase.execute({
226+
email,
227+
name,
228+
companyId,
229+
samlConfigId,
230+
samlNameId,
231+
samlAttributes
232+
});
233+
}
206234
}

backend/src/microservices/saas-microservice/saas.module.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { SaasUsualRegisterUseCase } from './use-cases/saas-usual-register-user.u
1515
import { SuspendUsersUseCase } from './use-cases/suspend-users.use.case.js';
1616
import { UnFreezeConnectionsInCompanyUseCase } from './use-cases/unfreeze-connections-in-company-use.case.js';
1717
import { SaasRegisterDemoUserAccountUseCase } from './use-cases/register-demo-user-account.use.case.js';
18+
import { SaaSRegisterUserWIthSamlUseCase } from './use-cases/register-user-with-saml-use.case.js';
1819

1920
@Module({
2021
imports: [],
@@ -71,6 +72,10 @@ import { SaasRegisterDemoUserAccountUseCase } from './use-cases/register-demo-us
7172
provide: UseCaseType.SAAS_DEMO_USER_REGISTRATION,
7273
useClass: SaasRegisterDemoUserAccountUseCase,
7374
},
75+
{
76+
provide: UseCaseType.SAAS_REGISTER_USER_WITH_SAML,
77+
useClass: SaaSRegisterUserWIthSamlUseCase,
78+
},
7479
],
7580
controllers: [SaasController],
7681
exports: [],
@@ -91,6 +96,7 @@ export class SaasModule {
9196
{ path: 'saas/company/:companyId/users/count', method: RequestMethod.GET },
9297
{ path: 'saas/company/freeze-connections', method: RequestMethod.PUT },
9398
{ path: 'saas/company/unfreeze-connections', method: RequestMethod.PUT },
99+
{ path: 'saas/user/saml/login', method: RequestMethod.POST },
94100
);
95101
}
96102
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
2+
import AbstractUseCase from '../../../common/abstract-use.case.js';
3+
import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js';
4+
import { BaseType } from '../../../common/data-injection.tokens.js';
5+
import { RegisterUserDs } from '../../../entities/user/application/data-structures/register-user-ds.js';
6+
import { ExternalRegistrationProviderEnum } from '../../../entities/user/enums/external-registration-provider.enum.js';
7+
import { UserRoleEnum } from '../../../entities/user/enums/user-role.enum.js';
8+
import { UserEntity } from '../../../entities/user/user.entity.js';
9+
import { Messages } from '../../../exceptions/text/messages.js';
10+
import { SaasSAMLUserRegisterDS } from '../data-structures/saas-saml-user-register.ds.js';
11+
12+
@Injectable()
13+
export class SaaSRegisterUserWIthSamlUseCase extends AbstractUseCase<SaasSAMLUserRegisterDS, UserEntity> {
14+
constructor(
15+
@Inject(BaseType.GLOBAL_DB_CONTEXT)
16+
protected _dbContext: IGlobalDatabaseContext,
17+
) {
18+
super();
19+
}
20+
21+
public async implementation(inputData: SaasSAMLUserRegisterDS): Promise<UserEntity> {
22+
const { email, name, samlNameId, companyId } = inputData;
23+
const foundUser = await this._dbContext.userRepository.findOneUserByEmail(
24+
email,
25+
ExternalRegistrationProviderEnum.SAML,
26+
samlNameId,
27+
);
28+
if (foundUser) {
29+
return foundUser;
30+
}
31+
32+
const userData: RegisterUserDs = {
33+
email: email,
34+
password: null,
35+
isActive: true,
36+
name: name ? name : null,
37+
gclidValue: null,
38+
};
39+
40+
const savedUser = await this._dbContext.userRepository.saveRegisteringUser(
41+
userData,
42+
ExternalRegistrationProviderEnum.SAML,
43+
);
44+
45+
const foundCompanyInfo = await this._dbContext.companyInfoRepository.findOne({ where: { id: companyId } });
46+
if (!foundCompanyInfo) {
47+
throw new NotFoundException(Messages.COMPANY_NOT_FOUND);
48+
}
49+
50+
savedUser.company = foundCompanyInfo;
51+
savedUser.samlNameId = samlNameId;
52+
savedUser.role = UserRoleEnum.USER;
53+
54+
return await this._dbContext.userRepository.saveUserEntity(savedUser);
55+
}
56+
}

0 commit comments

Comments
 (0)