@nextsparkjs/core 0.1.0-beta.184 → 0.1.0-beta.185
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/hooks/useAuth.d.ts +74 -25
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useValidationSchemas.d.ts +7 -7
- package/dist/lib/api/auth/dual-auth.d.ts +7 -0
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +1 -1
- package/dist/lib/api/auth.d.ts +20 -3
- package/dist/lib/api/auth.d.ts.map +1 -1
- package/dist/lib/api/auth.js +37 -19
- package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
- package/dist/lib/api/entity/generic-handler.js +19 -22
- package/dist/lib/auth.d.ts +1 -3057
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/billing/schema.d.ts +1 -1
- package/dist/lib/mcp/audit.d.ts +17 -0
- package/dist/lib/mcp/audit.d.ts.map +1 -0
- package/dist/lib/mcp/audit.js +48 -0
- package/dist/lib/mcp/engine.d.ts +28 -0
- package/dist/lib/mcp/engine.d.ts.map +1 -0
- package/dist/lib/mcp/engine.js +158 -0
- package/dist/lib/mcp/errors.d.ts +16 -0
- package/dist/lib/mcp/errors.d.ts.map +1 -0
- package/dist/lib/mcp/errors.js +66 -0
- package/dist/lib/mcp/executor.d.ts +15 -0
- package/dist/lib/mcp/executor.d.ts.map +1 -0
- package/dist/lib/mcp/executor.js +63 -0
- package/dist/lib/mcp/index.d.ts +9 -0
- package/dist/lib/mcp/index.d.ts.map +1 -0
- package/dist/lib/mcp/index.js +32 -0
- package/dist/lib/mcp/schema-builder.d.ts +45 -0
- package/dist/lib/mcp/schema-builder.d.ts.map +1 -0
- package/dist/lib/mcp/schema-builder.js +212 -0
- package/dist/lib/mcp/tool-generator.d.ts +31 -0
- package/dist/lib/mcp/tool-generator.d.ts.map +1 -0
- package/dist/lib/mcp/tool-generator.js +271 -0
- package/dist/lib/mcp/transport.d.ts +31 -0
- package/dist/lib/mcp/transport.d.ts.map +1 -0
- package/dist/lib/mcp/transport.js +58 -0
- package/dist/lib/mcp/types.d.ts +184 -0
- package/dist/lib/mcp/types.d.ts.map +1 -0
- package/dist/lib/mcp/types.js +11 -0
- package/dist/styles/classes.json +1 -1
- package/dist/styles/ui.css +1 -1
- package/dist/templates/app/api/mcp/route.ts +173 -0
- package/dist/templates/app/api/v1/api-keys/route.ts +15 -3
- package/dist/templates/app/layout.tsx +3 -0
- package/package.json +8 -3
- package/scripts/build/registry/__tests__/mcp-registry.test.mjs +64 -0
- package/scripts/build/registry/__tests__/scope-registry.test.mjs +88 -0
- package/scripts/build/registry/discovery/mcp-overrides.mjs +72 -0
- package/scripts/build/registry/discovery/mcp-overrides.test.mjs +86 -0
- package/scripts/build/registry/generators/mcp-registry.mjs +61 -0
- package/scripts/build/registry/generators/scope-registry.mjs +133 -88
- package/scripts/build/registry.mjs +8 -4
- package/templates/app/api/mcp/route.ts +173 -0
- package/templates/app/api/v1/api-keys/route.ts +15 -3
- package/templates/app/layout.tsx +3 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/scope-registry.ts +56 -6
- package/dist/lib/auth-client.d.ts +0 -3595
- package/dist/lib/auth-client.d.ts.map +0 -1
package/dist/hooks/useAuth.d.ts
CHANGED
|
@@ -2,44 +2,54 @@ import type { SessionUser } from '../lib/auth';
|
|
|
2
2
|
export declare function useAuth(): {
|
|
3
3
|
user: SessionUser | null;
|
|
4
4
|
session: {
|
|
5
|
-
user: {
|
|
5
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
6
6
|
id: string;
|
|
7
|
-
emailVerified: boolean;
|
|
8
|
-
name: string;
|
|
9
7
|
createdAt: Date;
|
|
10
8
|
updatedAt: Date;
|
|
9
|
+
emailVerified: boolean;
|
|
10
|
+
name: string;
|
|
11
11
|
email?: string;
|
|
12
12
|
image?: string;
|
|
13
|
-
}
|
|
14
|
-
session: {
|
|
13
|
+
} & {} & {}>;
|
|
14
|
+
session: import("better-auth").StripEmptyObjects<{
|
|
15
15
|
id: string;
|
|
16
|
-
userId: string;
|
|
17
|
-
expiresAt: Date;
|
|
18
16
|
createdAt: Date;
|
|
19
17
|
updatedAt: Date;
|
|
18
|
+
userId: string;
|
|
19
|
+
expiresAt: Date;
|
|
20
20
|
token: string;
|
|
21
21
|
ipAddress?: string;
|
|
22
22
|
userAgent?: string;
|
|
23
|
-
}
|
|
23
|
+
} & {} & {}>;
|
|
24
24
|
};
|
|
25
25
|
isLoading: boolean;
|
|
26
26
|
signIn: ({ email, password, redirectTo }: {
|
|
27
27
|
email: string;
|
|
28
28
|
password: string;
|
|
29
29
|
redirectTo?: string;
|
|
30
|
-
}) => Promise<{
|
|
30
|
+
}) => Promise<Omit<{
|
|
31
31
|
redirect: boolean;
|
|
32
32
|
token: string;
|
|
33
|
-
url
|
|
33
|
+
url?: string | undefined;
|
|
34
34
|
user: {
|
|
35
35
|
id: string;
|
|
36
|
+
createdAt: Date;
|
|
37
|
+
updatedAt: Date;
|
|
36
38
|
email: string;
|
|
37
|
-
name: string;
|
|
38
|
-
image: string | null | undefined;
|
|
39
39
|
emailVerified: boolean;
|
|
40
|
+
name: string;
|
|
41
|
+
image?: string | null | undefined;
|
|
42
|
+
};
|
|
43
|
+
}, "user"> & {
|
|
44
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
45
|
+
id: string;
|
|
40
46
|
createdAt: Date;
|
|
41
47
|
updatedAt: Date;
|
|
42
|
-
|
|
48
|
+
emailVerified: boolean;
|
|
49
|
+
name: string;
|
|
50
|
+
email?: string;
|
|
51
|
+
image?: string;
|
|
52
|
+
} & {} & {}>;
|
|
43
53
|
}>;
|
|
44
54
|
signUp: ({ email, password, firstName, lastName, intent }: {
|
|
45
55
|
email: string;
|
|
@@ -47,29 +57,49 @@ export declare function useAuth(): {
|
|
|
47
57
|
firstName?: string;
|
|
48
58
|
lastName?: string;
|
|
49
59
|
intent?: string;
|
|
50
|
-
}) => Promise<
|
|
60
|
+
}) => Promise<(Omit<{
|
|
51
61
|
token: null;
|
|
52
62
|
user: {
|
|
53
63
|
id: string;
|
|
64
|
+
createdAt: Date;
|
|
65
|
+
updatedAt: Date;
|
|
54
66
|
email: string;
|
|
55
|
-
name: string;
|
|
56
|
-
image: string | null | undefined;
|
|
57
67
|
emailVerified: boolean;
|
|
68
|
+
name: string;
|
|
69
|
+
image?: string | null | undefined;
|
|
70
|
+
};
|
|
71
|
+
}, "user"> & {
|
|
72
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
73
|
+
id: string;
|
|
58
74
|
createdAt: Date;
|
|
59
75
|
updatedAt: Date;
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
emailVerified: boolean;
|
|
77
|
+
name: string;
|
|
78
|
+
email?: string;
|
|
79
|
+
image?: string;
|
|
80
|
+
} & {} & {}>;
|
|
81
|
+
}) | (Omit<{
|
|
62
82
|
token: string;
|
|
63
83
|
user: {
|
|
64
84
|
id: string;
|
|
85
|
+
createdAt: Date;
|
|
86
|
+
updatedAt: Date;
|
|
65
87
|
email: string;
|
|
66
|
-
name: string;
|
|
67
|
-
image: string | null | undefined;
|
|
68
88
|
emailVerified: boolean;
|
|
89
|
+
name: string;
|
|
90
|
+
image?: string | null | undefined;
|
|
91
|
+
};
|
|
92
|
+
}, "user"> & {
|
|
93
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
94
|
+
id: string;
|
|
69
95
|
createdAt: Date;
|
|
70
96
|
updatedAt: Date;
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
emailVerified: boolean;
|
|
98
|
+
name: string;
|
|
99
|
+
email?: string;
|
|
100
|
+
image?: string;
|
|
101
|
+
} & {} & {}>;
|
|
102
|
+
})>;
|
|
73
103
|
signOut: () => Promise<void>;
|
|
74
104
|
googleSignIn: (redirectTo?: string) => Promise<void>;
|
|
75
105
|
resetPassword: (email: string) => Promise<{
|
|
@@ -80,6 +110,7 @@ export declare function useAuth(): {
|
|
|
80
110
|
success: boolean;
|
|
81
111
|
data: {
|
|
82
112
|
status: boolean;
|
|
113
|
+
message: string;
|
|
83
114
|
};
|
|
84
115
|
error: any;
|
|
85
116
|
}>;
|
|
@@ -100,17 +131,35 @@ export declare function useAuth(): {
|
|
|
100
131
|
error: string;
|
|
101
132
|
} | {
|
|
102
133
|
success: boolean;
|
|
103
|
-
data: {
|
|
134
|
+
data: Omit<{
|
|
104
135
|
token: string | null;
|
|
105
136
|
user: {
|
|
106
137
|
id: string;
|
|
138
|
+
createdAt: Date;
|
|
139
|
+
updatedAt: Date;
|
|
107
140
|
email: string;
|
|
108
|
-
name: string;
|
|
109
|
-
image: string | null | undefined;
|
|
110
141
|
emailVerified: boolean;
|
|
142
|
+
name: string;
|
|
143
|
+
image?: string | null | undefined;
|
|
144
|
+
} & Record<string, any> & {
|
|
145
|
+
id: string;
|
|
111
146
|
createdAt: Date;
|
|
112
147
|
updatedAt: Date;
|
|
148
|
+
email: string;
|
|
149
|
+
emailVerified: boolean;
|
|
150
|
+
name: string;
|
|
151
|
+
image?: string | null | undefined;
|
|
113
152
|
};
|
|
153
|
+
}, "user"> & {
|
|
154
|
+
user: import("better-auth").StripEmptyObjects<{
|
|
155
|
+
id: string;
|
|
156
|
+
createdAt: Date;
|
|
157
|
+
updatedAt: Date;
|
|
158
|
+
emailVerified: boolean;
|
|
159
|
+
name: string;
|
|
160
|
+
email?: string;
|
|
161
|
+
image?: string;
|
|
162
|
+
} & {} & {}>;
|
|
114
163
|
};
|
|
115
164
|
error: any;
|
|
116
165
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAS9C,wBAAgB,OAAO;UAuMS,WAAW,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;8CAjMa;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../src/hooks/useAuth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAS9C,wBAAgB,OAAO;UAuMS,WAAW,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;8CAjMa;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;+DAmBvC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAkC1H,MAAM;2BAYX,MAAM;;;;;;;;;;;;kCA6BC,MAAM,UAAU,MAAM;;;;;;;;;;;sCAwClB,MAAM,eAAe,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCA8B5B,MAAM;;;;;;;;;;;;;;;EA6C3D"}
|
|
@@ -6,37 +6,37 @@ export declare function useValidationSchemas(): {
|
|
|
6
6
|
login: import("zod").ZodObject<{
|
|
7
7
|
email: import("zod").ZodString;
|
|
8
8
|
password: import("zod").ZodString;
|
|
9
|
-
}, import("
|
|
9
|
+
}, import("zod/v4/core").$strip>;
|
|
10
10
|
signup: import("zod").ZodObject<{
|
|
11
11
|
firstName: import("zod").ZodString;
|
|
12
12
|
lastName: import("zod").ZodString;
|
|
13
13
|
email: import("zod").ZodString;
|
|
14
14
|
password: import("zod").ZodString;
|
|
15
15
|
confirmPassword: import("zod").ZodString;
|
|
16
|
-
}, import("
|
|
16
|
+
}, import("zod/v4/core").$strip>;
|
|
17
17
|
resetPassword: import("zod").ZodObject<{
|
|
18
18
|
email: import("zod").ZodString;
|
|
19
|
-
}, import("
|
|
19
|
+
}, import("zod/v4/core").$strip>;
|
|
20
20
|
newPassword: import("zod").ZodObject<{
|
|
21
21
|
password: import("zod").ZodString;
|
|
22
22
|
confirmPassword: import("zod").ZodString;
|
|
23
|
-
}, import("
|
|
23
|
+
}, import("zod/v4/core").$strip>;
|
|
24
24
|
changePassword: import("zod").ZodObject<{
|
|
25
25
|
currentPassword: import("zod").ZodString;
|
|
26
26
|
newPassword: import("zod").ZodString;
|
|
27
27
|
confirmPassword: import("zod").ZodString;
|
|
28
28
|
revokeOtherSessions: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
29
|
-
}, import("
|
|
29
|
+
}, import("zod/v4/core").$strip>;
|
|
30
30
|
profile: import("zod").ZodObject<{
|
|
31
31
|
firstName: import("zod").ZodString;
|
|
32
32
|
lastName: import("zod").ZodString;
|
|
33
33
|
country: import("zod").ZodString;
|
|
34
34
|
timezone: import("zod").ZodString;
|
|
35
35
|
language: import("zod").ZodString;
|
|
36
|
-
}, import("
|
|
36
|
+
}, import("zod/v4/core").$strip>;
|
|
37
37
|
task: import("zod").ZodObject<{
|
|
38
38
|
title: import("zod").ZodString;
|
|
39
39
|
description: import("zod").ZodOptional<import("zod").ZodString>;
|
|
40
|
-
}, import("
|
|
40
|
+
}, import("zod/v4/core").$strip>;
|
|
41
41
|
};
|
|
42
42
|
//# sourceMappingURL=useValidationSchemas.d.ts.map
|
|
@@ -49,6 +49,13 @@ export interface DualAuthResult {
|
|
|
49
49
|
export declare function authenticateRequest(request: NextRequest): Promise<DualAuthResult>;
|
|
50
50
|
/**
|
|
51
51
|
* Check if user has required scope (for API Key auth)
|
|
52
|
+
*
|
|
53
|
+
* `admin:all` was previously accepted as an implicit full-access scope here,
|
|
54
|
+
* but it was never in `API_SCOPES`, never mintable via `validateScopesForUser`,
|
|
55
|
+
* and undocumented — a full-access string nothing legitimately produced.
|
|
56
|
+
* Removed rather than formalized: the wildcard `*` (mintable only by a real
|
|
57
|
+
* superadmin, see `validateScopesForUser`) already covers the "this key has
|
|
58
|
+
* full access" case in a documented, auditable way.
|
|
52
59
|
*/
|
|
53
60
|
export declare function hasRequiredScope(authResult: DualAuthResult, requiredScope: string): boolean;
|
|
54
61
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAUvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD;;GAEG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AACnD,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAOjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,iBAAiB,CAAC,EAAE,QAAQ,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5D;AAqBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvF;AAqKD
|
|
1
|
+
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAUvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD;;GAEG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AACnD,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAOjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,iBAAiB,CAAC,EAAE,QAAQ,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5D;AAqBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvF;AAqKD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAW3F;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,cAAc,GACzB,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC,CAsChC;AAMD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAkBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,MAAkC,EAAE,MAAM,GAAE,MAAY;;;;GAShG"}
|
|
@@ -145,7 +145,7 @@ function hasRequiredScope(authResult, requiredScope) {
|
|
|
145
145
|
return true;
|
|
146
146
|
}
|
|
147
147
|
if (authResult.type === "api-key" && authResult.scopes) {
|
|
148
|
-
return authResult.scopes.includes(requiredScope) || authResult.scopes.includes("
|
|
148
|
+
return authResult.scopes.includes(requiredScope) || authResult.scopes.includes("*");
|
|
149
149
|
}
|
|
150
150
|
return false;
|
|
151
151
|
}
|
package/dist/lib/api/auth.d.ts
CHANGED
|
@@ -44,13 +44,30 @@ export declare function getApiKeyUser(auth: ApiKeyAuth): Promise<{
|
|
|
44
44
|
role: string;
|
|
45
45
|
}>;
|
|
46
46
|
/**
|
|
47
|
-
* Verifica si un usuario puede crear API keys
|
|
47
|
+
* Verifica si un usuario puede crear API keys.
|
|
48
|
+
*
|
|
49
|
+
* Superadmin is a GLOBAL role (`users.role`) and is always allowed. Any
|
|
50
|
+
* other user is allowed only if they hold a team role that itself grants
|
|
51
|
+
* `admin:api-keys`/`admin:users`-equivalent scopes for at least one team —
|
|
52
|
+
* i.e. the same source of truth `validateScopesForUser` uses, checked
|
|
53
|
+
* without a specific team in mind (any qualifying membership is enough).
|
|
48
54
|
*/
|
|
49
55
|
export declare function canCreateApiKeys(userId: string): Promise<boolean>;
|
|
50
56
|
/**
|
|
51
|
-
* Valida que los scopes solicitados sean permitidos para el usuario
|
|
57
|
+
* Valida que los scopes solicitados sean permitidos para el usuario, en el
|
|
58
|
+
* contexto de un team específico.
|
|
59
|
+
*
|
|
60
|
+
* Superadmin is a GLOBAL role (`users.role`) — it never appears in
|
|
61
|
+
* `AVAILABLE_ROLES` (team roles), so it's special-cased here rather than
|
|
62
|
+
* inside the scope registry. Every other user is resolved through their
|
|
63
|
+
* TEAM role for `teamId` (not their global `users.role` — NextSpark's
|
|
64
|
+
* entity authorization model is team-scoped, see `app.config.ts`), and the
|
|
65
|
+
* allowed-scope set comes from the same registry-backed
|
|
66
|
+
* `ScopeService.getRoleScopes()` that session auth's implicit scopes
|
|
67
|
+
* (`generateScopesForRole` in `helpers.ts`) and `checkPermission()` both
|
|
68
|
+
* ultimately derive from — so minting and enforcement can't diverge again.
|
|
52
69
|
*/
|
|
53
|
-
export declare function validateScopesForUser(userId: string, requestedScopes: string[]): Promise<{
|
|
70
|
+
export declare function validateScopesForUser(userId: string, teamId: string | null, requestedScopes: string[]): Promise<{
|
|
54
71
|
valid: boolean;
|
|
55
72
|
allowedScopes: string[];
|
|
56
73
|
deniedScopes: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/lib/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/lib/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO1C;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAEnF;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoHrF;AAiDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAM/E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAMhF;AAiBD;;GAEG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU;QAG1C,MAAM;WACH,MAAM;eACF,MAAM,GAAG,IAAI;cACd,MAAM,GAAG,IAAI;UACjB,MAAM;GAWjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC;IACT,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC,CAyED"}
|
package/dist/lib/api/auth.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { queryOne } from "../db.js";
|
|
2
2
|
import { ApiKeyManager } from "./keys.js";
|
|
3
3
|
import { apiKeyCache, getCacheKey } from "./cache.js";
|
|
4
|
+
import { TeamMemberService } from "../services/team-member.service.js";
|
|
5
|
+
import { ScopeService } from "../services/scope.service.js";
|
|
4
6
|
function hasScope(auth, requiredScope) {
|
|
5
7
|
return auth.scopes.includes(requiredScope);
|
|
6
8
|
}
|
|
@@ -154,13 +156,20 @@ async function canCreateApiKeys(userId) {
|
|
|
154
156
|
if (!user) {
|
|
155
157
|
return false;
|
|
156
158
|
}
|
|
157
|
-
|
|
159
|
+
if (user.role === "superadmin") {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
const memberships = await TeamMemberService.listByUser(userId);
|
|
163
|
+
return memberships.some(({ role }) => {
|
|
164
|
+
const scopes = ScopeService.getRoleScopes(role);
|
|
165
|
+
return scopes.includes("*") || scopes.includes("admin:api-keys");
|
|
166
|
+
});
|
|
158
167
|
} catch (error) {
|
|
159
168
|
console.error("Error checking API key creation permissions:", error);
|
|
160
169
|
return false;
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
|
-
async function validateScopesForUser(userId, requestedScopes) {
|
|
172
|
+
async function validateScopesForUser(userId, teamId, requestedScopes) {
|
|
164
173
|
try {
|
|
165
174
|
const user = await queryOne(
|
|
166
175
|
'SELECT role FROM "users" WHERE id = $1',
|
|
@@ -173,23 +182,32 @@ async function validateScopesForUser(userId, requestedScopes) {
|
|
|
173
182
|
deniedScopes: requestedScopes
|
|
174
183
|
};
|
|
175
184
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
if (user.role === "superadmin") {
|
|
186
|
+
return {
|
|
187
|
+
valid: true,
|
|
188
|
+
allowedScopes: requestedScopes,
|
|
189
|
+
deniedScopes: []
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!teamId) {
|
|
193
|
+
return {
|
|
194
|
+
valid: false,
|
|
195
|
+
allowedScopes: [],
|
|
196
|
+
deniedScopes: requestedScopes
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const teamRole = await TeamMemberService.getRole(teamId, userId);
|
|
200
|
+
if (!teamRole) {
|
|
201
|
+
return {
|
|
202
|
+
valid: false,
|
|
203
|
+
allowedScopes: [],
|
|
204
|
+
deniedScopes: requestedScopes
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const allowedScopes = [
|
|
208
|
+
...ScopeService.getBaseScopes(),
|
|
209
|
+
...ScopeService.getRoleScopes(teamRole)
|
|
210
|
+
];
|
|
193
211
|
if (allowedScopes.includes("*")) {
|
|
194
212
|
return {
|
|
195
213
|
valid: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-handler.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/entity/generic-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"generic-handler.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/entity/generic-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAmjBvD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAwgBnF;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAoZrF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CA0MpJ;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CA2XtJ;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAyJtJ;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAEtF"}
|
|
@@ -193,11 +193,11 @@ function getTeamIdFromRequest(request) {
|
|
|
193
193
|
function isBuilderRequest(request) {
|
|
194
194
|
return request.headers.get("x-builder-source") === "true";
|
|
195
195
|
}
|
|
196
|
-
async function resolveOwnershipFilter(entityConfig, userId, teamId, isBypass
|
|
196
|
+
async function resolveOwnershipFilter(entityConfig, userId, teamId, isBypass) {
|
|
197
197
|
var _a;
|
|
198
198
|
const ownershipFilter = (_a = entityConfig.access) == null ? void 0 : _a.ownershipFilter;
|
|
199
199
|
if (!ownershipFilter) return { applies: false };
|
|
200
|
-
if (isBypass
|
|
200
|
+
if (isBypass) return { applies: false };
|
|
201
201
|
const memberRow = await queryOneWithRLS(
|
|
202
202
|
'SELECT role FROM "team_members" WHERE "teamId" = $1 AND "userId" = $2',
|
|
203
203
|
[teamId, userId],
|
|
@@ -221,9 +221,9 @@ async function resolveOwnershipFilter(entityConfig, userId, teamId, isBypass, au
|
|
|
221
221
|
const filterValue = linkedRow ? linkedRow[valueField] ?? null : null;
|
|
222
222
|
return { applies: true, field: ownershipFilter.field, value: filterValue };
|
|
223
223
|
}
|
|
224
|
-
async function
|
|
224
|
+
async function checkAuthPermission(authResult, entitySlug, action, teamId, request) {
|
|
225
225
|
var _a;
|
|
226
|
-
if (
|
|
226
|
+
if (!((_a = authResult.user) == null ? void 0 : _a.id)) return null;
|
|
227
227
|
const permission = `${entitySlug}.${action}`;
|
|
228
228
|
try {
|
|
229
229
|
const allowed = await checkPermission(authResult.user.id, teamId, permission);
|
|
@@ -330,7 +330,7 @@ async function handleGenericList(request) {
|
|
|
330
330
|
teamId = teamValidation.teamId;
|
|
331
331
|
isBypass = teamValidation.isBypass;
|
|
332
332
|
if (!isBypass && teamId) {
|
|
333
|
-
const permDenied = await
|
|
333
|
+
const permDenied = await checkAuthPermission(authResult, resolution.entityConfig.slug, "list", teamId, request);
|
|
334
334
|
if (permDenied) return permDenied;
|
|
335
335
|
}
|
|
336
336
|
}
|
|
@@ -413,8 +413,7 @@ async function handleGenericList(request) {
|
|
|
413
413
|
entityConfig,
|
|
414
414
|
userId,
|
|
415
415
|
teamId,
|
|
416
|
-
isBypass
|
|
417
|
-
authResult.type
|
|
416
|
+
isBypass
|
|
418
417
|
);
|
|
419
418
|
}
|
|
420
419
|
if (ownershipFilter.applies && ownershipFilter.value === null) {
|
|
@@ -714,7 +713,7 @@ async function handleGenericCreate(request) {
|
|
|
714
713
|
const response2 = createApiError("Team context required for create operations. Include x-team-id header.", 400, void 0, "TEAM_CONTEXT_REQUIRED");
|
|
715
714
|
return addCorsHeaders(response2, request);
|
|
716
715
|
}
|
|
717
|
-
const permDenied = await
|
|
716
|
+
const permDenied = await checkAuthPermission(authResult, entityConfig.slug, "create", teamId, request);
|
|
718
717
|
if (permDenied) return permDenied;
|
|
719
718
|
if (idStrategy === "serial") {
|
|
720
719
|
insertFields = ['"userId"', '"teamId"'];
|
|
@@ -938,7 +937,7 @@ async function handleGenericRead(request, { params }) {
|
|
|
938
937
|
teamId = teamValidation.teamId;
|
|
939
938
|
isBypass = teamValidation.isBypass;
|
|
940
939
|
if (!isBypass && teamId) {
|
|
941
|
-
const permDenied = await
|
|
940
|
+
const permDenied = await checkAuthPermission(authResult, resolution.entityConfig.slug, "read", teamId, request);
|
|
942
941
|
if (permDenied) return permDenied;
|
|
943
942
|
}
|
|
944
943
|
}
|
|
@@ -982,8 +981,7 @@ async function handleGenericRead(request, { params }) {
|
|
|
982
981
|
entityConfig,
|
|
983
982
|
userId,
|
|
984
983
|
teamId,
|
|
985
|
-
isBypass
|
|
986
|
-
authResult.type
|
|
984
|
+
isBypass
|
|
987
985
|
);
|
|
988
986
|
if (readOwnershipFilter.applies) {
|
|
989
987
|
if (readOwnershipFilter.value === null) {
|
|
@@ -1045,7 +1043,7 @@ async function handleGenericRead(request, { params }) {
|
|
|
1045
1043
|
}
|
|
1046
1044
|
}
|
|
1047
1045
|
async function handleGenericUpdate(request, { params }) {
|
|
1048
|
-
var _a, _b, _c, _d, _e, _f;
|
|
1046
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
1049
1047
|
try {
|
|
1050
1048
|
const { id } = await params;
|
|
1051
1049
|
const resolution = await resolveEntityFromUrl(request.nextUrl.pathname);
|
|
@@ -1080,7 +1078,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1080
1078
|
const response2 = createApiError("Team context required for update operations. Include x-team-id header.", 400, void 0, "TEAM_CONTEXT_REQUIRED");
|
|
1081
1079
|
return addCorsHeaders(response2, request);
|
|
1082
1080
|
}
|
|
1083
|
-
const permDenied = await
|
|
1081
|
+
const permDenied = await checkAuthPermission(authResult, resolution.entityConfig.slug, "update", teamId, request);
|
|
1084
1082
|
if (permDenied) return permDenied;
|
|
1085
1083
|
let body;
|
|
1086
1084
|
try {
|
|
@@ -1092,7 +1090,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1092
1090
|
const { metas, ...entityData } = body;
|
|
1093
1091
|
const entityConfig = resolution.entityConfig;
|
|
1094
1092
|
const fieldGuards = (_b = (_a = entityConfig.access) == null ? void 0 : _a.ownershipFilter) == null ? void 0 : _b.fieldGuards;
|
|
1095
|
-
if (fieldGuards && fieldGuards.length > 0 && authResult.
|
|
1093
|
+
if (fieldGuards && fieldGuards.length > 0 && ((_c = authResult.user) == null ? void 0 : _c.id)) {
|
|
1096
1094
|
const guardMemberRow = await queryOneWithRLS(
|
|
1097
1095
|
'SELECT role FROM "team_members" WHERE "teamId" = $1 AND "userId" = $2',
|
|
1098
1096
|
[teamId, authResult.user.id],
|
|
@@ -1141,7 +1139,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1141
1139
|
const values = [];
|
|
1142
1140
|
let paramCount = 1;
|
|
1143
1141
|
const builderRequest = isBuilderRequest(request);
|
|
1144
|
-
const entityHasBuilder = ((
|
|
1142
|
+
const entityHasBuilder = ((_d = entityConfig.builder) == null ? void 0 : _d.enabled) === true;
|
|
1145
1143
|
if (builderRequest && entityHasBuilder && "blocks" in entityData) {
|
|
1146
1144
|
let blocksData = entityData.blocks;
|
|
1147
1145
|
blocksData = await filterOrphanedPatternReferences(blocksData, authResult.user.id);
|
|
@@ -1217,7 +1215,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1217
1215
|
whereConditions.push(`"teamId" = $${paramCount++}`);
|
|
1218
1216
|
values.push(teamId);
|
|
1219
1217
|
}
|
|
1220
|
-
if (!((
|
|
1218
|
+
if (!((_e = entityConfig.access) == null ? void 0 : _e.shared)) {
|
|
1221
1219
|
whereConditions.push(`"userId" = $${paramCount++}`);
|
|
1222
1220
|
values.push(authResult.user.id);
|
|
1223
1221
|
}
|
|
@@ -1225,8 +1223,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1225
1223
|
entityConfig,
|
|
1226
1224
|
authResult.user.id,
|
|
1227
1225
|
teamId,
|
|
1228
|
-
teamValidation.isBypass
|
|
1229
|
-
authResult.type
|
|
1226
|
+
teamValidation.isBypass
|
|
1230
1227
|
);
|
|
1231
1228
|
if (updateOwnershipFilter.applies) {
|
|
1232
1229
|
if (updateOwnershipFilter.value === null) {
|
|
@@ -1262,7 +1259,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1262
1259
|
}
|
|
1263
1260
|
updatedItem = result[0];
|
|
1264
1261
|
}
|
|
1265
|
-
if ((
|
|
1262
|
+
if ((_f = entityConfig.taxonomies) == null ? void 0 : _f.enabled) {
|
|
1266
1263
|
await processTaxonomyRelations(entityConfig, id, body, authResult.user.id, true);
|
|
1267
1264
|
}
|
|
1268
1265
|
if (metadataWasProvided) {
|
|
@@ -1274,7 +1271,7 @@ async function handleGenericUpdate(request, { params }) {
|
|
|
1274
1271
|
);
|
|
1275
1272
|
}
|
|
1276
1273
|
let responseItem = updatedItem;
|
|
1277
|
-
if ((
|
|
1274
|
+
if ((_g = entityConfig.taxonomies) == null ? void 0 : _g.enabled) {
|
|
1278
1275
|
const itemsWithTaxonomies = await includeTaxonomiesInData(
|
|
1279
1276
|
entityConfig,
|
|
1280
1277
|
[updatedItem],
|
|
@@ -1335,7 +1332,7 @@ async function handleGenericDelete(request, { params }) {
|
|
|
1335
1332
|
if (authResult.rateLimitResponse) {
|
|
1336
1333
|
return authResult.rateLimitResponse;
|
|
1337
1334
|
}
|
|
1338
|
-
if (!hasRequiredScope(authResult, `${resolution.entityConfig.slug}:
|
|
1335
|
+
if (!hasRequiredScope(authResult, `${resolution.entityConfig.slug}:delete`)) {
|
|
1339
1336
|
const response2 = createApiError("Insufficient permissions", 403);
|
|
1340
1337
|
return addCorsHeaders(response2, request);
|
|
1341
1338
|
}
|
|
@@ -1348,7 +1345,7 @@ async function handleGenericDelete(request, { params }) {
|
|
|
1348
1345
|
const response2 = createApiError("Team context required for delete operations. Include x-team-id header.", 400, void 0, "TEAM_CONTEXT_REQUIRED");
|
|
1349
1346
|
return addCorsHeaders(response2, request);
|
|
1350
1347
|
}
|
|
1351
|
-
const permDenied = await
|
|
1348
|
+
const permDenied = await checkAuthPermission(authResult, resolution.entityConfig.slug, "delete", teamId, request);
|
|
1352
1349
|
if (permDenied) return permDenied;
|
|
1353
1350
|
const entityConfig = resolution.entityConfig;
|
|
1354
1351
|
const tableName = getTableName(entityConfig);
|