1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tslib_1 = require("tslib");
|
4 | const common_1 = require("@nestjs/common");
|
5 | const uuidv4 = require("uuid/v4");
|
6 | const configuration_1 = require("../configuration");
|
7 | const transactional_1 = require("../datastore/transactional");
|
8 | const logging_1 = require("../gcloud/logging");
|
9 | const index_1 = require("../index");
|
10 | const invite_1 = require("../mail-templates/invite");
|
11 | const mail_sender_1 = require("../mail/mail.sender");
|
12 | const arrays_1 = require("../util/arrays");
|
13 | const auth_repository_1 = require("./auth.repository");
|
14 | const auth_service_1 = require("./auth.service");
|
15 | exports.DEFAULT_INVITE_CODE_EXPIRY = 7 * 24 * 60 * 60 * 1000;
|
16 | const DEFAULT_INVITE_CODE_EXPIRY_EMAIL_COPY = '7 days';
|
17 | const DEFAULT_INVITATION_EMAIL_COPY = 'You have been invited as a new user.';
|
18 | let InviteUserService = class InviteUserService {
|
19 | constructor(authRepository, mailSender, configuration, userService, userInviteRepository) {
|
20 | this.authRepository = authRepository;
|
21 | this.mailSender = mailSender;
|
22 | this.configuration = configuration;
|
23 | this.userService = userService;
|
24 | this.userInviteRepository = userInviteRepository;
|
25 | this.getActivationExpiryInMillis = () => {
|
26 | return this.configuration.auth.local && this.configuration.auth.local.activationExpiryInMinutes
|
27 | ? this.configuration.auth.local.activationExpiryInMinutes * 60 * 1000
|
28 | : exports.DEFAULT_INVITE_CODE_EXPIRY;
|
29 | };
|
30 | this.getActivationExpiryEmailCopy = () => !(this.configuration.auth.local && this.configuration.auth.local.activationExpiryInMinutes)
|
31 | ? DEFAULT_INVITE_CODE_EXPIRY_EMAIL_COPY
|
32 | : this.configuration.auth.local.activationExpiryEmailCopy;
|
33 | this.getInvitationCopy = () => !(this.configuration.auth.local && this.configuration.auth.local.invitationEmailCopy)
|
34 | ? DEFAULT_INVITATION_EMAIL_COPY
|
35 | : this.configuration.auth.local.invitationEmailCopy;
|
36 | this.logger = logging_1.createLogger('invite-user-service');
|
37 | }
|
38 | async inviteUserIfRequired(context, request) {
|
39 | return this.inviteUserInternal(context, request, false);
|
40 | }
|
41 | async inviteUser(context, request) {
|
42 | return this.inviteUserInternal(context, request, true);
|
43 | }
|
44 | async getInvitedUser(context, code) {
|
45 | const invite = await this.userInviteRepository.get(context, code);
|
46 | if (!invite) {
|
47 | return;
|
48 | }
|
49 | const activationExpiry = this.getActivationExpiryInMillis();
|
50 | if (Date.now() - invite.createdAt.getTime() > activationExpiry) {
|
51 | this.logger.info(`User invite for ${invite.email} has expired. Was created ${invite.createdAt}.`);
|
52 | throw new Error('Account activation code expired');
|
53 | }
|
54 | return this.userService.get(context, invite.userId);
|
55 | }
|
56 | async reInviteForUserId(context, userId) {
|
57 | this.logger.info(`Re Inviting user with id: ${userId}`);
|
58 | const existingInvite = await this.getUserInviteForUserId(context, userId);
|
59 | if (existingInvite) {
|
60 | return this.reInviteUser(context, existingInvite);
|
61 | }
|
62 | else {
|
63 | throw new Error('No user invites found.');
|
64 | }
|
65 | }
|
66 | async getUserInviteForUserId(context, userId) {
|
67 | const [existingInvites] = await this.userInviteRepository.query(context, {
|
68 | filters: {
|
69 | userId,
|
70 | },
|
71 | limit: 1,
|
72 | });
|
73 | this.logger.info(`Existing Invites ${existingInvites}`);
|
74 | if (existingInvites && existingInvites.length > 0) {
|
75 | return existingInvites[0];
|
76 | }
|
77 | else {
|
78 | return undefined;
|
79 | }
|
80 | }
|
81 | async reInviteUser(context, existingInvite) {
|
82 | const user = await this.userService.getByEmail(context, existingInvite.email);
|
83 | if (!user) {
|
84 | throw new Error('User not found');
|
85 | }
|
86 | const newInvite = await this.userInviteRepository.save(context, {
|
87 | id: uuidv4(),
|
88 | email: existingInvite.email,
|
89 | createdAt: new Date(),
|
90 | roles: existingInvite.roles,
|
91 | userId: existingInvite.userId,
|
92 | });
|
93 | await this.userInviteRepository.delete(context, existingInvite.id);
|
94 | const activateLink = await this.sendActivationEmail(context, newInvite.email, newInvite.id, false);
|
95 | return { user, inviteId: newInvite.id, activateLink };
|
96 | }
|
97 | async inviteUserInternal(context, request, validateNew) {
|
98 | const { email, roles } = request;
|
99 | this.logger.info(`Inviting user with email: ${email}, roles: ${roles}, validateNew: ${validateNew}`);
|
100 | if (roles.includes('super')) {
|
101 | throw new Error('Cannot assign super role to users');
|
102 | }
|
103 | const auth = await this.authRepository.get(context, email);
|
104 | if (validateNew && auth) {
|
105 | throw new Error('Email already exists');
|
106 | }
|
107 | let user = await this.userService.getByEmail(context, email);
|
108 | if (!user) {
|
109 | user = await this.userService.create(context, {
|
110 | email,
|
111 | name: request.name,
|
112 | enabled: false,
|
113 | });
|
114 | }
|
115 | if (auth) {
|
116 | this.logger.info(`User with email ${email} already has a login so does not need to be invited`);
|
117 | const updatedUser = await this.userService.update(context, user.id, {
|
118 | roles: arrays_1.unique(user.roles, ...roles),
|
119 | enabled: true,
|
120 | });
|
121 | return { user: updatedUser };
|
122 | }
|
123 | else {
|
124 | const inviteId = uuidv4();
|
125 | await this.userInviteRepository.save(context, {
|
126 | id: inviteId,
|
127 | email,
|
128 | createdAt: new Date(),
|
129 | roles,
|
130 | userId: user.id,
|
131 | });
|
132 | const activateLink = await this.sendActivationEmail(context, email, inviteId, request.skipEmail);
|
133 | return { user, inviteId, activateLink };
|
134 | }
|
135 | }
|
136 | async sendActivationEmail(context, email, inviteId, skipEmail) {
|
137 | const activateLink = `${this.configuration.host}/activate/${inviteId}`;
|
138 | if (skipEmail) {
|
139 | this.logger.info('Skipping sending invitation email based on request option');
|
140 | return activateLink;
|
141 | }
|
142 | this.logger.info(`Sending invitation email to ${email} with link ${activateLink}`);
|
143 | const title = 'Activate your account';
|
144 | await this.mailSender.send(context, {
|
145 | to: email,
|
146 | subject: title,
|
147 | html: invite_1.userInviteEmail(title, activateLink, this.getInvitationCopy(), this.getActivationExpiryEmailCopy()),
|
148 | });
|
149 | return activateLink;
|
150 | }
|
151 | async checkActivationCode(context, code) {
|
152 | const invite = await this.userInviteRepository.get(context, code);
|
153 | return this.checkInvite(context, invite);
|
154 | }
|
155 | async activateAccount(context, code, name, password) {
|
156 | const invite = await this.userInviteRepository.get(context, code);
|
157 | const err = await this.checkInvite(context, invite);
|
158 | if (err) {
|
159 | throw new Error(err);
|
160 | }
|
161 | const user = await this.userService.update(context, invite.userId, {
|
162 | name,
|
163 | roles: invite.roles,
|
164 | enabled: true,
|
165 | });
|
166 | this.logger.info(`Accepting invitation and activating account for email ${user.email}, code ${code}, name ${name}`);
|
167 | await this.authRepository.save(context, {
|
168 | id: invite.email,
|
169 | type: 'password',
|
170 | password: await auth_service_1.hashPassword(password),
|
171 | userId: user.id,
|
172 | });
|
173 | await this.userInviteRepository.delete(context, code);
|
174 | return user;
|
175 | }
|
176 | async checkInvite(context, invite) {
|
177 | if (!invite) {
|
178 | return 'This activation code is no longer available. Please use the \'Activate Account\' link in the most recent activation email you have received. If you\'re still experiencing problems please contact your administrator.';
|
179 | }
|
180 | const activationExpiry = this.getActivationExpiryInMillis();
|
181 | if (Date.now() - invite.createdAt.getTime() > activationExpiry) {
|
182 | return 'Sorry, your activation code has expired. Please contact your administrator';
|
183 | }
|
184 | const auth = await this.authRepository.get(context, invite.email);
|
185 | if (auth) {
|
186 | return 'Account already registered';
|
187 | }
|
188 | return null;
|
189 | }
|
190 | };
|
191 | tslib_1.__decorate([
|
192 | transactional_1.Transactional(),
|
193 | tslib_1.__metadata("design:type", Function),
|
194 | tslib_1.__metadata("design:paramtypes", [Object, Object]),
|
195 | tslib_1.__metadata("design:returntype", Promise)
|
196 | ], InviteUserService.prototype, "inviteUser", null);
|
197 | tslib_1.__decorate([
|
198 | transactional_1.Transactional(),
|
199 | tslib_1.__metadata("design:type", Function),
|
200 | tslib_1.__metadata("design:paramtypes", [Object, Object]),
|
201 | tslib_1.__metadata("design:returntype", Promise)
|
202 | ], InviteUserService.prototype, "reInviteUser", null);
|
203 | tslib_1.__decorate([
|
204 | transactional_1.Transactional(),
|
205 | tslib_1.__metadata("design:type", Function),
|
206 | tslib_1.__metadata("design:paramtypes", [Object, String, String, String]),
|
207 | tslib_1.__metadata("design:returntype", Promise)
|
208 | ], InviteUserService.prototype, "activateAccount", null);
|
209 | InviteUserService = tslib_1.__decorate([
|
210 | common_1.Injectable(),
|
211 | tslib_1.__param(1, common_1.Inject(mail_sender_1.MAIL_SENDER)),
|
212 | tslib_1.__param(2, common_1.Inject(configuration_1.CONFIGURATION)),
|
213 | tslib_1.__param(3, common_1.Inject(index_1.USER_SERVICE)),
|
214 | tslib_1.__metadata("design:paramtypes", [auth_repository_1.CredentialRepository, Object, Object, Object, auth_repository_1.UserInviteRepository])
|
215 | ], InviteUserService);
|
216 | exports.InviteUserService = InviteUserService;
|
217 |
|
\ | No newline at end of file |