@arch-cadre/core 0.0.23 → 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.cjs +1 -29
- package/dist/_virtual/_rolldown/runtime.mjs +1 -18
- package/dist/core/auth/augment.cjs +1 -71
- package/dist/core/auth/augment.d.cts.map +1 -1
- package/dist/core/auth/augment.d.mts.map +1 -1
- package/dist/core/auth/augment.mjs +1 -65
- package/dist/core/auth/augment.mjs.map +1 -1
- package/dist/core/auth/email-verification.cjs +1 -99
- package/dist/core/auth/email-verification.d.cts +1 -1
- package/dist/core/auth/email-verification.d.mts +1 -1
- package/dist/core/auth/email-verification.mjs +1 -91
- package/dist/core/auth/email-verification.mjs.map +1 -1
- package/dist/core/auth/logic.cjs +1 -224
- package/dist/core/auth/logic.d.cts +6 -6
- package/dist/core/auth/logic.d.mts +6 -6
- package/dist/core/auth/logic.mjs +1 -212
- package/dist/core/auth/logic.mjs.map +1 -1
- package/dist/core/auth/password-reset.cjs +1 -118
- package/dist/core/auth/password-reset.mjs +1 -110
- package/dist/core/auth/password-reset.mjs.map +1 -1
- package/dist/core/auth/rbac.cjs +1 -118
- package/dist/core/auth/rbac.d.cts +2 -2
- package/dist/core/auth/rbac.d.mts +2 -2
- package/dist/core/auth/rbac.mjs +1 -103
- package/dist/core/auth/rbac.mjs.map +1 -1
- package/dist/core/auth/session.cjs +1 -154
- package/dist/core/auth/session.mjs +1 -142
- package/dist/core/auth/session.mjs.map +1 -1
- package/dist/core/auth/types.d.cts.map +1 -1
- package/dist/core/auth/types.d.mts.map +1 -1
- package/dist/core/auth/utils/encode.cjs +1 -27
- package/dist/core/auth/utils/encode.mjs +1 -25
- package/dist/core/auth/utils/encode.mjs.map +1 -1
- package/dist/core/auth/utils/encryption.cjs +1 -67
- package/dist/core/auth/utils/encryption.mjs +1 -63
- package/dist/core/auth/utils/encryption.mjs.map +1 -1
- package/dist/core/auth/validation.cjs +1 -39
- package/dist/core/auth/validation.mjs +1 -30
- package/dist/core/auth/validation.mjs.map +1 -1
- package/dist/core/bootstrap.cjs +1 -39
- package/dist/core/bootstrap.mjs +1 -39
- package/dist/core/bootstrap.mjs.map +1 -1
- package/dist/core/config.cjs +1 -6
- package/dist/core/config.mjs +1 -5
- package/dist/core/config.mjs.map +1 -1
- package/dist/core/config.server.cjs +1 -60
- package/dist/core/config.server.mjs +1 -56
- package/dist/core/config.server.mjs.map +1 -1
- package/dist/core/event-bus.cjs +1 -48
- package/dist/core/event-bus.d.cts.map +1 -1
- package/dist/core/event-bus.d.mts.map +1 -1
- package/dist/core/event-bus.mjs +1 -47
- package/dist/core/event-bus.mjs.map +1 -1
- package/dist/core/filesystem/index.cjs +1 -11
- package/dist/core/filesystem/index.mjs +1 -12
- package/dist/core/filesystem/index.mjs.map +1 -1
- package/dist/core/filesystem/providers/local.cjs +1 -43
- package/dist/core/filesystem/providers/local.mjs +1 -40
- package/dist/core/filesystem/providers/local.mjs.map +1 -1
- package/dist/core/filesystem/service.cjs +1 -43
- package/dist/core/filesystem/service.mjs +1 -42
- package/dist/core/filesystem/service.mjs.map +1 -1
- package/dist/core/notifications/actions.cjs +1 -36
- package/dist/core/notifications/actions.d.cts +1 -1
- package/dist/core/notifications/actions.d.mts +1 -1
- package/dist/core/notifications/actions.mjs +1 -32
- package/dist/core/notifications/actions.mjs.map +1 -1
- package/dist/core/notifications/index.cjs +1 -2
- package/dist/core/notifications/index.mjs +1 -4
- package/dist/core/notifications/service.cjs +1 -30
- package/dist/core/notifications/service.mjs +1 -30
- package/dist/core/notifications/service.mjs.map +1 -1
- package/dist/core/setup.cjs +1 -25
- package/dist/core/setup.mjs +1 -24
- package/dist/core/setup.mjs.map +1 -1
- package/dist/index.cjs +1 -30
- package/dist/index.mjs +1 -6
- package/dist/server/auth/email.cjs +1 -24
- package/dist/server/auth/email.mjs +1 -22
- package/dist/server/auth/email.mjs.map +1 -1
- package/dist/server/auth/password.cjs +1 -37
- package/dist/server/auth/password.mjs +1 -33
- package/dist/server/auth/password.mjs.map +1 -1
- package/dist/server/auth/user.cjs +1 -165
- package/dist/server/auth/user.mjs +1 -152
- package/dist/server/auth/user.mjs.map +1 -1
- package/dist/server/database/inject.cjs +1 -24
- package/dist/server/database/inject.mjs +1 -22
- package/dist/server/database/inject.mjs.map +1 -1
- package/dist/server/database/schema.cjs +1 -163
- package/dist/server/database/schema.mjs +1 -150
- package/dist/server/database/schema.mjs.map +1 -1
- package/dist/server/emails/index.cjs +1 -32
- package/dist/server/emails/index.mjs +1 -28
- package/dist/server/emails/index.mjs.map +1 -1
- package/dist/server.cjs +1 -145
- package/dist/server.mjs +1 -23
- package/package.json +1 -1
package/dist/core/auth/logic.cjs
CHANGED
|
@@ -1,224 +1 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
|
|
3
|
-
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
4
|
-
const require_validation = require('./validation.cjs');
|
|
5
|
-
const require_event_bus = require('../event-bus.cjs');
|
|
6
|
-
const require_inject = require('../../server/database/inject.cjs');
|
|
7
|
-
const require_schema = require('../../server/database/schema.cjs');
|
|
8
|
-
const require_augment = require('./augment.cjs');
|
|
9
|
-
const require_password = require('../../server/auth/password.cjs');
|
|
10
|
-
const require_user = require('../../server/auth/user.cjs');
|
|
11
|
-
const require_email_verification = require('./email-verification.cjs');
|
|
12
|
-
const require_session = require('./session.cjs');
|
|
13
|
-
let drizzle_orm = require("drizzle-orm");
|
|
14
|
-
|
|
15
|
-
//#region src/core/auth/logic.ts
|
|
16
|
-
/**
|
|
17
|
-
* Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień
|
|
18
|
-
*/
|
|
19
|
-
async function coreRbacAugmenter(user) {
|
|
20
|
-
try {
|
|
21
|
-
const roles = (await require_inject.db.select({ name: require_schema.rolesTable.name }).from(require_schema.usersToRolesTable).innerJoin(require_schema.rolesTable, (0, drizzle_orm.eq)(require_schema.usersToRolesTable.roleId, require_schema.rolesTable.id)).where((0, drizzle_orm.eq)(require_schema.usersToRolesTable.userId, user.id))).map((r) => r.name);
|
|
22
|
-
const directPerms = (await require_inject.db.select({ name: require_schema.permissionsTable.name }).from(require_schema.usersToPermissionsTable).innerJoin(require_schema.permissionsTable, (0, drizzle_orm.eq)(require_schema.usersToPermissionsTable.permissionId, require_schema.permissionsTable.id)).where((0, drizzle_orm.eq)(require_schema.usersToPermissionsTable.userId, user.id))).map((p) => p.name);
|
|
23
|
-
let rolePerms = [];
|
|
24
|
-
if (roles.length > 0) {
|
|
25
|
-
const roleIds = (await require_inject.db.select({ id: require_schema.rolesTable.id }).from(require_schema.rolesTable).where((0, drizzle_orm.inArray)(require_schema.rolesTable.name, roles))).map((r) => r.id);
|
|
26
|
-
if (roleIds.length > 0) rolePerms = (await require_inject.db.select({ name: require_schema.permissionsTable.name }).from(require_schema.rolesToPermissionsTable).innerJoin(require_schema.permissionsTable, (0, drizzle_orm.eq)(require_schema.rolesToPermissionsTable.permissionId, require_schema.permissionsTable.id)).where((0, drizzle_orm.inArray)(require_schema.rolesToPermissionsTable.roleId, roleIds))).map((p) => p.name);
|
|
27
|
-
}
|
|
28
|
-
return {
|
|
29
|
-
roles,
|
|
30
|
-
permissions: Array.from(new Set([...directPerms, ...rolePerms]))
|
|
31
|
-
};
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.error("[Auth:RBAC] Failed to augment user:", error);
|
|
34
|
-
return {
|
|
35
|
-
roles: [],
|
|
36
|
-
permissions: []
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const globalForAuth = globalThis;
|
|
41
|
-
const authValidators = globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
42
|
-
const securityRequirements = globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ?? /* @__PURE__ */ new Set();
|
|
43
|
-
const passwordResetValidators = globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
44
|
-
const emailVerificationValidators = globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
45
|
-
globalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;
|
|
46
|
-
globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;
|
|
47
|
-
globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;
|
|
48
|
-
globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ = emailVerificationValidators;
|
|
49
|
-
async function registerAuthValidator(validator) {
|
|
50
|
-
authValidators.add(validator);
|
|
51
|
-
}
|
|
52
|
-
async function registerPasswordResetValidator(validator) {
|
|
53
|
-
passwordResetValidators.add(validator);
|
|
54
|
-
}
|
|
55
|
-
async function registerEmailVerificationValidator(validator) {
|
|
56
|
-
emailVerificationValidators.add(validator);
|
|
57
|
-
}
|
|
58
|
-
async function registerSecurityRequirement(requirement) {
|
|
59
|
-
securityRequirements.add(requirement);
|
|
60
|
-
}
|
|
61
|
-
async function runPasswordResetValidators(userId) {
|
|
62
|
-
for (const validator of passwordResetValidators) {
|
|
63
|
-
const interception = await validator(userId);
|
|
64
|
-
if (interception) return interception;
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
async function runEmailVerificationValidators(userId) {
|
|
69
|
-
for (const validator of emailVerificationValidators) {
|
|
70
|
-
const interception = await validator(userId);
|
|
71
|
-
if (interception) return interception;
|
|
72
|
-
}
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Augments a base user with data from all registered modules.
|
|
77
|
-
* This is now just a wrapper that includes core RBAC data.
|
|
78
|
-
*/
|
|
79
|
-
async function performFullUserAugmentation(user) {
|
|
80
|
-
return await require_augment.augmentUser(user, await coreRbacAugmenter(user));
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Checks if the current session satisfies all registered security requirements.
|
|
84
|
-
*/
|
|
85
|
-
async function checkSecurity(session, user, requiredRoles, requiredPermissions, fallbackRedirect) {
|
|
86
|
-
if (!user) {
|
|
87
|
-
console.warn("User is required for security check");
|
|
88
|
-
return {
|
|
89
|
-
satisfied: false,
|
|
90
|
-
redirect: fallbackRedirect ?? "/signin"
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
const userRoles = Array.isArray(user.roles) ? user.roles : [];
|
|
94
|
-
const userPermissions = Array.isArray(user.permissions) ? user.permissions : [];
|
|
95
|
-
if (requiredRoles && requiredRoles.length > 0) {
|
|
96
|
-
if (!requiredRoles.some((role) => userRoles.includes(role))) {
|
|
97
|
-
console.warn(`User lacks required roles: ${requiredRoles.join(", ")}`);
|
|
98
|
-
return {
|
|
99
|
-
satisfied: false,
|
|
100
|
-
redirect: fallbackRedirect
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (requiredPermissions && requiredPermissions.length > 0) {
|
|
105
|
-
if (!requiredPermissions.every((perm) => userPermissions.includes(perm))) {
|
|
106
|
-
console.warn(`User lacks required permissions: ${requiredPermissions.join(", ")}`);
|
|
107
|
-
return {
|
|
108
|
-
satisfied: false,
|
|
109
|
-
redirect: fallbackRedirect
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (securityRequirements) for (const requirement of securityRequirements) try {
|
|
114
|
-
const result = await requirement(session, user);
|
|
115
|
-
if (result && !result.satisfied) return {
|
|
116
|
-
...result,
|
|
117
|
-
redirect: result.redirect ?? fallbackRedirect
|
|
118
|
-
};
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error("[Auth:Security] Requirement failed:", error);
|
|
121
|
-
}
|
|
122
|
-
return { satisfied: true };
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Sign In Logic
|
|
126
|
-
*/
|
|
127
|
-
async function signIn(data) {
|
|
128
|
-
const { email, password } = await require_validation.loginSchema.parseAsync(data);
|
|
129
|
-
const user = await require_user.getUserFromEmail(email);
|
|
130
|
-
if (!user) return {
|
|
131
|
-
status: "ERROR",
|
|
132
|
-
message: "Invalid email or password"
|
|
133
|
-
};
|
|
134
|
-
const passwordHash = await require_user.getUserPasswordHash(user.id);
|
|
135
|
-
if (!passwordHash || !await require_password.verifyPasswordHash(passwordHash, password)) return {
|
|
136
|
-
status: "ERROR",
|
|
137
|
-
message: "Invalid email or password"
|
|
138
|
-
};
|
|
139
|
-
for (const validator of authValidators) {
|
|
140
|
-
const interception = await validator(user.id);
|
|
141
|
-
if (interception) return interception;
|
|
142
|
-
}
|
|
143
|
-
const sessionFlags = {};
|
|
144
|
-
const sessionToken = await require_session.generateSessionToken();
|
|
145
|
-
const session = await require_session.createSession(sessionToken, user.id, sessionFlags);
|
|
146
|
-
await require_session.setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
147
|
-
const fullUser = await performFullUserAugmentation(user);
|
|
148
|
-
await require_event_bus.eventBus.publish("auth:session-created", {
|
|
149
|
-
session,
|
|
150
|
-
user: fullUser
|
|
151
|
-
});
|
|
152
|
-
return {
|
|
153
|
-
status: "SUCCESS",
|
|
154
|
-
session: { ...session },
|
|
155
|
-
user: { ...fullUser }
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Sign Up Logic
|
|
160
|
-
*/
|
|
161
|
-
async function signUp(data) {
|
|
162
|
-
const { email, username, password } = require_validation.registerSchema.parse(data);
|
|
163
|
-
if (!await require_user.verifyUsernameInput(username)) throw new Error("Invalid username");
|
|
164
|
-
if (!await require_password.verifyPasswordStrength(password)) throw new Error("Weak password");
|
|
165
|
-
const user = await require_user.createUser(email, username, password);
|
|
166
|
-
const verificationRequest = await require_email_verification.createEmailVerificationRequest(user.id, user.email);
|
|
167
|
-
await require_email_verification.sendVerificationEmail(verificationRequest.email, verificationRequest.code);
|
|
168
|
-
await require_email_verification.setEmailVerificationRequestCookie(verificationRequest);
|
|
169
|
-
const sessionFlags = {};
|
|
170
|
-
const sessionToken = await require_session.generateSessionToken();
|
|
171
|
-
const session = await require_session.createSession(sessionToken, user.id, sessionFlags);
|
|
172
|
-
await require_session.setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
173
|
-
const fullUser = await performFullUserAugmentation(user);
|
|
174
|
-
await require_event_bus.eventBus.publish("auth:session-created", {
|
|
175
|
-
session,
|
|
176
|
-
user: fullUser
|
|
177
|
-
});
|
|
178
|
-
return {
|
|
179
|
-
session: { ...session },
|
|
180
|
-
user: { ...fullUser }
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Finalizes login after a challenge
|
|
185
|
-
*/
|
|
186
|
-
async function finalizeLogin(userId, flags) {
|
|
187
|
-
const sessionToken = await require_session.generateSessionToken();
|
|
188
|
-
const session = await require_session.createSession(sessionToken, userId, flags);
|
|
189
|
-
await require_session.setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
190
|
-
const user = await require_user.getUserById(userId);
|
|
191
|
-
if (user) await require_event_bus.eventBus.publish("auth:session-created", {
|
|
192
|
-
session,
|
|
193
|
-
user
|
|
194
|
-
});
|
|
195
|
-
return {
|
|
196
|
-
session: session ? { ...session } : null,
|
|
197
|
-
user: user ? { ...user } : null
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Sign Out
|
|
202
|
-
*/
|
|
203
|
-
async function signOut() {
|
|
204
|
-
const { session, user } = await require_session.getCurrentSession();
|
|
205
|
-
if (session) {
|
|
206
|
-
if (user) await require_event_bus.eventBus.publish("auth:signed-out", { userId: user.id });
|
|
207
|
-
await require_session.invalidateSession(session.id);
|
|
208
|
-
await require_session.deleteSessionTokenCookie();
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
//#endregion
|
|
213
|
-
exports.checkSecurity = checkSecurity;
|
|
214
|
-
exports.finalizeLogin = finalizeLogin;
|
|
215
|
-
exports.performFullUserAugmentation = performFullUserAugmentation;
|
|
216
|
-
exports.registerAuthValidator = registerAuthValidator;
|
|
217
|
-
exports.registerEmailVerificationValidator = registerEmailVerificationValidator;
|
|
218
|
-
exports.registerPasswordResetValidator = registerPasswordResetValidator;
|
|
219
|
-
exports.registerSecurityRequirement = registerSecurityRequirement;
|
|
220
|
-
exports.runEmailVerificationValidators = runEmailVerificationValidators;
|
|
221
|
-
exports.runPasswordResetValidators = runPasswordResetValidators;
|
|
222
|
-
exports.signIn = signIn;
|
|
223
|
-
exports.signOut = signOut;
|
|
224
|
-
exports.signUp = signUp;
|
|
1
|
+
"use server";require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`./validation.cjs`),t=require(`../event-bus.cjs`),n=require(`../../server/database/inject.cjs`),r=require(`../../server/database/schema.cjs`),i=require(`./augment.cjs`),a=require(`../../server/auth/password.cjs`),o=require(`../../server/auth/user.cjs`),s=require(`./email-verification.cjs`),c=require(`./session.cjs`);let l=require(`drizzle-orm`);async function u(e){try{let t=(await n.db.select({name:r.rolesTable.name}).from(r.usersToRolesTable).innerJoin(r.rolesTable,(0,l.eq)(r.usersToRolesTable.roleId,r.rolesTable.id)).where((0,l.eq)(r.usersToRolesTable.userId,e.id))).map(e=>e.name),i=(await n.db.select({name:r.permissionsTable.name}).from(r.usersToPermissionsTable).innerJoin(r.permissionsTable,(0,l.eq)(r.usersToPermissionsTable.permissionId,r.permissionsTable.id)).where((0,l.eq)(r.usersToPermissionsTable.userId,e.id))).map(e=>e.name),a=[];if(t.length>0){let e=(await n.db.select({id:r.rolesTable.id}).from(r.rolesTable).where((0,l.inArray)(r.rolesTable.name,t))).map(e=>e.id);e.length>0&&(a=(await n.db.select({name:r.permissionsTable.name}).from(r.rolesToPermissionsTable).innerJoin(r.permissionsTable,(0,l.eq)(r.rolesToPermissionsTable.permissionId,r.permissionsTable.id)).where((0,l.inArray)(r.rolesToPermissionsTable.roleId,e))).map(e=>e.name))}return{roles:t,permissions:Array.from(new Set([...i,...a]))}}catch(e){return console.error(`[Auth:RBAC] Failed to augment user:`,e),{roles:[],permissions:[]}}}const d=globalThis,f=d.__KRYO_AUTH_VALIDATORS__??new Set,p=d.__KRYO_SECURITY_REQUIREMENTS__??new Set,m=d.__KRYO_PASSWORD_RESET_VALIDATORS__??new Set,h=d.__KRYO_EMAIL_VERIFICATION_VALIDATORS__??new Set;d.__KRYO_AUTH_VALIDATORS__=f,d.__KRYO_SECURITY_REQUIREMENTS__=p,d.__KRYO_PASSWORD_RESET_VALIDATORS__=m,d.__KRYO_EMAIL_VERIFICATION_VALIDATORS__=h;async function g(e){f.add(e)}async function _(e){m.add(e)}async function v(e){h.add(e)}async function y(e){p.add(e)}async function b(e){for(let t of m){let n=await t(e);if(n)return n}return null}async function x(e){for(let t of h){let n=await t(e);if(n)return n}return null}async function S(e){return await i.augmentUser(e,await u(e))}async function C(e,t,n,r,i){if(!t)return console.warn(`User is required for security check`),{satisfied:!1,redirect:i??`/signin`};let a=Array.isArray(t.roles)?t.roles:[],o=Array.isArray(t.permissions)?t.permissions:[];if(n&&n.length>0&&!n.some(e=>a.includes(e)))return console.warn(`User lacks required roles: ${n.join(`, `)}`),{satisfied:!1,redirect:i};if(r&&r.length>0&&!r.every(e=>o.includes(e)))return console.warn(`User lacks required permissions: ${r.join(`, `)}`),{satisfied:!1,redirect:i};if(p)for(let n of p)try{let r=await n(e,t);if(r&&!r.satisfied)return{...r,redirect:r.redirect??i}}catch(e){console.error(`[Auth:Security] Requirement failed:`,e)}return{satisfied:!0}}async function w(n){let{email:r,password:i}=await e.loginSchema.parseAsync(n),s=await o.getUserFromEmail(r);if(!s)return{status:`ERROR`,message:`Invalid email or password`};let l=await o.getUserPasswordHash(s.id);if(!l||!await a.verifyPasswordHash(l,i))return{status:`ERROR`,message:`Invalid email or password`};for(let e of f){let t=await e(s.id);if(t)return t}let u={},d=await c.generateSessionToken(),p=await c.createSession(d,s.id,u);await c.setSessionTokenCookie(d,p.expiresAt);let m=await S(s);return await t.eventBus.publish(`auth:session-created`,{session:p,user:m}),{status:`SUCCESS`,session:{...p},user:{...m}}}async function T(n){let{email:r,username:i,password:l}=e.registerSchema.parse(n);if(!await o.verifyUsernameInput(i))throw Error(`Invalid username`);if(!await a.verifyPasswordStrength(l))throw Error(`Weak password`);let u=await o.createUser(r,i,l),d=await s.createEmailVerificationRequest(u.id,u.email);await s.sendVerificationEmail(d.email,d.code),await s.setEmailVerificationRequestCookie(d);let f={},p=await c.generateSessionToken(),m=await c.createSession(p,u.id,f);await c.setSessionTokenCookie(p,m.expiresAt);let h=await S(u);return await t.eventBus.publish(`auth:session-created`,{session:m,user:h}),{session:{...m},user:{...h}}}async function E(e,n){let r=await c.generateSessionToken(),i=await c.createSession(r,e,n);await c.setSessionTokenCookie(r,i.expiresAt);let a=await o.getUserById(e);return a&&await t.eventBus.publish(`auth:session-created`,{session:i,user:a}),{session:i?{...i}:null,user:a?{...a}:null}}async function D(){let{session:e,user:n}=await c.getCurrentSession();e&&(n&&await t.eventBus.publish(`auth:signed-out`,{userId:n.id}),await c.invalidateSession(e.id),await c.deleteSessionTokenCookie())}exports.checkSecurity=C,exports.finalizeLogin=E,exports.performFullUserAugmentation=S,exports.registerAuthValidator=g,exports.registerEmailVerificationValidator=v,exports.registerPasswordResetValidator=_,exports.registerSecurityRequirement=y,exports.runEmailVerificationValidators=x,exports.runPasswordResetValidators=b,exports.signIn=w,exports.signOut=D,exports.signUp=T;
|
|
@@ -58,15 +58,15 @@ declare function signUp(data: RegisterInput): Promise<{
|
|
|
58
58
|
createdAt: Date;
|
|
59
59
|
updatedAt: Date | null;
|
|
60
60
|
userId: string;
|
|
61
|
-
expiresAt: Date;
|
|
62
61
|
active_organization_id: string | null;
|
|
62
|
+
expiresAt: Date;
|
|
63
63
|
};
|
|
64
64
|
user: {
|
|
65
65
|
[x: string]: any;
|
|
66
|
-
id: string;
|
|
67
66
|
email: string;
|
|
68
|
-
name: string;
|
|
69
67
|
password: string | null;
|
|
68
|
+
name: string;
|
|
69
|
+
id: string;
|
|
70
70
|
image: string | null;
|
|
71
71
|
recovery_code: Buffer<ArrayBufferLike>;
|
|
72
72
|
emailVerifiedAt: Date | null;
|
|
@@ -86,14 +86,14 @@ declare function finalizeLogin(userId: string, flags: SessionFlags): Promise<{
|
|
|
86
86
|
createdAt: Date;
|
|
87
87
|
updatedAt: Date | null;
|
|
88
88
|
userId: string;
|
|
89
|
-
expiresAt: Date;
|
|
90
89
|
active_organization_id: string | null;
|
|
90
|
+
expiresAt: Date;
|
|
91
91
|
} | null;
|
|
92
92
|
user: {
|
|
93
|
-
id: string;
|
|
94
93
|
email: string;
|
|
95
|
-
name: string;
|
|
96
94
|
password: string | null;
|
|
95
|
+
name: string;
|
|
96
|
+
id: string;
|
|
97
97
|
image: string | null;
|
|
98
98
|
recovery_code: Buffer<ArrayBufferLike>;
|
|
99
99
|
emailVerifiedAt: Date | null;
|
|
@@ -58,15 +58,15 @@ declare function signUp(data: RegisterInput): Promise<{
|
|
|
58
58
|
createdAt: Date;
|
|
59
59
|
updatedAt: Date | null;
|
|
60
60
|
userId: string;
|
|
61
|
-
expiresAt: Date;
|
|
62
61
|
active_organization_id: string | null;
|
|
62
|
+
expiresAt: Date;
|
|
63
63
|
};
|
|
64
64
|
user: {
|
|
65
65
|
[x: string]: any;
|
|
66
|
-
id: string;
|
|
67
66
|
email: string;
|
|
68
|
-
name: string;
|
|
69
67
|
password: string | null;
|
|
68
|
+
name: string;
|
|
69
|
+
id: string;
|
|
70
70
|
image: string | null;
|
|
71
71
|
recovery_code: Buffer<ArrayBufferLike>;
|
|
72
72
|
emailVerifiedAt: Date | null;
|
|
@@ -86,14 +86,14 @@ declare function finalizeLogin(userId: string, flags: SessionFlags): Promise<{
|
|
|
86
86
|
createdAt: Date;
|
|
87
87
|
updatedAt: Date | null;
|
|
88
88
|
userId: string;
|
|
89
|
-
expiresAt: Date;
|
|
90
89
|
active_organization_id: string | null;
|
|
90
|
+
expiresAt: Date;
|
|
91
91
|
} | null;
|
|
92
92
|
user: {
|
|
93
|
-
id: string;
|
|
94
93
|
email: string;
|
|
95
|
-
name: string;
|
|
96
94
|
password: string | null;
|
|
95
|
+
name: string;
|
|
96
|
+
id: string;
|
|
97
97
|
image: string | null;
|
|
98
98
|
recovery_code: Buffer<ArrayBufferLike>;
|
|
99
99
|
emailVerifiedAt: Date | null;
|
package/dist/core/auth/logic.mjs
CHANGED
|
@@ -1,213 +1,2 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
|
|
3
|
-
import { loginSchema, registerSchema } from "./validation.mjs";
|
|
4
|
-
import { eventBus } from "../event-bus.mjs";
|
|
5
|
-
import { db } from "../../server/database/inject.mjs";
|
|
6
|
-
import { permissionsTable, rolesTable, rolesToPermissionsTable, usersToPermissionsTable, usersToRolesTable } from "../../server/database/schema.mjs";
|
|
7
|
-
import { augmentSession, augmentUser, registerIdentityAugmenter, registerPasswordResetSessionAugmenter, registerSessionAugmenter } from "./augment.mjs";
|
|
8
|
-
import { verifyPasswordHash, verifyPasswordStrength } from "../../server/auth/password.mjs";
|
|
9
|
-
import { createUser, getUserById, getUserFromEmail, getUserPasswordHash, verifyUsernameInput } from "../../server/auth/user.mjs";
|
|
10
|
-
import { createEmailVerificationRequest, sendVerificationEmail, setEmailVerificationRequestCookie } from "./email-verification.mjs";
|
|
11
|
-
import { createSession, deleteSessionTokenCookie, generateSessionToken, getCurrentSession, invalidateSession, setSessionTokenCookie } from "./session.mjs";
|
|
12
|
-
import { eq, inArray } from "drizzle-orm";
|
|
13
|
-
|
|
14
|
-
//#region src/core/auth/logic.ts
|
|
15
|
-
/**
|
|
16
|
-
* Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień
|
|
17
|
-
*/
|
|
18
|
-
async function coreRbacAugmenter(user) {
|
|
19
|
-
try {
|
|
20
|
-
const roles = (await db.select({ name: rolesTable.name }).from(usersToRolesTable).innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id)).where(eq(usersToRolesTable.userId, user.id))).map((r) => r.name);
|
|
21
|
-
const directPerms = (await db.select({ name: permissionsTable.name }).from(usersToPermissionsTable).innerJoin(permissionsTable, eq(usersToPermissionsTable.permissionId, permissionsTable.id)).where(eq(usersToPermissionsTable.userId, user.id))).map((p) => p.name);
|
|
22
|
-
let rolePerms = [];
|
|
23
|
-
if (roles.length > 0) {
|
|
24
|
-
const roleIds = (await db.select({ id: rolesTable.id }).from(rolesTable).where(inArray(rolesTable.name, roles))).map((r) => r.id);
|
|
25
|
-
if (roleIds.length > 0) rolePerms = (await db.select({ name: permissionsTable.name }).from(rolesToPermissionsTable).innerJoin(permissionsTable, eq(rolesToPermissionsTable.permissionId, permissionsTable.id)).where(inArray(rolesToPermissionsTable.roleId, roleIds))).map((p) => p.name);
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
roles,
|
|
29
|
-
permissions: Array.from(new Set([...directPerms, ...rolePerms]))
|
|
30
|
-
};
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.error("[Auth:RBAC] Failed to augment user:", error);
|
|
33
|
-
return {
|
|
34
|
-
roles: [],
|
|
35
|
-
permissions: []
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const globalForAuth = globalThis;
|
|
40
|
-
const authValidators = globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
41
|
-
const securityRequirements = globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ?? /* @__PURE__ */ new Set();
|
|
42
|
-
const passwordResetValidators = globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
43
|
-
const emailVerificationValidators = globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ?? /* @__PURE__ */ new Set();
|
|
44
|
-
globalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;
|
|
45
|
-
globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;
|
|
46
|
-
globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;
|
|
47
|
-
globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ = emailVerificationValidators;
|
|
48
|
-
async function registerAuthValidator(validator) {
|
|
49
|
-
authValidators.add(validator);
|
|
50
|
-
}
|
|
51
|
-
async function registerPasswordResetValidator(validator) {
|
|
52
|
-
passwordResetValidators.add(validator);
|
|
53
|
-
}
|
|
54
|
-
async function registerEmailVerificationValidator(validator) {
|
|
55
|
-
emailVerificationValidators.add(validator);
|
|
56
|
-
}
|
|
57
|
-
async function registerSecurityRequirement(requirement) {
|
|
58
|
-
securityRequirements.add(requirement);
|
|
59
|
-
}
|
|
60
|
-
async function runPasswordResetValidators(userId) {
|
|
61
|
-
for (const validator of passwordResetValidators) {
|
|
62
|
-
const interception = await validator(userId);
|
|
63
|
-
if (interception) return interception;
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
async function runEmailVerificationValidators(userId) {
|
|
68
|
-
for (const validator of emailVerificationValidators) {
|
|
69
|
-
const interception = await validator(userId);
|
|
70
|
-
if (interception) return interception;
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Augments a base user with data from all registered modules.
|
|
76
|
-
* This is now just a wrapper that includes core RBAC data.
|
|
77
|
-
*/
|
|
78
|
-
async function performFullUserAugmentation(user) {
|
|
79
|
-
return await augmentUser(user, await coreRbacAugmenter(user));
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Checks if the current session satisfies all registered security requirements.
|
|
83
|
-
*/
|
|
84
|
-
async function checkSecurity(session, user, requiredRoles, requiredPermissions, fallbackRedirect) {
|
|
85
|
-
if (!user) {
|
|
86
|
-
console.warn("User is required for security check");
|
|
87
|
-
return {
|
|
88
|
-
satisfied: false,
|
|
89
|
-
redirect: fallbackRedirect ?? "/signin"
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const userRoles = Array.isArray(user.roles) ? user.roles : [];
|
|
93
|
-
const userPermissions = Array.isArray(user.permissions) ? user.permissions : [];
|
|
94
|
-
if (requiredRoles && requiredRoles.length > 0) {
|
|
95
|
-
if (!requiredRoles.some((role) => userRoles.includes(role))) {
|
|
96
|
-
console.warn(`User lacks required roles: ${requiredRoles.join(", ")}`);
|
|
97
|
-
return {
|
|
98
|
-
satisfied: false,
|
|
99
|
-
redirect: fallbackRedirect
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (requiredPermissions && requiredPermissions.length > 0) {
|
|
104
|
-
if (!requiredPermissions.every((perm) => userPermissions.includes(perm))) {
|
|
105
|
-
console.warn(`User lacks required permissions: ${requiredPermissions.join(", ")}`);
|
|
106
|
-
return {
|
|
107
|
-
satisfied: false,
|
|
108
|
-
redirect: fallbackRedirect
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (securityRequirements) for (const requirement of securityRequirements) try {
|
|
113
|
-
const result = await requirement(session, user);
|
|
114
|
-
if (result && !result.satisfied) return {
|
|
115
|
-
...result,
|
|
116
|
-
redirect: result.redirect ?? fallbackRedirect
|
|
117
|
-
};
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error("[Auth:Security] Requirement failed:", error);
|
|
120
|
-
}
|
|
121
|
-
return { satisfied: true };
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Sign In Logic
|
|
125
|
-
*/
|
|
126
|
-
async function signIn(data) {
|
|
127
|
-
const { email, password } = await loginSchema.parseAsync(data);
|
|
128
|
-
const user = await getUserFromEmail(email);
|
|
129
|
-
if (!user) return {
|
|
130
|
-
status: "ERROR",
|
|
131
|
-
message: "Invalid email or password"
|
|
132
|
-
};
|
|
133
|
-
const passwordHash = await getUserPasswordHash(user.id);
|
|
134
|
-
if (!passwordHash || !await verifyPasswordHash(passwordHash, password)) return {
|
|
135
|
-
status: "ERROR",
|
|
136
|
-
message: "Invalid email or password"
|
|
137
|
-
};
|
|
138
|
-
for (const validator of authValidators) {
|
|
139
|
-
const interception = await validator(user.id);
|
|
140
|
-
if (interception) return interception;
|
|
141
|
-
}
|
|
142
|
-
const sessionFlags = {};
|
|
143
|
-
const sessionToken = await generateSessionToken();
|
|
144
|
-
const session = await createSession(sessionToken, user.id, sessionFlags);
|
|
145
|
-
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
146
|
-
const fullUser = await performFullUserAugmentation(user);
|
|
147
|
-
await eventBus.publish("auth:session-created", {
|
|
148
|
-
session,
|
|
149
|
-
user: fullUser
|
|
150
|
-
});
|
|
151
|
-
return {
|
|
152
|
-
status: "SUCCESS",
|
|
153
|
-
session: { ...session },
|
|
154
|
-
user: { ...fullUser }
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Sign Up Logic
|
|
159
|
-
*/
|
|
160
|
-
async function signUp(data) {
|
|
161
|
-
const { email, username, password } = registerSchema.parse(data);
|
|
162
|
-
if (!await verifyUsernameInput(username)) throw new Error("Invalid username");
|
|
163
|
-
if (!await verifyPasswordStrength(password)) throw new Error("Weak password");
|
|
164
|
-
const user = await createUser(email, username, password);
|
|
165
|
-
const verificationRequest = await createEmailVerificationRequest(user.id, user.email);
|
|
166
|
-
await sendVerificationEmail(verificationRequest.email, verificationRequest.code);
|
|
167
|
-
await setEmailVerificationRequestCookie(verificationRequest);
|
|
168
|
-
const sessionFlags = {};
|
|
169
|
-
const sessionToken = await generateSessionToken();
|
|
170
|
-
const session = await createSession(sessionToken, user.id, sessionFlags);
|
|
171
|
-
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
172
|
-
const fullUser = await performFullUserAugmentation(user);
|
|
173
|
-
await eventBus.publish("auth:session-created", {
|
|
174
|
-
session,
|
|
175
|
-
user: fullUser
|
|
176
|
-
});
|
|
177
|
-
return {
|
|
178
|
-
session: { ...session },
|
|
179
|
-
user: { ...fullUser }
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Finalizes login after a challenge
|
|
184
|
-
*/
|
|
185
|
-
async function finalizeLogin(userId, flags) {
|
|
186
|
-
const sessionToken = await generateSessionToken();
|
|
187
|
-
const session = await createSession(sessionToken, userId, flags);
|
|
188
|
-
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
189
|
-
const user = await getUserById(userId);
|
|
190
|
-
if (user) await eventBus.publish("auth:session-created", {
|
|
191
|
-
session,
|
|
192
|
-
user
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
session: session ? { ...session } : null,
|
|
196
|
-
user: user ? { ...user } : null
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Sign Out
|
|
201
|
-
*/
|
|
202
|
-
async function signOut() {
|
|
203
|
-
const { session, user } = await getCurrentSession();
|
|
204
|
-
if (session) {
|
|
205
|
-
if (user) await eventBus.publish("auth:signed-out", { userId: user.id });
|
|
206
|
-
await invalidateSession(session.id);
|
|
207
|
-
await deleteSessionTokenCookie();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
//#endregion
|
|
212
|
-
export { checkSecurity, finalizeLogin, performFullUserAugmentation, registerAuthValidator, registerEmailVerificationValidator, registerPasswordResetValidator, registerSecurityRequirement, runEmailVerificationValidators, runPasswordResetValidators, signIn, signOut, signUp };
|
|
1
|
+
"use server";import{loginSchema as e,registerSchema as t}from"./validation.mjs";import{eventBus as n}from"../event-bus.mjs";import{db as r}from"../../server/database/inject.mjs";import{permissionsTable as i,rolesTable as a,rolesToPermissionsTable as o,usersToPermissionsTable as s,usersToRolesTable as c}from"../../server/database/schema.mjs";import{augmentSession as l,augmentUser as u,registerIdentityAugmenter as d,registerPasswordResetSessionAugmenter as f,registerSessionAugmenter as p}from"./augment.mjs";import{verifyPasswordHash as m,verifyPasswordStrength as h}from"../../server/auth/password.mjs";import{createUser as g,getUserById as _,getUserFromEmail as v,getUserPasswordHash as y,verifyUsernameInput as b}from"../../server/auth/user.mjs";import{createEmailVerificationRequest as x,sendVerificationEmail as S,setEmailVerificationRequestCookie as C}from"./email-verification.mjs";import{createSession as w,deleteSessionTokenCookie as T,generateSessionToken as E,getCurrentSession as D,invalidateSession as O,setSessionTokenCookie as k}from"./session.mjs";import{eq as A,inArray as j}from"drizzle-orm";async function M(e){try{let t=(await r.select({name:a.name}).from(c).innerJoin(a,A(c.roleId,a.id)).where(A(c.userId,e.id))).map(e=>e.name),n=(await r.select({name:i.name}).from(s).innerJoin(i,A(s.permissionId,i.id)).where(A(s.userId,e.id))).map(e=>e.name),l=[];if(t.length>0){let e=(await r.select({id:a.id}).from(a).where(j(a.name,t))).map(e=>e.id);e.length>0&&(l=(await r.select({name:i.name}).from(o).innerJoin(i,A(o.permissionId,i.id)).where(j(o.roleId,e))).map(e=>e.name))}return{roles:t,permissions:Array.from(new Set([...n,...l]))}}catch(e){return console.error(`[Auth:RBAC] Failed to augment user:`,e),{roles:[],permissions:[]}}}const N=globalThis,P=N.__KRYO_AUTH_VALIDATORS__??new Set,F=N.__KRYO_SECURITY_REQUIREMENTS__??new Set,I=N.__KRYO_PASSWORD_RESET_VALIDATORS__??new Set,L=N.__KRYO_EMAIL_VERIFICATION_VALIDATORS__??new Set;N.__KRYO_AUTH_VALIDATORS__=P,N.__KRYO_SECURITY_REQUIREMENTS__=F,N.__KRYO_PASSWORD_RESET_VALIDATORS__=I,N.__KRYO_EMAIL_VERIFICATION_VALIDATORS__=L;async function R(e){P.add(e)}async function z(e){I.add(e)}async function B(e){L.add(e)}async function V(e){F.add(e)}async function H(e){for(let t of I){let n=await t(e);if(n)return n}return null}async function U(e){for(let t of L){let n=await t(e);if(n)return n}return null}async function W(e){return await u(e,await M(e))}async function G(e,t,n,r,i){if(!t)return console.warn(`User is required for security check`),{satisfied:!1,redirect:i??`/signin`};let a=Array.isArray(t.roles)?t.roles:[],o=Array.isArray(t.permissions)?t.permissions:[];if(n&&n.length>0&&!n.some(e=>a.includes(e)))return console.warn(`User lacks required roles: ${n.join(`, `)}`),{satisfied:!1,redirect:i};if(r&&r.length>0&&!r.every(e=>o.includes(e)))return console.warn(`User lacks required permissions: ${r.join(`, `)}`),{satisfied:!1,redirect:i};if(F)for(let n of F)try{let r=await n(e,t);if(r&&!r.satisfied)return{...r,redirect:r.redirect??i}}catch(e){console.error(`[Auth:Security] Requirement failed:`,e)}return{satisfied:!0}}async function K(t){let{email:r,password:i}=await e.parseAsync(t),a=await v(r);if(!a)return{status:`ERROR`,message:`Invalid email or password`};let o=await y(a.id);if(!o||!await m(o,i))return{status:`ERROR`,message:`Invalid email or password`};for(let e of P){let t=await e(a.id);if(t)return t}let s={},c=await E(),l=await w(c,a.id,s);await k(c,l.expiresAt);let u=await W(a);return await n.publish(`auth:session-created`,{session:l,user:u}),{status:`SUCCESS`,session:{...l},user:{...u}}}async function q(e){let{email:r,username:i,password:a}=t.parse(e);if(!await b(i))throw Error(`Invalid username`);if(!await h(a))throw Error(`Weak password`);let o=await g(r,i,a),s=await x(o.id,o.email);await S(s.email,s.code),await C(s);let c={},l=await E(),u=await w(l,o.id,c);await k(l,u.expiresAt);let d=await W(o);return await n.publish(`auth:session-created`,{session:u,user:d}),{session:{...u},user:{...d}}}async function J(e,t){let r=await E(),i=await w(r,e,t);await k(r,i.expiresAt);let a=await _(e);return a&&await n.publish(`auth:session-created`,{session:i,user:a}),{session:i?{...i}:null,user:a?{...a}:null}}async function Y(){let{session:e,user:t}=await D();e&&(t&&await n.publish(`auth:signed-out`,{userId:t.id}),await O(e.id),await T())}export{G as checkSecurity,J as finalizeLogin,W as performFullUserAugmentation,R as registerAuthValidator,B as registerEmailVerificationValidator,z as registerPasswordResetValidator,V as registerSecurityRequirement,U as runEmailVerificationValidators,H as runPasswordResetValidators,K as signIn,Y as signOut,q as signUp};
|
|
213
2
|
//# sourceMappingURL=logic.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logic.mjs","names":[],"sources":["../../../src/core/auth/logic.ts"],"sourcesContent":["\"use server\";\n\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n verifyPasswordHash,\n verifyPasswordStrength,\n} from \"../../server/auth/password\";\nimport {\n createUser,\n getUserById,\n getUserFromEmail,\n getUserPasswordHash,\n verifyUsernameInput,\n} from \"../../server/auth/user\";\nimport { db } from \"../../server/database/inject\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema\";\nimport { eventBus } from \"../event-bus\";\nimport {\n augmentSession,\n augmentUser,\n registerIdentityAugmenter,\n registerPasswordResetSessionAugmenter,\n registerSessionAugmenter,\n} from \"./augment\";\nimport {\n createEmailVerificationRequest,\n sendVerificationEmail,\n setEmailVerificationRequestCookie,\n} from \"./email-verification\";\nimport {\n createSession,\n deleteSessionTokenCookie,\n generateSessionToken,\n getCurrentSession,\n invalidateSession,\n setSessionTokenCookie,\n} from \"./session\";\nimport type {\n AuthResponse,\n FullUser,\n Session,\n SessionFlags,\n User,\n UserPermission,\n UserRole,\n} from \"./types\";\nimport {\n type LoginInput,\n loginSchema,\n type RegisterInput,\n registerSchema,\n} from \"./validation\";\n\n/**\n * Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień\n */\nasync function coreRbacAugmenter(user: User): Promise<Record<string, any>> {\n try {\n // 1. Fetch direct roles\n const userRoles = await db\n .select({ name: rolesTable.name })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, user.id));\n\n const roles = userRoles.map((r) => r.name);\n\n // 2. Fetch direct permissions\n const userDirectPerms = await db\n .select({ name: permissionsTable.name })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, user.id));\n\n const directPerms = userDirectPerms.map((p) => p.name);\n\n // 3. Fetch permissions from roles\n let rolePerms: string[] = [];\n if (roles.length > 0) {\n const roleIdsResult = await db\n .select({ id: rolesTable.id })\n .from(rolesTable)\n .where(inArray(rolesTable.name, roles));\n\n const roleIds = roleIdsResult.map((r) => r.id);\n\n if (roleIds.length > 0) {\n const rolePermsData = await db\n .select({ name: permissionsTable.name })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n rolePerms = rolePermsData.map((p) => p.name);\n }\n }\n\n return {\n roles,\n permissions: Array.from(new Set([...directPerms, ...rolePerms])),\n };\n } catch (error) {\n console.error(\"[Auth:RBAC] Failed to augment user:\", error);\n return { roles: [], permissions: [] };\n }\n}\n\n/**\n * Registry for login validators (e.g. 2FA module)\n */\ntype AuthValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for Security Requirements (e.g. checking if 2FA is needed for a session)\n */\ntype SecurityRequirement = (\n session: Session,\n user: FullUser,\n) => Promise<{ satisfied: boolean; redirect?: string } | null>;\n\n/**\n * Registry for password reset validators (e.g. 2FA module requiring check during reset)\n */\ntype PasswordResetValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for email verification validators\n */\ntype EmailVerificationValidator = (\n userId: string,\n) => Promise<AuthResponse | null>;\n\nconst globalForAuth = globalThis as unknown as {\n __KRYO_AUTH_VALIDATORS__: Set<AuthValidator> | undefined;\n __KRYO_SECURITY_REQUIREMENTS__: Set<SecurityRequirement> | undefined;\n __KRYO_PASSWORD_RESET_VALIDATORS__: Set<PasswordResetValidator> | undefined;\n __KRYO_EMAIL_VERIFICATION_VALIDATORS__:\n | Set<EmailVerificationValidator>\n | undefined;\n};\n\nconst authValidators =\n globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? new Set<AuthValidator>();\nconst securityRequirements =\n globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ??\n new Set<SecurityRequirement>();\nconst passwordResetValidators =\n globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ??\n new Set<PasswordResetValidator>();\nconst emailVerificationValidators =\n globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ??\n new Set<EmailVerificationValidator>();\n\nglobalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;\nglobalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;\nglobalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;\nglobalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ =\n emailVerificationValidators;\n\nexport async function registerAuthValidator(validator: AuthValidator) {\n authValidators.add(validator);\n}\n\nexport async function registerPasswordResetValidator(\n validator: PasswordResetValidator,\n) {\n passwordResetValidators.add(validator);\n}\n\nexport async function registerEmailVerificationValidator(\n validator: EmailVerificationValidator,\n) {\n emailVerificationValidators.add(validator);\n}\n\nexport {\n registerIdentityAugmenter,\n registerSessionAugmenter,\n registerPasswordResetSessionAugmenter,\n augmentUser,\n augmentSession,\n};\n\nexport async function registerSecurityRequirement(\n requirement: SecurityRequirement,\n) {\n securityRequirements.add(requirement);\n}\n\nexport async function runPasswordResetValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of passwordResetValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\nexport async function runEmailVerificationValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of emailVerificationValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\n/**\n * Augments a base user with data from all registered modules.\n * This is now just a wrapper that includes core RBAC data.\n */\nexport async function performFullUserAugmentation(\n user: User,\n): Promise<FullUser> {\n const coreRbacData = await coreRbacAugmenter(user);\n return await augmentUser(user, coreRbacData);\n}\n\n/**\n * Checks if the current session satisfies all registered security requirements.\n */\nexport async function checkSecurity(\n session: Session,\n user: FullUser,\n requiredRoles?: UserRole[],\n requiredPermissions?: UserPermission[],\n fallbackRedirect?: string,\n) {\n if (!user) {\n console.warn(\"User is required for security check\");\n return { satisfied: false, redirect: fallbackRedirect ?? \"/signin\" };\n }\n\n const userRoles = Array.isArray(user.roles) ? user.roles : [];\n const userPermissions = Array.isArray(user.permissions)\n ? user.permissions\n : [];\n\n // 1. Core Role Check (At least one role must match)\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => userRoles.includes(role));\n if (!hasRole) {\n console.warn(`User lacks required roles: ${requiredRoles.join(\", \")}`);\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 2. Core Permission Check (ALL permissions must match)\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasAllPermissions = requiredPermissions.every((perm) =>\n userPermissions.includes(perm),\n );\n if (!hasAllPermissions) {\n console.warn(\n `User lacks required permissions: ${requiredPermissions.join(\", \")}`,\n );\n\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 3. Modular Requirements Check\n if (securityRequirements) {\n for (const requirement of securityRequirements) {\n try {\n const result = await requirement(session, user);\n if (result && !result.satisfied) {\n return {\n ...result,\n redirect: result.redirect ?? fallbackRedirect,\n };\n }\n } catch (error) {\n console.error(\"[Auth:Security] Requirement failed:\", error);\n }\n }\n }\n return { satisfied: true };\n}\n\n/**\n * Sign In Logic\n */\nexport async function signIn(data: LoginInput): Promise<AuthResponse> {\n const { email, password } = await loginSchema.parseAsync(data);\n\n const user = await getUserFromEmail(email);\n if (!user) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n const passwordHash = await getUserPasswordHash(user.id);\n if (!passwordHash || !(await verifyPasswordHash(passwordHash, password))) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n // Interception Layer\n for (const validator of authValidators) {\n const interception = await validator(user.id);\n if (interception) return interception;\n }\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n status: \"SUCCESS\",\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Sign Up Logic\n */\nexport async function signUp(data: RegisterInput) {\n const { email, username, password } = registerSchema.parse(data);\n\n if (!(await verifyUsernameInput(username))) {\n throw new Error(\"Invalid username\");\n }\n\n if (!(await verifyPasswordStrength(password))) {\n throw new Error(\"Weak password\");\n }\n\n const user = await createUser(email, username, password);\n const verificationRequest = await createEmailVerificationRequest(\n user.id,\n user.email,\n );\n\n await sendVerificationEmail(\n verificationRequest.email,\n verificationRequest.code,\n );\n await setEmailVerificationRequestCookie(verificationRequest);\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Finalizes login after a challenge\n */\nexport async function finalizeLogin(userId: string, flags: SessionFlags) {\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, userId, flags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const user = await getUserById(userId);\n\n if (user) {\n await eventBus.publish(\"auth:session-created\", { session, user });\n }\n\n return {\n session: session ? { ...session } : null,\n user: user ? { ...user } : null,\n };\n}\n\n/**\n * Sign Out\n */\nexport async function signOut() {\n const { session, user } = await getCurrentSession();\n if (session) {\n if (user) {\n await eventBus.publish(\"auth:signed-out\", { userId: user.id });\n }\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"logic.mjs","names":[],"sources":["../../../src/core/auth/logic.ts"],"sourcesContent":["\"use server\";\n\nimport { eq, inArray } from \"drizzle-orm\";\nimport {\n verifyPasswordHash,\n verifyPasswordStrength,\n} from \"../../server/auth/password.js\";\nimport {\n createUser,\n getUserById,\n getUserFromEmail,\n getUserPasswordHash,\n verifyUsernameInput,\n} from \"../../server/auth/user.js\";\nimport { db } from \"../../server/database/inject.js\";\nimport {\n permissionsTable,\n rolesTable,\n rolesToPermissionsTable,\n usersToPermissionsTable,\n usersToRolesTable,\n} from \"../../server/database/schema.js\";\nimport { eventBus } from \"../event-bus.js\";\nimport {\n augmentSession,\n augmentUser,\n registerIdentityAugmenter,\n registerPasswordResetSessionAugmenter,\n registerSessionAugmenter,\n} from \"./augment.js\";\nimport {\n createEmailVerificationRequest,\n sendVerificationEmail,\n setEmailVerificationRequestCookie,\n} from \"./email-verification.js\";\nimport {\n createSession,\n deleteSessionTokenCookie,\n generateSessionToken,\n getCurrentSession,\n invalidateSession,\n setSessionTokenCookie,\n} from \"./session.js\";\nimport type {\n AuthResponse,\n FullUser,\n Session,\n SessionFlags,\n User,\n UserPermission,\n UserRole,\n} from \"./types.js\";\nimport {\n type LoginInput,\n loginSchema,\n type RegisterInput,\n registerSchema,\n} from \"./validation.js\";\n\n/**\n * Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień\n */\nasync function coreRbacAugmenter(user: User): Promise<Record<string, any>> {\n try {\n // 1. Fetch direct roles\n const userRoles = await db\n .select({ name: rolesTable.name })\n .from(usersToRolesTable)\n .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))\n .where(eq(usersToRolesTable.userId, user.id));\n\n const roles = userRoles.map((r) => r.name);\n\n // 2. Fetch direct permissions\n const userDirectPerms = await db\n .select({ name: permissionsTable.name })\n .from(usersToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(usersToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(eq(usersToPermissionsTable.userId, user.id));\n\n const directPerms = userDirectPerms.map((p) => p.name);\n\n // 3. Fetch permissions from roles\n let rolePerms: string[] = [];\n if (roles.length > 0) {\n const roleIdsResult = await db\n .select({ id: rolesTable.id })\n .from(rolesTable)\n .where(inArray(rolesTable.name, roles));\n\n const roleIds = roleIdsResult.map((r) => r.id);\n\n if (roleIds.length > 0) {\n const rolePermsData = await db\n .select({ name: permissionsTable.name })\n .from(rolesToPermissionsTable)\n .innerJoin(\n permissionsTable,\n eq(rolesToPermissionsTable.permissionId, permissionsTable.id),\n )\n .where(inArray(rolesToPermissionsTable.roleId, roleIds));\n rolePerms = rolePermsData.map((p) => p.name);\n }\n }\n\n return {\n roles,\n permissions: Array.from(new Set([...directPerms, ...rolePerms])),\n };\n } catch (error) {\n console.error(\"[Auth:RBAC] Failed to augment user:\", error);\n return { roles: [], permissions: [] };\n }\n}\n\n/**\n * Registry for login validators (e.g. 2FA module)\n */\ntype AuthValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for Security Requirements (e.g. checking if 2FA is needed for a session)\n */\ntype SecurityRequirement = (\n session: Session,\n user: FullUser,\n) => Promise<{ satisfied: boolean; redirect?: string } | null>;\n\n/**\n * Registry for password reset validators (e.g. 2FA module requiring check during reset)\n */\ntype PasswordResetValidator = (userId: string) => Promise<AuthResponse | null>;\n\n/**\n * Registry for email verification validators\n */\ntype EmailVerificationValidator = (\n userId: string,\n) => Promise<AuthResponse | null>;\n\nconst globalForAuth = globalThis as unknown as {\n __KRYO_AUTH_VALIDATORS__: Set<AuthValidator> | undefined;\n __KRYO_SECURITY_REQUIREMENTS__: Set<SecurityRequirement> | undefined;\n __KRYO_PASSWORD_RESET_VALIDATORS__: Set<PasswordResetValidator> | undefined;\n __KRYO_EMAIL_VERIFICATION_VALIDATORS__:\n | Set<EmailVerificationValidator>\n | undefined;\n};\n\nconst authValidators =\n globalForAuth.__KRYO_AUTH_VALIDATORS__ ?? new Set<AuthValidator>();\nconst securityRequirements =\n globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ ??\n new Set<SecurityRequirement>();\nconst passwordResetValidators =\n globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ ??\n new Set<PasswordResetValidator>();\nconst emailVerificationValidators =\n globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ ??\n new Set<EmailVerificationValidator>();\n\nglobalForAuth.__KRYO_AUTH_VALIDATORS__ = authValidators;\nglobalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;\nglobalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;\nglobalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ =\n emailVerificationValidators;\n\nexport async function registerAuthValidator(validator: AuthValidator) {\n authValidators.add(validator);\n}\n\nexport async function registerPasswordResetValidator(\n validator: PasswordResetValidator,\n) {\n passwordResetValidators.add(validator);\n}\n\nexport async function registerEmailVerificationValidator(\n validator: EmailVerificationValidator,\n) {\n emailVerificationValidators.add(validator);\n}\n\nexport {\n registerIdentityAugmenter,\n registerSessionAugmenter,\n registerPasswordResetSessionAugmenter,\n augmentUser,\n augmentSession,\n};\n\nexport async function registerSecurityRequirement(\n requirement: SecurityRequirement,\n) {\n securityRequirements.add(requirement);\n}\n\nexport async function runPasswordResetValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of passwordResetValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\nexport async function runEmailVerificationValidators(\n userId: string,\n): Promise<AuthResponse | null> {\n for (const validator of emailVerificationValidators) {\n const interception = await validator(userId);\n if (interception) return interception;\n }\n return null;\n}\n\n/**\n * Augments a base user with data from all registered modules.\n * This is now just a wrapper that includes core RBAC data.\n */\nexport async function performFullUserAugmentation(\n user: User,\n): Promise<FullUser> {\n const coreRbacData = await coreRbacAugmenter(user);\n return await augmentUser(user, coreRbacData);\n}\n\n/**\n * Checks if the current session satisfies all registered security requirements.\n */\nexport async function checkSecurity(\n session: Session,\n user: FullUser,\n requiredRoles?: UserRole[],\n requiredPermissions?: UserPermission[],\n fallbackRedirect?: string,\n) {\n if (!user) {\n console.warn(\"User is required for security check\");\n return { satisfied: false, redirect: fallbackRedirect ?? \"/signin\" };\n }\n\n const userRoles = Array.isArray(user.roles) ? user.roles : [];\n const userPermissions = Array.isArray(user.permissions)\n ? user.permissions\n : [];\n\n // 1. Core Role Check (At least one role must match)\n if (requiredRoles && requiredRoles.length > 0) {\n const hasRole = requiredRoles.some((role) => userRoles.includes(role));\n if (!hasRole) {\n console.warn(`User lacks required roles: ${requiredRoles.join(\", \")}`);\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 2. Core Permission Check (ALL permissions must match)\n if (requiredPermissions && requiredPermissions.length > 0) {\n const hasAllPermissions = requiredPermissions.every((perm) =>\n userPermissions.includes(perm),\n );\n if (!hasAllPermissions) {\n console.warn(\n `User lacks required permissions: ${requiredPermissions.join(\", \")}`,\n );\n\n return {\n satisfied: false,\n redirect: fallbackRedirect,\n };\n }\n }\n\n // 3. Modular Requirements Check\n if (securityRequirements) {\n for (const requirement of securityRequirements) {\n try {\n const result = await requirement(session, user);\n if (result && !result.satisfied) {\n return {\n ...result,\n redirect: result.redirect ?? fallbackRedirect,\n };\n }\n } catch (error) {\n console.error(\"[Auth:Security] Requirement failed:\", error);\n }\n }\n }\n return { satisfied: true };\n}\n\n/**\n * Sign In Logic\n */\nexport async function signIn(data: LoginInput): Promise<AuthResponse> {\n const { email, password } = await loginSchema.parseAsync(data);\n\n const user = await getUserFromEmail(email);\n if (!user) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n const passwordHash = await getUserPasswordHash(user.id);\n if (!passwordHash || !(await verifyPasswordHash(passwordHash, password))) {\n return { status: \"ERROR\", message: \"Invalid email or password\" };\n }\n\n // Interception Layer\n for (const validator of authValidators) {\n const interception = await validator(user.id);\n if (interception) return interception;\n }\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n status: \"SUCCESS\",\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Sign Up Logic\n */\nexport async function signUp(data: RegisterInput) {\n const { email, username, password } = registerSchema.parse(data);\n\n if (!(await verifyUsernameInput(username))) {\n throw new Error(\"Invalid username\");\n }\n\n if (!(await verifyPasswordStrength(password))) {\n throw new Error(\"Weak password\");\n }\n\n const user = await createUser(email, username, password);\n const verificationRequest = await createEmailVerificationRequest(\n user.id,\n user.email,\n );\n\n await sendVerificationEmail(\n verificationRequest.email,\n verificationRequest.code,\n );\n await setEmailVerificationRequestCookie(verificationRequest);\n\n const sessionFlags: SessionFlags = {};\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, user.id, sessionFlags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const fullUser = await performFullUserAugmentation(user);\n await eventBus.publish(\"auth:session-created\", { session, user: fullUser });\n\n return {\n session: { ...session },\n user: { ...fullUser },\n };\n}\n\n/**\n * Finalizes login after a challenge\n */\nexport async function finalizeLogin(userId: string, flags: SessionFlags) {\n const sessionToken = await generateSessionToken();\n const session = await createSession(sessionToken, userId, flags);\n await setSessionTokenCookie(sessionToken, session.expiresAt);\n\n const user = await getUserById(userId);\n\n if (user) {\n await eventBus.publish(\"auth:session-created\", { session, user });\n }\n\n return {\n session: session ? { ...session } : null,\n user: user ? { ...user } : null,\n };\n}\n\n/**\n * Sign Out\n */\nexport async function signOut() {\n const { session, user } = await getCurrentSession();\n if (session) {\n if (user) {\n await eventBus.publish(\"auth:signed-out\", { userId: user.id });\n }\n await invalidateSession(session.id);\n await deleteSessionTokenCookie();\n }\n}\n"],"mappings":"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"}
|