pgkiln 0.62.0 → 0.64.0
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/SECURITY.md +4 -0
- package/db/migrations/131_builder_sso.sql +69 -0
- package/db/migrations/132_app_user_mfa.sql +58 -0
- package/docs/apex-feature-parity.md +5 -2
- package/docs/guide/03-builder.md +46 -0
- package/docs/guide/08-security.md +48 -6
- package/docs/guide/09-reference.md +5 -1
- package/docs/guide/11-from-apex.md +2 -1
- package/docs/guide/12-development.md +9 -0
- package/docs/guide/18-cli.md +1 -0
- package/package.json +1 -1
- package/public/app.css +10 -0
- package/public/builder.css +4 -0
- package/src/account-mfa.ts +93 -0
- package/src/builder/advisor.ts +8 -0
- package/src/builder/builder-sso.ts +135 -0
- package/src/builder/i18n/de.ts +56 -0
- package/src/builder/i18n/en.ts +56 -0
- package/src/builder/i18n/es.ts +56 -0
- package/src/builder/i18n/fr.ts +56 -0
- package/src/builder/i18n/nl.ts +56 -0
- package/src/builder/i18n/pt.ts +56 -0
- package/src/builder/ldap.ts +2 -0
- package/src/builder/login-page.ts +26 -2
- package/src/builder/routes.ts +137 -21
- package/src/builder/security.ts +3 -1
- package/src/builder/ui.ts +2 -1
- package/src/builder/users.ts +36 -3
- package/src/builder/workspaces.ts +37 -3
- package/src/cli/main.ts +19 -5
- package/src/devsso.ts +149 -0
- package/src/i18n/ar.ts +34 -0
- package/src/i18n/cs.ts +34 -0
- package/src/i18n/da.ts +34 -0
- package/src/i18n/de.ts +34 -0
- package/src/i18n/el.ts +34 -0
- package/src/i18n/es.ts +34 -0
- package/src/i18n/fi.ts +34 -0
- package/src/i18n/fr.ts +34 -0
- package/src/i18n/he.ts +34 -0
- package/src/i18n/it.ts +34 -0
- package/src/i18n/ja.ts +34 -0
- package/src/i18n/ko.ts +34 -0
- package/src/i18n/nb.ts +34 -0
- package/src/i18n/pl.ts +34 -0
- package/src/i18n/pt.ts +34 -0
- package/src/i18n/ru.ts +34 -0
- package/src/i18n/sv.ts +34 -0
- package/src/i18n/tr.ts +34 -0
- package/src/i18n/uk.ts +34 -0
- package/src/i18n/zh.ts +34 -0
- package/src/i18n.ts +68 -0
- package/src/metadata.ts +4 -1
- package/src/mfa.ts +76 -52
- package/src/remember.ts +1 -1
- package/src/runtime/account.ts +4 -1
- package/src/runtime/routes.ts +24 -9
- package/src/runtime/two-step.ts +395 -0
- package/src/saml.ts +13 -7
- package/src/sharedlogin.ts +1 -1
- package/src/sso.ts +37 -7
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { owner } from '../db.ts';
|
|
3
|
+
import { html, raw, type Raw } from '../html.ts';
|
|
4
|
+
import { icon } from '../icons.ts';
|
|
5
|
+
import { clientIp, logActivity, type Session } from '../session.ts';
|
|
6
|
+
import { breakGlassAdmins, builderSources, identitiesOf, ssoRequired } from '../devsso.ts';
|
|
7
|
+
import { isAdmin } from './locks.ts';
|
|
8
|
+
import { tr } from './i18n.ts';
|
|
9
|
+
import { administrator, back, BASE, csrf, developer, flash, input, region, type Req } from './ui.ts';
|
|
10
|
+
|
|
11
|
+
// (sprint 129, SEC-1) Builder sign-in through identity providers and LDAP directories (devsso.ts):
|
|
12
|
+
// * Users → Identity providers / LDAP directories → "Builder sign-in": on or off, creating developers,
|
|
13
|
+
// linking existing ones, the groups; the developers linked there;
|
|
14
|
+
// * Security: the developer's own identities, "Link" for each provider, "Unlink";
|
|
15
|
+
// * Developers: break-glass administrators (they may use the pgkiln password where a workspace
|
|
16
|
+
// requires single sign-on).
|
|
17
|
+
|
|
18
|
+
type Kind = 'provider' | 'directory';
|
|
19
|
+
const TABLE = { provider: 'meta.auth_provider', directory: 'meta.ldap_directory' } as const;
|
|
20
|
+
const COLUMN = { provider: 'provider_id', directory: 'directory_id' } as const;
|
|
21
|
+
const PATH = { provider: 'providers', directory: 'directories' } as const;
|
|
22
|
+
|
|
23
|
+
const check = (name: string, label: string, on: boolean, help?: string) =>
|
|
24
|
+
html`<div class="field"><label class="check"><input type="checkbox" name="${name}" value="true"${on ? raw(' checked') : ''}> ${label}</label>${help ? html`<small class="help">${help}</small>` : ''}</div>`;
|
|
25
|
+
|
|
26
|
+
/** The "Builder sign-in" region of a provider's or directory's page. */
|
|
27
|
+
export async function builderSignInRegion(s: Session, kind: Kind, row: { id: number; builder: boolean; builder_auto_create: boolean; builder_link_existing: boolean; builder_group: string | null; builder_admin_group: string | null }): Promise<Raw> {
|
|
28
|
+
const tx = tr(s);
|
|
29
|
+
const linked = (await owner.query<{ id: number; username: string; linked_at: string }>(
|
|
30
|
+
`select id, username, linked_at from meta.developer_identity where ${COLUMN[kind]} = $1 order by username`, [row.id])).rows;
|
|
31
|
+
return region(tx('bsso.title'), html`<p class="muted u-mt0">${tx('bsso.intro')}</p>
|
|
32
|
+
<form method="post" action="${BASE}/users/${PATH[kind]}/${row.id}/builder">${csrf(s)}
|
|
33
|
+
${check('builder', tx('bsso.on'), row.builder, tx('bsso.on_help'))}
|
|
34
|
+
${check('builder_auto_create', tx('bsso.auto_create'), row.builder_auto_create, tx('bsso.auto_create_help'))}
|
|
35
|
+
${check('builder_link_existing', tx('bsso.link_existing'), row.builder_link_existing, tx('bsso.link_existing_help'))}
|
|
36
|
+
<div class="form-grid">
|
|
37
|
+
${input('builder_group', tx('bsso.group'), row.builder_group ?? '', { help: tx('bsso.group_help') })}
|
|
38
|
+
${input('builder_admin_group', tx('bsso.admin_group'), row.builder_admin_group ?? '', { help: tx('bsso.admin_group_help') })}
|
|
39
|
+
</div>
|
|
40
|
+
<div class="buttons"><button class="btn">${tx('bsso.save')}</button></div></form>
|
|
41
|
+
<h3>${tx('bsso.linked')}</h3>
|
|
42
|
+
${linked.length
|
|
43
|
+
? html`<ul class="plain-list">${linked.map((l) => html`<li>${icon('user')} ${l.username} <span class="muted small">${String(l.linked_at).slice(0, 10)}</span>
|
|
44
|
+
<form method="post" action="${BASE}/developers/identity/${l.id}/unlink" class="u-inline">${csrf(s)}<button class="link-button" data-confirm="${tx('bsso.unlink_confirm', { user: l.username })}">${tx('bsso.unlink')}</button></form></li>`)}</ul>`
|
|
45
|
+
: html`<p class="muted">${tx('bsso.none_linked')}</p>`}`, '', 'builder-sign-in');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The Security page's region: the developer's identities, and the providers to link. */
|
|
49
|
+
export async function identitiesRegion(s: Session, linkedNow: string | undefined): Promise<Raw | ''> {
|
|
50
|
+
const tx = tr(s);
|
|
51
|
+
const [mine, sources] = await Promise.all([identitiesOf(s.username!), builderSources()]);
|
|
52
|
+
if (!mine.length && !sources.providers.length && !sources.directories.length) return '';
|
|
53
|
+
const linkable = sources.providers.filter((p) => !mine.some((m) => m.kind === 'provider' && m.source === p.name));
|
|
54
|
+
const linked = linkedNow ? mine.find((m) => m.kind === 'provider' && m.source === linkedNow) : undefined;
|
|
55
|
+
return region(tx('bsso.yours'), html`<p class="muted u-mt0">${tx('bsso.yours_intro')}</p>
|
|
56
|
+
${linked ? html`<div class="alert alert-success" role="status">${tx('bsso.linked_now', { provider: linked.display_name })}</div>` : ''}
|
|
57
|
+
${mine.length
|
|
58
|
+
? html`<ul class="plain-list">${mine.map((m) => html`<li>${icon(m.kind === 'provider' ? 'log-in' : 'server')} ${m.display_name} <span class="muted small">${String(m.linked_at).slice(0, 10)}</span>
|
|
59
|
+
<form method="post" action="${BASE}/developers/identity/${m.id}/unlink" class="u-inline">${csrf(s)}<button class="link-button" data-confirm="${tx('bsso.unlink_mine_confirm', { provider: m.display_name })}">${tx('bsso.unlink')}</button></form></li>`)}</ul>`
|
|
60
|
+
: html`<p class="muted">${tx('bsso.none_yours')}</p>`}
|
|
61
|
+
${linkable.map((p) => html`<form method="post" action="${BASE}/security/link/${p.name}" class="u-inline">${csrf(s)}<button class="btn">${icon('link')} ${tx('bsso.link', { provider: p.display_name })}</button></form> `)}
|
|
62
|
+
${sources.directories.length ? html`<p class="muted small">${tx('bsso.directories_note')}</p>` : ''}`, '', 'identities');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Would this change leave a workspace that requires single sign-on without a break-glass administrator? */
|
|
66
|
+
export async function leavesNoBreakGlass(username: string) {
|
|
67
|
+
if (!(await owner.one('select 1 as ok from meta.workspace where sso_only limit 1'))) return false;
|
|
68
|
+
const left = (await breakGlassAdmins()).filter((u) => u !== username);
|
|
69
|
+
return left.length === 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function builderSsoRoutes(app: FastifyInstance) {
|
|
73
|
+
for (const kind of ['provider', 'directory'] as Kind[])
|
|
74
|
+
app.post(`${BASE}/users/${PATH[kind]}/:id(^\\d{1,9}$)/builder`, async (req: Req, reply) => {
|
|
75
|
+
const s = await administrator(req, reply, kind === 'provider' ? 'Identity providers' : 'LDAP directories');
|
|
76
|
+
if (!s) return;
|
|
77
|
+
const b = req.body ?? {};
|
|
78
|
+
const group = (v: unknown) => String(v ?? '').trim().slice(0, 200) || null;
|
|
79
|
+
const on = b.builder === 'true';
|
|
80
|
+
const r = await owner.one<{ name: string }>(
|
|
81
|
+
`update ${TABLE[kind]} set builder = $2, builder_auto_create = $3, builder_link_existing = $4, builder_group = $5, builder_admin_group = $6
|
|
82
|
+
where id = $1 returning name`,
|
|
83
|
+
[req.params.id, on, b.builder_auto_create === 'true', b.builder_link_existing === 'true', group(b.builder_group), group(b.builder_admin_group)],
|
|
84
|
+
);
|
|
85
|
+
if (!r) return reply.code(404).send('Not found');
|
|
86
|
+
await logActivity({ username: s.username, event: 'sso_policy', ip: clientIp(req), detail: `${kind} ${r.name}: builder sign-in ${on ? 'on' : 'off'}` });
|
|
87
|
+
flash(s, tr(s)('bsso.saved'));
|
|
88
|
+
return back(reply, s, `${BASE}/users/${PATH[kind]}/${req.params.id}#builder-sign-in`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// unlinking: the developer's own identity, or any for an administrator
|
|
92
|
+
app.post(`${BASE}/developers/identity/:id(^\\d{1,9}$)/unlink`, async (req: Req, reply) => {
|
|
93
|
+
const s = await developer(req, reply);
|
|
94
|
+
if (!s) return;
|
|
95
|
+
const tx = tr(s);
|
|
96
|
+
const i = await owner.one<{ username: string; provider_id: number | null; directory_id: number | null }>('select username, provider_id, directory_id from meta.developer_identity where id = $1', [req.params.id]);
|
|
97
|
+
const admin = await isAdmin(s.username);
|
|
98
|
+
if (!i || (i.username !== s.username && !admin)) return reply.code(404).send('Not found');
|
|
99
|
+
const where = i.username === s.username ? `${BASE}/security#identities` : `${BASE}/users/${i.provider_id ? 'providers' : 'directories'}/${i.provider_id ?? i.directory_id}#builder-sign-in`;
|
|
100
|
+
// the last identity of a developer who must use single sign-on: they could not sign in any more
|
|
101
|
+
const only = await owner.one<{ n: number }>('select count(*)::int as n from meta.developer_identity where username = $1', [i.username]);
|
|
102
|
+
if (only!.n <= 1 && (await ssoRequired(i.username))) {
|
|
103
|
+
flash(s, tx('bsso.keep_one'), 'error');
|
|
104
|
+
return back(reply, s, where);
|
|
105
|
+
}
|
|
106
|
+
await owner.query('delete from meta.developer_identity where id = $1', [req.params.id]);
|
|
107
|
+
await logActivity({ username: s.username, event: 'sso_unlinked', ip: clientIp(req), detail: `${i.username}: ${i.provider_id ? `provider ${i.provider_id}` : `directory ${i.directory_id}`}` });
|
|
108
|
+
flash(s, tx('bsso.unlinked'));
|
|
109
|
+
return back(reply, s, where);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// break-glass: administrators only, for administrators
|
|
113
|
+
app.post(`${BASE}/developers/break-glass`, async (req: Req, reply) => {
|
|
114
|
+
const s = await developer(req, reply);
|
|
115
|
+
if (!s) return;
|
|
116
|
+
if (!(await isAdmin(s.username))) return reply.code(403).send('Only administrators change break-glass access.');
|
|
117
|
+
const tx = tr(s);
|
|
118
|
+
const username = String(req.body?.username ?? '');
|
|
119
|
+
const on = req.body?.break_glass === 'true';
|
|
120
|
+
const d = await owner.one<{ is_admin: boolean }>('select is_admin from meta.developer where username = $1', [username]);
|
|
121
|
+
if (!d) return reply.code(404).send('Not found');
|
|
122
|
+
if (on && !d.is_admin) {
|
|
123
|
+
flash(s, tx('bsso.break_glass_admins'), 'error');
|
|
124
|
+
return back(reply, s, `${BASE}/developers`);
|
|
125
|
+
}
|
|
126
|
+
if (!on && (await leavesNoBreakGlass(username))) {
|
|
127
|
+
flash(s, tx('bsso.keep_break_glass'), 'error');
|
|
128
|
+
return back(reply, s, `${BASE}/developers`);
|
|
129
|
+
}
|
|
130
|
+
await owner.query('update meta.developer set break_glass = $2 where username = $1', [username, on]);
|
|
131
|
+
await logActivity({ username: s.username, event: 'sso_policy', ip: clientIp(req), detail: `${username}: break-glass ${on ? 'on' : 'off'}` });
|
|
132
|
+
flash(s, tx(on ? 'bsso.break_glass_on' : 'bsso.break_glass_off', { user: username }));
|
|
133
|
+
return back(reply, s, `${BASE}/developers`);
|
|
134
|
+
});
|
|
135
|
+
}
|
package/src/builder/i18n/de.ts
CHANGED
|
@@ -1820,4 +1820,60 @@ export const de: BuilderMessages = {
|
|
|
1820
1820
|
'wsp.mfa_save': 'Zwei-Faktor-Regel speichern',
|
|
1821
1821
|
'wsp.mfa_on': 'Die Entwickler dieses Workspace brauchen die Zwei-Faktor-Anmeldung.',
|
|
1822
1822
|
'wsp.mfa_off': 'Der Workspace verlangt keine Zwei-Faktor-Anmeldung mehr (die Richtlinie der Instanz gilt weiter).',
|
|
1823
|
+
'signin.method': 'Anmelden mit',
|
|
1824
|
+
'signin.method_pgkiln': 'pgkiln-Konto',
|
|
1825
|
+
'signin.with': 'Mit {provider} anmelden',
|
|
1826
|
+
'signin.ldap_unavailable': '{directory} ist nicht erreichbar. Versuchen Sie es später erneut.',
|
|
1827
|
+
'signin.sso_required': 'Ihr Workspace verlangt Single Sign-on: Melden Sie sich über Ihren Identitätsanbieter an.',
|
|
1828
|
+
'signin.sso_unavailable': '{provider} ist nicht erreichbar. Versuchen Sie es später erneut.',
|
|
1829
|
+
'bsso.title': 'Builder-Anmeldung',
|
|
1830
|
+
'bsso.intro': 'Entwickler können sich auch mit diesem Anbieter im Builder anmelden. Sie werden über die verknüpfte Identität gefunden, nie nur über den Namen.',
|
|
1831
|
+
'bsso.on': 'Entwickler können sich damit im Builder anmelden',
|
|
1832
|
+
'bsso.on_help': 'Die Anmeldeseite zeigt ihn an. Bei dieser Anmeldung entfällt der zweite Faktor von pgkiln: Der Anbieter entscheidet.',
|
|
1833
|
+
'bsso.auto_create': 'Entwickler bei der ersten Anmeldung anlegen',
|
|
1834
|
+
'bsso.auto_create_help': 'Für Mitglieder der unten angegebenen Entwicklergruppe (alle, wenn sie leer ist). Neue Entwickler kommen in den Default-Workspace.',
|
|
1835
|
+
'bsso.link_existing': 'Vorhandenen Entwickler mit demselben Benutzernamen verknüpfen',
|
|
1836
|
+
'bsso.link_existing_help': 'Einmalig, bei der ersten Anmeldung; nie ein Notfall-Administrator. Nur einschalten, wenn Sie den Benutzernamen des Anbieters vertrauen. Sonst verknüpfen Entwickler die Identität auf ihrer Sicherheitsseite.',
|
|
1837
|
+
'bsso.group': 'Entwicklergruppe',
|
|
1838
|
+
'bsso.group_help': 'Nur ihre Mitglieder erhalten ein Entwicklerkonto. Leer: alle, die der Anbieter anmeldet.',
|
|
1839
|
+
'bsso.admin_group': 'Administratorengruppe',
|
|
1840
|
+
'bsso.admin_group_help': 'Wenn gesetzt, sind ihre Mitglieder Administratoren der Instanz und andere nicht, geprüft bei jeder Anmeldung (Notfall-Administratoren behalten ihre Rolle).',
|
|
1841
|
+
'bsso.save': 'Builder-Anmeldung speichern',
|
|
1842
|
+
'bsso.saved': 'Builder-Anmeldung gespeichert.',
|
|
1843
|
+
'bsso.linked': 'Verknüpfte Entwickler',
|
|
1844
|
+
'bsso.none_linked': 'Noch kein Entwickler verknüpft.',
|
|
1845
|
+
'bsso.unlink': 'Verknüpfung lösen',
|
|
1846
|
+
'bsso.unlink_confirm': '{user} lösen? Die Person meldet sich mit dem Kennwort an oder verknüpft erneut.',
|
|
1847
|
+
'bsso.unlink_mine_confirm': '{provider} lösen? Sie können erneut verknüpfen.',
|
|
1848
|
+
'bsso.unlinked': 'Die Identität ist nicht mehr verknüpft.',
|
|
1849
|
+
'bsso.keep_one': 'Ein Workspace verlangt von diesem Entwickler Single Sign-on: Behalten Sie eine verknüpfte Identität.',
|
|
1850
|
+
'bsso.yours': 'Single Sign-on',
|
|
1851
|
+
'bsso.yours_intro': 'Die Identitätsanbieter, mit denen Sie sich im Builder anmelden können.',
|
|
1852
|
+
'bsso.none_yours': 'Noch keine Identität verknüpft.',
|
|
1853
|
+
'bsso.link': '{provider} verknüpfen',
|
|
1854
|
+
'bsso.linked_now': '{provider} ist verknüpft: Sie können sich damit anmelden.',
|
|
1855
|
+
'bsso.directories_note': 'Ein LDAP-Verzeichnis wird bei der ersten Anmeldung damit verknüpft (ein Administrator erlaubt das pro Verzeichnis).',
|
|
1856
|
+
'bsso.break_glass': 'Notfallzugang',
|
|
1857
|
+
'bsso.break_glass_help': 'Darf sich mit dem pgkiln-Kennwort anmelden, wo ein Workspace Single Sign-on verlangt – für den Fall, dass der Identitätsanbieter ausfällt.',
|
|
1858
|
+
'bsso.break_glass_make': 'Notfallzugang geben',
|
|
1859
|
+
'bsso.break_glass_remove': 'Notfallzugang entziehen',
|
|
1860
|
+
'bsso.break_glass_on': '{user} ist Notfall-Administrator. Richten Sie auch einen zweiten Faktor ein.',
|
|
1861
|
+
'bsso.break_glass_off': '{user} ist kein Notfall-Administrator mehr.',
|
|
1862
|
+
'bsso.break_glass_admins': 'Nur Administratoren können Notfallzugang haben.',
|
|
1863
|
+
'bsso.keep_break_glass': 'Ein Workspace verlangt Single Sign-on: Behalten Sie mindestens einen Notfall-Administrator.',
|
|
1864
|
+
'wsp.sso': 'Single Sign-on',
|
|
1865
|
+
'wsp.sso_only': 'Die Entwickler dieses Workspace müssen sich über einen Identitätsanbieter anmelden',
|
|
1866
|
+
'wsp.sso_only_help': 'Oder ein LDAP-Verzeichnis. Das pgkiln-Kennwort und ein Passkey allein werden abgelehnt, außer für Notfall-Administratoren. `pgkiln admin builder-sign-in password` schaltet es ab.',
|
|
1867
|
+
'wsp.sso_unlinked': 'Noch keine verknüpfte Identität (können sich nicht anmelden): {list}.',
|
|
1868
|
+
'wsp.sso_save': 'Single-Sign-on-Regel speichern',
|
|
1869
|
+
'wsp.sso_on': 'Die Entwickler dieses Workspace müssen Single Sign-on verwenden.',
|
|
1870
|
+
'wsp.sso_off': 'Die Entwickler des Workspace dürfen wieder das pgkiln-Kennwort verwenden.',
|
|
1871
|
+
'wsp.sso_no_break_glass': 'Geben Sie zuerst einem Administrator Notfallzugang (Entwickler), damit sich jemand anmelden kann, wenn der Identitätsanbieter ausfällt.',
|
|
1872
|
+
'wsp.sso_link_first': 'Verknüpfen Sie zuerst auf Ihrer Sicherheitsseite eine Identität, sonst könnten Sie sich selbst nicht anmelden.',
|
|
1873
|
+
'us.mfa_title': 'Zwei-Faktor-Anmeldung',
|
|
1874
|
+
'us.mfa_status': 'Authenticator-Apps: {n}. Verbleibende Wiederherstellungscodes: {codes}.',
|
|
1875
|
+
'us.mfa_none': 'Noch keine Authenticator-App.',
|
|
1876
|
+
'us.mfa_reset_help': 'Entfernt die Authenticator-Apps und Wiederherstellungscodes von {user} und meldet das Konto überall ab. Wo eine Anwendung die Zwei-Faktor-Anmeldung verlangt, wird bei der nächsten Anmeldung eine neue App hinzugefügt. Wird protokolliert.',
|
|
1877
|
+
'us.mfa_reset_done': 'Die Zwei-Faktor-Anmeldung von {user} wurde zurückgesetzt.',
|
|
1878
|
+
'us.mfa_reason_needed': 'Geben Sie einen Grund für das Zurücksetzen an.',
|
|
1823
1879
|
};
|
package/src/builder/i18n/en.ts
CHANGED
|
@@ -1823,4 +1823,60 @@ export const en = {
|
|
|
1823
1823
|
'wsp.mfa_save': 'Save two-factor rule',
|
|
1824
1824
|
'wsp.mfa_on': 'This workspace’s developers need two-factor sign-in.',
|
|
1825
1825
|
'wsp.mfa_off': 'The workspace no longer requires two-factor sign-in (the instance’s policy still applies).',
|
|
1826
|
+
'signin.method': 'Sign in with',
|
|
1827
|
+
'signin.method_pgkiln': 'pgkiln account',
|
|
1828
|
+
'signin.with': 'Sign in with {provider}',
|
|
1829
|
+
'signin.ldap_unavailable': '{directory} cannot be reached. Try again later.',
|
|
1830
|
+
'signin.sso_required': 'Your workspace requires single sign-on: use your identity provider.',
|
|
1831
|
+
'signin.sso_unavailable': '{provider} cannot be reached. Try again later.',
|
|
1832
|
+
'bsso.title': 'Builder sign-in',
|
|
1833
|
+
'bsso.intro': 'Developers can sign in to the builder with this provider too. They are found by the identity they linked, never by name alone.',
|
|
1834
|
+
'bsso.on': 'Developers can sign in to the builder with it',
|
|
1835
|
+
'bsso.on_help': 'The sign-in page shows it. Signing in this way skips pgkiln’s own second factor: the provider decides.',
|
|
1836
|
+
'bsso.auto_create': 'Create developers on their first sign-in',
|
|
1837
|
+
'bsso.auto_create_help': 'For members of the developers group below (anyone, when it is empty). New developers join the Default workspace.',
|
|
1838
|
+
'bsso.link_existing': 'Link an existing developer with the same user name',
|
|
1839
|
+
'bsso.link_existing_help': 'Once, on the first sign-in; never a break-glass administrator. Turn it on only when the provider’s user names are yours to trust. Otherwise developers link the identity on their Security page.',
|
|
1840
|
+
'bsso.group': 'Developers group',
|
|
1841
|
+
'bsso.group_help': 'Only its members get a developer account. Empty: anyone the provider signs in.',
|
|
1842
|
+
'bsso.admin_group': 'Administrators group',
|
|
1843
|
+
'bsso.admin_group_help': 'When set, its members are instance administrators and others are not, checked at every sign-in (break-glass administrators keep their role).',
|
|
1844
|
+
'bsso.save': 'Save builder sign-in',
|
|
1845
|
+
'bsso.saved': 'Builder sign-in saved.',
|
|
1846
|
+
'bsso.linked': 'Linked developers',
|
|
1847
|
+
'bsso.none_linked': 'No developer is linked yet.',
|
|
1848
|
+
'bsso.unlink': 'Unlink',
|
|
1849
|
+
'bsso.unlink_confirm': 'Unlink {user}? They sign in with the password, or link again.',
|
|
1850
|
+
'bsso.unlink_mine_confirm': 'Unlink {provider}? You can link it again.',
|
|
1851
|
+
'bsso.unlinked': 'The identity is unlinked.',
|
|
1852
|
+
'bsso.keep_one': 'A workspace requires single sign-on of this developer: keep one linked identity.',
|
|
1853
|
+
'bsso.yours': 'Single sign-on',
|
|
1854
|
+
'bsso.yours_intro': 'The identity providers you can sign in to the builder with.',
|
|
1855
|
+
'bsso.none_yours': 'No identity linked yet.',
|
|
1856
|
+
'bsso.link': 'Link {provider}',
|
|
1857
|
+
'bsso.linked_now': '{provider} is linked: you can sign in with it.',
|
|
1858
|
+
'bsso.directories_note': 'An LDAP directory is linked when you first sign in with it (an administrator allows that per directory).',
|
|
1859
|
+
'bsso.break_glass': 'break-glass',
|
|
1860
|
+
'bsso.break_glass_help': 'May sign in with the pgkiln password where a workspace requires single sign-on, for when the identity provider fails.',
|
|
1861
|
+
'bsso.break_glass_make': 'Make break-glass',
|
|
1862
|
+
'bsso.break_glass_remove': 'Remove break-glass',
|
|
1863
|
+
'bsso.break_glass_on': '{user} is a break-glass administrator. Give them a second factor too.',
|
|
1864
|
+
'bsso.break_glass_off': '{user} is no longer a break-glass administrator.',
|
|
1865
|
+
'bsso.break_glass_admins': 'Only administrators can have break-glass access.',
|
|
1866
|
+
'bsso.keep_break_glass': 'A workspace requires single sign-on: keep at least one break-glass administrator.',
|
|
1867
|
+
'wsp.sso': 'Single sign-on',
|
|
1868
|
+
'wsp.sso_only': 'This workspace’s developers must sign in through an identity provider',
|
|
1869
|
+
'wsp.sso_only_help': 'Or an LDAP directory. The pgkiln password and a passkey alone are refused, except for break-glass administrators. `pgkiln admin builder-sign-in password` turns it off.',
|
|
1870
|
+
'wsp.sso_unlinked': 'No linked identity yet (they cannot sign in): {list}.',
|
|
1871
|
+
'wsp.sso_save': 'Save single sign-on rule',
|
|
1872
|
+
'wsp.sso_on': 'This workspace’s developers must use single sign-on.',
|
|
1873
|
+
'wsp.sso_off': 'The workspace’s developers may use the pgkiln password again.',
|
|
1874
|
+
'wsp.sso_no_break_glass': 'Make an administrator break-glass first (Developers), so someone can still sign in when the identity provider fails.',
|
|
1875
|
+
'wsp.sso_link_first': 'Link an identity on your Security page first, or you could not sign in yourself.',
|
|
1876
|
+
'us.mfa_title': 'Two-factor sign-in',
|
|
1877
|
+
'us.mfa_status': 'Authenticator apps: {n}. Recovery codes left: {codes}.',
|
|
1878
|
+
'us.mfa_none': 'No authenticator app yet.',
|
|
1879
|
+
'us.mfa_reset_help': 'Removes the authenticator apps and recovery codes of {user} and signs them out everywhere. Where an application requires two-factor sign-in, they add a new app at their next sign-in. Logged.',
|
|
1880
|
+
'us.mfa_reset_done': 'Two-factor sign-in of {user} was reset.',
|
|
1881
|
+
'us.mfa_reason_needed': 'Give a reason for the reset.',
|
|
1826
1882
|
} as const;
|
package/src/builder/i18n/es.ts
CHANGED
|
@@ -1820,4 +1820,60 @@ export const es: BuilderMessages = {
|
|
|
1820
1820
|
'wsp.mfa_save': 'Guardar la regla de dos pasos',
|
|
1821
1821
|
'wsp.mfa_on': 'Los desarrolladores de este espacio de trabajo necesitan el inicio de sesión en dos pasos.',
|
|
1822
1822
|
'wsp.mfa_off': 'El espacio de trabajo ya no exige el inicio de sesión en dos pasos (la política de la instancia sigue vigente).',
|
|
1823
|
+
'signin.method': 'Iniciar sesión con',
|
|
1824
|
+
'signin.method_pgkiln': 'Cuenta de pgkiln',
|
|
1825
|
+
'signin.with': 'Iniciar sesión con {provider}',
|
|
1826
|
+
'signin.ldap_unavailable': 'No se puede acceder a {directory}. Inténtelo más tarde.',
|
|
1827
|
+
'signin.sso_required': 'Su espacio de trabajo exige el inicio de sesión único: use su proveedor de identidad.',
|
|
1828
|
+
'signin.sso_unavailable': 'No se puede acceder a {provider}. Inténtelo más tarde.',
|
|
1829
|
+
'bsso.title': 'Inicio de sesión en el builder',
|
|
1830
|
+
'bsso.intro': 'Los desarrolladores también pueden iniciar sesión en el builder con este proveedor. Se les identifica por la identidad vinculada, nunca solo por el nombre.',
|
|
1831
|
+
'bsso.on': 'Los desarrolladores pueden iniciar sesión en el builder con él',
|
|
1832
|
+
'bsso.on_help': 'La página de inicio de sesión lo muestra. Así se omite el segundo factor propio de pgkiln: decide el proveedor.',
|
|
1833
|
+
'bsso.auto_create': 'Crear desarrolladores en su primer inicio de sesión',
|
|
1834
|
+
'bsso.auto_create_help': 'Para los miembros del grupo de desarrolladores de abajo (cualquiera, si está vacío). Los nuevos desarrolladores entran en el espacio de trabajo Default.',
|
|
1835
|
+
'bsso.link_existing': 'Vincular un desarrollador existente con el mismo nombre de usuario',
|
|
1836
|
+
'bsso.link_existing_help': 'Una vez, en el primer inicio de sesión; nunca un administrador de emergencia. Actívelo solo si confía en los nombres de usuario del proveedor. Si no, los desarrolladores vinculan la identidad en su página de seguridad.',
|
|
1837
|
+
'bsso.group': 'Grupo de desarrolladores',
|
|
1838
|
+
'bsso.group_help': 'Solo sus miembros reciben una cuenta de desarrollador. Vacío: cualquiera que el proveedor autentique.',
|
|
1839
|
+
'bsso.admin_group': 'Grupo de administradores',
|
|
1840
|
+
'bsso.admin_group_help': 'Si se indica, sus miembros son administradores de la instancia y los demás no; se comprueba en cada inicio de sesión (los administradores de emergencia conservan su rol).',
|
|
1841
|
+
'bsso.save': 'Guardar el inicio de sesión en el builder',
|
|
1842
|
+
'bsso.saved': 'Inicio de sesión en el builder guardado.',
|
|
1843
|
+
'bsso.linked': 'Desarrolladores vinculados',
|
|
1844
|
+
'bsso.none_linked': 'Aún no hay ningún desarrollador vinculado.',
|
|
1845
|
+
'bsso.unlink': 'Desvincular',
|
|
1846
|
+
'bsso.unlink_confirm': '¿Desvincular a {user}? Iniciará sesión con la contraseña o volverá a vincular.',
|
|
1847
|
+
'bsso.unlink_mine_confirm': '¿Desvincular {provider}? Puede volver a vincularlo.',
|
|
1848
|
+
'bsso.unlinked': 'La identidad está desvinculada.',
|
|
1849
|
+
'bsso.keep_one': 'Un espacio de trabajo exige a este desarrollador el inicio de sesión único: mantenga una identidad vinculada.',
|
|
1850
|
+
'bsso.yours': 'Inicio de sesión único',
|
|
1851
|
+
'bsso.yours_intro': 'Los proveedores de identidad con los que puede iniciar sesión en el builder.',
|
|
1852
|
+
'bsso.none_yours': 'Aún no hay ninguna identidad vinculada.',
|
|
1853
|
+
'bsso.link': 'Vincular {provider}',
|
|
1854
|
+
'bsso.linked_now': '{provider} está vinculado: puede iniciar sesión con él.',
|
|
1855
|
+
'bsso.directories_note': 'Un directorio LDAP se vincula la primera vez que inicia sesión con él (un administrador lo permite por directorio).',
|
|
1856
|
+
'bsso.break_glass': 'de emergencia',
|
|
1857
|
+
'bsso.break_glass_help': 'Puede iniciar sesión con la contraseña de pgkiln donde un espacio de trabajo exige el inicio de sesión único, por si falla el proveedor de identidad.',
|
|
1858
|
+
'bsso.break_glass_make': 'Dar acceso de emergencia',
|
|
1859
|
+
'bsso.break_glass_remove': 'Quitar acceso de emergencia',
|
|
1860
|
+
'bsso.break_glass_on': '{user} es administrador de emergencia. Configúrele también un segundo factor.',
|
|
1861
|
+
'bsso.break_glass_off': '{user} ya no es administrador de emergencia.',
|
|
1862
|
+
'bsso.break_glass_admins': 'Solo los administradores pueden tener acceso de emergencia.',
|
|
1863
|
+
'bsso.keep_break_glass': 'Un espacio de trabajo exige el inicio de sesión único: mantenga al menos un administrador de emergencia.',
|
|
1864
|
+
'wsp.sso': 'Inicio de sesión único',
|
|
1865
|
+
'wsp.sso_only': 'Los desarrolladores de este espacio de trabajo deben iniciar sesión mediante un proveedor de identidad',
|
|
1866
|
+
'wsp.sso_only_help': 'O un directorio LDAP. Se rechazan la contraseña de pgkiln y una llave de acceso sola, salvo para los administradores de emergencia. `pgkiln admin builder-sign-in password` lo desactiva.',
|
|
1867
|
+
'wsp.sso_unlinked': 'Aún sin identidad vinculada (no pueden iniciar sesión): {list}.',
|
|
1868
|
+
'wsp.sso_save': 'Guardar la regla de inicio de sesión único',
|
|
1869
|
+
'wsp.sso_on': 'Los desarrolladores de este espacio de trabajo deben usar el inicio de sesión único.',
|
|
1870
|
+
'wsp.sso_off': 'Los desarrolladores del espacio de trabajo pueden volver a usar la contraseña de pgkiln.',
|
|
1871
|
+
'wsp.sso_no_break_glass': 'Primero dé acceso de emergencia a un administrador (Desarrolladores), para que alguien pueda entrar si falla el proveedor de identidad.',
|
|
1872
|
+
'wsp.sso_link_first': 'Vincule primero una identidad en su página de seguridad, o usted mismo no podría iniciar sesión.',
|
|
1873
|
+
'us.mfa_title': 'Inicio de sesión en dos pasos',
|
|
1874
|
+
'us.mfa_status': 'Aplicaciones de autenticación: {n}. Códigos de recuperación restantes: {codes}.',
|
|
1875
|
+
'us.mfa_none': 'Aún no hay ninguna aplicación de autenticación.',
|
|
1876
|
+
'us.mfa_reset_help': 'Quita las aplicaciones de autenticación y los códigos de recuperación de {user} y cierra todas sus sesiones. Donde una aplicación exija el inicio de sesión en dos pasos, añadirá una aplicación nueva en su próximo inicio de sesión. Queda registrado.',
|
|
1877
|
+
'us.mfa_reset_done': 'Se restableció el inicio de sesión en dos pasos de {user}.',
|
|
1878
|
+
'us.mfa_reason_needed': 'Indique un motivo para el restablecimiento.',
|
|
1823
1879
|
};
|
package/src/builder/i18n/fr.ts
CHANGED
|
@@ -1820,4 +1820,60 @@ export const fr: BuilderMessages = {
|
|
|
1820
1820
|
'wsp.mfa_save': 'Enregistrer la règle à deux facteurs',
|
|
1821
1821
|
'wsp.mfa_on': 'Les développeurs de cet espace de travail ont besoin de la connexion à deux facteurs.',
|
|
1822
1822
|
'wsp.mfa_off': 'L’espace de travail n’exige plus la connexion à deux facteurs (la politique de l’instance s’applique toujours).',
|
|
1823
|
+
'signin.method': 'Se connecter avec',
|
|
1824
|
+
'signin.method_pgkiln': 'Compte pgkiln',
|
|
1825
|
+
'signin.with': 'Se connecter avec {provider}',
|
|
1826
|
+
'signin.ldap_unavailable': '{directory} est injoignable. Réessayez plus tard.',
|
|
1827
|
+
'signin.sso_required': 'Votre espace de travail exige l’authentification unique : utilisez votre fournisseur d’identité.',
|
|
1828
|
+
'signin.sso_unavailable': '{provider} est injoignable. Réessayez plus tard.',
|
|
1829
|
+
'bsso.title': 'Connexion au builder',
|
|
1830
|
+
'bsso.intro': 'Les développeurs peuvent aussi se connecter au builder avec ce fournisseur. Ils sont reconnus par l’identité liée, jamais par le seul nom.',
|
|
1831
|
+
'bsso.on': 'Les développeurs peuvent s’en servir pour se connecter au builder',
|
|
1832
|
+
'bsso.on_help': 'La page de connexion l’affiche. Cette connexion se passe du second facteur de pgkiln : le fournisseur décide.',
|
|
1833
|
+
'bsso.auto_create': 'Créer les développeurs à leur première connexion',
|
|
1834
|
+
'bsso.auto_create_help': 'Pour les membres du groupe de développeurs ci-dessous (tout le monde s’il est vide). Les nouveaux développeurs rejoignent l’espace de travail Default.',
|
|
1835
|
+
'bsso.link_existing': 'Lier un développeur existant ayant le même nom d’utilisateur',
|
|
1836
|
+
'bsso.link_existing_help': 'Une fois, à la première connexion ; jamais un administrateur de secours. À activer seulement si vous faites confiance aux noms d’utilisateur du fournisseur. Sinon, les développeurs lient l’identité sur leur page Sécurité.',
|
|
1837
|
+
'bsso.group': 'Groupe des développeurs',
|
|
1838
|
+
'bsso.group_help': 'Seuls ses membres reçoivent un compte de développeur. Vide : toute personne que le fournisseur connecte.',
|
|
1839
|
+
'bsso.admin_group': 'Groupe des administrateurs',
|
|
1840
|
+
'bsso.admin_group_help': 'Si renseigné, ses membres sont administrateurs de l’instance et les autres non, vérifié à chaque connexion (les administrateurs de secours gardent leur rôle).',
|
|
1841
|
+
'bsso.save': 'Enregistrer la connexion au builder',
|
|
1842
|
+
'bsso.saved': 'Connexion au builder enregistrée.',
|
|
1843
|
+
'bsso.linked': 'Développeurs liés',
|
|
1844
|
+
'bsso.none_linked': 'Aucun développeur n’est encore lié.',
|
|
1845
|
+
'bsso.unlink': 'Délier',
|
|
1846
|
+
'bsso.unlink_confirm': 'Délier {user} ? La personne se connecte avec le mot de passe ou lie à nouveau.',
|
|
1847
|
+
'bsso.unlink_mine_confirm': 'Délier {provider} ? Vous pourrez le lier à nouveau.',
|
|
1848
|
+
'bsso.unlinked': 'L’identité est déliée.',
|
|
1849
|
+
'bsso.keep_one': 'Un espace de travail exige l’authentification unique de ce développeur : gardez une identité liée.',
|
|
1850
|
+
'bsso.yours': 'Authentification unique',
|
|
1851
|
+
'bsso.yours_intro': 'Les fournisseurs d’identité avec lesquels vous pouvez vous connecter au builder.',
|
|
1852
|
+
'bsso.none_yours': 'Aucune identité liée pour l’instant.',
|
|
1853
|
+
'bsso.link': 'Lier {provider}',
|
|
1854
|
+
'bsso.linked_now': '{provider} est lié : vous pouvez vous connecter avec.',
|
|
1855
|
+
'bsso.directories_note': 'Un annuaire LDAP est lié à votre première connexion avec lui (un administrateur l’autorise par annuaire).',
|
|
1856
|
+
'bsso.break_glass': 'de secours',
|
|
1857
|
+
'bsso.break_glass_help': 'Peut se connecter avec le mot de passe pgkiln là où un espace de travail exige l’authentification unique, au cas où le fournisseur d’identité tombe en panne.',
|
|
1858
|
+
'bsso.break_glass_make': 'Donner l’accès de secours',
|
|
1859
|
+
'bsso.break_glass_remove': 'Retirer l’accès de secours',
|
|
1860
|
+
'bsso.break_glass_on': '{user} est administrateur de secours. Donnez-lui aussi un second facteur.',
|
|
1861
|
+
'bsso.break_glass_off': '{user} n’est plus administrateur de secours.',
|
|
1862
|
+
'bsso.break_glass_admins': 'Seuls les administrateurs peuvent avoir l’accès de secours.',
|
|
1863
|
+
'bsso.keep_break_glass': 'Un espace de travail exige l’authentification unique : gardez au moins un administrateur de secours.',
|
|
1864
|
+
'wsp.sso': 'Authentification unique',
|
|
1865
|
+
'wsp.sso_only': 'Les développeurs de cet espace de travail doivent se connecter via un fournisseur d’identité',
|
|
1866
|
+
'wsp.sso_only_help': 'Ou un annuaire LDAP. Le mot de passe pgkiln et une clé d’accès seule sont refusés, sauf pour les administrateurs de secours. `pgkiln admin builder-sign-in password` le désactive.',
|
|
1867
|
+
'wsp.sso_unlinked': 'Pas encore d’identité liée (ne peuvent pas se connecter) : {list}.',
|
|
1868
|
+
'wsp.sso_save': 'Enregistrer la règle d’authentification unique',
|
|
1869
|
+
'wsp.sso_on': 'Les développeurs de cet espace de travail doivent utiliser l’authentification unique.',
|
|
1870
|
+
'wsp.sso_off': 'Les développeurs de l’espace de travail peuvent de nouveau utiliser le mot de passe pgkiln.',
|
|
1871
|
+
'wsp.sso_no_break_glass': 'Donnez d’abord l’accès de secours à un administrateur (Développeurs), pour que quelqu’un puisse se connecter si le fournisseur d’identité tombe en panne.',
|
|
1872
|
+
'wsp.sso_link_first': 'Liez d’abord une identité sur votre page Sécurité, sinon vous ne pourriez plus vous connecter.',
|
|
1873
|
+
'us.mfa_title': 'Connexion à deux facteurs',
|
|
1874
|
+
'us.mfa_status': 'Applications d’authentification : {n}. Codes de récupération restants : {codes}.',
|
|
1875
|
+
'us.mfa_none': 'Aucune application d’authentification pour l’instant.',
|
|
1876
|
+
'us.mfa_reset_help': 'Supprime les applications d’authentification et les codes de récupération de {user} et le déconnecte partout. Là où une application exige la connexion à deux facteurs, une nouvelle application est ajoutée à la prochaine connexion. Journalisé.',
|
|
1877
|
+
'us.mfa_reset_done': 'La connexion à deux facteurs de {user} a été réinitialisée.',
|
|
1878
|
+
'us.mfa_reason_needed': 'Indiquez la raison de la réinitialisation.',
|
|
1823
1879
|
};
|
package/src/builder/i18n/nl.ts
CHANGED
|
@@ -1820,4 +1820,60 @@ export const nl: BuilderMessages = {
|
|
|
1820
1820
|
'wsp.mfa_save': 'Tweestapsregel opslaan',
|
|
1821
1821
|
'wsp.mfa_on': 'De ontwikkelaars van deze werkruimte hebben tweestapsaanmelding nodig.',
|
|
1822
1822
|
'wsp.mfa_off': 'De werkruimte verplicht geen tweestapsaanmelding meer (het beleid van de instantie blijft gelden).',
|
|
1823
|
+
'signin.method': 'Aanmelden met',
|
|
1824
|
+
'signin.method_pgkiln': 'pgkiln-account',
|
|
1825
|
+
'signin.with': 'Aanmelden met {provider}',
|
|
1826
|
+
'signin.ldap_unavailable': '{directory} is niet bereikbaar. Probeer het later opnieuw.',
|
|
1827
|
+
'signin.sso_required': 'Uw werkruimte vereist single sign-on: gebruik uw identiteitsprovider.',
|
|
1828
|
+
'signin.sso_unavailable': '{provider} is niet bereikbaar. Probeer het later opnieuw.',
|
|
1829
|
+
'bsso.title': 'Aanmelden bij de builder',
|
|
1830
|
+
'bsso.intro': 'Ontwikkelaars kunnen met deze provider ook bij de builder aanmelden. Ze worden gevonden via de gekoppelde identiteit, nooit alleen via de naam.',
|
|
1831
|
+
'bsso.on': 'Ontwikkelaars kunnen hiermee bij de builder aanmelden',
|
|
1832
|
+
'bsso.on_help': 'De aanmeldpagina toont hem. Zo aanmelden slaat de eigen tweede factor van pgkiln over: de provider beslist.',
|
|
1833
|
+
'bsso.auto_create': 'Ontwikkelaars aanmaken bij hun eerste aanmelding',
|
|
1834
|
+
'bsso.auto_create_help': 'Voor leden van de ontwikkelaarsgroep hieronder (iedereen als die leeg is). Nieuwe ontwikkelaars komen in de werkruimte Default.',
|
|
1835
|
+
'bsso.link_existing': 'Een bestaande ontwikkelaar met dezelfde gebruikersnaam koppelen',
|
|
1836
|
+
'bsso.link_existing_help': 'Eenmalig, bij de eerste aanmelding; nooit een noodbeheerder. Zet het alleen aan als u de gebruikersnamen van de provider vertrouwt. Anders koppelen ontwikkelaars de identiteit op hun beveiligingspagina.',
|
|
1837
|
+
'bsso.group': 'Ontwikkelaarsgroep',
|
|
1838
|
+
'bsso.group_help': 'Alleen de leden krijgen een ontwikkelaarsaccount. Leeg: iedereen die de provider aanmeldt.',
|
|
1839
|
+
'bsso.admin_group': 'Beheerdersgroep',
|
|
1840
|
+
'bsso.admin_group_help': 'Indien ingevuld zijn de leden beheerder van de instantie en anderen niet, gecontroleerd bij elke aanmelding (noodbeheerders houden hun rol).',
|
|
1841
|
+
'bsso.save': 'Aanmelden bij de builder opslaan',
|
|
1842
|
+
'bsso.saved': 'Aanmelden bij de builder opgeslagen.',
|
|
1843
|
+
'bsso.linked': 'Gekoppelde ontwikkelaars',
|
|
1844
|
+
'bsso.none_linked': 'Nog geen ontwikkelaar gekoppeld.',
|
|
1845
|
+
'bsso.unlink': 'Ontkoppelen',
|
|
1846
|
+
'bsso.unlink_confirm': '{user} ontkoppelen? Diegene meldt zich aan met het wachtwoord of koppelt opnieuw.',
|
|
1847
|
+
'bsso.unlink_mine_confirm': '{provider} ontkoppelen? U kunt het opnieuw koppelen.',
|
|
1848
|
+
'bsso.unlinked': 'De identiteit is ontkoppeld.',
|
|
1849
|
+
'bsso.keep_one': 'Een werkruimte vereist single sign-on van deze ontwikkelaar: houd één gekoppelde identiteit.',
|
|
1850
|
+
'bsso.yours': 'Single sign-on',
|
|
1851
|
+
'bsso.yours_intro': 'De identiteitsproviders waarmee u bij de builder kunt aanmelden.',
|
|
1852
|
+
'bsso.none_yours': 'Nog geen identiteit gekoppeld.',
|
|
1853
|
+
'bsso.link': '{provider} koppelen',
|
|
1854
|
+
'bsso.linked_now': '{provider} is gekoppeld: u kunt ermee aanmelden.',
|
|
1855
|
+
'bsso.directories_note': 'Een LDAP-directory wordt gekoppeld als u er voor het eerst mee aanmeldt (een beheerder staat dat per directory toe).',
|
|
1856
|
+
'bsso.break_glass': 'noodtoegang',
|
|
1857
|
+
'bsso.break_glass_help': 'Mag met het pgkiln-wachtwoord aanmelden waar een werkruimte single sign-on vereist, voor als de identiteitsprovider uitvalt.',
|
|
1858
|
+
'bsso.break_glass_make': 'Noodtoegang geven',
|
|
1859
|
+
'bsso.break_glass_remove': 'Noodtoegang intrekken',
|
|
1860
|
+
'bsso.break_glass_on': '{user} is noodbeheerder. Geef diegene ook een tweede factor.',
|
|
1861
|
+
'bsso.break_glass_off': '{user} is geen noodbeheerder meer.',
|
|
1862
|
+
'bsso.break_glass_admins': 'Alleen beheerders kunnen noodtoegang hebben.',
|
|
1863
|
+
'bsso.keep_break_glass': 'Een werkruimte vereist single sign-on: houd minstens één noodbeheerder.',
|
|
1864
|
+
'wsp.sso': 'Single sign-on',
|
|
1865
|
+
'wsp.sso_only': 'De ontwikkelaars van deze werkruimte moeten aanmelden via een identiteitsprovider',
|
|
1866
|
+
'wsp.sso_only_help': 'Of een LDAP-directory. Het pgkiln-wachtwoord en alleen een passkey worden geweigerd, behalve voor noodbeheerders. `pgkiln admin builder-sign-in password` zet het uit.',
|
|
1867
|
+
'wsp.sso_unlinked': 'Nog geen gekoppelde identiteit (kunnen niet aanmelden): {list}.',
|
|
1868
|
+
'wsp.sso_save': 'Single-sign-onregel opslaan',
|
|
1869
|
+
'wsp.sso_on': 'De ontwikkelaars van deze werkruimte moeten single sign-on gebruiken.',
|
|
1870
|
+
'wsp.sso_off': 'De ontwikkelaars van de werkruimte mogen weer het pgkiln-wachtwoord gebruiken.',
|
|
1871
|
+
'wsp.sso_no_break_glass': 'Geef eerst een beheerder noodtoegang (Ontwikkelaars), zodat iemand kan aanmelden als de identiteitsprovider uitvalt.',
|
|
1872
|
+
'wsp.sso_link_first': 'Koppel eerst een identiteit op uw beveiligingspagina, anders kunt u zelf niet aanmelden.',
|
|
1873
|
+
'us.mfa_title': 'Aanmelden in twee stappen',
|
|
1874
|
+
'us.mfa_status': 'Authenticator-apps: {n}. Resterende herstelcodes: {codes}.',
|
|
1875
|
+
'us.mfa_none': 'Nog geen authenticator-app.',
|
|
1876
|
+
'us.mfa_reset_help': 'Verwijdert de authenticator-apps en herstelcodes van {user} en meldt het account overal af. Waar een applicatie aanmelden in twee stappen vereist, voegt de gebruiker bij de volgende aanmelding een nieuwe app toe. Wordt gelogd.',
|
|
1877
|
+
'us.mfa_reset_done': 'Aanmelden in twee stappen van {user} is gereset.',
|
|
1878
|
+
'us.mfa_reason_needed': 'Geef een reden voor de reset.',
|
|
1823
1879
|
};
|
package/src/builder/i18n/pt.ts
CHANGED
|
@@ -1820,4 +1820,60 @@ export const pt: BuilderMessages = {
|
|
|
1820
1820
|
'wsp.mfa_save': 'Salvar a regra de duas etapas',
|
|
1821
1821
|
'wsp.mfa_on': 'Os desenvolvedores deste espaço de trabalho precisam do login em duas etapas.',
|
|
1822
1822
|
'wsp.mfa_off': 'O espaço de trabalho já não exige login em duas etapas (a política da instância continua valendo).',
|
|
1823
|
+
'signin.method': 'Entrar com',
|
|
1824
|
+
'signin.method_pgkiln': 'Conta do pgkiln',
|
|
1825
|
+
'signin.with': 'Entrar com {provider}',
|
|
1826
|
+
'signin.ldap_unavailable': 'Não é possível acessar {directory}. Tente mais tarde.',
|
|
1827
|
+
'signin.sso_required': 'Seu espaço de trabalho exige login único: use seu provedor de identidade.',
|
|
1828
|
+
'signin.sso_unavailable': 'Não é possível acessar {provider}. Tente mais tarde.',
|
|
1829
|
+
'bsso.title': 'Login no builder',
|
|
1830
|
+
'bsso.intro': 'Os desenvolvedores também podem entrar no builder com este provedor. Eles são reconhecidos pela identidade vinculada, nunca só pelo nome.',
|
|
1831
|
+
'bsso.on': 'Os desenvolvedores podem entrar no builder com ele',
|
|
1832
|
+
'bsso.on_help': 'A página de login o mostra. Entrar assim dispensa o segundo fator do próprio pgkiln: o provedor decide.',
|
|
1833
|
+
'bsso.auto_create': 'Criar desenvolvedores no primeiro login',
|
|
1834
|
+
'bsso.auto_create_help': 'Para membros do grupo de desenvolvedores abaixo (qualquer um, se estiver vazio). Novos desenvolvedores entram no espaço de trabalho Default.',
|
|
1835
|
+
'bsso.link_existing': 'Vincular um desenvolvedor existente com o mesmo nome de usuário',
|
|
1836
|
+
'bsso.link_existing_help': 'Uma vez, no primeiro login; nunca um administrador de emergência. Ative só se confiar nos nomes de usuário do provedor. Caso contrário, os desenvolvedores vinculam a identidade na página de segurança.',
|
|
1837
|
+
'bsso.group': 'Grupo de desenvolvedores',
|
|
1838
|
+
'bsso.group_help': 'Só seus membros recebem uma conta de desenvolvedor. Vazio: qualquer pessoa que o provedor autentique.',
|
|
1839
|
+
'bsso.admin_group': 'Grupo de administradores',
|
|
1840
|
+
'bsso.admin_group_help': 'Se preenchido, seus membros são administradores da instância e os demais não, verificado a cada login (administradores de emergência mantêm a função).',
|
|
1841
|
+
'bsso.save': 'Salvar o login no builder',
|
|
1842
|
+
'bsso.saved': 'Login no builder salvo.',
|
|
1843
|
+
'bsso.linked': 'Desenvolvedores vinculados',
|
|
1844
|
+
'bsso.none_linked': 'Ainda não há desenvolvedor vinculado.',
|
|
1845
|
+
'bsso.unlink': 'Desvincular',
|
|
1846
|
+
'bsso.unlink_confirm': 'Desvincular {user}? A pessoa entra com a senha ou vincula de novo.',
|
|
1847
|
+
'bsso.unlink_mine_confirm': 'Desvincular {provider}? Você pode vinculá-lo de novo.',
|
|
1848
|
+
'bsso.unlinked': 'A identidade foi desvinculada.',
|
|
1849
|
+
'bsso.keep_one': 'Um espaço de trabalho exige login único deste desenvolvedor: mantenha uma identidade vinculada.',
|
|
1850
|
+
'bsso.yours': 'Login único',
|
|
1851
|
+
'bsso.yours_intro': 'Os provedores de identidade com que você pode entrar no builder.',
|
|
1852
|
+
'bsso.none_yours': 'Ainda não há identidade vinculada.',
|
|
1853
|
+
'bsso.link': 'Vincular {provider}',
|
|
1854
|
+
'bsso.linked_now': '{provider} está vinculado: você pode entrar com ele.',
|
|
1855
|
+
'bsso.directories_note': 'Um diretório LDAP é vinculado no primeiro login com ele (um administrador permite isso por diretório).',
|
|
1856
|
+
'bsso.break_glass': 'de emergência',
|
|
1857
|
+
'bsso.break_glass_help': 'Pode entrar com a senha do pgkiln onde um espaço de trabalho exige login único, para quando o provedor de identidade falhar.',
|
|
1858
|
+
'bsso.break_glass_make': 'Dar acesso de emergência',
|
|
1859
|
+
'bsso.break_glass_remove': 'Remover acesso de emergência',
|
|
1860
|
+
'bsso.break_glass_on': '{user} é administrador de emergência. Configure também um segundo fator.',
|
|
1861
|
+
'bsso.break_glass_off': '{user} não é mais administrador de emergência.',
|
|
1862
|
+
'bsso.break_glass_admins': 'Só administradores podem ter acesso de emergência.',
|
|
1863
|
+
'bsso.keep_break_glass': 'Um espaço de trabalho exige login único: mantenha pelo menos um administrador de emergência.',
|
|
1864
|
+
'wsp.sso': 'Login único',
|
|
1865
|
+
'wsp.sso_only': 'Os desenvolvedores deste espaço de trabalho devem entrar por um provedor de identidade',
|
|
1866
|
+
'wsp.sso_only_help': 'Ou um diretório LDAP. A senha do pgkiln e uma chave de acesso sozinha são recusadas, exceto para administradores de emergência. `pgkiln admin builder-sign-in password` desativa.',
|
|
1867
|
+
'wsp.sso_unlinked': 'Ainda sem identidade vinculada (não podem entrar): {list}.',
|
|
1868
|
+
'wsp.sso_save': 'Salvar a regra de login único',
|
|
1869
|
+
'wsp.sso_on': 'Os desenvolvedores deste espaço de trabalho devem usar login único.',
|
|
1870
|
+
'wsp.sso_off': 'Os desenvolvedores do espaço de trabalho podem voltar a usar a senha do pgkiln.',
|
|
1871
|
+
'wsp.sso_no_break_glass': 'Primeiro dê acesso de emergência a um administrador (Desenvolvedores), para que alguém possa entrar se o provedor de identidade falhar.',
|
|
1872
|
+
'wsp.sso_link_first': 'Vincule primeiro uma identidade na sua página de segurança, ou você mesmo não conseguiria entrar.',
|
|
1873
|
+
'us.mfa_title': 'Login em duas etapas',
|
|
1874
|
+
'us.mfa_status': 'Aplicativos autenticadores: {n}. Códigos de recuperação restantes: {codes}.',
|
|
1875
|
+
'us.mfa_none': 'Nenhum aplicativo autenticador ainda.',
|
|
1876
|
+
'us.mfa_reset_help': 'Remove os aplicativos autenticadores e os códigos de recuperação de {user} e encerra todas as sessões. Onde um aplicativo exigir login em duas etapas, um novo aplicativo será adicionado no próximo login. Registrado.',
|
|
1877
|
+
'us.mfa_reset_done': 'O login em duas etapas de {user} foi redefinido.',
|
|
1878
|
+
'us.mfa_reason_needed': 'Informe um motivo para a redefinição.',
|
|
1823
1879
|
};
|
package/src/builder/ldap.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { owner } from '../db.ts';
|
|
|
3
3
|
import { html, raw } from '../html.ts';
|
|
4
4
|
import { testDirectory, type Directory } from '../ldap.ts';
|
|
5
5
|
import { administrator, back, BASE, csrf, flash, input, region, send, shell, type Req } from './ui.ts';
|
|
6
|
+
import { builderSignInRegion } from './builder-sso.ts';
|
|
6
7
|
|
|
7
8
|
// Users → LDAP directories: the directories an application's password form
|
|
8
9
|
// can check (enabled per app under Settings → Sign-in methods). The bind
|
|
@@ -114,6 +115,7 @@ export async function ldapRoutes(app: FastifyInstance) {
|
|
|
114
115
|
<form method="post" action="${BASE}/users/directories/${d.id}/test" class="search u-mwnone">${csrf(s)}
|
|
115
116
|
<input name="username" placeholder="Username (optional)" aria-label="Username to look up" maxlength="100">
|
|
116
117
|
<button class="btn">Test</button></form>`)}
|
|
118
|
+
${await builderSignInRegion(s, 'directory', d)}
|
|
117
119
|
</div>`;
|
|
118
120
|
return send(reply, s, shell(s, d.display_name, [['Users', `${BASE}/users`], ['LDAP directories', `${BASE}/users/directories`], [d.display_name]], main, 'users'));
|
|
119
121
|
});
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { FastifyInstance } from 'fastify';
|
|
2
2
|
import { owner } from '../db.ts';
|
|
3
|
+
import { APP_MFA, type AppMfa } from '../account-mfa.ts';
|
|
3
4
|
import { html, raw } from '../html.ts';
|
|
5
|
+
import { secretKeyConfigured } from '../secrets.ts';
|
|
4
6
|
import { back, BASE, developer, flash, type Req } from './ui.ts';
|
|
5
7
|
|
|
6
8
|
// Application settings → Sign-in methods (migration 087): the application's
|
|
7
9
|
// own login page (APEX: the login page, usually 9999) and "Forgot password?".
|
|
8
10
|
// "Create login page" adds a minimal public page with a login region and
|
|
9
|
-
// makes it the login page.
|
|
11
|
+
// makes it the login page. (sprint 130, SEC-3) Two-factor sign-in for the
|
|
12
|
+
// application's users: off, optional, required, or required for some roles.
|
|
10
13
|
|
|
11
14
|
interface AppRow {
|
|
12
15
|
id: number;
|
|
@@ -15,6 +18,8 @@ interface AppRow {
|
|
|
15
18
|
local_login: boolean;
|
|
16
19
|
login_page: number | null;
|
|
17
20
|
password_reset: boolean;
|
|
21
|
+
mfa: AppMfa;
|
|
22
|
+
mfa_roles: string[];
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/** The fields in the settings form. */
|
|
@@ -41,14 +46,33 @@ export async function loginSettingsFields(a: AppRow) {
|
|
|
41
46
|
? html`Sent through mail service ${mail!.service} as ${mail!.from_address}.`
|
|
42
47
|
: html`<strong>Not offered yet:</strong> the application needs an enabled mail service with a From address (Workspace utilities → Mail services).`}
|
|
43
48
|
Only for the user directory's own passwords: not for LDAP, single sign-on, database accounts or custom authentication.</small></div>
|
|
49
|
+
</div>
|
|
50
|
+
<h3 id="two-factor">Two-factor sign-in</h3>
|
|
51
|
+
<div class="form-grid">
|
|
52
|
+
<div class="field"><label class="label" for="f_mfa">Second step</label>
|
|
53
|
+
<select id="f_mfa" name="mfa" aria-describedby="f_mfa_help">${(APP_MFA as string[]).map((m) => opt(m, MFA_LABELS[m as AppMfa], a.mfa))}</select>
|
|
54
|
+
<small class="help" id="f_mfa_help">After the password (or an LDAP directory), users give a code from an authenticator app or a recovery code. They add the app on <em>My account</em>, or while signing in when it is required. An identity provider asks for its own second factor. Accounts share their apps across applications.
|
|
55
|
+
${secretKeyConfigured() ? '' : html`<strong>Not available yet:</strong> the server needs <code>PGKILN_SECRET_KEY</code> to keep the apps' secrets.`}</small></div>
|
|
56
|
+
<div class="field"><label class="label" for="f_mfa_roles">Required for roles</label>
|
|
57
|
+
<input id="f_mfa_roles" name="mfa_roles" value="${(a.mfa_roles ?? []).join(', ')}" placeholder="admin, manager" aria-describedby="f_mfa_roles_help">
|
|
58
|
+
<small class="help" id="f_mfa_roles_help">With <em>Required for some roles</em>: users with one of these roles must use it, others may.</small></div>
|
|
44
59
|
</div>`;
|
|
45
60
|
}
|
|
46
61
|
|
|
62
|
+
const MFA_LABELS: Record<AppMfa, string> = { off: 'Off', optional: 'Optional (users may add an app)', required: 'Required', roles: 'Required for some roles' };
|
|
63
|
+
|
|
47
64
|
/** Save them (with the rest of the settings form). */
|
|
48
65
|
export async function saveLoginSettings(appId: string, b: Record<string, string | undefined>) {
|
|
49
66
|
const page = b.login_page?.trim() ? Number(b.login_page) : null;
|
|
50
67
|
if (page !== null && !(await owner.one('select 1 from meta.page where app_id = $1 and page_no = $2', [appId, page]))) throw new Error(`Login page: there is no page ${b.login_page}.`);
|
|
51
|
-
|
|
68
|
+
const mfa = (APP_MFA as string[]).includes(b.mfa ?? '') ? (b.mfa as AppMfa) : 'off';
|
|
69
|
+
const roles = [...new Set((b.mfa_roles ?? '').split(',').map((r) => r.trim().toLowerCase()).filter(Boolean))];
|
|
70
|
+
if (roles.some((r) => r.length > 100)) throw new Error('Two-factor sign-in: a role name is too long.');
|
|
71
|
+
if (mfa === 'roles' && !roles.length) throw new Error('Two-factor sign-in: name the roles that must use it.');
|
|
72
|
+
// without the instance key, users could not add an app: requiring one would lock them out
|
|
73
|
+
if ((mfa === 'required' || mfa === 'roles') && !secretKeyConfigured()) throw new Error('Two-factor sign-in: the server needs PGKILN_SECRET_KEY before it can be required.');
|
|
74
|
+
await owner.query('update meta.app set login_page = $2, password_reset = $3, mfa = $4, mfa_roles = $5 where id = $1',
|
|
75
|
+
[appId, page, b.password_reset === 'true', mfa, roles]);
|
|
52
76
|
}
|
|
53
77
|
|
|
54
78
|
export async function loginPageRoutes(app: FastifyInstance) {
|