@shamar/adonis 0.1.3 → 0.1.4

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.
Files changed (62) hide show
  1. package/README.md +75 -2
  2. package/assets/admin.css +1 -1
  3. package/dist/auth/ldap.d.ts +115 -0
  4. package/dist/auth/ldap.d.ts.map +1 -0
  5. package/dist/auth/ldap.js +275 -0
  6. package/dist/auth/ldap.js.map +1 -0
  7. package/dist/auth/masquerade.d.ts +31 -0
  8. package/dist/auth/masquerade.d.ts.map +1 -0
  9. package/dist/auth/masquerade.js +36 -0
  10. package/dist/auth/masquerade.js.map +1 -0
  11. package/dist/auth_login_view.d.ts +56 -0
  12. package/dist/auth_login_view.d.ts.map +1 -0
  13. package/dist/auth_login_view.js +56 -0
  14. package/dist/auth_login_view.js.map +1 -0
  15. package/dist/commands/commands.json +1 -0
  16. package/dist/commands/main.d.ts +4 -0
  17. package/dist/commands/main.js +36 -0
  18. package/dist/commands/publish_auth.d.ts +13 -0
  19. package/dist/commands/publish_auth.d.ts.map +1 -0
  20. package/dist/commands/publish_auth.js +26 -0
  21. package/dist/commands/publish_auth.js.map +1 -0
  22. package/dist/config.d.ts +32 -1
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js.map +1 -1
  25. package/dist/configure.d.ts +16 -1
  26. package/dist/configure.d.ts.map +1 -1
  27. package/dist/configure.js +6 -0
  28. package/dist/configure.js.map +1 -1
  29. package/dist/context.d.ts +3 -0
  30. package/dist/context.d.ts.map +1 -1
  31. package/dist/controllers/admin_controller.d.ts.map +1 -1
  32. package/dist/controllers/admin_controller.js +14 -6
  33. package/dist/controllers/admin_controller.js.map +1 -1
  34. package/dist/index.d.ts +6 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +6 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/publish_auth.d.ts +45 -0
  39. package/dist/publish_auth.d.ts.map +1 -0
  40. package/dist/publish_auth.js +69 -0
  41. package/dist/publish_auth.js.map +1 -0
  42. package/dist/resources/api_key_resource.d.ts +4 -6
  43. package/dist/resources/api_key_resource.d.ts.map +1 -1
  44. package/dist/resources/api_key_resource.js +4 -16
  45. package/dist/resources/api_key_resource.js.map +1 -1
  46. package/dist/shamar/auth.d.ts +13 -1
  47. package/dist/shamar/auth.d.ts.map +1 -1
  48. package/dist/shamar/auth.js +42 -5
  49. package/dist/shamar/auth.js.map +1 -1
  50. package/dist/shamar/relation-fields.d.ts.map +1 -1
  51. package/dist/shamar/relation-fields.js +3 -2
  52. package/dist/shamar/relation-fields.js.map +1 -1
  53. package/dist/shamar/view-context.d.ts +8 -0
  54. package/dist/shamar/view-context.d.ts.map +1 -1
  55. package/dist/shamar/view-context.js +1 -0
  56. package/dist/shamar/view-context.js.map +1 -1
  57. package/package.json +15 -6
  58. package/resources/views/shamar/partials/nav.edge +6 -1
  59. package/resources/views/shamar/partials/shell-open.edge +14 -0
  60. package/resources/views/shamar/partials/topbar.edge +3 -0
  61. package/stubs/views/auth/layout.edge +244 -0
  62. package/stubs/views/auth/login.edge +65 -0
