libre-webui 0.22.0 → 0.23.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/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +8 -0
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/main.js +2 -2
- package/backend/dist/main.js.map +1 -1
- package/backend/dist/middleware/auth.d.ts +15 -1
- package/backend/dist/middleware/auth.d.ts.map +1 -1
- package/backend/dist/middleware/auth.js +153 -33
- package/backend/dist/middleware/auth.js.map +1 -1
- package/backend/dist/persistence/index.d.ts +1 -0
- package/backend/dist/persistence/index.d.ts.map +1 -1
- package/backend/dist/persistence/index.js.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +2 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresPersistence.d.ts.map +1 -1
- package/backend/dist/persistence/postgresPersistence.js +4 -0
- package/backend/dist/persistence/postgresPersistence.js.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.d.ts +7 -0
- package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/postgresSecurityRepositories.js +435 -0
- package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -0
- package/backend/dist/persistence/postgresTrustFoundationMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresTrustFoundationMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresTrustFoundationMigration.js +151 -0
- package/backend/dist/persistence/postgresTrustFoundationMigration.js.map +1 -0
- package/backend/dist/persistence/securityTypes.d.ts +196 -0
- package/backend/dist/persistence/securityTypes.d.ts.map +1 -0
- package/backend/dist/persistence/securityTypes.js +18 -0
- package/backend/dist/persistence/securityTypes.js.map +1 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts +1 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +311 -0
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqlitePersistence.d.ts.map +1 -1
- package/backend/dist/persistence/sqlitePersistence.js +3 -0
- package/backend/dist/persistence/sqlitePersistence.js.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.d.ts +6 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.js +497 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.js +55 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
- package/backend/dist/persistence/types.d.ts +4 -0
- package/backend/dist/persistence/types.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +12 -0
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/platform/storage/storageFactory.d.ts +2 -0
- package/backend/dist/platform/storage/storageFactory.d.ts.map +1 -1
- package/backend/dist/platform/storage/storageFactory.js +15 -3
- package/backend/dist/platform/storage/storageFactory.js.map +1 -1
- package/backend/dist/routes/access.d.ts +3 -0
- package/backend/dist/routes/access.d.ts.map +1 -0
- package/backend/dist/routes/access.js +134 -0
- package/backend/dist/routes/access.js.map +1 -0
- package/backend/dist/routes/audit.d.ts +3 -0
- package/backend/dist/routes/audit.d.ts.map +1 -0
- package/backend/dist/routes/audit.js +82 -0
- package/backend/dist/routes/audit.js.map +1 -0
- package/backend/dist/routes/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +322 -11
- package/backend/dist/routes/auth.js.map +1 -1
- package/backend/dist/routes/groups.d.ts +3 -0
- package/backend/dist/routes/groups.d.ts.map +1 -0
- package/backend/dist/routes/groups.js +161 -0
- package/backend/dist/routes/groups.js.map +1 -0
- package/backend/dist/routes/jobs.d.ts.map +1 -1
- package/backend/dist/routes/jobs.js +9 -0
- package/backend/dist/routes/jobs.js.map +1 -1
- package/backend/dist/services/apiTokenService.d.ts +51 -0
- package/backend/dist/services/apiTokenService.d.ts.map +1 -0
- package/backend/dist/services/apiTokenService.js +220 -0
- package/backend/dist/services/apiTokenService.js.map +1 -0
- package/backend/dist/services/authService.d.ts +20 -4
- package/backend/dist/services/authService.d.ts.map +1 -1
- package/backend/dist/services/authService.js +44 -6
- package/backend/dist/services/authService.js.map +1 -1
- package/backend/dist/services/authSessionService.d.ts +34 -0
- package/backend/dist/services/authSessionService.d.ts.map +1 -0
- package/backend/dist/services/authSessionService.js +205 -0
- package/backend/dist/services/authSessionService.js.map +1 -0
- package/backend/dist/services/authorizationService.d.ts +64 -0
- package/backend/dist/services/authorizationService.d.ts.map +1 -0
- package/backend/dist/services/authorizationService.js +176 -0
- package/backend/dist/services/authorizationService.js.map +1 -0
- package/backend/dist/services/groupService.d.ts +27 -0
- package/backend/dist/services/groupService.d.ts.map +1 -0
- package/backend/dist/services/groupService.js +245 -0
- package/backend/dist/services/groupService.js.map +1 -0
- package/backend/dist/services/groupVectorPrincipalResolver.d.ts +12 -0
- package/backend/dist/services/groupVectorPrincipalResolver.d.ts.map +1 -0
- package/backend/dist/services/groupVectorPrincipalResolver.js +33 -0
- package/backend/dist/services/groupVectorPrincipalResolver.js.map +1 -0
- package/backend/dist/services/modelAccessService.d.ts +1 -0
- package/backend/dist/services/modelAccessService.d.ts.map +1 -1
- package/backend/dist/services/modelAccessService.js +3 -5
- package/backend/dist/services/modelAccessService.js.map +1 -1
- package/backend/dist/services/oauthSecurity.d.ts +12 -1
- package/backend/dist/services/oauthSecurity.d.ts.map +1 -1
- package/backend/dist/services/oauthSecurity.js +41 -0
- package/backend/dist/services/oauthSecurity.js.map +1 -1
- package/backend/dist/services/pluginService.d.ts.map +1 -1
- package/backend/dist/services/pluginService.js +35 -2
- package/backend/dist/services/pluginService.js.map +1 -1
- package/backend/dist/services/resourceGrantService.d.ts +28 -0
- package/backend/dist/services/resourceGrantService.d.ts.map +1 -0
- package/backend/dist/services/resourceGrantService.js +192 -0
- package/backend/dist/services/resourceGrantService.js.map +1 -0
- package/backend/dist/services/securityAuditService.d.ts +35 -0
- package/backend/dist/services/securityAuditService.d.ts.map +1 -0
- package/backend/dist/services/securityAuditService.js +159 -0
- package/backend/dist/services/securityAuditService.js.map +1 -0
- package/backend/dist/services/simpleGitHubOAuth.d.ts.map +1 -1
- package/backend/dist/services/simpleGitHubOAuth.js +4 -2
- package/backend/dist/services/simpleGitHubOAuth.js.map +1 -1
- package/backend/dist/services/simpleOidcOAuth.d.ts +62 -0
- package/backend/dist/services/simpleOidcOAuth.d.ts.map +1 -0
- package/backend/dist/services/simpleOidcOAuth.js +397 -0
- package/backend/dist/services/simpleOidcOAuth.js.map +1 -0
- package/backend/dist/services/webSearchService.d.ts +1 -0
- package/backend/dist/services/webSearchService.d.ts.map +1 -1
- package/backend/dist/services/webSearchService.js +3 -5
- package/backend/dist/services/webSearchService.js.map +1 -1
- package/backend/dist/services/websocketTicketService.d.ts +3 -1
- package/backend/dist/services/websocketTicketService.d.ts.map +1 -1
- package/backend/dist/services/websocketTicketService.js +4 -1
- package/backend/dist/services/websocketTicketService.js.map +1 -1
- package/backend/dist/services/workAccessService.d.ts +1 -0
- package/backend/dist/services/workAccessService.d.ts.map +1 -1
- package/backend/dist/services/workAccessService.js +3 -5
- package/backend/dist/services/workAccessService.js.map +1 -1
- package/backend/dist/services/workEventService.d.ts.map +1 -1
- package/backend/dist/services/workEventService.js +74 -18
- package/backend/dist/services/workEventService.js.map +1 -1
- package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeService.js +19 -1
- package/backend/dist/services/workRuntimeService.js.map +1 -1
- package/backend/dist/utils/pluginUpload.d.ts.map +1 -1
- package/backend/dist/utils/pluginUpload.js +10 -7
- package/backend/dist/utils/pluginUpload.js.map +1 -1
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +25 -1
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/dist/workTerminalServer.d.ts.map +1 -1
- package/backend/dist/workTerminalServer.js +19 -1
- package/backend/dist/workTerminalServer.js.map +1 -1
- package/backend/dist/worker.js +4 -2
- package/backend/dist/worker.js.map +1 -1
- package/backend/package.json +1 -1
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-CudKJ5Bs.js +27 -0
- package/frontend/dist/assets/index-DBaj1DRj.css +1 -0
- package/frontend/dist/index.html +11 -11
- package/frontend/dist/js/ArtifactContainer-CigyGLnW.js +1 -0
- package/frontend/dist/js/{ArtifactDemoPage-B0gN3YTl.js → ArtifactDemoPage-CITpRQXa.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-CLBXQkZs.js → ArtifactSandboxFrame-C8_7sKm1.js} +2 -2
- package/frontend/dist/js/ArtifactSlideOutPanel-D1f9zUZN.js +1 -0
- package/frontend/dist/js/ChatPage-D-Q1SGlI.js +23 -0
- package/frontend/dist/js/FirstTimeSetup-DwvqU8Jk.js +1 -0
- package/frontend/dist/js/{GalleryPage-W4uI043I.js → GalleryPage-CFU6YW9r.js} +1 -1
- package/frontend/dist/js/{HomePage-DlxehbyO.js → HomePage-CEoZABYG.js} +1 -1
- package/frontend/dist/js/{LibreClawPage-CgD0_G_F.js → LibreClawPage-VV4sD4-v.js} +1 -1
- package/frontend/dist/js/{ModelManagerModals-D7jIT3v1.js → ModelManagerModals-CZAWUxjI.js} +1 -1
- package/frontend/dist/js/{ModelSelector-BYxJU1UV.js → ModelSelector-DVN9nTCB.js} +1 -1
- package/frontend/dist/js/ModelsPage-Ce41crA_.js +2 -0
- package/frontend/dist/js/{NotesPage-CDECOl4V.js → NotesPage-BV08Gh2X.js} +1 -1
- package/frontend/dist/js/{PersonasPage-CjSLPrgS.js → PersonasPage-CSpK5FvR.js} +2 -2
- package/frontend/dist/js/{PluginUsagePage-DJOXJuH4.js → PluginUsagePage-B9YPhusn.js} +1 -1
- package/frontend/dist/js/{RichMessageContent-Debeh6bi.js → RichMessageContent-C3qddYMx.js} +1 -1
- package/frontend/dist/js/SettingsModal-CQSumlFr.js +1 -0
- package/frontend/dist/js/SystemPage-7KbZlMb_.js +1 -0
- package/frontend/dist/js/UserManagementPage-DnyW0Wmx.js +1 -0
- package/frontend/dist/js/WorkPage-B3cuSSwH.js +6 -0
- package/frontend/dist/js/api-DSzsasYT.js +14 -0
- package/frontend/dist/js/{appStore-DKwC2vgn.js → appStore-CYhqdjge.js} +2 -2
- package/frontend/dist/js/ar-D0z0P4rC.js +2 -0
- package/frontend/dist/js/{authStore-OaTuapL7.js → authStore-CnSn5zM7.js} +3 -3
- package/frontend/dist/js/bn-BYYU41A7.js +2 -0
- package/frontend/dist/js/chatStore-P1aGCgiF.js +3 -0
- package/frontend/dist/js/{config-DPEEuTAe.js → config-EEx_rELa.js} +1 -1
- package/frontend/dist/js/cs-BFx6KNZe.js +2 -0
- package/frontend/dist/js/da-DSDuY5Du.js +2 -0
- package/frontend/dist/js/de-YqhP0FmX.js +2 -0
- package/frontend/dist/js/es-Dpr_y1cN.js +2 -0
- package/frontend/dist/js/{estree-ChwJsD2w.js → estree-Dc6SMAYy.js} +1 -1
- package/frontend/dist/js/fr-nVg_iAEC.js +2 -0
- package/frontend/dist/js/hi-gHKYCgkP.js +2 -0
- package/frontend/dist/js/{html-DFrVjciF.js → html-Bax5_Bzw.js} +1 -1
- package/frontend/dist/js/id-C_yID1Zk.js +2 -0
- package/frontend/dist/js/is-MoRPLbOM.js +2 -0
- package/frontend/dist/js/it-BZTscLxR.js +2 -0
- package/frontend/dist/js/ja-DpIbtmZq.js +2 -0
- package/frontend/dist/js/ko-LkWggmjU.js +2 -0
- package/frontend/dist/js/{markdown-FLLx7b67.js → markdown-DxaneQad.js} +1 -1
- package/frontend/dist/js/{messageCodeStyles-CBkkZY4n.js → messageCodeStyles-CxXrl4jD.js} +2 -2
- package/frontend/dist/js/ms-p3XiQSAo.js +2 -0
- package/frontend/dist/js/nl-sEHHO2Kb.js +2 -0
- package/frontend/dist/js/pl-D2GD6PDW.js +2 -0
- package/frontend/dist/js/pt-DPkn3GUn.js +2 -0
- package/frontend/dist/js/ru-C42iHrEd.js +2 -0
- package/frontend/dist/js/{standalone-i8-5bg5p.js → standalone-CCpjftAD.js} +1 -1
- package/frontend/dist/js/sv-CGvHo4os.js +2 -0
- package/frontend/dist/js/th-BaDNpCdY.js +2 -0
- package/frontend/dist/js/tr-BHQvi1cN.js +2 -0
- package/frontend/dist/js/{typescript-CQ3x-Uni.js → typescript-DtQEF6kq.js} +1 -1
- package/frontend/dist/js/{ui-BHhR3Vok.js → ui-CUMAcARd.js} +2 -2
- package/frontend/dist/js/{ui-vendor-D3Ahl1x5.js → ui-vendor-54SgntkX.js} +1 -1
- package/frontend/dist/js/uk-7pNGS1b2.js +2 -0
- package/frontend/dist/js/vi-DgAfh6mo.js +2 -0
- package/frontend/dist/js/{workApi-Czu69yWS.js → workApi-vRuNfpEf.js} +2 -2
- package/frontend/dist/js/zh-FRTz6ZqA.js +2 -0
- package/package.json +2 -2
- package/frontend/dist/assets/index-CUhEG7E2.js +0 -33
- package/frontend/dist/assets/index-kxtZQLV6.css +0 -1
- package/frontend/dist/js/ArtifactContainer-BTEDhBnv.js +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-CfqHPFdE.js +0 -1
- package/frontend/dist/js/ChatPage-DIkAXZWA.js +0 -23
- package/frontend/dist/js/FirstTimeSetup-B8phXGNA.js +0 -1
- package/frontend/dist/js/ModelsPage-C4XdCKP5.js +0 -2
- package/frontend/dist/js/SettingsModal-cUqAeYUP.js +0 -1
- package/frontend/dist/js/SystemPage-DxPw0GS7.js +0 -1
- package/frontend/dist/js/UserManagementPage-CEdYFe_Z.js +0 -1
- package/frontend/dist/js/WorkPage-DDVeuG_9.js +0 -6
- package/frontend/dist/js/api-IzobHGND.js +0 -14
- package/frontend/dist/js/ar-xTSvTq05.js +0 -2
- package/frontend/dist/js/bn-iYAsohEA.js +0 -2
- package/frontend/dist/js/chatStore-DXGUVLXD.js +0 -3
- package/frontend/dist/js/cs-YBHN88Hb.js +0 -2
- package/frontend/dist/js/da-D7qlLy_k.js +0 -2
- package/frontend/dist/js/de-DV671k2c.js +0 -2
- package/frontend/dist/js/es-kyBgzNq-.js +0 -2
- package/frontend/dist/js/fr-Cw1vut7I.js +0 -2
- package/frontend/dist/js/hi-DWEFC024.js +0 -2
- package/frontend/dist/js/id-DlMHjjy0.js +0 -2
- package/frontend/dist/js/is-B9E_a7dG.js +0 -2
- package/frontend/dist/js/it-DawmVbLC.js +0 -2
- package/frontend/dist/js/ja-DmVvwY3W.js +0 -2
- package/frontend/dist/js/ko-DzOPpkVs.js +0 -2
- package/frontend/dist/js/ms-DpsaOPht.js +0 -2
- package/frontend/dist/js/nl-B1d8W22n.js +0 -2
- package/frontend/dist/js/pl-nCncRVgt.js +0 -2
- package/frontend/dist/js/pt-DHw8fGuu.js +0 -2
- package/frontend/dist/js/ru-Derl_YP_.js +0 -2
- package/frontend/dist/js/sv-pJAfBVo0.js +0 -2
- package/frontend/dist/js/th-C_7fIGnk.js +0 -2
- package/frontend/dist/js/tr-CPqsJvaP.js +0 -2
- package/frontend/dist/js/uk-3o8P_kFi.js +0 -2
- package/frontend/dist/js/vi-Dv6N7OVD.js +0 -2
- package/frontend/dist/js/zh-qg5iqtPi.js +0 -2
|
@@ -59,11 +59,32 @@ export const parseJwtLifetime = (value = process.env.JWT_EXPIRES_IN) => {
|
|
|
59
59
|
return '7d';
|
|
60
60
|
};
|
|
61
61
|
export const JWT_EXPIRES_IN = parseJwtLifetime();
|
|
62
|
+
/** The configured JWT lifetime in milliseconds. */
|
|
63
|
+
export const jwtLifetimeMs = (lifetime = JWT_EXPIRES_IN) => {
|
|
64
|
+
if (typeof lifetime === 'number')
|
|
65
|
+
return lifetime * 1000;
|
|
66
|
+
const match = /^(\d+)(ms|s|m|h|d|w|y)$/i.exec(String(lifetime ?? '7d'));
|
|
67
|
+
if (!match)
|
|
68
|
+
return 7 * 24 * 60 * 60 * 1000;
|
|
69
|
+
const amount = Number.parseInt(match[1], 10);
|
|
70
|
+
const unit = match[2].toLowerCase();
|
|
71
|
+
const unitMs = {
|
|
72
|
+
ms: 1,
|
|
73
|
+
s: 1000,
|
|
74
|
+
m: 60000,
|
|
75
|
+
h: 3600000,
|
|
76
|
+
d: 86400000,
|
|
77
|
+
w: 7 * 86400000,
|
|
78
|
+
y: 365 * 86400000,
|
|
79
|
+
};
|
|
80
|
+
return amount * (unitMs[unit] ?? 86400000);
|
|
81
|
+
};
|
|
62
82
|
export class AuthService {
|
|
63
83
|
/**
|
|
64
|
-
* Generate JWT token for user
|
|
84
|
+
* Generate JWT token for user. When a server-side session id is provided
|
|
85
|
+
* the token carries it as `sid`, making the token revocable.
|
|
65
86
|
*/
|
|
66
|
-
generateToken(user) {
|
|
87
|
+
generateToken(user, sessionId) {
|
|
67
88
|
if (user.status !== 'active') {
|
|
68
89
|
throw new Error('Cannot create a session for an inactive account');
|
|
69
90
|
}
|
|
@@ -71,9 +92,20 @@ export class AuthService {
|
|
|
71
92
|
userId: user.id,
|
|
72
93
|
username: user.username,
|
|
73
94
|
role: user.role,
|
|
95
|
+
...(sessionId ? { sid: sessionId } : {}),
|
|
74
96
|
};
|
|
75
97
|
return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
|
|
76
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Create a server-side auth session and mint the JWT bound to it. All
|
|
101
|
+
* login paths (password, signup bootstrap, OAuth, OIDC) issue through
|
|
102
|
+
* here so every new token is revocable from the session inventory.
|
|
103
|
+
*/
|
|
104
|
+
async issueSession(user, metadata) {
|
|
105
|
+
const { createAuthSession } = await import('./authSessionService.js');
|
|
106
|
+
const session = await createAuthSession(user.id, metadata, Date.now() + jwtLifetimeMs());
|
|
107
|
+
return this.generateToken(user, session.id);
|
|
108
|
+
}
|
|
77
109
|
/**
|
|
78
110
|
* Verify JWT token
|
|
79
111
|
*/
|
|
@@ -94,7 +126,7 @@ export class AuthService {
|
|
|
94
126
|
/**
|
|
95
127
|
* Login user
|
|
96
128
|
*/
|
|
97
|
-
async login(username, password) {
|
|
129
|
+
async login(username, password, metadata) {
|
|
98
130
|
const user = await userModel.verifyPassword(username, password);
|
|
99
131
|
if (!user)
|
|
100
132
|
return null;
|
|
@@ -104,7 +136,10 @@ export class AuthService {
|
|
|
104
136
|
if (userPublic.status !== 'active') {
|
|
105
137
|
return { status: 'pending', user: userPublic };
|
|
106
138
|
}
|
|
107
|
-
const token = this.
|
|
139
|
+
const token = await this.issueSession(userPublic, {
|
|
140
|
+
kind: 'password',
|
|
141
|
+
...metadata,
|
|
142
|
+
});
|
|
108
143
|
return { status: 'authenticated', user: userPublic, token };
|
|
109
144
|
}
|
|
110
145
|
/**
|
|
@@ -152,7 +187,7 @@ export class AuthService {
|
|
|
152
187
|
/**
|
|
153
188
|
* Signup user
|
|
154
189
|
*/
|
|
155
|
-
async signup(username, password, email) {
|
|
190
|
+
async signup(username, password, email, metadata) {
|
|
156
191
|
try {
|
|
157
192
|
if (!(await this.canCreateLocalAccount())) {
|
|
158
193
|
logger.warn('Blocked account creation because registration is disabled');
|
|
@@ -175,7 +210,10 @@ export class AuthService {
|
|
|
175
210
|
if (user.status === 'pending') {
|
|
176
211
|
return { status: 'pending', user };
|
|
177
212
|
}
|
|
178
|
-
const token = this.
|
|
213
|
+
const token = await this.issueSession(user, {
|
|
214
|
+
kind: 'signup',
|
|
215
|
+
...metadata,
|
|
216
|
+
});
|
|
179
217
|
return { status: 'authenticated', user, token };
|
|
180
218
|
}
|
|
181
219
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,GAAyB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,SAAS,EAAc,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAyB,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,MAAM,GAAG,YAAY,CAAC,uBAAuB,CAAC,CAAC;AAErD,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,iCAAiC;AACjC,IAAI,cAAc,GAAG,OAAO,CAAC;AAC7B,IAAI,CAAC;IACH,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,cAAc,GAAG,WAAW,CAAC,OAAO,CAAC;AACvC,CAAC;AAAC,OAAO,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;AACzE,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,UAAU,GACrB,OAAO,CAAC,GAAG,CAAC,UAAU;IACtB,CAAC,GAAG,EAAE;QACJ,MAAM,eAAe,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,CAAC,IAAI,CACT,wEAAwE,CACzE,CAAC;QACF,MAAM,CAAC,IAAI,CACT,4FAA4F,CAC7F,CAAC;QACF,OAAO,eAAe,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC;AAEP,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EACR,EAAE;IAC5B,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrE,IAAI,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAChD,OAAO,UAAsC,CAAC;IAChD,CAAC;IACD,MAAM,CAAC,IAAI,CACT,0CAA0C,UAAU,cAAc,CACnE,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"authService.js","sourceRoot":"","sources":["../../src/services/authService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { StoredAuthSessionRecord } from '../persistence/index.js';
|
|
2
|
+
export declare const SESSION_REVOCATION_TOPIC = "security.sessions.revoked.v1";
|
|
3
|
+
export interface SessionRevocationEvent {
|
|
4
|
+
version: 1;
|
|
5
|
+
userId: string;
|
|
6
|
+
sessionIds: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface SessionIssueMetadata {
|
|
9
|
+
kind: string;
|
|
10
|
+
ip?: string | undefined;
|
|
11
|
+
userAgent?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
type SessionRevocationListener = (event: SessionRevocationEvent) => void;
|
|
14
|
+
/** Register for advisory revocation events (e.g. to close live sockets). */
|
|
15
|
+
export declare const registerSessionRevocationListener: (listener: SessionRevocationListener) => (() => void);
|
|
16
|
+
export declare const ensureSessionRevocationSubscription: () => Promise<void>;
|
|
17
|
+
export declare const createAuthSession: (userId: string, metadata: SessionIssueMetadata, expiresAt: number) => Promise<StoredAuthSessionRecord>;
|
|
18
|
+
/** A session usable for authentication right now, or null. */
|
|
19
|
+
export declare const getValidSession: (sessionId: string) => Promise<StoredAuthSessionRecord | null>;
|
|
20
|
+
export declare const touchSessionThrottled: (record: StoredAuthSessionRecord) => Promise<void>;
|
|
21
|
+
export declare const listSessionsForUser: (userId: string) => Promise<StoredAuthSessionRecord[]>;
|
|
22
|
+
export declare const findSessionById: (sessionId: string) => Promise<StoredAuthSessionRecord | null>;
|
|
23
|
+
export declare const revokeAuthSession: (sessionId: string, revokedBy: string) => Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Revoke every session for a user (optionally sparing the current one).
|
|
26
|
+
* Also stamps the invalid-before epoch so legacy tokens without a session
|
|
27
|
+
* id are rejected too.
|
|
28
|
+
*/
|
|
29
|
+
export declare const revokeAllAuthSessions: (userId: string, revokedBy: string, exceptSessionId?: string) => Promise<string[]>;
|
|
30
|
+
/** Epoch (ms) before which any token for this user is rejected. */
|
|
31
|
+
export declare const getTokenInvalidBefore: (userId: string) => Promise<number>;
|
|
32
|
+
export declare const closeSessionRevocationSubscription: () => Promise<void>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=authSessionService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authSessionService.d.ts","sourceRoot":"","sources":["../../src/services/authSessionService.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAOvE,eAAO,MAAM,wBAAwB,iCAAiC,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AASD,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;AA8BzE,4EAA4E;AAC5E,eAAO,MAAM,iCAAiC,aAClC,yBAAyB,KAClC,CAAC,MAAM,IAAI,CAGb,CAAC;AAEF,eAAO,MAAM,mCAAmC,QAAa,OAAO,CAAC,IAAI,CAkBxE,CAAC;AAwCF,eAAO,MAAM,iBAAiB,WACpB,MAAM,YACJ,oBAAoB,aACnB,MAAM,KAChB,OAAO,CAAC,uBAAuB,CAoBjC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,eAAe,cACf,MAAM,KAChB,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAOxC,CAAC;AAEF,eAAO,MAAM,qBAAqB,WACxB,uBAAuB,KAC9B,OAAO,CAAC,IAAI,CAMd,CAAC;AAEF,eAAO,MAAM,mBAAmB,WACtB,MAAM,KACb,OAAO,CAAC,uBAAuB,EAAE,CACQ,CAAC;AAE7C,eAAO,MAAM,eAAe,cACf,MAAM,KAChB,OAAO,CAAC,uBAAuB,GAAG,IAAI,CACI,CAAC;AAE9C,eAAO,MAAM,iBAAiB,cACjB,MAAM,aACN,MAAM,KAChB,OAAO,CAAC,OAAO,CAUjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,WACxB,MAAM,aACH,MAAM,oBACC,MAAM,KACvB,OAAO,CAAC,MAAM,EAAE,CAalB,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,WACxB,MAAM,KACb,OAAO,CAAC,MAAM,CAIhB,CAAC;AAEF,eAAO,MAAM,kCAAkC,QAAa,OAAO,CAAC,IAAI,CAKvE,CAAC"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Server-side auth sessions (IAM-04).
|
|
19
|
+
*
|
|
20
|
+
* Every issued JWT is bound to an `auth_sessions` row through its `sid`
|
|
21
|
+
* claim. The database is authoritative: a revoked or expired row makes the
|
|
22
|
+
* token unusable on the next request regardless of the JWT lifetime. The
|
|
23
|
+
* coordinator topic is an advisory fan-out that lets other replicas and live
|
|
24
|
+
* WebSocket connections react immediately.
|
|
25
|
+
*
|
|
26
|
+
* Tokens issued before this feature carry no `sid`. They stay valid until
|
|
27
|
+
* expiry unless the user (or an administrator) revokes all sessions, which
|
|
28
|
+
* also records a per-user invalid-before timestamp that rejects any token
|
|
29
|
+
* minted earlier — so "sign out everywhere" is complete even for legacy
|
|
30
|
+
* tokens.
|
|
31
|
+
*/
|
|
32
|
+
import { randomUUID } from 'node:crypto';
|
|
33
|
+
import { getInitializedCoordinator } from '../platform/coordination/service.js';
|
|
34
|
+
import { getPersistence } from '../persistence/index.js';
|
|
35
|
+
import { encryptionService } from './encryptionService.js';
|
|
36
|
+
import { getSystemSetting, setSystemSetting } from './systemSettingsService.js';
|
|
37
|
+
import { createLogger } from '../utils/logger.js';
|
|
38
|
+
const logger = createLogger('services:auth-sessions');
|
|
39
|
+
export const SESSION_REVOCATION_TOPIC = 'security.sessions.revoked.v1';
|
|
40
|
+
const LAST_SEEN_THROTTLE_MS = 60000;
|
|
41
|
+
const PURGE_EVERY_N_READS = 1000;
|
|
42
|
+
let readsSincePurge = 0;
|
|
43
|
+
const invalidBeforeKey = (userId) => `auth_token_invalid_before:${userId}`;
|
|
44
|
+
const listeners = new Set();
|
|
45
|
+
let subscription;
|
|
46
|
+
const isRevocationEvent = (value) => {
|
|
47
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
48
|
+
return false;
|
|
49
|
+
const candidate = value;
|
|
50
|
+
return (candidate.version === 1 &&
|
|
51
|
+
typeof candidate.userId === 'string' &&
|
|
52
|
+
Array.isArray(candidate.sessionIds) &&
|
|
53
|
+
candidate.sessionIds.every(id => typeof id === 'string'));
|
|
54
|
+
};
|
|
55
|
+
const dispatch = (event) => {
|
|
56
|
+
for (const listener of [...listeners]) {
|
|
57
|
+
try {
|
|
58
|
+
listener(event);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// The database stays authoritative; a broken consumer must not stop
|
|
62
|
+
// other live connections from being closed.
|
|
63
|
+
logger.warn('A session revocation listener failed');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
/** Register for advisory revocation events (e.g. to close live sockets). */
|
|
68
|
+
export const registerSessionRevocationListener = (listener) => {
|
|
69
|
+
listeners.add(listener);
|
|
70
|
+
return () => listeners.delete(listener);
|
|
71
|
+
};
|
|
72
|
+
export const ensureSessionRevocationSubscription = async () => {
|
|
73
|
+
const coordinator = getInitializedCoordinator();
|
|
74
|
+
if (!coordinator)
|
|
75
|
+
return;
|
|
76
|
+
if (subscription?.coordinator === coordinator)
|
|
77
|
+
return;
|
|
78
|
+
const previous = subscription;
|
|
79
|
+
subscription = undefined;
|
|
80
|
+
if (previous)
|
|
81
|
+
await previous.unsubscribe().catch(() => undefined);
|
|
82
|
+
const unsubscribe = await coordinator.subscribe(SESSION_REVOCATION_TOPIC, event => {
|
|
83
|
+
if (!isRevocationEvent(event.payload)) {
|
|
84
|
+
logger.warn('Ignoring an invalid session revocation message');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
dispatch(event.payload);
|
|
88
|
+
});
|
|
89
|
+
subscription = { coordinator, unsubscribe };
|
|
90
|
+
};
|
|
91
|
+
const publishRevocation = async (userId, sessionIds) => {
|
|
92
|
+
const event = { version: 1, userId, sessionIds };
|
|
93
|
+
// Local listeners first: revocation must reach this replica's sockets even
|
|
94
|
+
// when the coordinator is degraded.
|
|
95
|
+
dispatch(event);
|
|
96
|
+
try {
|
|
97
|
+
const coordinator = getInitializedCoordinator();
|
|
98
|
+
if (!coordinator)
|
|
99
|
+
return;
|
|
100
|
+
await ensureSessionRevocationSubscription();
|
|
101
|
+
await coordinator.publish(SESSION_REVOCATION_TOPIC, event);
|
|
102
|
+
await coordinator.revoke(`user-sessions:${userId}`).catch(() => undefined);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// The revoked rows are already committed; every replica enforces them on
|
|
106
|
+
// its next database read even without the advisory fan-out.
|
|
107
|
+
logger.warn('Session revocation publication failed');
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const security = () => getPersistence(encryptionService).repositories.security;
|
|
111
|
+
const purgeOpportunistically = async () => {
|
|
112
|
+
readsSincePurge += 1;
|
|
113
|
+
if (readsSincePurge < PURGE_EVERY_N_READS)
|
|
114
|
+
return;
|
|
115
|
+
readsSincePurge = 0;
|
|
116
|
+
try {
|
|
117
|
+
// Keep expired rows for seven days so the session inventory can show
|
|
118
|
+
// recently expired devices before they disappear.
|
|
119
|
+
await security().authSessions.deleteExpired(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.warn('Expired session purge failed', { error });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
export const createAuthSession = async (userId, metadata, expiresAt) => {
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
const record = {
|
|
128
|
+
id: randomUUID(),
|
|
129
|
+
user_id: userId,
|
|
130
|
+
kind: metadata.kind,
|
|
131
|
+
ip_hash: null,
|
|
132
|
+
user_agent: metadata.userAgent?.slice(0, 256) ?? null,
|
|
133
|
+
created_at: now,
|
|
134
|
+
last_seen_at: now,
|
|
135
|
+
expires_at: expiresAt,
|
|
136
|
+
revoked_at: null,
|
|
137
|
+
revoked_by: null,
|
|
138
|
+
};
|
|
139
|
+
if (metadata.ip) {
|
|
140
|
+
const { hashClientIp } = await import('./securityAuditService.js');
|
|
141
|
+
record.ip_hash = hashClientIp(metadata.ip);
|
|
142
|
+
}
|
|
143
|
+
await security().authSessions.insert(record);
|
|
144
|
+
return record;
|
|
145
|
+
};
|
|
146
|
+
/** A session usable for authentication right now, or null. */
|
|
147
|
+
export const getValidSession = async (sessionId) => {
|
|
148
|
+
const record = await security().authSessions.findById(sessionId);
|
|
149
|
+
await purgeOpportunistically();
|
|
150
|
+
if (!record)
|
|
151
|
+
return null;
|
|
152
|
+
if (record.revoked_at !== null)
|
|
153
|
+
return null;
|
|
154
|
+
if (record.expires_at <= Date.now())
|
|
155
|
+
return null;
|
|
156
|
+
return record;
|
|
157
|
+
};
|
|
158
|
+
export const touchSessionThrottled = async (record) => {
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
if (now - record.last_seen_at < LAST_SEEN_THROTTLE_MS)
|
|
161
|
+
return;
|
|
162
|
+
await security()
|
|
163
|
+
.authSessions.touch(record.id, now)
|
|
164
|
+
.catch(() => undefined);
|
|
165
|
+
};
|
|
166
|
+
export const listSessionsForUser = (userId) => security().authSessions.listByUser(userId);
|
|
167
|
+
export const findSessionById = (sessionId) => security().authSessions.findById(sessionId);
|
|
168
|
+
export const revokeAuthSession = async (sessionId, revokedBy) => {
|
|
169
|
+
const record = await security().authSessions.findById(sessionId);
|
|
170
|
+
if (!record)
|
|
171
|
+
return false;
|
|
172
|
+
const revoked = await security().authSessions.revoke(sessionId, Date.now(), revokedBy);
|
|
173
|
+
if (revoked)
|
|
174
|
+
await publishRevocation(record.user_id, [sessionId]);
|
|
175
|
+
return revoked;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Revoke every session for a user (optionally sparing the current one).
|
|
179
|
+
* Also stamps the invalid-before epoch so legacy tokens without a session
|
|
180
|
+
* id are rejected too.
|
|
181
|
+
*/
|
|
182
|
+
export const revokeAllAuthSessions = async (userId, revokedBy, exceptSessionId) => {
|
|
183
|
+
const revokedIds = await security().authSessions.revokeAllForUser(userId, Date.now(), revokedBy, exceptSessionId);
|
|
184
|
+
if (!exceptSessionId) {
|
|
185
|
+
// A full sign-out invalidates sid-less legacy tokens as well.
|
|
186
|
+
await setSystemSetting(invalidBeforeKey(userId), String(Date.now()));
|
|
187
|
+
}
|
|
188
|
+
if (revokedIds.length > 0)
|
|
189
|
+
await publishRevocation(userId, revokedIds);
|
|
190
|
+
return revokedIds;
|
|
191
|
+
};
|
|
192
|
+
/** Epoch (ms) before which any token for this user is rejected. */
|
|
193
|
+
export const getTokenInvalidBefore = async (userId) => {
|
|
194
|
+
const value = await getSystemSetting(invalidBeforeKey(userId));
|
|
195
|
+
const parsed = Number.parseInt(value ?? '', 10);
|
|
196
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
|
197
|
+
};
|
|
198
|
+
export const closeSessionRevocationSubscription = async () => {
|
|
199
|
+
const current = subscription;
|
|
200
|
+
subscription = undefined;
|
|
201
|
+
listeners.clear();
|
|
202
|
+
if (current)
|
|
203
|
+
await current.unsubscribe().catch(() => undefined);
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=authSessionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authSessionService.js","sourceRoot":"","sources":["../../src/services/authSessionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ArchiveOwnedResource, GrantPermission, StoredResourceGrantRecord } from '../persistence/index.js';
|
|
2
|
+
export type AuthzAction = 'read' | 'write' | 'manage' | 'use';
|
|
3
|
+
export type FeatureId = 'work' | 'model-download' | 'web-search' | 'agents';
|
|
4
|
+
/** Grantable resource families reuse the portable-archive taxonomy. */
|
|
5
|
+
export type ShareableResourceType = ArchiveOwnedResource;
|
|
6
|
+
export declare const SHAREABLE_RESOURCE_TYPES: readonly ShareableResourceType[];
|
|
7
|
+
export declare const isShareableResourceType: (value: unknown) => value is ShareableResourceType;
|
|
8
|
+
export interface AuthzActor {
|
|
9
|
+
userId: string;
|
|
10
|
+
role?: string | undefined;
|
|
11
|
+
status?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export type AuthzResource = {
|
|
14
|
+
type: 'feature';
|
|
15
|
+
id: FeatureId;
|
|
16
|
+
} | {
|
|
17
|
+
type: ShareableResourceType;
|
|
18
|
+
id: string;
|
|
19
|
+
ownerUserId?: string;
|
|
20
|
+
};
|
|
21
|
+
export interface AuthzDecision {
|
|
22
|
+
allowed: boolean;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
export declare class AuthorizationError extends Error {
|
|
26
|
+
readonly statusCode = 403;
|
|
27
|
+
constructor(message?: string);
|
|
28
|
+
}
|
|
29
|
+
/** Group memberships resolved fresh so removals apply immediately. */
|
|
30
|
+
export declare const resolveGroupIdsForUser: (userId: string) => Promise<string[]>;
|
|
31
|
+
/** Effective grants a user holds on a resource, via user and group rows. */
|
|
32
|
+
export declare const effectiveGrantsFor: (userId: string, resourceType: ShareableResourceType, resourceId: string) => Promise<StoredResourceGrantRecord[]>;
|
|
33
|
+
export declare const authorize: (actor: AuthzActor, action: AuthzAction, resource: AuthzResource) => Promise<AuthzDecision>;
|
|
34
|
+
/** Throwing variant for route and service guards. */
|
|
35
|
+
export declare const requireAuthorized: (actor: AuthzActor, action: AuthzAction, resource: AuthzResource) => Promise<AuthzDecision>;
|
|
36
|
+
export interface EffectiveAccessView {
|
|
37
|
+
userId: string;
|
|
38
|
+
role: string | null;
|
|
39
|
+
status: string | null;
|
|
40
|
+
groups: Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
}>;
|
|
44
|
+
features: Record<FeatureId, boolean>;
|
|
45
|
+
grants: Array<{
|
|
46
|
+
id: string;
|
|
47
|
+
resourceType: string;
|
|
48
|
+
resourceId: string;
|
|
49
|
+
permission: GrantPermission;
|
|
50
|
+
via: 'user' | 'group';
|
|
51
|
+
principalId: string;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* "Why can this user access this?" — the complete effective-access picture
|
|
56
|
+
* for one user: role, groups, feature gates, and every grant that reaches
|
|
57
|
+
* them directly or through a group.
|
|
58
|
+
*/
|
|
59
|
+
export declare const explainEffectiveAccess: (user: {
|
|
60
|
+
id: string;
|
|
61
|
+
role?: string;
|
|
62
|
+
status?: string;
|
|
63
|
+
}) => Promise<EffectiveAccessView>;
|
|
64
|
+
//# sourceMappingURL=authorizationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizationService.d.ts","sourceRoot":"","sources":["../../src/services/authorizationService.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAOjC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE9D,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,gBAAgB,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE5E,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAEzD,eAAO,MAAM,wBAAwB,EAAE,SAAS,qBAAqB,EAOpE,CAAC;AAEF,eAAO,MAAM,uBAAuB,UAC3B,OAAO,KACb,KAAK,IAAI,qBAEqD,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,SAAS,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,UAAU,OAAO;IAC1B,YAAY,OAAO,SAAkB,EAGpC;CACF;AAOD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,WAAY,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CACzB,CAAC;AAoChD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,WACrB,MAAM,gBACA,qBAAqB,cACvB,MAAM,KACjB,OAAO,CAAC,yBAAyB,EAAE,CAQrC,CAAC;AAEF,eAAO,MAAM,SAAS,UACb,UAAU,UACT,WAAW,YACT,aAAa,KACtB,OAAO,CAAC,aAAa,CAsCvB,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,UACrB,UAAU,UACT,WAAW,YACT,aAAa,KACtB,OAAO,CAAC,aAAa,CAIvB,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,eAAe,CAAC;QAC5B,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,SAAgB;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,KAAG,OAAO,CAAC,mBAAmB,CA2C9B,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Central authorization (IAM-01).
|
|
19
|
+
*
|
|
20
|
+
* One decision point for feature gates and shareable-resource access:
|
|
21
|
+
*
|
|
22
|
+
* role/feature permissions + ownership + direct grants + group grants
|
|
23
|
+
* = effective allow
|
|
24
|
+
*
|
|
25
|
+
* Feature gates keep their existing semantics (admin always allowed, others
|
|
26
|
+
* by the admin-controlled mode); the per-feature services now delegate here.
|
|
27
|
+
* Resource decisions are private by default: the owner has every permission,
|
|
28
|
+
* other users only what an explicit user or group grant confers. The admin
|
|
29
|
+
* role deliberately does NOT bypass resource privacy — administration is a
|
|
30
|
+
* feature-level power, not a content read.
|
|
31
|
+
*
|
|
32
|
+
* `explainEffectiveAccess` answers "why can this user access this?" for the
|
|
33
|
+
* administrator-facing effective-access view.
|
|
34
|
+
*/
|
|
35
|
+
import { getPersistence } from '../persistence/index.js';
|
|
36
|
+
import { encryptionService } from './encryptionService.js';
|
|
37
|
+
import { getWorkAccessMode } from './workAccessService.js';
|
|
38
|
+
import { getModelDownloadMode } from './modelAccessService.js';
|
|
39
|
+
import { getAgentsEnabled } from './agentAccessService.js';
|
|
40
|
+
import { getWebSearchAccessMode } from './webSearchService.js';
|
|
41
|
+
export const SHAREABLE_RESOURCE_TYPES = [
|
|
42
|
+
'session',
|
|
43
|
+
'session-folder',
|
|
44
|
+
'note',
|
|
45
|
+
'knowledge-collection',
|
|
46
|
+
'document',
|
|
47
|
+
'persona',
|
|
48
|
+
];
|
|
49
|
+
export const isShareableResourceType = (value) => typeof value === 'string' &&
|
|
50
|
+
SHAREABLE_RESOURCE_TYPES.includes(value);
|
|
51
|
+
export class AuthorizationError extends Error {
|
|
52
|
+
constructor(message = 'Access denied') {
|
|
53
|
+
super(message);
|
|
54
|
+
this.statusCode = 403;
|
|
55
|
+
this.name = 'AuthorizationError';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const security = () => getPersistence(encryptionService).repositories.security;
|
|
59
|
+
const archive = () => getPersistence(encryptionService).repositories.resources.archive;
|
|
60
|
+
/** Group memberships resolved fresh so removals apply immediately. */
|
|
61
|
+
export const resolveGroupIdsForUser = (userId) => security().groups.listGroupIdsForUser(userId);
|
|
62
|
+
const permissionSatisfies = (granted, action) => {
|
|
63
|
+
if (action === 'read' || action === 'use')
|
|
64
|
+
return true;
|
|
65
|
+
if (action === 'write')
|
|
66
|
+
return granted === 'write' || granted === 'admin';
|
|
67
|
+
return granted === 'admin';
|
|
68
|
+
};
|
|
69
|
+
const featureDecision = async (actor, featureId) => {
|
|
70
|
+
if (actor.role === 'admin')
|
|
71
|
+
return { allowed: true, reason: 'admin-role' };
|
|
72
|
+
switch (featureId) {
|
|
73
|
+
case 'work':
|
|
74
|
+
return (await getWorkAccessMode()) === 'all-users'
|
|
75
|
+
? { allowed: true, reason: 'feature-open-to-all-users' }
|
|
76
|
+
: { allowed: false, reason: 'feature-restricted-to-admins' };
|
|
77
|
+
case 'model-download':
|
|
78
|
+
return (await getModelDownloadMode()) === 'all-users'
|
|
79
|
+
? { allowed: true, reason: 'feature-open-to-all-users' }
|
|
80
|
+
: { allowed: false, reason: 'feature-restricted-to-admins' };
|
|
81
|
+
case 'web-search':
|
|
82
|
+
return (await getWebSearchAccessMode()) === 'all-users'
|
|
83
|
+
? { allowed: true, reason: 'feature-open-to-all-users' }
|
|
84
|
+
: { allowed: false, reason: 'feature-restricted-to-admins' };
|
|
85
|
+
case 'agents':
|
|
86
|
+
return (await getAgentsEnabled())
|
|
87
|
+
? { allowed: true, reason: 'feature-enabled' }
|
|
88
|
+
: { allowed: false, reason: 'feature-disabled' };
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
/** Effective grants a user holds on a resource, via user and group rows. */
|
|
92
|
+
export const effectiveGrantsFor = async (userId, resourceType, resourceId) => {
|
|
93
|
+
const groupIds = await resolveGroupIdsForUser(userId);
|
|
94
|
+
return security().grants.listEffective(resourceType, resourceId, userId, groupIds);
|
|
95
|
+
};
|
|
96
|
+
export const authorize = async (actor, action, resource) => {
|
|
97
|
+
if (actor.status !== undefined && actor.status !== 'active') {
|
|
98
|
+
return { allowed: false, reason: 'inactive-account' };
|
|
99
|
+
}
|
|
100
|
+
if (resource.type === 'feature') {
|
|
101
|
+
return featureDecision(actor, resource.id);
|
|
102
|
+
}
|
|
103
|
+
const ownerUserId = resource.ownerUserId ??
|
|
104
|
+
(await archive().ownerOf(resource.type, resource.id));
|
|
105
|
+
if (ownerUserId === actor.userId) {
|
|
106
|
+
return { allowed: true, reason: 'owner' };
|
|
107
|
+
}
|
|
108
|
+
if (ownerUserId === null) {
|
|
109
|
+
return { allowed: false, reason: 'resource-not-found' };
|
|
110
|
+
}
|
|
111
|
+
const grants = await effectiveGrantsFor(actor.userId, resource.type, resource.id);
|
|
112
|
+
const qualifying = grants.find(grant => permissionSatisfies(grant.permission, action));
|
|
113
|
+
if (qualifying) {
|
|
114
|
+
return {
|
|
115
|
+
allowed: true,
|
|
116
|
+
reason: qualifying.principal_type === 'user' ? 'direct-grant' : 'group-grant',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
allowed: false,
|
|
121
|
+
reason: grants.length > 0 ? 'insufficient-permission' : 'no-grant',
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/** Throwing variant for route and service guards. */
|
|
125
|
+
export const requireAuthorized = async (actor, action, resource) => {
|
|
126
|
+
const decision = await authorize(actor, action, resource);
|
|
127
|
+
if (!decision.allowed)
|
|
128
|
+
throw new AuthorizationError();
|
|
129
|
+
return decision;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* "Why can this user access this?" — the complete effective-access picture
|
|
133
|
+
* for one user: role, groups, feature gates, and every grant that reaches
|
|
134
|
+
* them directly or through a group.
|
|
135
|
+
*/
|
|
136
|
+
export const explainEffectiveAccess = async (user) => {
|
|
137
|
+
const actor = {
|
|
138
|
+
userId: user.id,
|
|
139
|
+
role: user.role,
|
|
140
|
+
status: user.status,
|
|
141
|
+
};
|
|
142
|
+
const groups = await security().groups.listGroupsForUser(user.id);
|
|
143
|
+
const [work, modelDownload, webSearch, agents] = await Promise.all([
|
|
144
|
+
authorize(actor, 'use', { type: 'feature', id: 'work' }),
|
|
145
|
+
authorize(actor, 'use', { type: 'feature', id: 'model-download' }),
|
|
146
|
+
authorize(actor, 'use', { type: 'feature', id: 'web-search' }),
|
|
147
|
+
authorize(actor, 'use', { type: 'feature', id: 'agents' }),
|
|
148
|
+
]);
|
|
149
|
+
const grantRows = [
|
|
150
|
+
...(await security().grants.listForPrincipal('user', user.id)),
|
|
151
|
+
];
|
|
152
|
+
for (const group of groups) {
|
|
153
|
+
grantRows.push(...(await security().grants.listForPrincipal('group', group.id)));
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
userId: user.id,
|
|
157
|
+
role: user.role ?? null,
|
|
158
|
+
status: user.status ?? null,
|
|
159
|
+
groups: groups.map(group => ({ id: group.id, name: group.name })),
|
|
160
|
+
features: {
|
|
161
|
+
work: work.allowed,
|
|
162
|
+
'model-download': modelDownload.allowed,
|
|
163
|
+
'web-search': webSearch.allowed,
|
|
164
|
+
agents: agents.allowed,
|
|
165
|
+
},
|
|
166
|
+
grants: grantRows.map(grant => ({
|
|
167
|
+
id: grant.id,
|
|
168
|
+
resourceType: grant.resource_type,
|
|
169
|
+
resourceId: grant.resource_id,
|
|
170
|
+
permission: grant.permission,
|
|
171
|
+
via: grant.principal_type,
|
|
172
|
+
principalId: grant.principal_id,
|
|
173
|
+
})),
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
//# sourceMappingURL=authorizationService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizationService.js","sourceRoot":"","sources":["../../src/services/authorizationService.ts"],"names":[],"mappings":"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"}
|