@getstrata/core 2.2.40 → 2.2.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/core/http/response.d.ts +3 -1
- package/dist/core/view/index.d.ts +1 -1
- package/dist/core/view/viewRenderer.d.ts +6 -2
- package/dist/entries/auth/membershipMiddleware.js +1 -113
- package/dist/entries/auth/membershipScope.js +0 -36
- package/dist/entries/auth/membershipService.js +1 -43
- package/dist/entries/http/authorizeMiddleware.js +2 -1
- package/dist/entries/http/bodySizeLimitMiddleware.js +2 -1
- package/dist/entries/http/memoryThrottleMiddleware.js +3 -1
- package/dist/entries/http/requireAbilityMiddleware.js +3 -2
- package/dist/entries/http/requireAuthMiddleware.js +2 -1
- package/dist/entries/http/requireGlobalAdminMiddleware.js +2 -1
- package/dist/entries/http/requirePasswordConfirmMiddleware.js +4 -2
- package/dist/entries/http/requireVerifiedMiddleware.js +3 -1
- package/dist/entries/http/response.js +10 -299
- package/dist/entries/http/scimThrottleMiddleware.js +3 -1
- package/dist/entries/http/throttleMiddleware.js +3 -1
- package/dist/framework/public-api.d.ts +2 -2
- package/dist/index.js +1301 -1276
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.42
|
|
4
|
+
|
|
5
|
+
Generate auth JSON and forms with explicit private, no-store cache policy. Add privateJsonResponse and typed view namespace header defaults while preserving normal public defaults, native cookies, per-response overrides and HTMX representation behavior. Cover packed APIs and actual generated cookie/token/JWT/CSRF/profile/form HTTP responses.
|
|
6
|
+
|
|
7
|
+
Use the canonical error serializer for core JSON admission denials so authentication, policy/admin/ability, verification, password confirmation and body-limit failures retain no-store. Preserve exact status/message contracts, keep checker details private and retain HTML redirects/throws and successful response headers. Add packed producer regressions and a native detail-projection case.
|
|
8
|
+
|
|
9
|
+
## 2.2.41
|
|
10
|
+
|
|
11
|
+
Make framework HTML/JSON error responses non-storeable, including configured/fallback renderers and custom-body 404 responses; retain success-response cache policy and add packed header regressions.
|
|
12
|
+
|
|
13
|
+
Fix packaged error wrappers ignoring the configured application error renderer; preserve API JSON precedence and add packed configured/default rendering regressions.
|
|
14
|
+
|
|
15
|
+
Fix packaged membership middleware and default service ignoring the configured application lookup. Use the canonical membership context while preserving explicit service injection and request isolation. Add mandatory packed external-app coverage for concurrent users, replacement configuration and propagated lookup failures.
|
|
16
|
+
|
|
3
17
|
## 2.2.40
|
|
4
18
|
|
|
5
19
|
Fix shared DNS fixture state in packaged outbound consumers. safeFetch and GitHub OAuth now use the canonical safeUrl resolver while retaining DNS pinning, private-address validation and request deadlines. Add mandatory packed external-app regressions for resolver replacement and private-target refusal; document controlled-provider qualification limits.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Middleware } from "./middleware";
|
|
2
2
|
declare function jsonResponse(data: unknown, init?: ResponseInit): Response;
|
|
3
|
+
/** Explicit private payloads (identity, credentials and CSRF); public JSON defaults stay unchanged. */
|
|
4
|
+
declare function privateJsonResponse(data: unknown, init?: ResponseInit): Response;
|
|
3
5
|
declare function createdResponse(data: unknown, init?: ResponseInit): Response;
|
|
4
6
|
declare function noContentResponse(): Response;
|
|
5
7
|
declare function errorResponse(error: unknown): Response;
|
|
@@ -9,4 +11,4 @@ declare function withJsonErrorHandling<THandler extends (request: Request) => Re
|
|
|
9
11
|
declare function withJsonErrorHandling<TArgs extends unknown[]>(handler: (...args: TArgs) => Response | Promise<Response>): (...args: TArgs) => Promise<Response>;
|
|
10
12
|
declare function createJsonErrorMiddleware(): Middleware;
|
|
11
13
|
declare function createWebErrorMiddleware(): Middleware;
|
|
12
|
-
export { createdResponse, createJsonErrorMiddleware, createWebErrorMiddleware, errorResponse, jsonResponse, noContentResponse, withErrorHandling, withJsonErrorHandling, };
|
|
14
|
+
export { createdResponse, createJsonErrorMiddleware, createWebErrorMiddleware, errorResponse, jsonResponse, noContentResponse, privateJsonResponse, withErrorHandling, withJsonErrorHandling, };
|
|
@@ -2,7 +2,7 @@ export type { LayoutDataResolver, RenderOptions } from "./etaViewEngine";
|
|
|
2
2
|
export { DEFAULT_VIEWS_DIRECTORY, EtaViewEngine } from "./etaViewEngine";
|
|
3
3
|
export { htmlResponse, isHtmxRequest, redirectResponse, rssResponse, textResponse, xmlResponse, } from "./htmlResponse";
|
|
4
4
|
export type { ViewEngine } from "./viewEngine";
|
|
5
|
-
export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewResponseOptions, } from "./viewRenderer";
|
|
5
|
+
export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewNamespaceOptions, ViewResponseOptions, } from "./viewRenderer";
|
|
6
6
|
export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer } from "./viewRenderer";
|
|
7
7
|
export type { WebErrorViewInput, WebErrorViewOptions } from "./webErrorView";
|
|
8
8
|
export { configureWebErrorView, errorTemplateName, htmlErrorResponse, notFoundHtmlResponse, renderKernelErrorChrome, renderWebErrorHtml, } from "./webErrorView";
|
|
@@ -10,6 +10,10 @@ interface HtmxViewResponseOptions extends ViewResponseOptions {
|
|
|
10
10
|
/** Optional fragment template; otherwise render the page template without its layout. */
|
|
11
11
|
fragment?: string;
|
|
12
12
|
}
|
|
13
|
+
interface ViewNamespaceOptions {
|
|
14
|
+
/** Copied response defaults. Per-render headers override defaults with the same name. */
|
|
15
|
+
headers?: ConstructorParameters<typeof Headers>[0];
|
|
16
|
+
}
|
|
13
17
|
interface NamespacedViewRenderer {
|
|
14
18
|
render: ViewRenderer["render"];
|
|
15
19
|
renderHtmx: ViewRenderer["renderHtmx"];
|
|
@@ -20,11 +24,11 @@ declare class ViewRenderer {
|
|
|
20
24
|
private readonly defaultLayout?;
|
|
21
25
|
constructor(engine: ViewEngine, defaultLayout?: string | false | undefined);
|
|
22
26
|
/** Explicit template prefix; layout names and response contracts remain unchanged. */
|
|
23
|
-
namespace(prefix: string): NamespacedViewRenderer;
|
|
27
|
+
namespace(prefix: string, defaults?: ViewNamespaceOptions): NamespacedViewRenderer;
|
|
24
28
|
render(name: string, data?: Record<string, unknown>, options?: ViewResponseOptions): Promise<Response>;
|
|
25
29
|
/** Opt-in representation selection; auth/CSRF/tenancy remain the route's responsibility. */
|
|
26
30
|
renderHtmx(name: string, data?: Record<string, unknown>, options?: HtmxViewResponseOptions): Promise<Response>;
|
|
27
31
|
}
|
|
28
32
|
declare const CORE_VIEW_RENDERER_TOKEN: import("../contracts/container").ServiceToken<ViewRenderer>;
|
|
29
|
-
export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewResponseOptions };
|
|
33
|
+
export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewNamespaceOptions, ViewResponseOptions, };
|
|
30
34
|
export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer };
|
|
@@ -1,118 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var __esm = (fn, res, err) => () => {
|
|
3
|
-
if (fn)
|
|
4
|
-
try {
|
|
5
|
-
res = fn(fn = 0);
|
|
6
|
-
} catch (e) {
|
|
7
|
-
err = [e];
|
|
8
|
-
}
|
|
9
|
-
if (err)
|
|
10
|
-
throw err[0];
|
|
11
|
-
return res;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
15
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
16
|
-
function createAsyncContextStore(key) {
|
|
17
|
-
const symbol = Symbol.for(key);
|
|
18
|
-
const globalRecord = globalThis;
|
|
19
|
-
const existing = globalRecord[symbol];
|
|
20
|
-
if (existing) {
|
|
21
|
-
return existing;
|
|
22
|
-
}
|
|
23
|
-
const store = new AsyncLocalStorage;
|
|
24
|
-
globalRecord[symbol] = store;
|
|
25
|
-
return store;
|
|
26
|
-
}
|
|
27
|
-
var init_asyncContextStore = () => {};
|
|
28
|
-
|
|
29
|
-
// ../../src/core/auth/membershipContext.ts
|
|
30
|
-
init_asyncContextStore();
|
|
31
|
-
|
|
32
|
-
// ../../src/core/auth/accessControl.ts
|
|
33
|
-
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
34
|
-
|
|
35
|
-
// ../../src/core/auth/authContext.ts
|
|
36
|
-
init_asyncContextStore();
|
|
37
|
-
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
38
|
-
var credentialSourceContext = createAsyncContextStore("@getstrata/credentialSource");
|
|
39
|
-
function currentAuthUser() {
|
|
40
|
-
return authContext.getStore() ?? null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ../../src/core/auth/accessControl.ts
|
|
44
|
-
var ROLE_RANK = {
|
|
45
|
-
member: 1,
|
|
46
|
-
admin: 2,
|
|
47
|
-
owner: 3
|
|
48
|
-
};
|
|
49
|
-
function isGlobalAdmin(user) {
|
|
50
|
-
return user?.role === "admin";
|
|
51
|
-
}
|
|
52
|
-
function hasMinimumOrgRole(role, minimum) {
|
|
53
|
-
if (!role) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
return ROLE_RANK[role] >= ROLE_RANK[minimum];
|
|
57
|
-
}
|
|
58
|
-
function requireAuthenticatedUser() {
|
|
59
|
-
const user = currentAuthUser();
|
|
60
|
-
if (!user) {
|
|
61
|
-
throw new ForbiddenError("Authentication required.");
|
|
62
|
-
}
|
|
63
|
-
return user;
|
|
64
|
-
}
|
|
65
|
-
function resolveUserId(user) {
|
|
66
|
-
const userId = typeof user.id === "number" ? user.id : Number(user.id);
|
|
67
|
-
if (!Number.isInteger(userId) || userId <= 0) {
|
|
68
|
-
throw new ForbiddenError("Invalid authenticated user.");
|
|
69
|
-
}
|
|
70
|
-
return userId;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ../../src/core/auth/membershipContext.ts
|
|
74
|
-
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
75
|
-
var uninitializedMembershipLookup = {
|
|
76
|
-
async listForUser() {
|
|
77
|
-
return [];
|
|
78
|
-
},
|
|
79
|
-
async findMembership() {
|
|
80
|
-
return null;
|
|
81
|
-
},
|
|
82
|
-
async listForOrganization() {
|
|
83
|
-
return [];
|
|
84
|
-
},
|
|
85
|
-
async addMember() {
|
|
86
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
87
|
-
},
|
|
88
|
-
async removeMember() {
|
|
89
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
90
|
-
},
|
|
91
|
-
async updateMemberRole() {
|
|
92
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
var membershipRepository = uninitializedMembershipLookup;
|
|
96
|
-
function resolveMembershipLookup() {
|
|
97
|
-
return membershipRepository;
|
|
98
|
-
}
|
|
99
|
-
async function runWithMembershipContext(callback) {
|
|
100
|
-
const user = currentAuthUser();
|
|
101
|
-
if (!user || isGlobalAdmin(user)) {
|
|
102
|
-
return await callback();
|
|
103
|
-
}
|
|
104
|
-
const memberships = await membershipRepository.listForUser(resolveUserId(user));
|
|
105
|
-
const context = {
|
|
106
|
-
organizationIds: memberships.map((membership) => membership.organization_id),
|
|
107
|
-
rolesByOrganizationId: new Map(memberships.map((membership) => [membership.organization_id, membership.role]))
|
|
108
|
-
};
|
|
109
|
-
return await membershipContext.run(context, callback);
|
|
110
|
-
}
|
|
111
|
-
function currentOrganizationIds() {
|
|
112
|
-
return membershipContext.getStore()?.organizationIds ?? [];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
2
|
// ../../src/core/auth/membershipContextMiddleware.ts
|
|
3
|
+
import { runWithMembershipContext } from "@getstrata/core/auth/membershipContext";
|
|
116
4
|
function createMembershipContextMiddleware() {
|
|
117
5
|
return async (_request, next) => {
|
|
118
6
|
return await runWithMembershipContext(async () => await next());
|
|
@@ -74,42 +74,6 @@ function resolveUserId(user) {
|
|
|
74
74
|
// ../../src/core/auth/membershipContext.ts
|
|
75
75
|
init_asyncContextStore();
|
|
76
76
|
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
77
|
-
var uninitializedMembershipLookup = {
|
|
78
|
-
async listForUser() {
|
|
79
|
-
return [];
|
|
80
|
-
},
|
|
81
|
-
async findMembership() {
|
|
82
|
-
return null;
|
|
83
|
-
},
|
|
84
|
-
async listForOrganization() {
|
|
85
|
-
return [];
|
|
86
|
-
},
|
|
87
|
-
async addMember() {
|
|
88
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
89
|
-
},
|
|
90
|
-
async removeMember() {
|
|
91
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
92
|
-
},
|
|
93
|
-
async updateMemberRole() {
|
|
94
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
var membershipRepository = uninitializedMembershipLookup;
|
|
98
|
-
function resolveMembershipLookup() {
|
|
99
|
-
return membershipRepository;
|
|
100
|
-
}
|
|
101
|
-
async function runWithMembershipContext(callback) {
|
|
102
|
-
const user = currentAuthUser();
|
|
103
|
-
if (!user || isGlobalAdmin(user)) {
|
|
104
|
-
return await callback();
|
|
105
|
-
}
|
|
106
|
-
const memberships = await membershipRepository.listForUser(resolveUserId(user));
|
|
107
|
-
const context = {
|
|
108
|
-
organizationIds: memberships.map((membership) => membership.organization_id),
|
|
109
|
-
rolesByOrganizationId: new Map(memberships.map((membership) => [membership.organization_id, membership.role]))
|
|
110
|
-
};
|
|
111
|
-
return await membershipContext.run(context, callback);
|
|
112
|
-
}
|
|
113
77
|
function currentOrganizationIds() {
|
|
114
78
|
return membershipContext.getStore()?.organizationIds ?? [];
|
|
115
79
|
}
|
|
@@ -27,6 +27,7 @@ function createAsyncContextStore(key) {
|
|
|
27
27
|
var init_asyncContextStore = () => {};
|
|
28
28
|
|
|
29
29
|
// ../../src/core/auth/membershipService.ts
|
|
30
|
+
import { resolveMembershipLookup } from "@getstrata/core/auth/membershipContext";
|
|
30
31
|
import { ForbiddenError as ForbiddenError2 } from "@getstrata/core/errors/http";
|
|
31
32
|
|
|
32
33
|
// ../../src/core/auth/accessControl.ts
|
|
@@ -70,49 +71,6 @@ function resolveUserId(user) {
|
|
|
70
71
|
return userId;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
// ../../src/core/auth/membershipContext.ts
|
|
74
|
-
init_asyncContextStore();
|
|
75
|
-
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
76
|
-
var uninitializedMembershipLookup = {
|
|
77
|
-
async listForUser() {
|
|
78
|
-
return [];
|
|
79
|
-
},
|
|
80
|
-
async findMembership() {
|
|
81
|
-
return null;
|
|
82
|
-
},
|
|
83
|
-
async listForOrganization() {
|
|
84
|
-
return [];
|
|
85
|
-
},
|
|
86
|
-
async addMember() {
|
|
87
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
88
|
-
},
|
|
89
|
-
async removeMember() {
|
|
90
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
91
|
-
},
|
|
92
|
-
async updateMemberRole() {
|
|
93
|
-
throw new Error("configureMembershipLookup() must be called before mutating memberships.");
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
var membershipRepository = uninitializedMembershipLookup;
|
|
97
|
-
function resolveMembershipLookup() {
|
|
98
|
-
return membershipRepository;
|
|
99
|
-
}
|
|
100
|
-
async function runWithMembershipContext(callback) {
|
|
101
|
-
const user = currentAuthUser();
|
|
102
|
-
if (!user || isGlobalAdmin(user)) {
|
|
103
|
-
return await callback();
|
|
104
|
-
}
|
|
105
|
-
const memberships = await membershipRepository.listForUser(resolveUserId(user));
|
|
106
|
-
const context = {
|
|
107
|
-
organizationIds: memberships.map((membership) => membership.organization_id),
|
|
108
|
-
rolesByOrganizationId: new Map(memberships.map((membership) => [membership.organization_id, membership.role]))
|
|
109
|
-
};
|
|
110
|
-
return await membershipContext.run(context, callback);
|
|
111
|
-
}
|
|
112
|
-
function currentOrganizationIds() {
|
|
113
|
-
return membershipContext.getStore()?.organizationIds ?? [];
|
|
114
|
-
}
|
|
115
|
-
|
|
116
74
|
// ../../src/core/auth/resolveMembershipService.ts
|
|
117
75
|
import { resolveApplicationDependencies } from "@getstrata/core/runtime/applicationRegistry";
|
|
118
76
|
function resolveMembershipService() {
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// ../../src/core/http/authorizeMiddleware.ts
|
|
3
3
|
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
4
4
|
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
5
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
5
6
|
function createAuthorizeMiddleware(gate, auth, resource, action) {
|
|
6
7
|
return async (request, next) => {
|
|
7
8
|
const user = currentAuthUser() ?? await auth.resolve(request);
|
|
8
9
|
if (!gate.allows(resource, action, user)) {
|
|
9
10
|
const error = new ForbiddenError;
|
|
10
|
-
return
|
|
11
|
+
return errorResponse(error);
|
|
11
12
|
}
|
|
12
13
|
return await next();
|
|
13
14
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/bodySizeLimitMiddleware.ts
|
|
3
3
|
import { PayloadTooLargeError } from "@getstrata/core/errors/http";
|
|
4
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
4
5
|
var guards = new WeakMap;
|
|
5
6
|
function guardBody(request, maxBytes) {
|
|
6
7
|
const existing = guards.get(request);
|
|
@@ -76,7 +77,7 @@ function createBodySizeLimitMiddleware(maxBytes = resolveMaxBodyBytes()) {
|
|
|
76
77
|
return async (request, next) => {
|
|
77
78
|
const tooLarge = () => {
|
|
78
79
|
const error = new PayloadTooLargeError(`Request body exceeds the ${maxBytes} byte limit.`);
|
|
79
|
-
return
|
|
80
|
+
return errorResponse(error);
|
|
80
81
|
};
|
|
81
82
|
const length = Number(request.headers.get("content-length"));
|
|
82
83
|
if (Number.isFinite(length) && length > maxBytes)
|
|
@@ -403,7 +403,9 @@ async function renderWebErrorHtml(input) {
|
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
405
|
async function htmlErrorResponse(input) {
|
|
406
|
-
|
|
406
|
+
const response = htmlResponse(await renderWebErrorHtml(input), { status: input.status });
|
|
407
|
+
response.headers.set("Cache-Control", "no-store");
|
|
408
|
+
return response;
|
|
407
409
|
}
|
|
408
410
|
|
|
409
411
|
// ../../src/core/http/contentNegotiation.ts
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/requireAbilityMiddleware.ts
|
|
3
3
|
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
4
|
-
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
4
|
+
import { ForbiddenError, HttpError } from "@getstrata/core/errors/http";
|
|
5
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
5
6
|
|
|
6
7
|
// ../../src/core/runtime/frontendMode.ts
|
|
7
8
|
var FRONTEND_MODES = ["api", "server-htmx", "spa-react", "hybrid"];
|
|
@@ -80,7 +81,7 @@ function createRequireAbilityMiddleware(abilityChecker) {
|
|
|
80
81
|
if (isViewsEnabled() && !requestPrefersJson(request)) {
|
|
81
82
|
throw error;
|
|
82
83
|
}
|
|
83
|
-
return
|
|
84
|
+
return errorResponse(new HttpError(error.status, error.message));
|
|
84
85
|
}
|
|
85
86
|
throw error;
|
|
86
87
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/requireAuthMiddleware.ts
|
|
3
3
|
import { UnauthorizedError } from "@getstrata/core/errors/http";
|
|
4
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
4
5
|
function createRequireAuthMiddleware(auth) {
|
|
5
6
|
return async (request, next) => {
|
|
6
7
|
if (!await auth.check(request)) {
|
|
7
8
|
const error = new UnauthorizedError;
|
|
8
|
-
return
|
|
9
|
+
return errorResponse(error);
|
|
9
10
|
}
|
|
10
11
|
return await next();
|
|
11
12
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { isGlobalAdmin } from "@getstrata/core/auth/accessControl";
|
|
4
4
|
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
5
5
|
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
6
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
6
7
|
import { logSecurityEvent } from "@getstrata/core/security/securityEvents";
|
|
7
8
|
function createRequireGlobalAdminMiddleware() {
|
|
8
9
|
return async (_request, next) => {
|
|
@@ -13,7 +14,7 @@ function createRequireGlobalAdminMiddleware() {
|
|
|
13
14
|
path: new URL(_request.url).pathname
|
|
14
15
|
});
|
|
15
16
|
const error = new ForbiddenError("Platform admin access required.");
|
|
16
|
-
return
|
|
17
|
+
return errorResponse(error);
|
|
17
18
|
}
|
|
18
19
|
return await next();
|
|
19
20
|
};
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { resolveUserId } from "@getstrata/core/auth/accessControl";
|
|
4
4
|
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
5
5
|
import { hasFreshPasswordConfirmation } from "@getstrata/core/auth/passwordConfirmCookie";
|
|
6
|
+
import { HttpError, UnauthorizedError } from "@getstrata/core/errors/http";
|
|
7
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
6
8
|
|
|
7
9
|
// ../../src/core/http/contentNegotiation.ts
|
|
8
10
|
function requestPrefersJson(request) {
|
|
@@ -101,7 +103,7 @@ function createRequirePasswordConfirmMiddleware() {
|
|
|
101
103
|
const user = currentAuthUser();
|
|
102
104
|
if (!user) {
|
|
103
105
|
if (requestPrefersJson(request)) {
|
|
104
|
-
return
|
|
106
|
+
return errorResponse(new UnauthorizedError("Authentication required."));
|
|
105
107
|
}
|
|
106
108
|
return Response.redirect("/login", 302);
|
|
107
109
|
}
|
|
@@ -110,7 +112,7 @@ function createRequirePasswordConfirmMiddleware() {
|
|
|
110
112
|
return await next();
|
|
111
113
|
}
|
|
112
114
|
if (requestPrefersJson(request)) {
|
|
113
|
-
return
|
|
115
|
+
return errorResponse(new HttpError(423, PASSWORD_CONFIRM_MESSAGE));
|
|
114
116
|
}
|
|
115
117
|
const url = new URL(request.url);
|
|
116
118
|
const intended = request.method === "GET" ? `${url.pathname}${url.search}` : "/account";
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
isEmailVerificationRequired
|
|
7
7
|
} from "@getstrata/core/auth/emailVerification";
|
|
8
8
|
import { createIntendedUrlCookieFromRequest } from "@getstrata/core/auth/intendedUrlCookie";
|
|
9
|
+
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
10
|
+
import { errorResponse } from "@getstrata/core/http/response";
|
|
9
11
|
|
|
10
12
|
// ../../src/core/http/contentNegotiation.ts
|
|
11
13
|
function requestPrefersJson(request) {
|
|
@@ -46,7 +48,7 @@ function createRequireVerifiedMiddleware(auth) {
|
|
|
46
48
|
return await next();
|
|
47
49
|
}
|
|
48
50
|
if (requestPrefersJson(request)) {
|
|
49
|
-
return
|
|
51
|
+
return errorResponse(new ForbiddenError(UNVERIFIED_MESSAGE));
|
|
50
52
|
}
|
|
51
53
|
const headers = new Headers({ Location: VERIFY_NOTICE_PATH });
|
|
52
54
|
const intended = createIntendedUrlCookieFromRequest(request);
|