@@ -0,0 +1,275 @@
1
+ import { mapGroupsToRoleIds, parseLdapUsername, } from '@shamar/cherubim';
2
+ export const LDAP_TIMEOUT_MESSAGE = 'Directory server timed out. Please try again.';
3
+ /** Thrown / rethrown when ldapts (or the network) times out contacting a domain. */
4
+ export class LdapTimeoutError extends Error {
5
+ domainId;
6
+ constructor(domainId, cause) {
7
+ const detail = cause instanceof Error && cause.message.trim()
8
+ ? cause.message.trim()
9
+ : 'operation timed out';
10
+ super(`LDAP domain "${domainId}" timed out: ${detail}`);
11
+ this.name = 'LdapTimeoutError';
12
+ this.domainId = domainId;
13
+ if (cause !== undefined) {
14
+ this.cause = cause;
15
+ }
16
+ }
17
+ }
18
+ export function isLdapTimeoutError(error) {
19
+ if (error instanceof LdapTimeoutError)
20
+ return true;
21
+ if (!error || typeof error !== 'object')
22
+ return false;
23
+ const err = error;
24
+ const code = String(err.code ?? '').toUpperCase();
25
+ if (code === 'ETIMEDOUT' || code === 'TIMEOUT' || code === 'ECONNABORTED') {
26
+ return true;
27
+ }
28
+ const name = String(err.name ?? '');
29
+ if (/timeout/i.test(name))
30
+ return true;
31
+ const message = String(err.message ?? '').toLowerCase();
32
+ return (message.includes('timed out') ||
33
+ message.includes('timeout') ||
34
+ message.includes('connect timeout'));
35
+ }
36
+ export function resolveLdapProvisioning(mode) {
37
+ return mode === 'create' ? 'create' : 'existing';
38
+ }
39
+ export function resolveAuthLoginMode(mode, domains) {
40
+ if (mode === 'local' || mode === 'ldap' || mode === 'both')
41
+ return mode;
42
+ return domains?.length ? 'both' : 'local';
43
+ }
44
+ /**
45
+ * Order domains: hinted matches first, then remaining config order.
46
+ */
47
+ export function orderLdapDomains(usernameInput, domains) {
48
+ const parsed = parseLdapUsername(usernameInput);
49
+ const preferred = [];
50
+ const rest = [];
51
+ for (const domain of domains) {
52
+ let match = false;
53
+ if (parsed.netbios && domain.netbios) {
54
+ match = domain.netbios.toUpperCase() === parsed.netbios.toUpperCase();
55
+ }
56
+ if (!match && parsed.emailDomain && domain.emailDomains?.length) {
57
+ const needle = parsed.emailDomain.toLowerCase();
58
+ match = domain.emailDomains.some((d) => d.toLowerCase() === needle);
59
+ }
60
+ if (match)
61
+ preferred.push(domain);
62
+ else
63
+ rest.push(domain);
64
+ }
65
+ return [...preferred, ...rest];
66
+ }
67
+ export function searchUsernameForDomain(usernameInput, _domain) {
68
+ const parsed = parseLdapUsername(usernameInput);
69
+ return parsed.username || usernameInput.trim();
70
+ }
71
+ /**
72
+ * Try domains in order until one authenticates.
73
+ * Returns `{ identity: null }` when none succeed (caller decides local fallback).
74
+ * Sets `timedOut` when any domain attempt hit a connect/operation timeout.
75
+ */
76
+ export async function authenticateLdap(usernameInput, password, domains, client) {
77
+ if (!password || !usernameInput.trim() || domains.length === 0) {
78
+ return { identity: null, timedOut: false };
79
+ }
80
+ const ordered = orderLdapDomains(usernameInput, domains);
81
+ let timedOut = false;
82
+ for (const domain of ordered) {
83
+ const username = searchUsernameForDomain(usernameInput, domain);
84
+ try {
85
+ const identity = await client.authenticate(username, password, domain);
86
+ if (identity)
87
+ return { identity, timedOut };
88
+ }
89
+ catch (error) {
90
+ if (isLdapTimeoutError(error)) {
91
+ timedOut = true;
92
+ console.warn(`[shamar/ldap] domain "${domain.id}" timed out:`, error instanceof Error ? error.message : error);
93
+ continue;
94
+ }
95
+ // Unreachable / misconfigured domain — try the next one.
96
+ console.warn(`[shamar/ldap] domain "${domain.id}" failed:`, error instanceof Error ? error.message : error);
97
+ }
98
+ }
99
+ return { identity: null, timedOut };
100
+ }
101
+ /**
102
+ * Orchestrate local / LDAP / both login modes.
103
+ *
104
+ * After a successful directory bind, {@link linkFromLdap} must resolve a local
105
+ * user (matched by sync UID / externalId). When `provisioning` is `existing`
106
+ * and no local row exists, LDAP login fails (or falls through to local when
107
+ * `loginMode` is `both`).
108
+ */
109
+ export async function resolvePasswordLogin(options) {
110
+ const mode = resolveAuthLoginMode(options.loginMode, options.domains);
111
+ const domains = options.domains ?? [];
112
+ const provisioning = resolveLdapProvisioning(options.provisioning);
113
+ if (mode === 'local') {
114
+ const user = await options.verifyLocal(options.username, options.password);
115
+ if (!user)
116
+ return { ok: false, message: 'Invalid user credentials' };
117
+ return { ok: true, user, method: 'local' };
118
+ }
119
+ if (domains.length === 0) {
120
+ return {
121
+ ok: false,
122
+ message: 'LDAP login is enabled but no domains are configured',
123
+ };
124
+ }
125
+ const { identity, timedOut } = await authenticateLdap(options.username, options.password, domains, options.client);
126
+ if (identity) {
127
+ const user = await options.linkFromLdap(identity);
128
+ if (user) {
129
+ return { ok: true, user, method: 'ldap' };
130
+ }
131
+ if (provisioning === 'existing') {
132
+ // Bound in LDAP but no local sync row — do not invent a user.
133
+ if (mode === 'ldap') {
134
+ return {
135
+ ok: false,
136
+ message: 'No local account is linked to this directory user',
137
+ };
138
+ }
139
+ // both: fall through to local password check
140
+ }
141
+ else {
142
+ // create mode with null link is a host bug
143
+ return {
144
+ ok: false,
145
+ message: 'Unable to provision local account for directory user',
146
+ };
147
+ }
148
+ }
149
+ if (mode === 'ldap') {
150
+ if (timedOut) {
151
+ return { ok: false, message: LDAP_TIMEOUT_MESSAGE, code: 'ldap_timeout' };
152
+ }
153
+ return { ok: false, message: 'Invalid user credentials' };
154
+ }
155
+ // both → local fallback (also when LDAP user is not linked locally)
156
+ const user = await options.verifyLocal(options.username, options.password);
157
+ if (user)
158
+ return { ok: true, user, method: 'local' };
159
+ if (timedOut) {
160
+ return { ok: false, message: LDAP_TIMEOUT_MESSAGE, code: 'ldap_timeout' };
161
+ }
162
+ return { ok: false, message: 'Invalid user credentials' };
163
+ }
164
+ /** Apply domain groupRoleMap to an identity's groups. */
165
+ export function roleIdsFromLdapIdentity(identity, domain) {
166
+ return mapGroupsToRoleIds(identity.groups, domain.groupRoleMap ?? {});
167
+ }
168
+ function attrValue(entry, name) {
169
+ const raw = entry[name] ?? entry[name.toLowerCase()];
170
+ if (raw == null)
171
+ return undefined;
172
+ if (Array.isArray(raw)) {
173
+ const first = raw[0];
174
+ return first != null ? String(first) : undefined;
175
+ }
176
+ return String(raw);
177
+ }
178
+ function attrValues(entry, name) {
179
+ const raw = entry[name] ?? entry[name.toLowerCase()];
180
+ if (raw == null)
181
+ return [];
182
+ if (Array.isArray(raw)) {
183
+ return raw
184
+ .filter((item) => item != null && String(item).trim() !== '')
185
+ .map((item) => String(item));
186
+ }
187
+ const value = String(raw).trim();
188
+ return value ? [value] : [];
189
+ }
190
+ function buildSearchFilter(template, username) {
191
+ const escaped = username.replace(/[\\*()]/g, (ch) => `\\${ch}`);
192
+ return template.replaceAll('{{username}}', escaped);
193
+ }
194
+ /**
195
+ * Default ldapts-backed client. Requires the optional `ldapts` peer dependency.
196
+ */
197
+ export function createLdaptsDirectoryClient() {
198
+ return {
199
+ async authenticate(username, password, domain) {
200
+ let Client;
201
+ try {
202
+ ({ Client } = await import('ldapts'));
203
+ }
204
+ catch {
205
+ throw new Error('LDAP login requires the optional peer dependency "ldapts". Install it in your app.');
206
+ }
207
+ const timeout = domain.timeoutMs ?? 10_000;
208
+ const client = new Client({
209
+ url: domain.url,
210
+ timeout,
211
+ connectTimeout: domain.connectTimeoutMs ?? timeout,
212
+ tlsOptions: domain.tls,
213
+ });
214
+ try {
215
+ if (domain.bindDn) {
216
+ await client.bind(domain.bindDn, domain.bindPassword ?? '');
217
+ }
218
+ const filter = buildSearchFilter(domain.searchFilter || '(uid={{username}})', username);
219
+ const emailAttr = domain.emailAttribute ?? 'mail';
220
+ const nameAttr = domain.nameAttribute ?? 'cn';
221
+ const groupsAttr = domain.groupsAttribute ?? 'memberOf';
222
+ const { searchEntries } = await client.search(domain.searchBase, {
223
+ scope: 'sub',
224
+ filter,
225
+ attributes: [
226
+ 'dn',
227
+ 'distinguishedName',
228
+ emailAttr,
229
+ nameAttr,
230
+ groupsAttr,
231
+ 'uid',
232
+ 'sAMAccountName',
233
+ ],
234
+ sizeLimit: 1,
235
+ });
236
+ const entry = searchEntries[0];
237
+ if (!entry)
238
+ return null;
239
+ const dn = (typeof entry.dn === 'string' && entry.dn) ||
240
+ attrValue(entry, 'distinguishedName') ||
241
+ attrValue(entry, 'dn');
242
+ if (!dn)
243
+ return null;
244
+ // Re-bind as the user to verify credentials.
245
+ await client.bind(dn, password);
246
+ const groups = attrValues(entry, groupsAttr);
247
+ return {
248
+ provider: 'ldap',
249
+ domainId: domain.id,
250
+ subject: dn,
251
+ username,
252
+ email: attrValue(entry, emailAttr),
253
+ name: attrValue(entry, nameAttr),
254
+ groups,
255
+ raw: { ...entry },
256
+ };
257
+ }
258
+ catch (error) {
259
+ if (isLdapTimeoutError(error)) {
260
+ throw new LdapTimeoutError(domain.id, error);
261
+ }
262
+ return null;
263
+ }
264
+ finally {
265
+ try {
266
+ await client.unbind();
267
+ }
268
+ catch {
269
+ /* ignore */
270
+ }
271
+ }
272
+ },
273
+ };
274
+ }
275
+ //# sourceMappingURL=ldap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ldap.js","sourceRoot":"","sources":["../../src/auth/ldap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** Session flag set after a successful masquerade login. */
2
+ export declare const MASQUERADE_SESSION_KEY = "shamar_masquerade";
3
+ /**
4
+ * True when a non-empty masquerade password is configured and
5
+ * `NODE_ENV` is not `production`. Production never activates.
6
+ */
7
+ export declare function isMasqueradeEnabled(config?: {
8
+ auth?: {
9
+ masquerade?: {
10
+ password?: string;
11
+ };
12
+ };
13
+ } | null, env?: NodeJS.ProcessEnv): boolean;
14
+ /** Timing-safe string compare (false when lengths differ or either is empty). */
15
+ export declare function passwordsMatch(provided: string, expected: string): boolean;
16
+ /**
17
+ * Whether the submitted password matches the configured masquerade
18
+ * password while masquerade is enabled.
19
+ */
20
+ export declare function isMasqueradePassword(provided: string, config?: {
21
+ auth?: {
22
+ masquerade?: {
23
+ password?: string;
24
+ };
25
+ };
26
+ } | null, env?: NodeJS.ProcessEnv): boolean;
27
+ /** Read the session flag set at masquerade login. */
28
+ export declare function isMasqueradeSession(session: {
29
+ get?(key: string): unknown;
30
+ }): boolean;
31
+ //# sourceMappingURL=masquerade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masquerade.d.ts","sourceRoot":"","sources":["../../src/auth/masquerade.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,EACjE,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAIT;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK1E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,EACjE,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAIT;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B,GAAG,OAAO,CAEV"}
@@ -0,0 +1,36 @@
1
+ import { timingSafeEqual } from 'node:crypto';
2
+ /** Session flag set after a successful masquerade login. */
3
+ export const MASQUERADE_SESSION_KEY = 'shamar_masquerade';
4
+ /**
5
+ * True when a non-empty masquerade password is configured and
6
+ * `NODE_ENV` is not `production`. Production never activates.
7
+ */
8
+ export function isMasqueradeEnabled(config, env = process.env) {
9
+ if (String(env.NODE_ENV ?? '').toLowerCase() === 'production')
10
+ return false;
11
+ const password = config?.auth?.masquerade?.password;
12
+ return typeof password === 'string' && password.trim() !== '';
13
+ }
14
+ /** Timing-safe string compare (false when lengths differ or either is empty). */
15
+ export function passwordsMatch(provided, expected) {
16
+ const a = Buffer.from(String(provided ?? ''), 'utf8');
17
+ const b = Buffer.from(String(expected ?? ''), 'utf8');
18
+ if (a.length === 0 || b.length === 0 || a.length !== b.length)
19
+ return false;
20
+ return timingSafeEqual(a, b);
21
+ }
22
+ /**
23
+ * Whether the submitted password matches the configured masquerade
24
+ * password while masquerade is enabled.
25
+ */
26
+ export function isMasqueradePassword(provided, config, env = process.env) {
27
+ if (!isMasqueradeEnabled(config, env))
28
+ return false;
29
+ const expected = config?.auth?.masquerade?.password ?? '';
30
+ return passwordsMatch(provided, expected);
31
+ }
32
+ /** Read the session flag set at masquerade login. */
33
+ export function isMasqueradeSession(session) {
34
+ return session.get?.(MASQUERADE_SESSION_KEY) === true;
35
+ }
36
+ //# sourceMappingURL=masquerade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masquerade.js","sourceRoot":"","sources":["../../src/auth/masquerade.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,4DAA4D;AAC5D,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAiE,EACjE,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,YAAY;QAAE,OAAO,KAAK,CAAC;IAC5E,MAAM,QAAQ,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC;IACpD,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAChE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,QAAgB;IAC/D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5E,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,MAAiE,EACjE,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC1D,OAAO,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,mBAAmB,CAAC,OAEnC;IACC,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAC;AACxD,CAAC"}
@@ -0,0 +1,56 @@
1
+ import type { AuthLoginMode } from '@shamar/cherubim';
2
+ import type { PanelBranding } from '@shamar/core';
3
+ import { type ShamarBranding } from './shamar/branding.js';
4
+ export interface AuthLoginCopy {
5
+ /** Subtitle under the brand name. */
6
+ subtitle?: string;
7
+ /** Optional hint under the form. Empty/omit hides it. */
8
+ footer?: string;
9
+ /** Placeholder for the username / email field. */
10
+ usernamePlaceholder?: string;
11
+ /** Label for the username / email field. */
12
+ usernameLabel?: string;
13
+ }
14
+ export interface AuthLoginViewConfig {
15
+ branding?: PanelBranding;
16
+ auth?: {
17
+ loginMode?: AuthLoginMode | string;
18
+ login?: AuthLoginCopy;
19
+ masquerade?: {
20
+ password?: string;
21
+ };
22
+ };
23
+ }
24
+ export interface AuthLoginViewData {
25
+ loginMode: AuthLoginMode | string;
26
+ branding: ShamarBranding;
27
+ brandingCss: string;
28
+ /** Convenience alias for templates / titles. */
29
+ brandName: string;
30
+ /** Resolved subtitle under the brand. */
31
+ loginSubtitle: string;
32
+ /** Resolved footer hint; empty string means hide. */
33
+ loginFooter: string;
34
+ /** Username / email field label. */
35
+ loginUsernameLabel: string;
36
+ /** Username / email field placeholder. */
37
+ loginUsernamePlaceholder: string;
38
+ /** True when non-production masquerade login is available. */
39
+ masqueradeEnabled: boolean;
40
+ }
41
+ /**
42
+ * Resolve login page subtitle / footer / username field copy from config.
43
+ * Footer defaults to empty (hidden).
44
+ */
45
+ export declare function resolveAuthLoginCopy(loginMode?: AuthLoginMode | string, copy?: AuthLoginCopy): {
46
+ subtitle: string;
47
+ footer: string;
48
+ usernameLabel: string;
49
+ usernamePlaceholder: string;
50
+ };
51
+ /**
52
+ * View locals for the published Shamar login page.
53
+ * Uses the same branding resolution as the admin shell.
54
+ */
55
+ export declare function buildAuthLoginViewData(config?: AuthLoginViewConfig): AuthLoginViewData;
56
+ //# sourceMappingURL=auth_login_view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth_login_view.d.ts","sourceRoot":"","sources":["../src/auth_login_view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,IAAI,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;QACnC,KAAK,CAAC,EAAE,aAAa,CAAC;QACtB,UAAU,CAAC,EAAE;YACX,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,aAAa,GAAG,MAAM,CAAC;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,wBAAwB,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAoBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,aAAa,GAAG,MAAgB,EAC3C,IAAI,CAAC,EAAE,aAAa,GACnB;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAgBA;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,mBAAwB,GAAG,iBAAiB,CAkB1F"}
@@ -0,0 +1,56 @@
1
+ import { isMasqueradeEnabled } from './auth/masquerade.js';
2
+ import { buildBrandingCss, resolveBranding, } from './shamar/branding.js';
3
+ const DEFAULT_SUBTITLES = {
4
+ local: 'Sign in to continue.',
5
+ ldap: 'Sign in with your directory account.',
6
+ both: 'Sign in with your directory or local account.',
7
+ };
8
+ const DEFAULT_USERNAME_LABELS = {
9
+ local: 'Email',
10
+ ldap: 'Email or username',
11
+ both: 'Email or username',
12
+ };
13
+ const DEFAULT_USERNAME_PLACEHOLDERS = {
14
+ local: 'you@example.com',
15
+ ldap: 'user@domain or DOMAIN\\user',
16
+ both: 'user@domain or DOMAIN\\user',
17
+ };
18
+ /**
19
+ * Resolve login page subtitle / footer / username field copy from config.
20
+ * Footer defaults to empty (hidden).
21
+ */
22
+ export function resolveAuthLoginCopy(loginMode = 'local', copy) {
23
+ const mode = String(loginMode || 'local');
24
+ const subtitle = copy?.subtitle?.trim() ||
25
+ DEFAULT_SUBTITLES[mode] ||
26
+ DEFAULT_SUBTITLES.local;
27
+ const footer = copy?.footer?.trim() ?? '';
28
+ const usernameLabel = copy?.usernameLabel?.trim() ||
29
+ DEFAULT_USERNAME_LABELS[mode] ||
30
+ DEFAULT_USERNAME_LABELS.local;
31
+ const usernamePlaceholder = copy?.usernamePlaceholder?.trim() ||
32
+ DEFAULT_USERNAME_PLACEHOLDERS[mode] ||
33
+ DEFAULT_USERNAME_PLACEHOLDERS.local;
34
+ return { subtitle, footer, usernameLabel, usernamePlaceholder };
35
+ }
36
+ /**
37
+ * View locals for the published Shamar login page.
38
+ * Uses the same branding resolution as the admin shell.
39
+ */
40
+ export function buildAuthLoginViewData(config = {}) {
41
+ const loginMode = config.auth?.loginMode ?? 'local';
42
+ const branding = resolveBranding(config.branding);
43
+ const { subtitle, footer, usernameLabel, usernamePlaceholder } = resolveAuthLoginCopy(loginMode, config.auth?.login);
44
+ return {
45
+ loginMode,
46
+ branding,
47
+ brandingCss: buildBrandingCss(branding),
48
+ brandName: branding.brandName,
49
+ loginSubtitle: subtitle,
50
+ loginFooter: footer,
51
+ loginUsernameLabel: usernameLabel,
52
+ loginUsernamePlaceholder: usernamePlaceholder,
53
+ masqueradeEnabled: isMasqueradeEnabled(config),
54
+ };
55
+ }
56
+ //# sourceMappingURL=auth_login_view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth_login_view.js","sourceRoot":"","sources":["../src/auth_login_view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAChB,eAAe,GAEhB,MAAM,sBAAsB,CAAC;AA0C9B,MAAM,iBAAiB,GAA2B;IAChD,KAAK,EAAE,sBAAsB;IAC7B,IAAI,EAAE,sCAAsC;IAC5C,IAAI,EAAE,+CAA+C;CACtD,CAAC;AAEF,MAAM,uBAAuB,GAA2B;IACtD,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE,mBAAmB;CAC1B,CAAC;AAEF,MAAM,6BAA6B,GAA2B;IAC5D,KAAK,EAAE,iBAAiB;IACxB,IAAI,EAAE,6BAA6B;IACnC,IAAI,EAAE,6BAA6B;CACpC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAoC,OAAO,EAC3C,IAAoB;IAOpB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC;IAC1C,MAAM,QAAQ,GACZ,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;QACtB,iBAAiB,CAAC,IAAI,CAAC;QACvB,iBAAiB,CAAC,KAAM,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC1C,MAAM,aAAa,GACjB,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;QAC3B,uBAAuB,CAAC,IAAI,CAAC;QAC7B,uBAAuB,CAAC,KAAM,CAAC;IACjC,MAAM,mBAAmB,GACvB,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE;QACjC,6BAA6B,CAAC,IAAI,CAAC;QACnC,6BAA6B,CAAC,KAAM,CAAC;IACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAA8B,EAAE;IACrE,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,IAAI,OAAO,CAAC;IACpD,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,mBAAmB,EAAE,GAAG,oBAAoB,CACnF,SAAS,EACT,MAAM,CAAC,IAAI,EAAE,KAAK,CACnB,CAAC;IACF,OAAO;QACL,SAAS;QACT,QAAQ;QACR,WAAW,EAAE,gBAAgB,CAAC,QAAQ,CAAC;QACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,aAAa,EAAE,QAAQ;QACvB,WAAW,EAAE,MAAM;QACnB,kBAAkB,EAAE,aAAa;QACjC,wBAAwB,EAAE,mBAAmB;QAC7C,iBAAiB,EAAE,mBAAmB,CAAC,MAAM,CAAC;KAC/C,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ {"commands":[{"commandName":"shamar:publish-auth","description":"Publish opinionated Shamar auth layout and login page into the application","help":"","namespace":"shamar","aliases":[],"flags":[{"name":"force","flagName":"force","required":false,"type":"boolean","description":"Overwrite existing auth views without confirmation","alias":"f"}],"args":[],"options":{},"filePath":"publish_auth.js"}],"version":1}
@@ -0,0 +1,4 @@
1
+ import { CommandMetaData, Command } from '@adonisjs/ace/types';
2
+
3
+ export function getMetaData(): Promise<CommandMetaData[]>
4
+ export function getCommand(metaData: CommandMetaData): Promise<Command | null>
@@ -0,0 +1,36 @@
1
+ import { readFile } from 'node:fs/promises'
2
+
3
+ /**
4
+ * In-memory cache of commands after they have been loaded
5
+ */
6
+ let commandsMetaData
7
+
8
+ /**
9
+ * Reads the commands from the "./commands.json" file. Since, the commands.json
10
+ * file is generated automatically, we do not have to validate its contents
11
+ */
12
+ export async function getMetaData() {
13
+ if (commandsMetaData) {
14
+ return commandsMetaData
15
+ }
16
+
17
+ const commandsIndex = await readFile(new URL('./commands.json', import.meta.url), 'utf-8')
18
+ commandsMetaData = JSON.parse(commandsIndex).commands
19
+
20
+ return commandsMetaData
21
+ }
22
+
23
+ /**
24
+ * Imports the command by lookingup its path from the commands
25
+ * metadata
26
+ */
27
+ export async function getCommand(metaData) {
28
+ const commands = await getMetaData()
29
+ const command = commands.find(({ commandName }) => metaData.commandName === commandName)
30
+ if (!command) {
31
+ return null
32
+ }
33
+
34
+ const { default: commandConstructor } = await import(new URL(command.filePath, import.meta.url).href)
35
+ return commandConstructor
36
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '@adonisjs/core/ace';
2
+ import type { CommandOptions } from '@adonisjs/core/types/ace';
3
+ /**
4
+ * Publish the opinionated Shamar login layout + page into the host app.
5
+ */
6
+ export default class PublishAuth extends BaseCommand {
7
+ static commandName: string;
8
+ static description: string;
9
+ static options: CommandOptions;
10
+ force: boolean;
11
+ run(): Promise<void>;
12
+ }
13
+ //# sourceMappingURL=publish_auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish_auth.d.ts","sourceRoot":"","sources":["../../src/commands/publish_auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,MAAM,CAAC,WAAW,SAAyB;IAC3C,MAAM,CAAC,WAAW,SAC6D;IAE/E,MAAM,CAAC,OAAO,EAAE,cAAc,CAAM;IAM5B,KAAK,EAAE,OAAO,CAAC;IAEjB,GAAG;CAGV"}
@@ -0,0 +1,26 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { BaseCommand, flags } from '@adonisjs/core/ace';
8
+ import { publishAuthViews } from '../publish_auth.js';
9
+ /**
10
+ * Publish the opinionated Shamar login layout + page into the host app.
11
+ */
12
+ export default class PublishAuth extends BaseCommand {
13
+ static commandName = 'shamar:publish-auth';
14
+ static description = 'Publish opinionated Shamar auth layout and login page into the application';
15
+ static options = {};
16
+ async run() {
17
+ await publishAuthViews(this, { force: this.force });
18
+ }
19
+ }
20
+ __decorate([
21
+ flags.boolean({
22
+ description: 'Overwrite existing auth views without confirmation',
23
+ alias: 'f',
24
+ })
25
+ ], PublishAuth.prototype, "force", void 0);
26
+ //# sourceMappingURL=publish_auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish_auth.js","sourceRoot":"","sources":["../../src/commands/publish_auth.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,MAAM,CAAC,WAAW,GAAG,qBAAqB,CAAC;IAC3C,MAAM,CAAC,WAAW,GAChB,4EAA4E,CAAC;IAE/E,MAAM,CAAC,OAAO,GAAmB,EAAE,CAAC;IAQpC,KAAK,CAAC,GAAG;QACP,MAAM,gBAAgB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;;AAJO;IAJP,KAAK,CAAC,OAAO,CAAC;QACb,WAAW,EAAE,oDAAoD;QACjE,KAAK,EAAE,GAAG;KACX,CAAC;0CACqB"}
package/dist/config.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import type { AuthorizerOptions, CherubimUser, RoleResolver } from '@shamar/cherubim';
1
+ import type { AuthLoginMode, AuthorizerOptions, CherubimUser, RoleResolver } from '@shamar/cherubim';
2
2
  import type { PolicyClass } from '@shamar/core';
3
3
  import type { DataAdapter, PanelConfig, Resource } from '@shamar/core';
4
4
  import { panel as createPanel, type PanelBuilder } from '@shamar/core';
5
5
  import type { ShamarHttpContext } from './context.js';
6
+ import type { LdapAuthSettings } from './auth/ldap.js';
6
7
  export type ShamarOrm = 'lucid' | 'mongoose';
7
8
  export interface ShamarConfig {
8
9
  /** @deprecated Prefer `panels`. Kept for single-panel apps. */
@@ -68,6 +69,36 @@ export interface ShamarConfig {
68
69
  };
69
70
  /** Custom principal resolver (overrides guard-based mapping). */
70
71
  resolveUser?: (ctx: ShamarHttpContext) => CherubimUser | null | Promise<CherubimUser | null>;
72
+ /**
73
+ * Password login strategy:
74
+ * - `local` — local passwords only (default when no LDAP domains)
75
+ * - `ldap` — LDAP directories only
76
+ * - `both` — try LDAP first, then fall back to local
77
+ */
78
+ loginMode?: AuthLoginMode;
79
+ /**
80
+ * Copy for the published login page (`buildAuthLoginViewData`).
81
+ * When omitted, sensible mode-based subtitles are used and the footer is hidden.
82
+ */
83
+ login?: {
84
+ /** Subtitle under the brand name. */
85
+ subtitle?: string;
86
+ /** Optional hint under the form. Empty/omit hides it. */
87
+ footer?: string;
88
+ /** Placeholder for the username / email field. */
89
+ usernamePlaceholder?: string;
90
+ /** Label for the username / email field. */
91
+ usernameLabel?: string;
92
+ };
93
+ /** Multi-domain LDAP directory settings. */
94
+ ldap?: LdapAuthSettings;
95
+ /**
96
+ * Dev-only shared password: login as any existing local user.
97
+ * Ignored unless `NODE_ENV !== 'production'` and `password` is non-empty.
98
+ */
99
+ masquerade?: {
100
+ password?: string;
101
+ };
71
102
  };
72
103
  /** Default branding inherited by panels without their own. */
73
104
  branding?: PanelConfig['branding'];
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}