@atlasauth/cli 0.11.0 → 0.12.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/CHANGELOG.md +9 -0
- package/dist/args.d.ts +106 -0
- package/dist/args.js +38 -0
- package/dist/args.js.map +1 -1
- package/dist/commands/init.js +13 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +6 -2
- package/dist/commands/migrate.js +13 -3
- package/dist/commands/migrate.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.js +38 -0
- package/dist/help.js.map +1 -1
- package/dist/migrate/auth0/plugin.d.ts +2 -0
- package/dist/migrate/auth0/plugin.js +126 -0
- package/dist/migrate/auth0/plugin.js.map +1 -0
- package/dist/migrate/clerk/plugin.d.ts +2 -0
- package/dist/migrate/clerk/plugin.js +76 -0
- package/dist/migrate/clerk/plugin.js.map +1 -0
- package/dist/migrate/descope/plugin.d.ts +2 -0
- package/dist/migrate/descope/plugin.js +83 -0
- package/dist/migrate/descope/plugin.js.map +1 -0
- package/dist/migrate/export/auth0-config.d.ts +29 -0
- package/dist/migrate/export/auth0-config.js +249 -0
- package/dist/migrate/export/auth0-config.js.map +1 -0
- package/dist/migrate/export/auth0.d.ts +62 -0
- package/dist/migrate/export/auth0.js +575 -0
- package/dist/migrate/export/auth0.js.map +1 -0
- package/dist/migrate/export/clerk-config.d.ts +26 -0
- package/dist/migrate/export/clerk-config.js +227 -0
- package/dist/migrate/export/clerk-config.js.map +1 -0
- package/dist/migrate/export/clerk.d.ts +46 -0
- package/dist/migrate/export/clerk.js +486 -0
- package/dist/migrate/export/clerk.js.map +1 -0
- package/dist/migrate/export/descope.d.ts +35 -0
- package/dist/migrate/export/descope.js +208 -0
- package/dist/migrate/export/descope.js.map +1 -0
- package/dist/migrate/export/fake-vendor.test-helper.d.ts +27 -0
- package/dist/migrate/export/fake-vendor.test-helper.js +41 -0
- package/dist/migrate/export/fake-vendor.test-helper.js.map +1 -0
- package/dist/migrate/export/frameworks/app-users.d.ts +42 -0
- package/dist/migrate/export/frameworks/app-users.js +161 -0
- package/dist/migrate/export/frameworks/app-users.js.map +1 -0
- package/dist/migrate/export/frameworks/authjs.d.ts +41 -0
- package/dist/migrate/export/frameworks/authjs.js +146 -0
- package/dist/migrate/export/frameworks/authjs.js.map +1 -0
- package/dist/migrate/export/frameworks/devise.d.ts +39 -0
- package/dist/migrate/export/frameworks/devise.js +218 -0
- package/dist/migrate/export/frameworks/devise.js.map +1 -0
- package/dist/migrate/export/frameworks/django.d.ts +45 -0
- package/dist/migrate/export/frameworks/django.js +315 -0
- package/dist/migrate/export/frameworks/django.js.map +1 -0
- package/dist/migrate/export/frameworks/lucia.d.ts +42 -0
- package/dist/migrate/export/frameworks/lucia.js +263 -0
- package/dist/migrate/export/frameworks/lucia.js.map +1 -0
- package/dist/migrate/export/frameworks/tables.d.ts +90 -0
- package/dist/migrate/export/frameworks/tables.js +325 -0
- package/dist/migrate/export/frameworks/tables.js.map +1 -0
- package/dist/migrate/export/frameworks/webauthn.d.ts +25 -0
- package/dist/migrate/export/frameworks/webauthn.js +130 -0
- package/dist/migrate/export/frameworks/webauthn.js.map +1 -0
- package/dist/migrate/export/kinde-descope-config.d.ts +38 -0
- package/dist/migrate/export/kinde-descope-config.js +172 -0
- package/dist/migrate/export/kinde-descope-config.js.map +1 -0
- package/dist/migrate/export/kinde.d.ts +57 -0
- package/dist/migrate/export/kinde.js +333 -0
- package/dist/migrate/export/kinde.js.map +1 -0
- package/dist/migrate/export/okta-config.d.ts +30 -0
- package/dist/migrate/export/okta-config.js +205 -0
- package/dist/migrate/export/okta-config.js.map +1 -0
- package/dist/migrate/export/okta.d.ts +43 -0
- package/dist/migrate/export/okta.js +337 -0
- package/dist/migrate/export/okta.js.map +1 -0
- package/dist/migrate/export/plugin-kit.d.ts +25 -0
- package/dist/migrate/export/plugin-kit.js +82 -0
- package/dist/migrate/export/plugin-kit.js.map +1 -0
- package/dist/migrate/export/record-set.d.ts +44 -0
- package/dist/migrate/export/record-set.js +98 -0
- package/dist/migrate/export/record-set.js.map +1 -0
- package/dist/migrate/export/translate-kit.d.ts +46 -0
- package/dist/migrate/export/translate-kit.js +148 -0
- package/dist/migrate/export/translate-kit.js.map +1 -0
- package/dist/migrate/export/vendor-http.d.ts +46 -0
- package/dist/migrate/export/vendor-http.js +138 -0
- package/dist/migrate/export/vendor-http.js.map +1 -0
- package/dist/migrate/export/zip.d.ts +8 -0
- package/dist/migrate/export/zip.js +66 -0
- package/dist/migrate/export/zip.js.map +1 -0
- package/dist/migrate/framework/builtins.d.ts +2 -0
- package/dist/migrate/framework/builtins.js +23 -0
- package/dist/migrate/framework/builtins.js.map +1 -0
- package/dist/migrate/framework/commands.d.ts +20 -0
- package/dist/migrate/framework/commands.js +58 -0
- package/dist/migrate/framework/commands.js.map +1 -0
- package/dist/migrate/framework/custom.d.ts +12 -0
- package/dist/migrate/framework/custom.js +96 -0
- package/dist/migrate/framework/custom.js.map +1 -0
- package/dist/migrate/framework/orchestrator.d.ts +113 -0
- package/dist/migrate/framework/orchestrator.js +531 -0
- package/dist/migrate/framework/orchestrator.js.map +1 -0
- package/dist/migrate/framework/plugin.d.ts +165 -0
- package/dist/migrate/framework/plugin.js +3 -0
- package/dist/migrate/framework/plugin.js.map +1 -0
- package/dist/migrate/framework/registry.d.ts +11 -0
- package/dist/migrate/framework/registry.js +44 -0
- package/dist/migrate/framework/registry.js.map +1 -0
- package/dist/migrate/framework/report.d.ts +17 -0
- package/dist/migrate/framework/report.js +103 -0
- package/dist/migrate/framework/report.js.map +1 -0
- package/dist/migrate/frameworks/plugins.d.ts +29 -0
- package/dist/migrate/frameworks/plugins.js +402 -0
- package/dist/migrate/frameworks/plugins.js.map +1 -0
- package/dist/migrate/frameworks/report.d.ts +92 -0
- package/dist/migrate/frameworks/report.js +207 -0
- package/dist/migrate/frameworks/report.js.map +1 -0
- package/dist/migrate/frameworks/translate-js.d.ts +5 -0
- package/dist/migrate/frameworks/translate-js.js +503 -0
- package/dist/migrate/frameworks/translate-js.js.map +1 -0
- package/dist/migrate/frameworks/translate-rails-django.d.ts +3 -0
- package/dist/migrate/frameworks/translate-rails-django.js +1391 -0
- package/dist/migrate/frameworks/translate-rails-django.js.map +1 -0
- package/dist/migrate/kinde/plugin.d.ts +2 -0
- package/dist/migrate/kinde/plugin.js +105 -0
- package/dist/migrate/kinde/plugin.js.map +1 -0
- package/dist/migrate/okta/plugin.d.ts +2 -0
- package/dist/migrate/okta/plugin.js +105 -0
- package/dist/migrate/okta/plugin.js.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AUTHJS_TABLES = void 0;
|
|
4
|
+
exports.exportAuthJs = exportAuthJs;
|
|
5
|
+
const tables_1 = require("./tables");
|
|
6
|
+
exports.AUTHJS_TABLES = {
|
|
7
|
+
user: ['User', 'users', 'user', 'next_auth.users'],
|
|
8
|
+
account: ['Account', 'accounts', 'account', 'next_auth.accounts'],
|
|
9
|
+
session: ['Session', 'sessions', 'session', 'next_auth.sessions'],
|
|
10
|
+
authenticator: ['Authenticator', 'authenticators', 'authenticator'],
|
|
11
|
+
};
|
|
12
|
+
const PASSWORD_COLUMNS = ['password', 'passwordHash', 'password_hash', 'hashedPassword', 'hashed_password', 'encryptedPassword'];
|
|
13
|
+
function exportAuthJs(tables, options = {}) {
|
|
14
|
+
const out = new tables_1.FrameworkExport();
|
|
15
|
+
const now = (options.now ?? new Date()).getTime();
|
|
16
|
+
const users = tables.find(...exports.AUTHJS_TABLES.user);
|
|
17
|
+
if (!users) {
|
|
18
|
+
out.warn(`No users table found (looked for ${exports.AUTHJS_TABLES.user.join(', ')}).`);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
const userIds = new Set();
|
|
22
|
+
let passwordsCarried = 0;
|
|
23
|
+
let passwordsUnknown = 0;
|
|
24
|
+
for (const row of users.rows) {
|
|
25
|
+
const id = (0, tables_1.text)((0, tables_1.col)(row, 'id', '_id'));
|
|
26
|
+
const email = (0, tables_1.text)((0, tables_1.col)(row, 'email'));
|
|
27
|
+
if (!id) {
|
|
28
|
+
out.omit('user', 'row without an id');
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const verifiedAt = (0, tables_1.col)(row, 'emailVerified', 'email_verified');
|
|
32
|
+
const name = (0, tables_1.text)((0, tables_1.col)(row, 'name'));
|
|
33
|
+
const digest = (0, tables_1.text)(options.passwordColumn ? (0, tables_1.col)(row, options.passwordColumn) : (0, tables_1.col)(row, ...PASSWORD_COLUMNS));
|
|
34
|
+
let password;
|
|
35
|
+
if (digest) {
|
|
36
|
+
const known = (0, tables_1.selfDescribingHash)(digest);
|
|
37
|
+
if (known) {
|
|
38
|
+
password = known;
|
|
39
|
+
passwordsCarried++;
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
passwordsUnknown++;
|
|
43
|
+
}
|
|
44
|
+
if (!email) {
|
|
45
|
+
out.omit('user', 'no email address (Auth.js users without one cannot sign in to Atlas by email)');
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
userIds.add(id);
|
|
49
|
+
out.add('user', {
|
|
50
|
+
id,
|
|
51
|
+
emails: [{ email: email.toLowerCase(), verified: Boolean(verifiedAt) && verifiedAt !== false && verifiedAt !== 'false', primary: true }],
|
|
52
|
+
name,
|
|
53
|
+
...(0, tables_1.splitName)(name),
|
|
54
|
+
image_url: (0, tables_1.text)((0, tables_1.col)(row, 'image')),
|
|
55
|
+
password,
|
|
56
|
+
created_at: (0, tables_1.time)((0, tables_1.col)(row, 'createdAt', 'created_at')),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (passwordsCarried)
|
|
60
|
+
out.warn(`${passwordsCarried} Credentials-provider password hash(es) carried over; they verify on first sign-in and are upgraded to Argon2id.`);
|
|
61
|
+
if (passwordsUnknown) {
|
|
62
|
+
out.warn(`${passwordsUnknown} password value(s) are not a hash format Atlas verifies (bcrypt, Argon2, scrypt salt:key, PBKDF2): those users reset their password, or use lazy migration against your old /api/auth/callback/credentials.`);
|
|
63
|
+
}
|
|
64
|
+
const providers = new Set();
|
|
65
|
+
for (const row of tables.rows(...exports.AUTHJS_TABLES.account)) {
|
|
66
|
+
const userId = (0, tables_1.text)((0, tables_1.col)(row, 'userId', 'user_id'));
|
|
67
|
+
const provider = (0, tables_1.text)((0, tables_1.col)(row, 'provider'));
|
|
68
|
+
const subject = (0, tables_1.text)((0, tables_1.col)(row, 'providerAccountId', 'provider_account_id'));
|
|
69
|
+
const type = (0, tables_1.text)((0, tables_1.col)(row, 'type')) ?? 'oauth';
|
|
70
|
+
if (!userId || !provider || !subject || !userIds.has(userId)) {
|
|
71
|
+
out.omit('identity', 'account row without a known user, provider or providerAccountId');
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (type === 'email' || type === 'credentials' || type === 'webauthn')
|
|
75
|
+
continue;
|
|
76
|
+
providers.add(provider);
|
|
77
|
+
const expiresAt = (0, tables_1.col)(row, 'expires_at', 'expiresAt');
|
|
78
|
+
out.add('identity', {
|
|
79
|
+
user_id: userId,
|
|
80
|
+
provider,
|
|
81
|
+
provider_user_id: subject,
|
|
82
|
+
kind: type === 'oidc' ? 'oidc' : 'oauth',
|
|
83
|
+
...(options.oauthTokens
|
|
84
|
+
? {
|
|
85
|
+
access_token: (0, tables_1.text)((0, tables_1.col)(row, 'access_token', 'accessToken')),
|
|
86
|
+
refresh_token: (0, tables_1.text)((0, tables_1.col)(row, 'refresh_token', 'refreshToken')),
|
|
87
|
+
// Auth.js stores expires_at in epoch SECONDS.
|
|
88
|
+
expires_at: expiresAt !== undefined ? (0, tables_1.time)(Number(expiresAt) * (Number(expiresAt) < 1e11 ? 1000 : 1)) : undefined,
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
91
|
+
scopes: (0, tables_1.text)((0, tables_1.col)(row, 'scope'))?.split(/[\s,]+/).filter(Boolean),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
out.manifest.providers = [...providers].sort();
|
|
95
|
+
const authenticators = tables.rows(...exports.AUTHJS_TABLES.authenticator);
|
|
96
|
+
if (authenticators.length && !options.rpId) {
|
|
97
|
+
out.omit('passkey', 'no RP id given (--rp-id): a passkey only works on the domain it was registered for', authenticators.length);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
for (const row of authenticators) {
|
|
101
|
+
const userId = (0, tables_1.text)((0, tables_1.col)(row, 'userId', 'user_id'));
|
|
102
|
+
const credentialId = (0, tables_1.base64url)((0, tables_1.col)(row, 'credentialID', 'credential_id', 'credentialId'));
|
|
103
|
+
const publicKey = (0, tables_1.base64url)((0, tables_1.col)(row, 'credentialPublicKey', 'credential_public_key'));
|
|
104
|
+
if (!userId || !userIds.has(userId) || !credentialId || !publicKey) {
|
|
105
|
+
out.omit('passkey', 'authenticator row without a known user, credential id or public key');
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const transports = (0, tables_1.text)((0, tables_1.col)(row, 'transports'));
|
|
109
|
+
out.add('passkey', {
|
|
110
|
+
user_id: userId,
|
|
111
|
+
credential_id: credentialId,
|
|
112
|
+
public_key: publicKey,
|
|
113
|
+
sign_count: Number((0, tables_1.col)(row, 'counter') ?? 0) || 0,
|
|
114
|
+
rp_id: options.rpId,
|
|
115
|
+
transports: transports ? transports.split(/[\s,]+/).filter(Boolean) : undefined,
|
|
116
|
+
backed_up: (0, tables_1.bool)((0, tables_1.col)(row, 'credentialBackedUp', 'credential_backed_up')),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (options.rpId)
|
|
120
|
+
out.manifest.rpId = options.rpId;
|
|
121
|
+
}
|
|
122
|
+
if (options.sessions !== false) {
|
|
123
|
+
for (const row of tables.rows(...exports.AUTHJS_TABLES.session)) {
|
|
124
|
+
const token = (0, tables_1.text)((0, tables_1.col)(row, 'sessionToken', 'session_token'));
|
|
125
|
+
const userId = (0, tables_1.text)((0, tables_1.col)(row, 'userId', 'user_id'));
|
|
126
|
+
const expires = (0, tables_1.timeMs)((0, tables_1.col)(row, 'expires', 'expires_at', 'expiresAt'));
|
|
127
|
+
if (!token || !userId || !userIds.has(userId) || expires === undefined) {
|
|
128
|
+
out.omit('session', 'session row without a token, a known user or an expiry');
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (expires <= now) {
|
|
132
|
+
out.omit('session', 'expired');
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
out.add('session', {
|
|
136
|
+
id: (0, tables_1.text)((0, tables_1.col)(row, 'id', '_id')) ?? (0, tables_1.sha256Hex)(token).slice(0, 32),
|
|
137
|
+
user_id: userId,
|
|
138
|
+
token_hash: (0, tables_1.sha256Hex)(token),
|
|
139
|
+
expires_at: new Date(expires).toISOString(),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
out.manifest.settings = { session_cookie: ['authjs.session-token', 'next-auth.session-token'] };
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=authjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authjs.js","sourceRoot":"","sources":["../../../../src/migrate/export/frameworks/authjs.ts"],"names":[],"mappings":";;;AAyDA,oCAoIC;AA7LD,qCAYkB;AAoCL,QAAA,aAAa,GAAG;IAC3B,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC;IAClD,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,CAAC;IACjE,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,CAAC;IACjE,aAAa,EAAE,CAAC,eAAe,EAAE,gBAAgB,EAAE,eAAe,CAAC;CAC3D,CAAC;AAEX,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;AAEjI,SAAgB,YAAY,CAAC,MAAc,EAAE,UAA+B,EAAE;IAC5E,MAAM,GAAG,GAAG,IAAI,wBAAe,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAa,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,GAAG,CAAC,IAAI,CAAC,oCAAoC,qBAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChF,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,EAAE,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;YACtC,SAAS;QACX,CAAC;QACD,MAAM,UAAU,GAAG,IAAA,YAAG,EAAC,GAAG,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAA,aAAI,EAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,IAAA,YAAG,EAAC,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAA,YAAG,EAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC;QAC/G,IAAI,QAAyD,CAAC;QAC9D,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAA,2BAAkB,EAAC,MAAM,CAAC,CAAC;YACzC,IAAI,KAAK,EAAE,CAAC;gBACV,QAAQ,GAAG,KAAK,CAAC;gBACjB,gBAAgB,EAAE,CAAC;YACrB,CAAC;;gBAAM,gBAAgB,EAAE,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,+EAA+E,CAAC,CAAC;YAClG,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE;YACd,EAAE;YACF,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,KAAK,KAAK,IAAI,UAAU,KAAK,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACxI,IAAI;YACJ,GAAG,IAAA,kBAAS,EAAC,IAAI,CAAC;YAClB,SAAS,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,QAAQ;YACR,UAAU,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IACD,IAAI,gBAAgB;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,gBAAgB,kHAAkH,CAAC,CAAC;IACtK,IAAI,gBAAgB,EAAE,CAAC;QACrB,GAAG,CAAC,IAAI,CACN,GAAG,gBAAgB,6MAA6M,CACjO,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,MAAM,MAAM,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,mBAAmB,EAAE,qBAAqB,CAAC,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC;QAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,iEAAiE,CAAC,CAAC;YACxF,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,UAAU;YAAE,SAAS;QAChF,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,MAAM,SAAS,GAAG,IAAA,YAAG,EAAC,GAAG,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;QACtD,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE;YAClB,OAAO,EAAE,MAAM;YACf,QAAQ;YACR,gBAAgB,EAAE,OAAO;YACzB,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;YACxC,GAAG,CAAC,OAAO,CAAC,WAAW;gBACrB,CAAC,CAAC;oBACE,YAAY,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,cAAc,EAAE,aAAa,CAAC,CAAC;oBAC3D,aAAa,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC;oBAC9D,8CAA8C;oBAC9C,UAAU,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAA,aAAI,EAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;iBAClH;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;SACjE,CAAC,CAAC;IACL,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,SAAS,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/C,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAa,CAAC,aAAa,CAAC,CAAC;IACnE,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC3C,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,oFAAoF,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACnI,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;YACnD,MAAM,YAAY,GAAG,IAAA,kBAAS,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC;YAC1F,MAAM,SAAS,GAAG,IAAA,kBAAS,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,qBAAqB,EAAE,uBAAuB,CAAC,CAAC,CAAC;YACtF,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnE,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,qEAAqE,CAAC,CAAC;gBAC3F,SAAS;YACX,CAAC;YACD,MAAM,UAAU,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;YAChD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE;gBACjB,OAAO,EAAE,MAAM;gBACf,aAAa,EAAE,YAAY;gBAC3B,UAAU,EAAE,SAAS;gBACrB,UAAU,EAAE,MAAM,CAAC,IAAA,YAAG,EAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;gBACjD,KAAK,EAAE,OAAO,CAAC,IAAK;gBACpB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC/E,SAAS,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,oBAAoB,EAAE,sBAAsB,CAAC,CAAC;aACxE,CAAC,CAAC;QACL,CAAC;QACD,IAAI,OAAO,CAAC,IAAI;YAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACrD,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,qBAAa,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,MAAM,KAAK,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,IAAA,eAAM,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;YACvE,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBACvE,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,wDAAwD,CAAC,CAAC;gBAC9E,SAAS;YACX,CAAC;YACD,IAAI,OAAO,IAAI,GAAG,EAAE,CAAC;gBACnB,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;gBAC/B,SAAS;YACX,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE;gBACjB,EAAE,EAAE,IAAA,aAAI,EAAC,IAAA,YAAG,EAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,IAAA,kBAAS,EAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAChE,OAAO,EAAE,MAAM;gBACf,UAAU,EAAE,IAAA,kBAAS,EAAC,KAAK,CAAC;gBAC5B,UAAU,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE;aAC5C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,QAAQ,GAAG,EAAE,cAAc,EAAE,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,EAAE,CAAC;IAChG,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FrameworkExport, type Tables } from './tables';
|
|
2
|
+
/**
|
|
3
|
+
* Devise (Rails) → canonical records, from the rows the Rails runner script
|
|
4
|
+
* ({@link DEVISE_EXPORT_SCRIPT}) streams out of the app with its own models —
|
|
5
|
+
* so attributes Rails encrypts (devise-two-factor's `otp_secret` under Active
|
|
6
|
+
* Record encryption or attr_encrypted) arrive decrypted, and the Devise config
|
|
7
|
+
* (pepper, stretches, the modules each model enables) comes along.
|
|
8
|
+
*
|
|
9
|
+
* users → user: email + confirmable (`confirmed_at`), the bcrypt
|
|
10
|
+
* `encrypted_password` (with `config.pepper` when set),
|
|
11
|
+
* lockable (`locked_at`), trackable timestamps
|
|
12
|
+
* omniauth → identity: `provider`/`uid` on users, or an
|
|
13
|
+
* `identities` / `authentications` table
|
|
14
|
+
* devise-two-factor → mfa_factor totp (`otp_secret`) + backup codes (their
|
|
15
|
+
* Devise-bcrypt digests, verified like passwords)
|
|
16
|
+
* rolify / `admin` → app roles
|
|
17
|
+
* activerecord-session_store → session (the cookie store has no rows: the
|
|
18
|
+
* bridge decrypts the `_<app>_session` cookie instead)
|
|
19
|
+
*
|
|
20
|
+
* devise-encryptable digests (sha1 / sha512 / authlogic / restful_authentication)
|
|
21
|
+
* are not carried: those users reset their password or migrate lazily.
|
|
22
|
+
*/
|
|
23
|
+
export interface DeviseExportOptions {
|
|
24
|
+
/** The Warden scope / model table (default `users`). */
|
|
25
|
+
usersTable?: string;
|
|
26
|
+
/** Treat emails as verified when the model does not use :confirmable. Default false. */
|
|
27
|
+
trustUnconfirmable?: boolean;
|
|
28
|
+
sessions?: boolean;
|
|
29
|
+
now?: Date;
|
|
30
|
+
}
|
|
31
|
+
export declare function exportDevise(tables: Tables, options?: DeviseExportOptions): FrameworkExport;
|
|
32
|
+
/**
|
|
33
|
+
* The Rails runner script that streams a Devise app's rows as NDJSON
|
|
34
|
+
* (`{"table": …, "row": {…}}`), straight into `atlas migrate export devise
|
|
35
|
+
* --file -`, so decrypted secrets only ever travel through the pipe:
|
|
36
|
+
*
|
|
37
|
+
* bin/rails runner atlas_export.rb | npx atlas migrate devise export --file -
|
|
38
|
+
*/
|
|
39
|
+
export declare const DEVISE_EXPORT_SCRIPT = "# Atlas export for a Devise app. Run: bin/rails runner atlas_export.rb [ModelName]\nrequire \"json\"\n\nmodel = (ARGV[0] || \"User\").constantize\nemit = ->(table, row) { $stdout.puts({ table: table, row: row }.to_json) }\n\nsession_key = Rails.application.config.session_options[:key] rescue nil\nemit.call(\"devise_config\", {\n devise_version: Devise::VERSION,\n pepper: Devise.pepper,\n stretches: Devise.stretches,\n modules: model.devise_modules.join(\",\"),\n session_store: Rails.application.config.session_store.to_s.demodulize.underscore,\n session_key: session_key,\n session_ttl_seconds: (Rails.application.config.session_options[:expire_after] || 1209600).to_i,\n})\n\nattrs = model.column_names - %w[encrypted_otp_secret encrypted_otp_secret_iv encrypted_otp_secret_salt]\nmodel.find_each(batch_size: 1000) do |u|\n row = attrs.index_with { |c| u.read_attribute(c) }\n # devise-two-factor: read the secret through the model so Rails decrypts it.\n row[\"otp_secret\"] = u.otp_secret if u.respond_to?(:otp_secret)\n emit.call(model.table_name, row)\nend\n\n%w[Identity Authentication].each do |name|\n next unless Object.const_defined?(name)\n klass = name.constantize\n next unless klass < ActiveRecord::Base\n klass.find_each { |i| emit.call(klass.table_name, i.attributes.slice(\"user_id\", \"provider\", \"uid\", \"email\")) }\nend\n\nif Object.const_defined?(:Role) && ActiveRecord::Base.connection.table_exists?(\"users_roles\")\n Role.find_each { |r| emit.call(\"roles\", r.attributes.slice(\"id\", \"name\", \"resource_type\", \"resource_id\")) }\n ActiveRecord::Base.connection.select_all(\"SELECT user_id, role_id FROM users_roles\").each { |r| emit.call(\"users_roles\", r) }\nend\n\nif defined?(ActiveRecord::SessionStore::Session)\n scope = \"warden.user.#{Devise::Mapping.find_scope!(model)}.key\"\n ActiveRecord::SessionStore::Session.find_each do |s|\n entry = s.data[scope] rescue nil\n next unless entry\n emit.call(\"sessions\", { session_id: s.session_id, user_id: Array(entry[0]).first, updated_at: s.updated_at })\n end\nend\n";
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEVISE_EXPORT_SCRIPT = void 0;
|
|
4
|
+
exports.exportDevise = exportDevise;
|
|
5
|
+
const tables_1 = require("./tables");
|
|
6
|
+
function exportDevise(tables, options = {}) {
|
|
7
|
+
const out = new tables_1.FrameworkExport();
|
|
8
|
+
const now = (options.now ?? new Date()).getTime();
|
|
9
|
+
const config = tables.rows('devise_config')[0] ?? {};
|
|
10
|
+
const pepper = (0, tables_1.text)(config.pepper);
|
|
11
|
+
const modules = new Set(String(config.modules ?? '').split(/[\s,]+/).filter(Boolean));
|
|
12
|
+
const confirmable = modules.size ? modules.has('confirmable') : undefined;
|
|
13
|
+
out.manifest.sourceVersion = (0, tables_1.text)(config.devise_version) ? `devise ${(0, tables_1.text)(config.devise_version)}` : 'devise';
|
|
14
|
+
if (pepper)
|
|
15
|
+
out.manifest.settings = { pepper_set: true };
|
|
16
|
+
const users = tables.find(...(options.usersTable ? [options.usersTable] : ['users', 'user', 'accounts', 'admins']));
|
|
17
|
+
if (!users) {
|
|
18
|
+
out.warn('No users rows found (run the Rails export script with your Devise model).');
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
const ids = new Set();
|
|
22
|
+
const providers = new Set();
|
|
23
|
+
let encryptable = 0;
|
|
24
|
+
let mfa = 0;
|
|
25
|
+
for (const row of users.rows) {
|
|
26
|
+
const id = (0, tables_1.text)((0, tables_1.col)(row, 'id'));
|
|
27
|
+
const email = (0, tables_1.text)((0, tables_1.col)(row, 'email'))?.toLowerCase();
|
|
28
|
+
if (!id || !email) {
|
|
29
|
+
out.omit('user', 'row without an id or email');
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const encrypted = (0, tables_1.text)((0, tables_1.col)(row, 'encrypted_password'));
|
|
33
|
+
let password;
|
|
34
|
+
if (encrypted && /^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}$/.test(encrypted)) {
|
|
35
|
+
password = pepper ? { algorithm: 'bcrypt_pepper', hash: encrypted, params: { pepper } } : { algorithm: 'bcrypt', hash: encrypted };
|
|
36
|
+
}
|
|
37
|
+
else if (encrypted)
|
|
38
|
+
encryptable++;
|
|
39
|
+
const confirmedAt = (0, tables_1.col)(row, 'confirmed_at');
|
|
40
|
+
const verified = confirmable === false || (confirmable === undefined && !('confirmed_at' in row))
|
|
41
|
+
? options.trustUnconfirmable === true
|
|
42
|
+
: Boolean(confirmedAt);
|
|
43
|
+
const name = (0, tables_1.text)((0, tables_1.col)(row, 'name', 'full_name'));
|
|
44
|
+
const roles = new Set();
|
|
45
|
+
if ((0, tables_1.bool)((0, tables_1.col)(row, 'admin', 'is_admin')))
|
|
46
|
+
roles.add('admin');
|
|
47
|
+
const role = (0, tables_1.text)((0, tables_1.col)(row, 'role'));
|
|
48
|
+
if (role)
|
|
49
|
+
roles.add(role);
|
|
50
|
+
ids.add(id);
|
|
51
|
+
out.add('user', {
|
|
52
|
+
id,
|
|
53
|
+
emails: [{ email, verified, primary: true }],
|
|
54
|
+
username: (0, tables_1.text)((0, tables_1.col)(row, 'username')),
|
|
55
|
+
name,
|
|
56
|
+
first_name: (0, tables_1.text)((0, tables_1.col)(row, 'first_name')) ?? (0, tables_1.splitName)(name).first_name,
|
|
57
|
+
last_name: (0, tables_1.text)((0, tables_1.col)(row, 'last_name')) ?? (0, tables_1.splitName)(name).last_name,
|
|
58
|
+
password,
|
|
59
|
+
locked: Boolean((0, tables_1.col)(row, 'locked_at')) || undefined,
|
|
60
|
+
roles: roles.size ? [...roles] : undefined,
|
|
61
|
+
created_at: (0, tables_1.time)((0, tables_1.col)(row, 'created_at')),
|
|
62
|
+
last_sign_in_at: (0, tables_1.time)((0, tables_1.col)(row, 'current_sign_in_at', 'last_sign_in_at')),
|
|
63
|
+
private_metadata: (0, tables_1.text)((0, tables_1.col)(row, 'unconfirmed_email')) ? { unconfirmed_email: (0, tables_1.text)((0, tables_1.col)(row, 'unconfirmed_email')) } : undefined,
|
|
64
|
+
});
|
|
65
|
+
// omniauth's single-provider convention: provider + uid columns on the user.
|
|
66
|
+
const provider = (0, tables_1.text)((0, tables_1.col)(row, 'provider'));
|
|
67
|
+
const uid = (0, tables_1.text)((0, tables_1.col)(row, 'uid'));
|
|
68
|
+
if (provider && uid && provider !== 'email') {
|
|
69
|
+
providers.add(provider);
|
|
70
|
+
out.add('identity', { user_id: id, provider, provider_user_id: uid, kind: 'oauth' });
|
|
71
|
+
}
|
|
72
|
+
// devise-two-factor.
|
|
73
|
+
const secret = (0, tables_1.text)((0, tables_1.col)(row, 'otp_secret'));
|
|
74
|
+
if (secret && ((0, tables_1.bool)((0, tables_1.col)(row, 'otp_required_for_login')) || !('otp_required_for_login' in row))) {
|
|
75
|
+
mfa++;
|
|
76
|
+
out.add('mfa_factor', { user_id: id, type: 'totp', secret_base32: secret.toUpperCase(), digits: 6, period: 30, algorithm: 'SHA1', verified: true });
|
|
77
|
+
const codes = backupCodes((0, tables_1.col)(row, 'otp_backup_codes'));
|
|
78
|
+
if (codes.length) {
|
|
79
|
+
out.add('mfa_factor', {
|
|
80
|
+
user_id: id,
|
|
81
|
+
type: 'backup_codes',
|
|
82
|
+
backup_code_hashes: codes.map((hash) => pepper ? { algorithm: 'bcrypt_pepper', hash, params: { pepper } } : { algorithm: 'bcrypt', hash }),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (encryptable)
|
|
88
|
+
out.warn(`${encryptable} encrypted_password value(s) are not bcrypt (devise-encryptable): those users reset their password or migrate lazily.`);
|
|
89
|
+
if (mfa)
|
|
90
|
+
out.warn(`${mfa} devise-two-factor TOTP secret(s) carried over; authenticator apps keep working.`);
|
|
91
|
+
for (const row of tables.rows('identities', 'authentications', 'user_identities', 'omniauth_identities')) {
|
|
92
|
+
const userId = (0, tables_1.text)((0, tables_1.col)(row, 'user_id'));
|
|
93
|
+
const provider = (0, tables_1.text)((0, tables_1.col)(row, 'provider'));
|
|
94
|
+
const uid = (0, tables_1.text)((0, tables_1.col)(row, 'uid', 'provider_uid'));
|
|
95
|
+
if (!userId || !provider || !uid || !ids.has(userId)) {
|
|
96
|
+
out.omit('identity', 'identity row without a known user, provider or uid');
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
providers.add(provider);
|
|
100
|
+
out.add('identity', { user_id: userId, provider, provider_user_id: uid, kind: 'oauth', email: (0, tables_1.text)((0, tables_1.col)(row, 'email')) });
|
|
101
|
+
}
|
|
102
|
+
out.manifest.providers = [...providers].sort();
|
|
103
|
+
// rolify: roles(id, name, resource_type, resource_id) + users_roles(user_id, role_id). Global roles only.
|
|
104
|
+
const roleNames = new Map();
|
|
105
|
+
for (const r of tables.rows('roles')) {
|
|
106
|
+
const rid = (0, tables_1.text)((0, tables_1.col)(r, 'id'));
|
|
107
|
+
const name = (0, tables_1.text)((0, tables_1.col)(r, 'name'));
|
|
108
|
+
if (rid && name && !(0, tables_1.text)((0, tables_1.col)(r, 'resource_type')))
|
|
109
|
+
roleNames.set(rid, name);
|
|
110
|
+
}
|
|
111
|
+
if (roleNames.size) {
|
|
112
|
+
for (const name of new Set(roleNames.values()))
|
|
113
|
+
out.add('role', { key: name, name, scope: 'app' });
|
|
114
|
+
const extra = new Map();
|
|
115
|
+
for (const ur of tables.rows('users_roles')) {
|
|
116
|
+
const uid = (0, tables_1.text)((0, tables_1.col)(ur, 'user_id'));
|
|
117
|
+
const name = roleNames.get((0, tables_1.text)((0, tables_1.col)(ur, 'role_id')) ?? '');
|
|
118
|
+
if (!uid || !name || !ids.has(uid))
|
|
119
|
+
continue;
|
|
120
|
+
const set = extra.get(uid) ?? new Set();
|
|
121
|
+
set.add(name);
|
|
122
|
+
extra.set(uid, set);
|
|
123
|
+
}
|
|
124
|
+
for (const user of out.list('user')) {
|
|
125
|
+
const more = extra.get(user.id);
|
|
126
|
+
if (more)
|
|
127
|
+
user.roles = [...new Set([...(user.roles ?? []), ...more])];
|
|
128
|
+
}
|
|
129
|
+
const scoped = tables.rows('roles').length - roleNames.size;
|
|
130
|
+
if (scoped > 0)
|
|
131
|
+
out.warn(`${scoped} rolify role(s) scoped to a resource were not carried: model them as Atlas organization roles or permissions.`);
|
|
132
|
+
}
|
|
133
|
+
// activerecord-session_store rows, as the export script decoded them.
|
|
134
|
+
if (options.sessions !== false) {
|
|
135
|
+
for (const row of tables.rows('sessions')) {
|
|
136
|
+
const raw = (0, tables_1.text)((0, tables_1.col)(row, 'session_id'));
|
|
137
|
+
const userId = (0, tables_1.text)((0, tables_1.col)(row, 'user_id'));
|
|
138
|
+
const updated = (0, tables_1.timeMs)((0, tables_1.col)(row, 'expires_at')) ?? ((0, tables_1.timeMs)((0, tables_1.col)(row, 'updated_at')) !== undefined ? (0, tables_1.timeMs)((0, tables_1.col)(row, 'updated_at')) + Number(config.session_ttl_seconds ?? 1209600) * 1000 : undefined);
|
|
139
|
+
if (!raw || !userId || !ids.has(userId) || updated === undefined) {
|
|
140
|
+
out.omit('session', 'session row without an id, a known user or an expiry');
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (updated <= now) {
|
|
144
|
+
out.omit('session', 'expired');
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
// Rack stores the PRIVATE id `2::<sha256 hex of the cookie's public id>`.
|
|
148
|
+
const tokenHash = /^2::[0-9a-f]{64}$/.test(raw) ? raw.slice(3) : (0, tables_1.sha256Hex)(raw);
|
|
149
|
+
out.add('session', { id: tokenHash.slice(0, 32), user_id: userId, token_hash: tokenHash, expires_at: new Date(updated).toISOString() });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
out.manifest.settings = {
|
|
153
|
+
...(out.manifest.settings ?? {}),
|
|
154
|
+
session_store: (0, tables_1.text)(config.session_store) ?? 'cookie_store',
|
|
155
|
+
session_cookie: (0, tables_1.text)(config.session_key) ?? null,
|
|
156
|
+
};
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
function backupCodes(v) {
|
|
160
|
+
const list = Array.isArray(v) ? v : typeof v === 'string' ? (v.trim().startsWith('[') ? JSON.parse(v) : v.replace(/^\{|\}$/g, '').split(',')) : [];
|
|
161
|
+
return list
|
|
162
|
+
.map((c) => String(c).replace(/^"|"$/g, '').trim())
|
|
163
|
+
.filter((c) => /^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}$/.test(c));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The Rails runner script that streams a Devise app's rows as NDJSON
|
|
167
|
+
* (`{"table": …, "row": {…}}`), straight into `atlas migrate export devise
|
|
168
|
+
* --file -`, so decrypted secrets only ever travel through the pipe:
|
|
169
|
+
*
|
|
170
|
+
* bin/rails runner atlas_export.rb | npx atlas migrate devise export --file -
|
|
171
|
+
*/
|
|
172
|
+
exports.DEVISE_EXPORT_SCRIPT = `# Atlas export for a Devise app. Run: bin/rails runner atlas_export.rb [ModelName]
|
|
173
|
+
require "json"
|
|
174
|
+
|
|
175
|
+
model = (ARGV[0] || "User").constantize
|
|
176
|
+
emit = ->(table, row) { $stdout.puts({ table: table, row: row }.to_json) }
|
|
177
|
+
|
|
178
|
+
session_key = Rails.application.config.session_options[:key] rescue nil
|
|
179
|
+
emit.call("devise_config", {
|
|
180
|
+
devise_version: Devise::VERSION,
|
|
181
|
+
pepper: Devise.pepper,
|
|
182
|
+
stretches: Devise.stretches,
|
|
183
|
+
modules: model.devise_modules.join(","),
|
|
184
|
+
session_store: Rails.application.config.session_store.to_s.demodulize.underscore,
|
|
185
|
+
session_key: session_key,
|
|
186
|
+
session_ttl_seconds: (Rails.application.config.session_options[:expire_after] || 1209600).to_i,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
attrs = model.column_names - %w[encrypted_otp_secret encrypted_otp_secret_iv encrypted_otp_secret_salt]
|
|
190
|
+
model.find_each(batch_size: 1000) do |u|
|
|
191
|
+
row = attrs.index_with { |c| u.read_attribute(c) }
|
|
192
|
+
# devise-two-factor: read the secret through the model so Rails decrypts it.
|
|
193
|
+
row["otp_secret"] = u.otp_secret if u.respond_to?(:otp_secret)
|
|
194
|
+
emit.call(model.table_name, row)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
%w[Identity Authentication].each do |name|
|
|
198
|
+
next unless Object.const_defined?(name)
|
|
199
|
+
klass = name.constantize
|
|
200
|
+
next unless klass < ActiveRecord::Base
|
|
201
|
+
klass.find_each { |i| emit.call(klass.table_name, i.attributes.slice("user_id", "provider", "uid", "email")) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
if Object.const_defined?(:Role) && ActiveRecord::Base.connection.table_exists?("users_roles")
|
|
205
|
+
Role.find_each { |r| emit.call("roles", r.attributes.slice("id", "name", "resource_type", "resource_id")) }
|
|
206
|
+
ActiveRecord::Base.connection.select_all("SELECT user_id, role_id FROM users_roles").each { |r| emit.call("users_roles", r) }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
if defined?(ActiveRecord::SessionStore::Session)
|
|
210
|
+
scope = "warden.user.#{Devise::Mapping.find_scope!(model)}.key"
|
|
211
|
+
ActiveRecord::SessionStore::Session.find_each do |s|
|
|
212
|
+
entry = s.data[scope] rescue nil
|
|
213
|
+
next unless entry
|
|
214
|
+
emit.call("sessions", { session_id: s.session_id, user_id: Array(entry[0]).first, updated_at: s.updated_at })
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
`;
|
|
218
|
+
//# sourceMappingURL=devise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devise.js","sourceRoot":"","sources":["../../../../src/migrate/export/frameworks/devise.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { FrameworkExport, type Row, type Tables } from './tables';
|
|
2
|
+
/**
|
|
3
|
+
* Django (django.contrib.auth, django-allauth) → canonical records, from
|
|
4
|
+
* `manage.py dumpdata` JSON (`[{ "model", "pk", "fields" }]`) — or the export
|
|
5
|
+
* script ({@link DJANGO_EXPORT_SCRIPT}), which runs the same dump inside the
|
|
6
|
+
* app and adds what only the app's own code can produce: allauth TOTP secrets
|
|
7
|
+
* through the MFA adapter's `decrypt`, and the unused recovery codes.
|
|
8
|
+
*
|
|
9
|
+
* auth.user (or your AUTH_USER_MODEL) → user: the `password` column verbatim
|
|
10
|
+
* (pbkdf2_sha256 / argon2 / bcrypt_sha256 / scrypt / legacy md5+sha1 —
|
|
11
|
+
* every Django hasher Atlas verifies); `!`-prefixed unusable passwords are
|
|
12
|
+
* dropped; is_active=false → banned; is_staff / is_superuser and groups →
|
|
13
|
+
* app roles
|
|
14
|
+
* account.emailaddress → every address with its verified / primary flags
|
|
15
|
+
* socialaccount.socialaccount (+ socialtoken) → identity
|
|
16
|
+
* mfa.authenticator → totp, recovery codes (script), WebAuthn passkeys
|
|
17
|
+
* sessions.session (database backend) → session (sha256 of the session key)
|
|
18
|
+
*/
|
|
19
|
+
export interface DjangoExportOptions {
|
|
20
|
+
/** `app_label.model` of AUTH_USER_MODEL (detected: a model with a `password` field and `username` or `email`). */
|
|
21
|
+
userModel?: string;
|
|
22
|
+
/** The WebAuthn RP id allauth registered passkeys against. */
|
|
23
|
+
rpId?: string;
|
|
24
|
+
oauthTokens?: boolean;
|
|
25
|
+
sessions?: boolean;
|
|
26
|
+
/** Treat users.email as verified when allauth is absent. Default false. */
|
|
27
|
+
trustEmails?: boolean;
|
|
28
|
+
now?: Date;
|
|
29
|
+
}
|
|
30
|
+
export declare function exportDjango(tables: Tables, options?: DjangoExportOptions): FrameworkExport;
|
|
31
|
+
/**
|
|
32
|
+
* Decode `django_session.session_data` without the SECRET_KEY (the rows come
|
|
33
|
+
* from the app's own database): Django ≥ 3.1 `signing.dumps` (`payload:ts:sig`,
|
|
34
|
+
* payload zlib-compressed when it starts with `.`), and the pre-3.1 form,
|
|
35
|
+
* base64(`<hash>:<json>`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function decodeSessionData(raw: string): Row | null;
|
|
38
|
+
/**
|
|
39
|
+
* The export script, run inside the Django app so allauth's own code decrypts
|
|
40
|
+
* TOTP secrets and lists the unused recovery codes; it streams NDJSON straight
|
|
41
|
+
* into the CLI:
|
|
42
|
+
*
|
|
43
|
+
* python manage.py shell < atlas_export.py | npx atlas migrate django export --file -
|
|
44
|
+
*/
|
|
45
|
+
export declare const DJANGO_EXPORT_SCRIPT = "# Atlas export for a Django app (django.contrib.auth + django-allauth).\nimport io, json, sys\nfrom django.apps import apps\nfrom django.conf import settings\nfrom django.core.management import call_command\n\ndef emit(table, row):\n sys.stdout.write(json.dumps({\"table\": table, \"row\": row}, default=str) + \"\\n\")\n\nlabels = [settings.AUTH_USER_MODEL, \"auth.group\", \"auth.permission\"]\nfor label in [\"account.emailaddress\", \"socialaccount.socialaccount\", \"socialaccount.socialtoken\", \"mfa.authenticator\", \"sessions.session\"]:\n try:\n apps.get_model(label)\n labels.append(label)\n except (LookupError, ValueError):\n pass\n\nbuf = io.StringIO()\ncall_command(\"dumpdata\", *labels, format=\"json\", stdout=buf)\nfor obj in json.loads(buf.getvalue()):\n emit(\"dumpdata\", obj)\n\ntry:\n from allauth.mfa.models import Authenticator\n from allauth.mfa.adapter import get_adapter\nexcept ImportError:\n Authenticator = None\n\nif Authenticator is not None:\n try:\n from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes\n except ImportError:\n try:\n from allauth.mfa.recovery_codes import RecoveryCodes\n except ImportError:\n RecoveryCodes = None\n adapter = get_adapter()\n for a in Authenticator.objects.all():\n if a.type == \"totp\":\n emit(\"mfa_secrets\", {\"authenticator_id\": a.pk, \"secret\": adapter.decrypt(a.data[\"secret\"])})\n elif a.type == \"recovery_codes\" and RecoveryCodes is not None:\n emit(\"mfa_secrets\", {\"authenticator_id\": a.pk, \"codes\": RecoveryCodes(a).get_unused_codes()})\n";
|