@things-factory/auth-base 10.1.31 → 10.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/controllers/invitation.d.ts +47 -10
- package/dist-server/controllers/invitation.js +159 -113
- package/dist-server/controllers/invitation.js.map +1 -1
- package/dist-server/controllers/profile.d.ts +1 -0
- package/dist-server/router/auth-public-process-router.js +36 -13
- package/dist-server/router/auth-public-process-router.js.map +1 -1
- package/dist-server/router/oauth2/oauth2-router.js +7 -2
- package/dist-server/router/oauth2/oauth2-router.js.map +1 -1
- package/dist-server/router/oauth2/oauth2-server.js +4 -4
- package/dist-server/router/oauth2/oauth2-server.js.map +1 -1
- package/dist-server/service/app-binding/app-binding-mutation.d.ts +27 -0
- package/dist-server/service/app-binding/app-binding-mutation.js +39 -6
- package/dist-server/service/app-binding/app-binding-mutation.js.map +1 -1
- package/dist-server/service/app-binding/app-binding-query.js +14 -7
- package/dist-server/service/app-binding/app-binding-query.js.map +1 -1
- package/dist-server/service/app-binding/app-binding.d.ts +9 -0
- package/dist-server/service/app-binding/app-binding.js +10 -1
- package/dist-server/service/app-binding/app-binding.js.map +1 -1
- package/dist-server/service/appliance/appliance-mutation.js +34 -1
- package/dist-server/service/appliance/appliance-mutation.js.map +1 -1
- package/dist-server/service/appliance/appliance-query.d.ts +2 -0
- package/dist-server/service/appliance/appliance-query.js +48 -0
- package/dist-server/service/appliance/appliance-query.js.map +1 -1
- package/dist-server/service/appliance/appliance.d.ts +1 -0
- package/dist-server/service/appliance/appliance.js +33 -3
- package/dist-server/service/appliance/appliance.js.map +1 -1
- package/dist-server/service/application/application-mutation.js +51 -6
- package/dist-server/service/application/application-mutation.js.map +1 -1
- package/dist-server/service/application/application.d.ts +9 -3
- package/dist-server/service/application/application.js +24 -8
- package/dist-server/service/application/application.js.map +1 -1
- package/dist-server/service/auth-provider/auth-provider-mutation.js +5 -0
- package/dist-server/service/auth-provider/auth-provider-mutation.js.map +1 -1
- package/dist-server/service/domain-generator/domain-generator-mutation.js +3 -0
- package/dist-server/service/domain-generator/domain-generator-mutation.js.map +1 -1
- package/dist-server/service/invitation/invitation-mutation.d.ts +17 -15
- package/dist-server/service/invitation/invitation-mutation.js +83 -56
- package/dist-server/service/invitation/invitation-mutation.js.map +1 -1
- package/dist-server/service/invitation/invitation-query.d.ts +20 -5
- package/dist-server/service/invitation/invitation-query.js +60 -19
- package/dist-server/service/invitation/invitation-query.js.map +1 -1
- package/dist-server/service/invitation/invitation.d.ts +14 -2
- package/dist-server/service/invitation/invitation.js +77 -8
- package/dist-server/service/invitation/invitation.js.map +1 -1
- package/dist-server/service/login-history/login-history-query.js +3 -0
- package/dist-server/service/login-history/login-history-query.js.map +1 -1
- package/dist-server/service/role/role-mutation.js +14 -15
- package/dist-server/service/role/role-mutation.js.map +1 -1
- package/dist-server/service/role/role-query.js +26 -4
- package/dist-server/service/role/role-query.js.map +1 -1
- package/dist-server/service/role/role-types.d.ts +2 -0
- package/dist-server/service/role/role-types.js +8 -0
- package/dist-server/service/role/role-types.js.map +1 -1
- package/dist-server/service/role-template/role-template-mutation.d.ts +17 -1
- package/dist-server/service/role-template/role-template-mutation.js +14 -3
- package/dist-server/service/role-template/role-template-mutation.js.map +1 -1
- package/dist-server/service/user/user-mutation.js +1 -0
- package/dist-server/service/user/user-mutation.js.map +1 -1
- package/dist-server/service/user/user.d.ts +1 -0
- package/dist-server/service/user/user.js +19 -4
- package/dist-server/service/user/user.js.map +1 -1
- package/dist-server/templates/invitation-email.d.ts +2 -1
- package/dist-server/templates/invitation-email.js +16 -4
- package/dist-server/templates/invitation-email.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/utils/credential-at-rest.d.ts +28 -0
- package/dist-server/utils/credential-at-rest.js +36 -0
- package/dist-server/utils/credential-at-rest.js.map +1 -0
- package/dist-server/utils/credential-features.d.ts +35 -0
- package/dist-server/utils/credential-features.js +45 -0
- package/dist-server/utils/credential-features.js.map +1 -0
- package/dist-server/utils/credential-serial-rule.d.ts +41 -0
- package/dist-server/utils/credential-serial-rule.js +43 -0
- package/dist-server/utils/credential-serial-rule.js.map +1 -0
- package/dist-server/utils/invitation-state.d.ts +52 -0
- package/dist-server/utils/invitation-state.js +76 -0
- package/dist-server/utils/invitation-state.js.map +1 -0
- package/dist-server/utils/refuse-role-name.d.ts +42 -0
- package/dist-server/utils/refuse-role-name.js +76 -0
- package/dist-server/utils/refuse-role-name.js.map +1 -0
- package/dist-server/utils/role-name-standing.d.ts +58 -0
- package/dist-server/utils/role-name-standing.js +72 -0
- package/dist-server/utils/role-name-standing.js.map +1 -0
- package/package.json +4 -4
- package/tests/app-binding-delete-db.test.ts +188 -0
- package/tests/appliance-credential-state-db.test.ts +207 -0
- package/tests/appliance-delete-db.test.ts +119 -0
- package/tests/appliance-revoke-db.test.ts +73 -28
- package/tests/appliance-schema.test.ts +129 -0
- package/tests/application-delete-db.test.ts +190 -0
- package/tests/application-token-payload-db.test.ts +185 -0
- package/tests/checkin-privilege-seam-db.test.ts +158 -0
- package/tests/credential-at-rest-db.test.ts +246 -0
- package/tests/credential-features-off-db.test.ts +189 -0
- package/tests/credential-serial-rule.test.ts +84 -0
- package/tests/invitation-db.test.ts +416 -0
- package/tests/invitation-state.test.ts +92 -0
- package/tests/open-doors.test.ts +132 -0
- package/tests/role-inheritance-db.test.ts +286 -0
- package/tests/role-mutation-db.test.ts +21 -7
- package/tests/role-name-lookup-sentinel.test.ts +79 -0
- package/tests/role-name-standing.test.ts +83 -0
- package/tests/token-issuance.test.ts +7 -3
- package/translations/en.json +2 -0
- package/translations/ja.json +2 -0
- package/translations/ko.json +2 -0
- package/translations/ms.json +2 -0
- package/translations/zh.json +2 -0
|
@@ -1,12 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
import { Domain } from '@things-factory/shell';
|
|
3
|
+
import { Invitation } from '../service/invitation/invitation.js';
|
|
4
|
+
import { Role } from '../service/role/role.js';
|
|
5
|
+
import { User } from '../service/user/user.js';
|
|
6
|
+
/**
|
|
7
|
+
* Create a pending invitation and mail it.
|
|
8
|
+
*
|
|
9
|
+
* ⚠ One pending invitation per person and domain. The history keeps every row, so this cannot be
|
|
10
|
+
* a unique index without forbidding "invited, left, invited again" — see the note on the entity.
|
|
11
|
+
* An existing pending one is re-issued rather than duplicated: a new token, a new expiry, and the
|
|
12
|
+
* same row, so the list shows one line per invitation rather than one per time somebody pressed
|
|
13
|
+
* the button.
|
|
14
|
+
*/
|
|
15
|
+
export declare function inviteByEmail({ email, domain, roles, user, context, tx }: {
|
|
16
|
+
email: string;
|
|
17
|
+
domain: Domain;
|
|
18
|
+
roles?: Role[];
|
|
19
|
+
user?: User;
|
|
6
20
|
context: any;
|
|
7
|
-
|
|
8
|
-
|
|
21
|
+
tx?: EntityManager;
|
|
22
|
+
}): Promise<Invitation>;
|
|
23
|
+
/** Mail the link. Kept apart so re-sending does not have to go through creating one. */
|
|
24
|
+
export declare function mailInvitation(invitation: Invitation, context: any): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Accept an invitation.
|
|
27
|
+
*
|
|
28
|
+
* The invited address has to be given as well as the token — see the note at the top. Everything
|
|
29
|
+
* that refuses here refuses the same way on purpose: a wrong token, a wrong address, an expired
|
|
30
|
+
* one and a cancelled one are one answer, so that trying them apart tells the caller nothing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function acceptInvitation({ token, email, name, password, context, tx }: {
|
|
33
|
+
token: string;
|
|
9
34
|
email: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
35
|
+
name?: string;
|
|
36
|
+
password?: string;
|
|
37
|
+
context: any;
|
|
38
|
+
tx?: EntityManager;
|
|
39
|
+
}): Promise<{
|
|
40
|
+
domain: Domain;
|
|
41
|
+
created: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Withdraw a pending invitation.
|
|
45
|
+
*
|
|
46
|
+
* The row stays and says it was cancelled. Deleting it would take with it the fact that somebody
|
|
47
|
+
* was invited at all, which is the thing the list exists to show.
|
|
48
|
+
*/
|
|
49
|
+
export declare function cancelInvitation(id: string, domain: Domain, user: User, tx?: EntityManager): Promise<boolean>;
|
|
@@ -1,138 +1,184 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.inviteByEmail = inviteByEmail;
|
|
4
|
+
exports.mailInvitation = mailInvitation;
|
|
4
5
|
exports.acceptInvitation = acceptInvitation;
|
|
5
|
-
exports.
|
|
6
|
-
exports.resendInvitationEmail = resendInvitationEmail;
|
|
6
|
+
exports.cancelInvitation = cancelInvitation;
|
|
7
7
|
const typeorm_1 = require("typeorm");
|
|
8
|
-
const url_1 = require("url");
|
|
9
8
|
const email_base_1 = require("@things-factory/email-base");
|
|
9
|
+
const env_1 = require("@things-factory/env");
|
|
10
10
|
const shell_1 = require("@things-factory/shell");
|
|
11
11
|
const invitation_js_1 = require("../service/invitation/invitation.js");
|
|
12
12
|
const user_js_1 = require("../service/user/user.js");
|
|
13
13
|
const invitation_email_js_1 = require("../templates/invitation-email.js");
|
|
14
|
+
const build_auth_url_js_1 = require("../utils/build-auth-url.js");
|
|
15
|
+
const invitation_state_js_1 = require("../utils/invitation-state.js");
|
|
14
16
|
const make_invitation_token_js_1 = require("./utils/make-invitation-token.js");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Inviting somebody into a domain, and their accepting it.
|
|
19
|
+
*
|
|
20
|
+
* ── What this is for, and what it is not ────────────────────────────────────
|
|
21
|
+
* `inviteUser` already puts a person who **has an account** into a domain, at once, with no mail
|
|
22
|
+
* and no say from them. That is the right tool when the administrator has the authority and the
|
|
23
|
+
* person already exists.
|
|
24
|
+
*
|
|
25
|
+
* This is for the case the `user-invite` screen records as having no path at all: **somebody with
|
|
26
|
+
* no account, in an installation with no default password.** Nobody can set their password for
|
|
27
|
+
* them, so the only way in is a mail they can prove they received. That is what the token is.
|
|
28
|
+
*
|
|
29
|
+
* ── Where the token sits ────────────────────────────────────────────────────
|
|
30
|
+
* In the link's **fragment** — `…/accept-invitation#<token>` — which browsers do not send to the
|
|
31
|
+
* server. So it stays out of access logs, proxy logs and `Referer`. The page strips it from the
|
|
32
|
+
* address bar as soon as it has read it.
|
|
33
|
+
*
|
|
34
|
+
* That leaves one way it can still travel: the mail itself being forwarded. Nothing about where
|
|
35
|
+
* the token sits can close that, so accepting asks for the invited address as well. Holding the
|
|
36
|
+
* link is then not enough on its own.
|
|
37
|
+
*
|
|
38
|
+
* ⚠ `/verify/:token`, `/reset-password?token=` and `/unlock-user?token=` still put theirs in the
|
|
39
|
+
* URL. A password reset token is not lighter than this one. Moving those three is its own job.
|
|
40
|
+
*/
|
|
41
|
+
/** How long an invitation stands. Short, because it is a credential someone is holding. */
|
|
42
|
+
const EXPIRES_IN_HOURS = Number(env_1.config.get('invitation/expiresInHours', 48)) || 48;
|
|
43
|
+
function expiryFrom(now) {
|
|
44
|
+
return new Date(now.getTime() + EXPIRES_IN_HOURS * 60 * 60 * 1000);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a pending invitation and mail it.
|
|
48
|
+
*
|
|
49
|
+
* ⚠ One pending invitation per person and domain. The history keeps every row, so this cannot be
|
|
50
|
+
* a unique index without forbidding "invited, left, invited again" — see the note on the entity.
|
|
51
|
+
* An existing pending one is re-issued rather than duplicated: a new token, a new expiry, and the
|
|
52
|
+
* same row, so the list shows one line per invitation rather than one per time somebody pressed
|
|
53
|
+
* the button.
|
|
54
|
+
*/
|
|
55
|
+
async function inviteByEmail({ email, domain, roles, user, context, tx }) {
|
|
56
|
+
const repository = (0, shell_1.getRepository)(invitation_js_1.Invitation, tx);
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const alreadyIn = await (0, shell_1.getRepository)(user_js_1.User, tx).findOne({
|
|
59
|
+
where: { email: (0, typeorm_1.ILike)(email) },
|
|
21
60
|
relations: ['domains']
|
|
22
61
|
});
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
where: { email: (0, typeorm_1.ILike)(username) },
|
|
26
|
-
relations: ['domains']
|
|
27
|
-
});
|
|
62
|
+
if (alreadyIn?.domains?.some(d => d.id === domain.id)) {
|
|
63
|
+
throw new shell_1.Refusal('invitation-already-a-member', { email, domain: domain.name }, `${email} is already in ${domain.name}.`);
|
|
28
64
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
65
|
+
const standing = await repository.findOne({
|
|
66
|
+
where: { email: (0, typeorm_1.ILike)(email), domain: { id: domain.id }, status: invitation_state_js_1.InvitationStatus.PENDING },
|
|
67
|
+
relations: ['domain']
|
|
68
|
+
});
|
|
69
|
+
const invitation = await repository.save({
|
|
70
|
+
...(standing || {}),
|
|
71
|
+
email,
|
|
72
|
+
domain,
|
|
73
|
+
roles: roles || [],
|
|
74
|
+
status: invitation_state_js_1.InvitationStatus.PENDING,
|
|
75
|
+
token: (0, make_invitation_token_js_1.makeInvitationToken)(),
|
|
76
|
+
expiresAt: expiryFrom(now),
|
|
77
|
+
acceptedAt: null,
|
|
78
|
+
creator: standing ? standing['creator'] : user,
|
|
79
|
+
updater: user
|
|
80
|
+
});
|
|
81
|
+
await mailInvitation(invitation, context);
|
|
82
|
+
return invitation;
|
|
83
|
+
}
|
|
84
|
+
/** Mail the link. Kept apart so re-sending does not have to go through creating one. */
|
|
85
|
+
async function mailInvitation(invitation, context) {
|
|
86
|
+
/*
|
|
87
|
+
* `buildAuthURL` reads the address this request arrived on, and falls back the way sign-in and
|
|
88
|
+
* OAuth do. The four mail controllers here each built theirs from `context.header.referer`
|
|
89
|
+
* instead, which **throws when the header is absent** — and the whole send sits in a
|
|
90
|
+
* `try { … } catch { return false }`, so it failed silently. This is that helper's first caller.
|
|
91
|
+
*/
|
|
92
|
+
const acceptUrl = `${(0, build_auth_url_js_1.buildAuthURL)('/accept-invitation', context)}#${invitation.token}`;
|
|
93
|
+
await (0, email_base_1.sendEmail)({
|
|
94
|
+
receiver: invitation.email,
|
|
95
|
+
subject: 'Invitation',
|
|
96
|
+
content: (0, invitation_email_js_1.getInvitationEmailForm)({
|
|
97
|
+
username: invitation.email,
|
|
98
|
+
email: invitation.email,
|
|
99
|
+
acceptUrl,
|
|
100
|
+
/* Printed as text too: the link's fragment needs JavaScript, and this does not. */
|
|
101
|
+
code: invitation.token
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Accept an invitation.
|
|
107
|
+
*
|
|
108
|
+
* The invited address has to be given as well as the token — see the note at the top. Everything
|
|
109
|
+
* that refuses here refuses the same way on purpose: a wrong token, a wrong address, an expired
|
|
110
|
+
* one and a cancelled one are one answer, so that trying them apart tells the caller nothing.
|
|
111
|
+
*/
|
|
112
|
+
async function acceptInvitation({ token, email, name, password, context, tx }) {
|
|
113
|
+
const repository = (0, shell_1.getRepository)(invitation_js_1.Invitation, tx);
|
|
114
|
+
const invitation = token && email
|
|
115
|
+
? await repository.findOne({ where: { token }, relations: ['domain', 'roles'] })
|
|
116
|
+
: null;
|
|
117
|
+
const addressMatches = invitation?.email && email && invitation.email.toLowerCase() === email.trim().toLowerCase();
|
|
118
|
+
if (!invitation || !addressMatches || !(0, invitation_state_js_1.mayAccept)(invitation)) {
|
|
119
|
+
throw new shell_1.Refusal('invitation-not-open', {}, 'This invitation cannot be used.');
|
|
37
120
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
type
|
|
45
|
-
});
|
|
46
|
-
if (!invitation) {
|
|
47
|
-
invitation = await (0, shell_1.getRepository)(invitation_js_1.Invitation).save({
|
|
48
|
-
email,
|
|
49
|
-
reference,
|
|
50
|
-
type
|
|
51
|
-
});
|
|
121
|
+
const userRepository = (0, shell_1.getRepository)(user_js_1.User, tx);
|
|
122
|
+
let person = await userRepository.findOne({ where: { email: (0, typeorm_1.ILike)(invitation.email) }, relations: ['domains'] });
|
|
123
|
+
const created = !person;
|
|
124
|
+
if (!person) {
|
|
125
|
+
if (!password) {
|
|
126
|
+
throw new shell_1.Refusal('invitation-needs-a-password', {}, 'Choose a password to finish joining.');
|
|
52
127
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
128
|
+
/* The same rule the sign-up path applies; it throws its own message in the caller's language. */
|
|
129
|
+
user_js_1.User.validatePasswordByRule(password, context?.lng);
|
|
130
|
+
const salt = user_js_1.User.generateSalt();
|
|
131
|
+
person = await userRepository.save({
|
|
132
|
+
userType: 'user',
|
|
133
|
+
name: name || invitation.email,
|
|
134
|
+
username: invitation.email,
|
|
135
|
+
email: invitation.email,
|
|
136
|
+
salt,
|
|
137
|
+
password: user_js_1.User.encode(password, salt),
|
|
138
|
+
passwordUpdatedAt: new Date(),
|
|
139
|
+
/* Arriving through a mail they received is the verification, so they arrive usable. */
|
|
140
|
+
status: user_js_1.UserStatus.ACTIVATED,
|
|
141
|
+
domains: [invitation.domain]
|
|
57
142
|
});
|
|
58
143
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
await
|
|
144
|
+
else if (!person.domains?.some(d => d.id === invitation.domain.id)) {
|
|
145
|
+
person.domains = [...(person.domains || []), invitation.domain];
|
|
146
|
+
await userRepository.save(person);
|
|
62
147
|
}
|
|
63
|
-
|
|
64
|
-
|
|
148
|
+
/*
|
|
149
|
+
* Roles are given on arrival. Without them the person signs in and has no screens at all —
|
|
150
|
+
* which is why the invitation carries them, rather than leaving the administrator to notice
|
|
151
|
+
* afterwards.
|
|
152
|
+
*/
|
|
153
|
+
if (invitation.roles?.length) {
|
|
154
|
+
const loaded = await userRepository.findOne({ where: { id: person.id }, relations: ['roles'] });
|
|
155
|
+
const held = new Set((loaded?.roles || []).map(role => role.id));
|
|
156
|
+
await userRepository.save({
|
|
157
|
+
...loaded,
|
|
158
|
+
roles: [...(loaded?.roles || []), ...invitation.roles.filter(role => !held.has(role.id))]
|
|
159
|
+
});
|
|
65
160
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
161
|
+
await repository.save({
|
|
162
|
+
...invitation,
|
|
163
|
+
status: invitation_state_js_1.InvitationStatus.ACCEPTED,
|
|
164
|
+
acceptedAt: new Date()
|
|
70
165
|
});
|
|
71
|
-
|
|
72
|
-
throw new Error(`not found invitation.`);
|
|
73
|
-
}
|
|
74
|
-
var { email, reference, type } = invitation;
|
|
75
|
-
var user = await (0, shell_1.getRepository)(user_js_1.User).findOne({ where: { email: (0, typeorm_1.ILike)(email) }, relations: ['domains'] });
|
|
76
|
-
if (user) {
|
|
77
|
-
var domains = user.domains;
|
|
78
|
-
const domain = domains.find(domain => domain.id == reference);
|
|
79
|
-
if (domain) {
|
|
80
|
-
const msg = `user already a member of the ${type}.`;
|
|
81
|
-
throw new Error(msg);
|
|
82
|
-
}
|
|
83
|
-
user.domains = [...domains, await (0, shell_1.getRepository)(shell_1.Domain).findOneBy({ id: reference })];
|
|
84
|
-
await (0, shell_1.getRepository)(user_js_1.User).save(user);
|
|
85
|
-
await (0, shell_1.getRepository)(invitation_js_1.Invitation).delete(invitation.id);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// TODO goto signup
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
166
|
+
return { domain: invitation.domain, created };
|
|
91
167
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
email: invitation.email,
|
|
104
|
-
acceptUrl: serviceUrl
|
|
105
|
-
})
|
|
106
|
-
});
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (e) {
|
|
168
|
+
/**
|
|
169
|
+
* Withdraw a pending invitation.
|
|
170
|
+
*
|
|
171
|
+
* The row stays and says it was cancelled. Deleting it would take with it the fact that somebody
|
|
172
|
+
* was invited at all, which is the thing the list exists to show.
|
|
173
|
+
*/
|
|
174
|
+
async function cancelInvitation(id, domain, user, tx) {
|
|
175
|
+
const repository = (0, shell_1.getRepository)(invitation_js_1.Invitation, tx);
|
|
176
|
+
const invitation = await repository.findOne({ where: { id, domain: { id: domain.id } }, relations: ['domain'] });
|
|
177
|
+
if (!invitation || !(0, invitation_state_js_1.mayAccept)(invitation)) {
|
|
178
|
+
/* Nothing to withdraw: already accepted, already cancelled, or already run out. */
|
|
111
179
|
return false;
|
|
112
180
|
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
var invitation = await (0, shell_1.getRepository)(invitation_js_1.Invitation).findOneBy({
|
|
116
|
-
email: (0, typeorm_1.ILike)(email),
|
|
117
|
-
reference,
|
|
118
|
-
type
|
|
119
|
-
});
|
|
120
|
-
if (!invitation) {
|
|
121
|
-
throw new Error(`not found invitation.`);
|
|
122
|
-
}
|
|
123
|
-
var user = await (0, shell_1.getRepository)(user_js_1.User).findOne({
|
|
124
|
-
where: {
|
|
125
|
-
email: (0, typeorm_1.ILike)(email),
|
|
126
|
-
status: user_js_1.UserStatus.ACTIVATED
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
if (!user) {
|
|
130
|
-
throw new Error(`user not found: ${email}`);
|
|
131
|
-
}
|
|
132
|
-
return await sendInvitationEmail({
|
|
133
|
-
invitation,
|
|
134
|
-
user,
|
|
135
|
-
context
|
|
136
|
-
});
|
|
181
|
+
await repository.save({ ...invitation, status: invitation_state_js_1.InvitationStatus.CANCELLED, updater: user });
|
|
182
|
+
return true;
|
|
137
183
|
}
|
|
138
184
|
//# sourceMappingURL=invitation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invitation.js","sourceRoot":"","sources":["../../server/controllers/invitation.ts"],"names":[],"mappings":"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","sourcesContent":["import { ILike } from 'typeorm'\nimport { URL } from 'url'\n\nimport { sendEmail } from '@things-factory/email-base'\nimport { Domain, getRepository } from '@things-factory/shell'\n\nimport { Invitation } from '../service/invitation/invitation.js'\nimport { User, UserStatus } from '../service/user/user.js'\nimport { getInvitationEmailForm } from '../templates/invitation-email.js'\nimport { makeInvitationToken } from './utils/make-invitation-token.js'\nimport { saveInvitationToken } from './utils/save-invitation-token.js'\n\nexport async function invite(attrs, withEmailInvitation?: Boolean) {\n const { username, reference, type, context } = attrs\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(username)) {\n user = await repository.findOne({\n where: { email: ILike(username) },\n relations: ['domains']\n })\n }\n\n var domains = user.domains\n\n // TODO reference should not be a domain.id (security reason)\n\n if (user) {\n const domain = domains.find(domain => domain.id == reference)\n\n if (domain) {\n const msg = `user already a member of the ${type}.`\n throw new Error(msg)\n }\n }\n\n if (withEmailInvitation) {\n const email = user.email\n\n // TODO 초대장의 유효기간을 설정할 수 있어야 함.\n var invitation = await getRepository(Invitation).findOneBy({\n email: ILike(email),\n reference,\n type\n })\n\n if (!invitation) {\n invitation = await getRepository(Invitation).save({\n email,\n reference,\n type\n })\n }\n\n return await sendInvitationEmail({\n invitation,\n user,\n context\n })\n }\n\n if (user) {\n user.domains = [...domains, await getRepository(Domain).findOneBy({ id: reference })]\n await getRepository(User).save(user)\n } else {\n // TODO need to signup\n }\n}\n\nexport async function acceptInvitation(token) {\n var invitation = await getRepository(Invitation).findOneBy({\n token\n })\n\n if (!invitation) {\n throw new Error(`not found invitation.`)\n }\n\n var { email, reference, type } = invitation\n\n var user = await getRepository(User).findOne({ where: { email: ILike(email) }, relations: ['domains'] })\n\n if (user) {\n var domains = user.domains\n const domain = domains.find(domain => domain.id == reference)\n\n if (domain) {\n const msg = `user already a member of the ${type}.`\n throw new Error(msg)\n }\n\n user.domains = [...domains, await getRepository(Domain).findOneBy({ id: reference })]\n await getRepository(User).save(user)\n\n await getRepository(Invitation).delete(invitation.id)\n } else {\n // TODO goto signup\n }\n\n return true\n}\n\nexport async function sendInvitationEmail({ invitation, user, context }) {\n try {\n var token = makeInvitationToken()\n var verifaction = await saveInvitationToken(invitation.id, token)\n\n if (verifaction) {\n var serviceUrl = new URL(`/auth/accept/${token}`, context.header.referer)\n\n await sendEmail({\n receiver: invitation.email,\n subject: 'Invitation',\n content: getInvitationEmailForm({\n username: user.username,\n email: invitation.email,\n acceptUrl: serviceUrl\n })\n })\n\n return true\n }\n } catch (e) {\n return false\n }\n}\n\nexport async function resendInvitationEmail(\n { email, reference, type }: { email: string; reference: string; type: string },\n context\n) {\n var invitation = await getRepository(Invitation).findOneBy({\n email: ILike(email),\n reference,\n type\n })\n\n if (!invitation) {\n throw new Error(`not found invitation.`)\n }\n\n var user = await getRepository(User).findOne({\n where: {\n email: ILike(email),\n status: UserStatus.ACTIVATED\n }\n })\n\n if (!user) {\n throw new Error(`user not found: ${email}`)\n }\n\n return await sendInvitationEmail({\n invitation,\n user,\n context\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"invitation.js","sourceRoot":"","sources":["../../server/controllers/invitation.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, ILike } from 'typeorm'\n\nimport { sendEmail } from '@things-factory/email-base'\nimport { config } from '@things-factory/env'\nimport { Domain, getRepository, Refusal } from '@things-factory/shell'\n\nimport { Invitation } from '../service/invitation/invitation.js'\nimport { Role } from '../service/role/role.js'\nimport { User, UserStatus } from '../service/user/user.js'\nimport { getInvitationEmailForm } from '../templates/invitation-email.js'\nimport { buildAuthURL } from '../utils/build-auth-url.js'\nimport { InvitationStatus, mayAccept } from '../utils/invitation-state.js'\nimport { makeInvitationToken } from './utils/make-invitation-token.js'\n\n/**\n * Inviting somebody into a domain, and their accepting it.\n *\n * ── What this is for, and what it is not ────────────────────────────────────\n * `inviteUser` already puts a person who **has an account** into a domain, at once, with no mail\n * and no say from them. That is the right tool when the administrator has the authority and the\n * person already exists.\n *\n * This is for the case the `user-invite` screen records as having no path at all: **somebody with\n * no account, in an installation with no default password.** Nobody can set their password for\n * them, so the only way in is a mail they can prove they received. That is what the token is.\n *\n * ── Where the token sits ────────────────────────────────────────────────────\n * In the link's **fragment** — `…/accept-invitation#<token>` — which browsers do not send to the\n * server. So it stays out of access logs, proxy logs and `Referer`. The page strips it from the\n * address bar as soon as it has read it.\n *\n * That leaves one way it can still travel: the mail itself being forwarded. Nothing about where\n * the token sits can close that, so accepting asks for the invited address as well. Holding the\n * link is then not enough on its own.\n *\n * ⚠ `/verify/:token`, `/reset-password?token=` and `/unlock-user?token=` still put theirs in the\n * URL. A password reset token is not lighter than this one. Moving those three is its own job.\n */\n\n/** How long an invitation stands. Short, because it is a credential someone is holding. */\nconst EXPIRES_IN_HOURS = Number(config.get('invitation/expiresInHours', 48)) || 48\n\nfunction expiryFrom(now: Date): Date {\n return new Date(now.getTime() + EXPIRES_IN_HOURS * 60 * 60 * 1000)\n}\n\n/**\n * Create a pending invitation and mail it.\n *\n * ⚠ One pending invitation per person and domain. The history keeps every row, so this cannot be\n * a unique index without forbidding \"invited, left, invited again\" — see the note on the entity.\n * An existing pending one is re-issued rather than duplicated: a new token, a new expiry, and the\n * same row, so the list shows one line per invitation rather than one per time somebody pressed\n * the button.\n */\nexport async function inviteByEmail({\n email,\n domain,\n roles,\n user,\n context,\n tx\n}: {\n email: string\n domain: Domain\n roles?: Role[]\n user?: User\n context: any\n tx?: EntityManager\n}): Promise<Invitation> {\n const repository = getRepository(Invitation, tx)\n const now = new Date()\n\n const alreadyIn = await getRepository(User, tx).findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n\n if (alreadyIn?.domains?.some(d => d.id === domain.id)) {\n throw new Refusal(\n 'invitation-already-a-member',\n { email, domain: domain.name },\n `${email} is already in ${domain.name}.`\n )\n }\n\n const standing = await repository.findOne({\n where: { email: ILike(email), domain: { id: domain.id }, status: InvitationStatus.PENDING },\n relations: ['domain']\n })\n\n const invitation = await repository.save({\n ...(standing || {}),\n email,\n domain,\n roles: roles || [],\n status: InvitationStatus.PENDING,\n token: makeInvitationToken(),\n expiresAt: expiryFrom(now),\n acceptedAt: null,\n creator: standing ? standing['creator'] : user,\n updater: user\n } as any)\n\n await mailInvitation(invitation, context)\n\n return invitation\n}\n\n/** Mail the link. Kept apart so re-sending does not have to go through creating one. */\nexport async function mailInvitation(invitation: Invitation, context: any): Promise<void> {\n /*\n * `buildAuthURL` reads the address this request arrived on, and falls back the way sign-in and\n * OAuth do. The four mail controllers here each built theirs from `context.header.referer`\n * instead, which **throws when the header is absent** — and the whole send sits in a\n * `try { … } catch { return false }`, so it failed silently. This is that helper's first caller.\n */\n const acceptUrl = `${buildAuthURL('/accept-invitation', context)}#${invitation.token}`\n\n await sendEmail({\n receiver: invitation.email,\n subject: 'Invitation',\n content: getInvitationEmailForm({\n username: invitation.email,\n email: invitation.email,\n acceptUrl,\n /* Printed as text too: the link's fragment needs JavaScript, and this does not. */\n code: invitation.token\n })\n })\n}\n\n/**\n * Accept an invitation.\n *\n * The invited address has to be given as well as the token — see the note at the top. Everything\n * that refuses here refuses the same way on purpose: a wrong token, a wrong address, an expired\n * one and a cancelled one are one answer, so that trying them apart tells the caller nothing.\n */\nexport async function acceptInvitation({\n token,\n email,\n name,\n password,\n context,\n tx\n}: {\n token: string\n email: string\n name?: string\n password?: string\n context: any\n tx?: EntityManager\n}): Promise<{ domain: Domain; created: boolean }> {\n const repository = getRepository(Invitation, tx)\n\n const invitation =\n token && email\n ? await repository.findOne({ where: { token }, relations: ['domain', 'roles'] })\n : null\n\n const addressMatches = invitation?.email && email && invitation.email.toLowerCase() === email.trim().toLowerCase()\n\n if (!invitation || !addressMatches || !mayAccept(invitation)) {\n throw new Refusal('invitation-not-open', {}, 'This invitation cannot be used.')\n }\n\n const userRepository = getRepository(User, tx)\n\n let person = await userRepository.findOne({ where: { email: ILike(invitation.email) }, relations: ['domains'] })\n const created = !person\n\n if (!person) {\n if (!password) {\n throw new Refusal('invitation-needs-a-password', {}, 'Choose a password to finish joining.')\n }\n\n /* The same rule the sign-up path applies; it throws its own message in the caller's language. */\n User.validatePasswordByRule(password, context?.lng)\n\n const salt = User.generateSalt()\n\n person = await userRepository.save({\n userType: 'user',\n name: name || invitation.email,\n username: invitation.email,\n email: invitation.email,\n salt,\n password: User.encode(password, salt),\n passwordUpdatedAt: new Date(),\n /* Arriving through a mail they received is the verification, so they arrive usable. */\n status: UserStatus.ACTIVATED,\n domains: [invitation.domain]\n } as any)\n } else if (!person.domains?.some(d => d.id === invitation.domain.id)) {\n person.domains = [...(person.domains || []), invitation.domain]\n await userRepository.save(person)\n }\n\n /*\n * Roles are given on arrival. Without them the person signs in and has no screens at all —\n * which is why the invitation carries them, rather than leaving the administrator to notice\n * afterwards.\n */\n if (invitation.roles?.length) {\n const loaded = await userRepository.findOne({ where: { id: person.id }, relations: ['roles'] })\n const held = new Set((loaded?.roles || []).map(role => role.id))\n\n await userRepository.save({\n ...loaded,\n roles: [...(loaded?.roles || []), ...invitation.roles.filter(role => !held.has(role.id))]\n } as any)\n }\n\n await repository.save({\n ...invitation,\n status: InvitationStatus.ACCEPTED,\n acceptedAt: new Date()\n } as any)\n\n return { domain: invitation.domain, created }\n}\n\n/**\n * Withdraw a pending invitation.\n *\n * The row stays and says it was cancelled. Deleting it would take with it the fact that somebody\n * was invited at all, which is the thing the list exists to show.\n */\nexport async function cancelInvitation(id: string, domain: Domain, user: User, tx?: EntityManager): Promise<boolean> {\n const repository = getRepository(Invitation, tx)\n\n const invitation = await repository.findOne({ where: { id, domain: { id: domain.id } }, relations: ['domain'] })\n\n if (!invitation || !mayAccept(invitation)) {\n /* Nothing to withdraw: already accepted, already cancelled, or already run out. */\n return false\n }\n\n await repository.save({ ...invitation, status: InvitationStatus.CANCELLED, updater: user } as any)\n\n return true\n}\n"]}
|
|
@@ -140,19 +140,42 @@ exports.authPublicProcessRouter.post('/resend-verification-email', async (contex
|
|
|
140
140
|
context.body = message;
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
143
|
+
/*
|
|
144
|
+
* ── The invitation page ─────────────────────────────────────────────────────
|
|
145
|
+
* Public on purpose: the person following this link is not a member of the domain yet, which is
|
|
146
|
+
* the entire point of an invitation. The old link went to `/auth/accept/<token>`, a path with no
|
|
147
|
+
* route on any router, so it fell through to the private history-fallback — and the domain gate
|
|
148
|
+
* there turned the invitee away for not being a member. It asked somebody trying to join to
|
|
149
|
+
* already have joined.
|
|
150
|
+
*
|
|
151
|
+
* ── Why the token is not in this path ───────────────────────────────────────
|
|
152
|
+
* It rides in the link's fragment (`#<token>`), which browsers do not send. So the request line
|
|
153
|
+
* here carries nothing, and the token stays out of access logs, proxy logs and `Referer`. The
|
|
154
|
+
* page reads it from the fragment, clears it from the address bar, and posts it below.
|
|
155
|
+
*/
|
|
156
|
+
exports.authPublicProcessRouter.get('/accept-invitation', async (context, next) => {
|
|
157
|
+
await context.render('auth-page', {
|
|
158
|
+
pageElement: 'auth-accept-invitation',
|
|
159
|
+
elementScript: '/auth/accept-invitation.js',
|
|
160
|
+
data: {
|
|
161
|
+
disableUserFavoredLanguage,
|
|
162
|
+
languages
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
/*
|
|
167
|
+
* The address is asked for as well as the token. A forwarded mail is the one way the token can
|
|
168
|
+
* still travel, and nothing about where it sits can prevent that — so holding the link is not
|
|
169
|
+
* enough on its own.
|
|
170
|
+
*/
|
|
171
|
+
exports.authPublicProcessRouter.post('/accept-invitation', async (context, next) => {
|
|
172
|
+
const { token, email, name, password } = context.request.body || {};
|
|
173
|
+
const { domain, created } = await (0, invitation_js_1.acceptInvitation)({ token, email, name, password, context });
|
|
174
|
+
context.body = {
|
|
175
|
+
subdomain: domain?.subdomain,
|
|
176
|
+
created,
|
|
177
|
+
redirectTo: `/auth/signin?redirect_to=${encodeURIComponent('/')}`
|
|
178
|
+
};
|
|
156
179
|
});
|
|
157
180
|
exports.authPublicProcessRouter.post('/forgot-password', async (context, next) => {
|
|
158
181
|
const { t } = context;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { resendInvitationEmail } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/verify/:token', async (context, next) => {\n const { header, t } = context\n var token = context.params.token\n\n await verify(token)\n\n var message = t('text.user activated successfully')\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n var succeed = await resendVerificationEmail(email, context)\n var message = t('text.verification email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/resend-invitation-email', async (context, next) => {\n const { t } = context\n const { email, reference, type } = context.request.body\n\n var succeed = await resendInvitationEmail(\n {\n email,\n reference,\n type\n },\n context\n )\n\n var message = t('text.invitation email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n const succeed = await sendPasswordResetEmail({\n user,\n context\n })\n\n if (succeed) {\n context.status = 200\n context.body = t('text.password reset email sent')\n }\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n /* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body\n carrying a password and no token passed the guard and reached the unlock with no token —\n see the note in `unlockUser`. */\n if (!(token && password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n const succeed = await unlockUser(token, password)\n\n /*\n * Say what happened. The success page used to render whether or not the unlock took, so a\n * reused link, a token for another account, or an account that was never locked all read as\n * \"account is reactivated\" — and the person then could not sign in.\n *\n * A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.\n */\n if (!succeed) {\n const message = t('text.invalid verification token')\n\n context.status = 404\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
1
|
+
{"version":3,"file":"auth-public-process-router.js","sourceRoot":"","sources":["../../server/router/auth-public-process-router.ts"],"names":[],"mappings":"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","sourcesContent":["import Router from 'koa-router'\nimport { ILike } from 'typeorm'\n\nimport { config } from '@things-factory/env'\nimport { getRepository, getSiteRootPath } from '@things-factory/shell'\n\nimport { acceptInvitation } from '../controllers/invitation.js'\nimport { resetPassword, sendPasswordResetEmail } from '../controllers/reset-password.js'\nimport { unlockUser } from '../controllers/unlock-user.js'\nimport { resendVerificationEmail, verify } from '../controllers/verification.js'\nimport { User } from '../service/user/user.js'\nimport { accepts } from '../utils/accepts.js'\nimport { clearAccessTokenCookie } from '../utils/access-token-cookie.js'\n\nconst disableUserSignupProcess = config.get('disableUserSignupProcess', false)\nconst disableUserFavoredLanguage = config.get('i18n/disableUserFavoredLanguage', false)\nconst languages = config.get('i18n/languages', false)\nconst passwordRule = config.get('password') || {\n lowerCase: true,\n upperCase: true,\n digit: true,\n specialCharacter: true,\n allowRepeat: false,\n useTightPattern: true,\n useLoosePattern: false,\n tightCharacterLength: 8,\n looseCharacterLength: 15\n}\n\nexport const authPublicProcessRouter = new Router({\n prefix: '/auth'\n})\n\nauthPublicProcessRouter.post('/join', async (context, next) => {\n const { email } = context.request.body || {}\n\n const repository = getRepository(User)\n\n var user = await repository.findOne({\n where: { username: email },\n relations: ['domains']\n })\n\n if (!user && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n user = await repository.findOne({\n where: { email: ILike(email) },\n relations: ['domains']\n })\n }\n\n if (user) {\n context.redirect(`/auth/signin?username=${email}`)\n } else {\n context.redirect(`/auth/signup?username=${email}&email=${email}`)\n }\n})\n\nauthPublicProcessRouter.all('/signout', async (context, next) => {\n const { header, t } = context\n clearAccessTokenCookie(context)\n\n context.body = t('text.signout successfully')\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n context.redirect(getSiteRootPath(context))\n }\n})\n\nauthPublicProcessRouter.get('/forgot-password', async (context, next) => {\n const { email } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'forgot-password',\n elementScript: '/auth/forgot-password.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/reset-password', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/unlock-user', async (context, next) => {\n const { token } = context.request.query\n\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/activate/:email', async (context, next) => {\n const { email } = context.params\n\n await context.render('auth-page', {\n pageElement: 'auth-activate',\n elementScript: '/auth/activate.js',\n data: {\n email,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\nauthPublicProcessRouter.get('/verify/:token', async (context, next) => {\n const { header, t } = context\n var token = context.params.token\n\n await verify(token)\n\n var message = t('text.user activated successfully')\n\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n\nauthPublicProcessRouter.post('/resend-verification-email', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n var succeed = await resendVerificationEmail(email, context)\n var message = t('text.verification email sent')\n\n if (succeed) {\n context.status = 200\n context.body = message\n }\n})\n\n/*\n * ── The invitation page ─────────────────────────────────────────────────────\n * Public on purpose: the person following this link is not a member of the domain yet, which is\n * the entire point of an invitation. The old link went to `/auth/accept/<token>`, a path with no\n * route on any router, so it fell through to the private history-fallback — and the domain gate\n * there turned the invitee away for not being a member. It asked somebody trying to join to\n * already have joined.\n *\n * ── Why the token is not in this path ───────────────────────────────────────\n * It rides in the link's fragment (`#<token>`), which browsers do not send. So the request line\n * here carries nothing, and the token stays out of access logs, proxy logs and `Referer`. The\n * page reads it from the fragment, clears it from the address bar, and posts it below.\n */\nauthPublicProcessRouter.get('/accept-invitation', async (context, next) => {\n await context.render('auth-page', {\n pageElement: 'auth-accept-invitation',\n elementScript: '/auth/accept-invitation.js',\n data: {\n disableUserFavoredLanguage,\n languages\n }\n })\n})\n\n/*\n * The address is asked for as well as the token. A forwarded mail is the one way the token can\n * still travel, and nothing about where it sits can prevent that — so holding the link is not\n * enough on its own.\n */\nauthPublicProcessRouter.post('/accept-invitation', async (context, next) => {\n const { token, email, name, password } = context.request.body || ({} as any)\n\n const { domain, created } = await acceptInvitation({ token, email, name, password, context })\n\n context.body = {\n subdomain: domain?.subdomain,\n created,\n redirectTo: `/auth/signin?redirect_to=${encodeURIComponent('/')}`\n }\n})\n\nauthPublicProcessRouter.post('/forgot-password', async (context, next) => {\n const { t } = context\n const { email } = context.request.body\n\n if (!email) return next()\n\n const userRepo = getRepository(User)\n const user = await userRepo.findOne({\n where: {\n email\n }\n })\n\n const succeed = await sendPasswordResetEmail({\n user,\n context\n })\n\n if (succeed) {\n context.status = 200\n context.body = t('text.password reset email sent')\n }\n})\n\nauthPublicProcessRouter.post('/reset-password', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n try {\n if (!(token && password)) {\n let message = t('error.token or password is invalid')\n\n context.status = 404\n context.body = {\n message\n }\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n await resetPassword(token, password, context)\n\n var message = t('text.password changed successfully')\n context.body = message\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n } catch (e) {\n context.status = 404\n context.body = e.message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'reset-password',\n elementScript: '/auth/reset-password.js',\n data: {\n token,\n message: e.message,\n passwordRule,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n }\n})\n\nauthPublicProcessRouter.post('/unlock-user', async (context, next) => {\n const { header, t } = context\n const { password, token } = context.request.body\n\n /* Both are required, the way `/auth/reset-password` requires them. This read `||`, so a body\n carrying a password and no token passed the guard and reached the unlock with no token —\n see the note in `unlockUser`. */\n if (!(token && password)) {\n context.status = 404\n context.body = t('error.token or password is invalid')\n\n return\n }\n\n const succeed = await unlockUser(token, password)\n\n /*\n * Say what happened. The success page used to render whether or not the unlock took, so a\n * reused link, a token for another account, or an account that was never locked all read as\n * \"account is reactivated\" — and the person then could not sign in.\n *\n * A refusal returns to the form with a reason, the shape `/auth/reset-password` already uses.\n */\n if (!succeed) {\n const message = t('text.invalid verification token')\n\n context.status = 404\n context.body = message\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'unlock-user',\n elementScript: '/auth/unlock-user.js',\n data: {\n token,\n message,\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n\n return\n }\n\n context.body = t('text.password reset succeed')\n\n clearAccessTokenCookie(context)\n\n if (accepts(header.accept, ['text/html', '*/*'])) {\n await context.render('auth-page', {\n pageElement: 'auth-result',\n elementScript: '/auth/result.js',\n data: {\n message: t('text.account is reactivated'),\n disableUserSignupProcess,\n disableUserFavoredLanguage,\n languages\n }\n })\n }\n})\n"]}
|
|
@@ -66,8 +66,13 @@ exports.oauth2Router.post('/refresh-token', async (context, next) => {
|
|
|
66
66
|
throw new Error('Domain is not found');
|
|
67
67
|
const appKey = decoded.application.appKey;
|
|
68
68
|
const scopes = decoded.scope;
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
/*
|
|
70
|
+
* This route reads the key back out of the token rather than holding the application, so it
|
|
71
|
+
* names the field. The signer takes `{ appKey }` for exactly this reason — see the note on
|
|
72
|
+
* `Application.sign`.
|
|
73
|
+
*/
|
|
74
|
+
const newAccessToken = application_js_1.Application.generateAccessToken(domain, appUser, { appKey }, scopes);
|
|
75
|
+
const newRefreshToken = application_js_1.Application.generateRefreshToken(domain, appUser, { appKey }, scopes);
|
|
71
76
|
appUser.password = newRefreshToken;
|
|
72
77
|
await (0, shell_1.getRepository)(user_js_1.User).save(appUser);
|
|
73
78
|
(0, access_token_cookie_js_1.setAccessTokenCookie)(context, newAccessToken);
|