UNPKG

12.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tslib_1 = require("tslib");
4const common_1 = require("@nestjs/common");
5const bcrypt = require("bcryptjs");
6const emails = require("email-addresses");
7const t = require("io-ts");
8const io_ts_reporters_1 = require("io-ts-reporters");
9const lodash_1 = require("lodash");
10const __1 = require("..");
11const configuration_1 = require("../configuration");
12const auth_repository_1 = require("./auth.repository");
13const user_service_1 = require("./user.service");
14const auth_callbacks_1 = require("./auth.callbacks");
15const userProfile = t.interface({
16 id: t.string,
17 emails: t.array(t.interface({
18 value: t.string,
19 verified: t.boolean,
20 })),
21 displayName: t.string,
22});
23class AuthenticationFailedException extends common_1.HttpException {
24 constructor(message) {
25 super(message, common_1.HttpStatus.UNAUTHORIZED);
26 }
27}
28exports.AuthenticationFailedException = AuthenticationFailedException;
29const PASSWORD_ROUNDS = 10;
30async function hashPassword(password) {
31 return await bcrypt.hash(password, PASSWORD_ROUNDS);
32}
33exports.hashPassword = hashPassword;
34let AuthService = class AuthService {
35 constructor(authRepository, userService, configurationProvider, authCallbacks) {
36 this.authRepository = authRepository;
37 this.userService = userService;
38 this.configurationProvider = configurationProvider;
39 this.authCallbacks = authCallbacks;
40 this.logger = __1.createLogger('account-service');
41 }
42 async validateUser(context, username, password) {
43 const account = await this.getAccountByEmail(context, username);
44 if (!account) {
45 throw new AuthenticationFailedException('No credentials found for user');
46 }
47 if (account.type !== 'password') {
48 throw new AuthenticationFailedException('No credentials found for user');
49 }
50 const result = await bcrypt.compare(password, account.password);
51 if (!result) {
52 throw new AuthenticationFailedException(`Invalid password for user`);
53 }
54 return await this.loadUserAndCheckEnabled(context, account.userId);
55 }
56 async validateFakeLogin(context, secret, email, name, roles, orgId, props) {
57 this.logger.info(`Validating fake login for ${email}`);
58 const configSecret = this.configurationProvider.auth.fake.secret;
59 if (configSecret && configSecret !== secret) {
60 throw new AuthenticationFailedException('Fake login secret invalid');
61 }
62 const user = await this.userService.getByEmail(context, email);
63 if (user) {
64 if (!user.enabled) {
65 throw new AuthenticationFailedException('User account is disabled');
66 }
67 return await this.userService.update(context, user.id, Object.assign({}, user, { name,
68 roles,
69 orgId,
70 props }));
71 }
72 else {
73 return await this.userService.create(context, {
74 email,
75 name,
76 roles,
77 orgId,
78 props,
79 enabled: true,
80 });
81 }
82 }
83 async validateUserGoogle(context, inputProfile) {
84 const validationResult = userProfile.decode(inputProfile);
85 if (validationResult.isLeft()) {
86 throw new Error(io_ts_reporters_1.reporter(validationResult).join(', '));
87 }
88 const profile = validationResult.value;
89 const accountEmails = profile.emails.find(accountEmail => accountEmail.verified);
90 if (!accountEmails) {
91 throw new AuthenticationFailedException('No credentials found for user');
92 }
93 const email = accountEmails.value;
94 const account = await this.getAccountByEmail(context, email);
95 if (!account) {
96 if (!this.configurationProvider.auth.google || !this.configurationProvider.auth.google.signUpEnabled) {
97 throw new AuthenticationFailedException('No credentials found for user');
98 }
99 const { domain } = emails.parseOneAddress(email);
100 const signUpDomains = this.configurationProvider.auth.google.signUpDomains || [];
101 if (!signUpDomains.includes(domain)) {
102 throw new AuthenticationFailedException('No credentials found for user');
103 }
104 const createdUser = await this.userService.create(context, {
105 roles: this.configurationProvider.auth.google.signUpRoles,
106 email,
107 name: profile.displayName,
108 enabled: true,
109 });
110 await this.authRepository.save(context, {
111 id: email,
112 type: 'google',
113 userId: createdUser.id,
114 });
115 return createdUser;
116 }
117 if (account.type !== 'google' && account.type !== 'password') {
118 throw new AuthenticationFailedException('No credentials found for user');
119 }
120 return await this.loadUserAndCheckEnabled(context, account.userId);
121 }
122 async validateUserSaml(context, profile) {
123 return this.validateOrCreateExternalAuthAccount(context, profile.email, {
124 type: 'saml',
125 newUserRequest: () => ({
126 roles: [],
127 email: profile.email,
128 name: this.toName(profile),
129 enabled: true,
130 }),
131 });
132 }
133 async validateUserOidc(context, profile, overwriteCredentials) {
134 const profileJson = profile['_json'];
135 const email = profile.email || (profileJson && profileJson.email);
136 const roles = this.buildUserRoles(profile, []);
137 const props = this.buildUserProperties(profile, {});
138 return this.validateOrCreateExternalAuthAccount(context, email, {
139 type: 'oidc',
140 overwriteCredentials,
141 newUserRequest: () => ({
142 email,
143 name: profile.displayName,
144 roles,
145 props,
146 enabled: true,
147 }),
148 updateUser: user => {
149 return this.userService.update(context, user.id, Object.assign({}, user, { roles,
150 props, name: profile.displayName }));
151 },
152 });
153 }
154 buildUserRoles(ipdProfileData, defaultRoles = []) {
155 if (this.authCallbacks && this.authCallbacks.buildUserRolesList) {
156 return this.authCallbacks.buildUserRolesList(ipdProfileData);
157 }
158 return defaultRoles;
159 }
160 buildUserProperties(ipdProfileData, defaultProperties = {}) {
161 if (this.authCallbacks && this.authCallbacks.buildUserPropertiesObject) {
162 return this.authCallbacks.buildUserPropertiesObject(ipdProfileData);
163 }
164 return defaultProperties;
165 }
166 async validateUserAuth0(context, email, name, orgId, roles, props) {
167 return this.validateOrCreateExternalAuthAccount(context, email, {
168 type: 'auth0',
169 newUserRequest: () => ({
170 roles,
171 orgId,
172 email,
173 name,
174 props,
175 enabled: true,
176 }),
177 updateUser: user => {
178 user.name = name;
179 user.roles = roles;
180 user.orgId = orgId;
181 user.props = props;
182 return this.userService.update(context, user.id, user);
183 },
184 });
185 }
186 async createAccount(context, email, password, account) {
187 const existingCredentials = await this.getAccountByEmail(context, email);
188 if (!existingCredentials) {
189 return await this.authRepository.save(context, {
190 id: email,
191 password: await hashPassword(password),
192 userId: account,
193 type: 'password',
194 });
195 }
196 return existingCredentials;
197 }
198 async validateOrCreateExternalAuthAccount(context, email, options) {
199 const { newUserRequest, updateUser, type } = options;
200 this.logger.info(`Validating ${type} user profile`);
201 const account = await this.getAccountByEmail(context, email);
202 if (!account) {
203 this.logger.info(`No login credentials found for ${email}, creating credentials and creating or updating user.`);
204 const updatedUser = await this.userService.createOrUpdate(context, newUserRequest(), this.validateUserEnabled);
205 await this.authRepository.save(context, {
206 id: email,
207 type,
208 userId: updatedUser.id,
209 });
210 return updatedUser;
211 }
212 if (!options.overwriteCredentials && account.type !== type) {
213 throw new AuthenticationFailedException('No credentials found for user');
214 }
215 const user = await this.loadUserAndCheckEnabled(context, account.userId);
216 if (account.type !== type) {
217 this.logger.info(`Updating auth type to [${type}] for [${email}]`);
218 await this.authRepository.save(context, {
219 id: account.id,
220 type,
221 userId: account.userId,
222 });
223 }
224 this.logger.info(`User ${email} validated`);
225 return updateUser ? await updateUser(user) : user;
226 }
227 async loadUserAndCheckEnabled(context, userId) {
228 const user = await this.userService.get(context, userId);
229 if (!user) {
230 throw new AuthenticationFailedException('User not found');
231 }
232 this.validateUserEnabled(user);
233 return user;
234 }
235 validateUserEnabled(user) {
236 if (!user.enabled) {
237 throw new AuthenticationFailedException('User account is disabled');
238 }
239 }
240 getAccountByEmail(context, email) {
241 const normalisedEmail = __1.normaliseEmail(email);
242 this.logger.info(`Looking up user by email ${normalisedEmail}`);
243 return this.authRepository.get(context, normalisedEmail);
244 }
245 toName(profile) {
246 return [profile.firstName, profile.lastName].filter(part => !lodash_1.isNil(part)).join(' ');
247 }
248};
249tslib_1.__decorate([
250 __1.Transactional(),
251 tslib_1.__metadata("design:type", Function),
252 tslib_1.__metadata("design:paramtypes", [Object, Object, String, String, Array, String, Object]),
253 tslib_1.__metadata("design:returntype", Promise)
254], AuthService.prototype, "validateFakeLogin", null);
255tslib_1.__decorate([
256 __1.Transactional(),
257 tslib_1.__metadata("design:type", Function),
258 tslib_1.__metadata("design:paramtypes", [Object, Object]),
259 tslib_1.__metadata("design:returntype", Promise)
260], AuthService.prototype, "validateUserGoogle", null);
261tslib_1.__decorate([
262 __1.Transactional(),
263 tslib_1.__metadata("design:type", Function),
264 tslib_1.__metadata("design:paramtypes", [Object, Object]),
265 tslib_1.__metadata("design:returntype", Promise)
266], AuthService.prototype, "validateUserSaml", null);
267tslib_1.__decorate([
268 __1.Transactional(),
269 tslib_1.__metadata("design:type", Function),
270 tslib_1.__metadata("design:paramtypes", [Object, Object, Boolean]),
271 tslib_1.__metadata("design:returntype", Promise)
272], AuthService.prototype, "validateUserOidc", null);
273tslib_1.__decorate([
274 __1.Transactional(),
275 tslib_1.__metadata("design:type", Function),
276 tslib_1.__metadata("design:paramtypes", [Object, String, String, String, Array, Object]),
277 tslib_1.__metadata("design:returntype", Promise)
278], AuthService.prototype, "validateUserAuth0", null);
279tslib_1.__decorate([
280 __1.Transactional(),
281 tslib_1.__metadata("design:type", Function),
282 tslib_1.__metadata("design:paramtypes", [Object, String, String, String]),
283 tslib_1.__metadata("design:returntype", Promise)
284], AuthService.prototype, "createAccount", null);
285AuthService = tslib_1.__decorate([
286 common_1.Injectable(),
287 tslib_1.__param(1, common_1.Inject(user_service_1.USER_SERVICE)),
288 tslib_1.__param(2, common_1.Inject(configuration_1.CONFIGURATION)),
289 tslib_1.__param(3, common_1.Optional()), tslib_1.__param(3, common_1.Inject(auth_callbacks_1.AUTH_CALLBACKS)),
290 tslib_1.__metadata("design:paramtypes", [auth_repository_1.CredentialRepository, Object, Object, Object])
291], AuthService);
292exports.AuthService = AuthService;
293//# sourceMappingURL=auth.service.js.map
\No newline at end of file