@stonecrop/casl-middleware 0.30.0 → 0.32.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.
Files changed (38) hide show
  1. package/dist/casl-middleware.d.ts +15 -0
  2. package/dist/casl-middleware.js +3328 -2650
  3. package/dist/casl-middleware.js.map +1 -1
  4. package/dist/tsdoc-metadata.json +1 -1
  5. package/package.json +23 -22
  6. package/dist/casl-middleware.tsbuildinfo +0 -1
  7. package/dist/src/index.d.ts +0 -6
  8. package/dist/src/index.d.ts.map +0 -1
  9. package/dist/src/index.js +0 -3
  10. package/dist/src/middleware/ability.d.ts +0 -55
  11. package/dist/src/middleware/ability.d.ts.map +0 -1
  12. package/dist/src/middleware/ability.js +0 -139
  13. package/dist/src/middleware/graphql.d.ts +0 -11
  14. package/dist/src/middleware/graphql.d.ts.map +0 -1
  15. package/dist/src/middleware/graphql.js +0 -120
  16. package/dist/src/middleware/introspection.d.ts +0 -71
  17. package/dist/src/middleware/introspection.d.ts.map +0 -1
  18. package/dist/src/middleware/introspection.js +0 -169
  19. package/dist/src/middleware/jwt.d.ts +0 -114
  20. package/dist/src/middleware/jwt.d.ts.map +0 -1
  21. package/dist/src/middleware/jwt.js +0 -302
  22. package/dist/src/middleware/postgraphile.d.ts +0 -7
  23. package/dist/src/middleware/postgraphile.d.ts.map +0 -1
  24. package/dist/src/middleware/postgraphile.js +0 -79
  25. package/dist/src/middleware/yoga.d.ts +0 -15
  26. package/dist/src/middleware/yoga.d.ts.map +0 -1
  27. package/dist/src/middleware/yoga.js +0 -30
  28. package/dist/src/types/index.d.ts +0 -114
  29. package/dist/src/types/index.d.ts.map +0 -1
  30. package/dist/src/types/index.js +0 -0
  31. package/src/index.ts +0 -15
  32. package/src/middleware/ability.ts +0 -197
  33. package/src/middleware/graphql.ts +0 -157
  34. package/src/middleware/introspection.ts +0 -258
  35. package/src/middleware/jwt.ts +0 -405
  36. package/src/middleware/postgraphile.ts +0 -89
  37. package/src/middleware/yoga.ts +0 -36
  38. package/src/types/index.ts +0 -133
@@ -1 +0,0 @@
1
- {"version":3,"file":"yoga.d.ts","sourceRoot":"","sources":["../../../src/middleware/yoga.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAG1C,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAO1D,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,CAO1C,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAU,iBAAsB;mBAE1C,GAAG;CAMzB,CAAA"}
@@ -1,30 +0,0 @@
1
- import { createAbility } from './ability';
2
- const getLoggedInUser = () => {
3
- // Mock user for demonstration purposes
4
- return { id: '1', roles: ['editor'] };
5
- };
6
- export const yogaCaslPlugin = {
7
- onContextBuilding: async ({ extendContext }) => {
8
- // Make this async
9
- const user = getLoggedInUser();
10
- const ability = await createAbility(user); // Await here
11
- extendContext({ ability, user });
12
- },
13
- };
14
- /**
15
- * Create a GraphQL Yoga plugin for CASL authorization
16
- * Note: This is a placeholder for future implementation
17
- *
18
- * @param options - CASL middleware configuration options
19
- * @returns Yoga plugin
20
- * @public
21
- */
22
- export const createYogaPlugin = (_options = {}) => {
23
- return {
24
- onExecute: async (_) => {
25
- // TODO: Implement Yoga plugin via createCaslMiddleware(_options).
26
- // This would integrate with GraphQL Yoga's plugin system.
27
- console.warn('Yoga plugin not yet implemented');
28
- },
29
- };
30
- };
@@ -1,114 +0,0 @@
1
- import { GraphQLResolveInfo } from 'graphql';
2
- import type { AppAbility, AbilityBuilderFunction } from '../middleware/ability';
3
- /**
4
- * User information for authorization
5
- * @public
6
- */
7
- export interface User {
8
- /** Unique identifier for the user */
9
- id: string;
10
- /** Array of role names assigned to the user */
11
- roles?: string[];
12
- /** Additional user properties */
13
- [key: string]: any;
14
- }
15
- /**
16
- * GraphQL context with CASL ability and user information
17
- * @public
18
- */
19
- export interface Context {
20
- /** CASL ability instance for authorization checks */
21
- ability?: AppAbility;
22
- /** Current authenticated user */
23
- user?: User;
24
- /** Additional context properties */
25
- [key: string]: any;
26
- }
27
- /**
28
- * Configuration options for CASL middleware
29
- * @public
30
- */
31
- export interface MiddlewareOptions {
32
- /** Mapping of GraphQL types to authorization subjects */
33
- subjectMap?: Record<string, string>;
34
- /** Mapping of GraphQL operations to CASL actions */
35
- actionMap?: Record<string, string>;
36
- /** Field-level permission rules */
37
- fieldPermissions?: Record<string, FieldPermission[]>;
38
- /** Custom function to build user abilities */
39
- abilityBuilder?: AbilityBuilderFunction;
40
- /** Enable debug logging */
41
- debug?: boolean;
42
- }
43
- /**
44
- * Field-level permission definition for fine-grained access control
45
- * @public
46
- */
47
- export interface FieldPermission {
48
- /** CASL action (e.g., 'read', 'write') */
49
- action: string;
50
- /** Subject/resource type to apply permission to */
51
- subject: string;
52
- /** Specific field name (optional) */
53
- field?: string;
54
- /** Conditional rules for permission */
55
- conditions?: any;
56
- }
57
- /**
58
- * GraphQL resolver function type
59
- * @public
60
- */
61
- export type ResolverFn = (root: any, args: any, context: Context, info: GraphQLResolveInfo) => any;
62
- /**
63
- * Middleware function that wraps a GraphQL resolver with authorization logic
64
- * @public
65
- */
66
- export type MiddlewareFn = (resolve: ResolverFn, root: any, args: any, context: Context, info: GraphQLResolveInfo) => any;
67
- /**
68
- * Plugin configuration options for framework integrations
69
- * @public
70
- */
71
- export interface PluginOptions extends MiddlewareOptions {
72
- /** Custom function to build user abilities */
73
- abilityBuilder?: AbilityBuilderFunction;
74
- /** Ability caching configuration */
75
- cacheOptions?: {
76
- /** Time-to-live in milliseconds */
77
- ttl?: number;
78
- /** Function to generate cache key from user */
79
- key?: (user?: User) => string;
80
- };
81
- }
82
- /**
83
- * Response type for ability creation mutations
84
- * @public
85
- */
86
- export interface AbilityResponse {
87
- /** Whether the ability was created successfully */
88
- success: boolean;
89
- /** The created ability rules */
90
- ability: any;
91
- /** Success or error message */
92
- message: string;
93
- }
94
- /**
95
- * Input type for creating a new ability
96
- * @public
97
- */
98
- export interface CreateAbilityInput {
99
- /** User ID to create ability for */
100
- userId: string;
101
- /** Array of role names to assign */
102
- roles: string[];
103
- }
104
- export interface AbilityRule {
105
- id?: string;
106
- roleId?: string;
107
- userId?: string;
108
- action: string | string[];
109
- subject: string;
110
- fields?: string[];
111
- conditions?: any;
112
- inverted?: boolean;
113
- }
114
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"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"}
File without changes
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- export { createAbility, detectSubjectType } from './middleware/ability'
2
- export type { AppAbility, AbilityBuilderFunction } from './middleware/ability'
3
- export { createCaslMiddleware } from './middleware/graphql'
4
- export { pglCaslPlugin } from './middleware/postgraphile'
5
- export type {
6
- User,
7
- Context,
8
- MiddlewareOptions,
9
- FieldPermission,
10
- ResolverFn,
11
- MiddlewareFn,
12
- PluginOptions,
13
- AbilityResponse,
14
- CreateAbilityInput,
15
- } from './types'
@@ -1,197 +0,0 @@
1
- import {
2
- AbilityBuilder,
3
- PureAbility,
4
- type AbilityClass,
5
- type FieldMatcher,
6
- type ConditionsMatcher,
7
- } from '@casl/ability'
8
-
9
- import type { Context } from '../types'
10
-
11
- /**
12
- * Detects the subject type from an object for CASL authorization
13
- * @param object - The object to detect the subject type from
14
- * @returns The subject type string
15
- * @public
16
- */
17
- export const detectSubjectType = (object: any): string => {
18
- // Handle CASL's subject() helper objects
19
- if (object && typeof object === 'object') {
20
- // CASL uses __caslSubjectType__ property
21
- if ('__caslSubjectType__' in object) {
22
- return object.__caslSubjectType__
23
- }
24
- // Fallback to other type indicators
25
- if (object.type) return object.type
26
- if (object.constructor?.name && object.constructor.name !== 'Object') {
27
- return object.constructor.name
28
- }
29
- }
30
- return 'Unknown'
31
- }
32
-
33
- // Simple field matcher - checks if a field is in the restricted list
34
- const fieldMatcher: FieldMatcher = fields => field => {
35
- if (!fields || !field) return false
36
- if (Array.isArray(fields)) {
37
- return fields.includes(field)
38
- }
39
- return fields === field
40
- }
41
-
42
- // Simple conditions matcher - basic equality check
43
- const conditionsMatcher: ConditionsMatcher<any> = conditions => object => {
44
- if (!conditions || !object) return true
45
-
46
- return Object.keys(conditions).every(key => {
47
- const conditionValue = conditions[key]
48
- const objectValue = object[key]
49
-
50
- // Simple equality check
51
- return objectValue === conditionValue
52
- })
53
- }
54
-
55
- /**
56
- * CASL ability type for authorization with flexible subject types
57
- * @public
58
- */
59
- export type AppAbility = PureAbility<[string, any], any>
60
-
61
- /**
62
- * Function type for building user abilities
63
- * @public
64
- */
65
- export type AbilityBuilderFunction = (user?: Context['user']) => Promise<AppAbility> | AppAbility
66
-
67
- const Ability = PureAbility as AbilityClass<AppAbility>
68
-
69
- /**
70
- * Default ability builder - only provides minimal public access
71
- */
72
- export const defaultAbilityBuilder = (_user?: Context['user']): AppAbility => {
73
- const { can, build } = new AbilityBuilder<AppAbility>(Ability)
74
-
75
- can('read', 'Query')
76
-
77
- return build({
78
- detectSubjectType,
79
- fieldMatcher,
80
- conditionsMatcher,
81
- })
82
- }
83
-
84
- /**
85
- * Create ability using a provided builder function
86
- * @param user - User information for building the ability
87
- * @param builderFn - Function to build the ability
88
- * @returns Promise resolving to the created ability
89
- * @public
90
- */
91
- export const createAbility = async (
92
- user?: Context['user'],
93
- builderFn: AbilityBuilderFunction = defaultAbilityBuilder
94
- ): Promise<AppAbility> => {
95
- return await builderFn(user)
96
- }
97
-
98
- /**
99
- * Factory function for creating custom ability builders
100
- */
101
- export const createDatabaseAbilityBuilder = (
102
- fetchRules: (userId?: string) => Promise<any[]>
103
- ): AbilityBuilderFunction => {
104
- return async (user?: Context['user']) => {
105
- const { can, cannot, build } = new AbilityBuilder<AppAbility>(Ability)
106
-
107
- if (user?.id) {
108
- const rules = await fetchRules(user.id)
109
-
110
- rules.forEach(rule => {
111
- if (rule.inverted) {
112
- cannot(rule.action, rule.subject, rule.fields, rule.conditions)
113
- } else {
114
- can(rule.action, rule.subject, rule.fields, rule.conditions)
115
- }
116
- })
117
- } else {
118
- can('read', 'Query')
119
- }
120
-
121
- return build({
122
- detectSubjectType,
123
- fieldMatcher,
124
- conditionsMatcher,
125
- })
126
- }
127
- }
128
-
129
- /**
130
- * Create an ability builder from static configuration
131
- */
132
- export const createConfigBasedAbilityBuilder = (config: {
133
- roles: Record<
134
- string,
135
- Array<{
136
- action: string | string[]
137
- subject: string
138
- fields?: string | string[]
139
- conditions?: any
140
- inverted?: boolean
141
- }>
142
- >
143
- defaultRules?: Array<{
144
- action: string | string[]
145
- subject: string
146
- fields?: string | string[]
147
- conditions?: any
148
- }>
149
- }): AbilityBuilderFunction => {
150
- return (user?: Context['user']) => {
151
- const { can, cannot, build } = new AbilityBuilder<AppAbility>(Ability)
152
-
153
- // Apply default rules
154
- if (config.defaultRules) {
155
- config.defaultRules.forEach(rule => {
156
- can(rule.action, rule.subject, rule.fields, rule.conditions)
157
- })
158
- }
159
-
160
- // Apply role-based rules
161
- if (user?.roles) {
162
- user.roles.forEach(role => {
163
- const rules = config.roles[role]
164
- if (rules) {
165
- rules.forEach(rule => {
166
- // Process template variables in conditions
167
- let processedConditions = rule.conditions
168
- if (processedConditions && user.id) {
169
- const condStr = JSON.stringify(processedConditions)
170
- if (condStr.includes('{{userId}}')) {
171
- processedConditions = JSON.parse(condStr.replace(/{{userId}}/g, user.id))
172
- }
173
- }
174
-
175
- if (rule.inverted) {
176
- cannot(rule.action, rule.subject, rule.fields, processedConditions)
177
- } else {
178
- can(rule.action, rule.subject, rule.fields, processedConditions)
179
- }
180
- })
181
- }
182
- })
183
- }
184
-
185
- return build({
186
- detectSubjectType,
187
- fieldMatcher,
188
- conditionsMatcher,
189
- })
190
- }
191
- }
192
-
193
- export const createAbilityFactory = <T extends PureAbility>(
194
- builderFn: (user?: Context['user']) => T
195
- ): ((user?: Context['user']) => T) => {
196
- return (user?: Context['user']) => builderFn(user)
197
- }
@@ -1,157 +0,0 @@
1
- import { subject } from '@casl/ability'
2
- import { GraphQLError, GraphQLResolveInfo } from 'graphql'
3
- import { parse, visit, FieldNode, DocumentNode } from 'graphql/language'
4
- import { Context, MiddlewareOptions, ResolverFn } from '../types'
5
- import { createAbility, defaultAbilityBuilder } from './ability'
6
-
7
- // Helper to extract operation name from GraphQL info
8
- const getOperationName = (info: GraphQLResolveInfo): string => {
9
- return info.operation.operation.toLowerCase()
10
- }
11
-
12
- // Helper to get full field path
13
- const getFieldPath = (info: GraphQLResolveInfo): string => {
14
- return info.path.typename ? `${info.path.typename}.${info.path.key}` : String(info.path.key)
15
- }
16
-
17
- // Parse GraphQL query to extract accessed fields
18
- const extractAccessedFields = (query: string): string[] => {
19
- const fields: string[] = []
20
-
21
- try {
22
- const ast: DocumentNode = parse(query)
23
-
24
- visit(ast, {
25
- Field: {
26
- enter(node: FieldNode) {
27
- if (node.name.value !== '__typename') {
28
- fields.push(node.name.value)
29
- }
30
- },
31
- },
32
- })
33
- } catch (error) {
34
- console.error('Failed to parse GraphQL query:', error)
35
- }
36
-
37
- return fields
38
- }
39
-
40
- // Check if ability allows the operation
41
- const checkPermission = (context: Context, action: string, subjectName: string, conditions?: any): boolean => {
42
- if (!context.ability) {
43
- return false
44
- }
45
-
46
- try {
47
- // Only use subject helper if we have specific conditions to check
48
- // (not just resolver args, but actual CASL conditions)
49
- if (conditions && typeof conditions === 'object') {
50
- return context.ability.can(action, subject(subjectName, conditions))
51
- } else {
52
- // Simple check without conditions - just action and subject type
53
- return context.ability.can(action, subjectName)
54
- }
55
- } catch (error) {
56
- console.error('Permission check failed:', error)
57
- return false
58
- }
59
- }
60
-
61
- /**
62
- * Creates CASL authorization middleware for GraphQL resolvers
63
- * @param options - Configuration options for the middleware
64
- * @returns Middleware function that wraps resolvers with authorization checks
65
- * @public
66
- */
67
- export const createCaslMiddleware = (options: MiddlewareOptions = {}) => {
68
- const {
69
- subjectMap = {},
70
- actionMap = {
71
- query: 'read',
72
- mutation: 'update',
73
- subscription: 'read',
74
- },
75
- fieldPermissions = {},
76
- abilityBuilder = defaultAbilityBuilder,
77
- debug = false,
78
- } = options
79
-
80
- return async (
81
- resolve: ResolverFn,
82
- root: any,
83
- args: any,
84
- context: Context,
85
- info: GraphQLResolveInfo
86
- ): Promise<any> => {
87
- // Create ability if not already in context
88
- if (!context.ability) {
89
- context.ability = await createAbility(context.user, abilityBuilder)
90
- }
91
-
92
- const operation = getOperationName(info)
93
- const fieldPath = getFieldPath(info)
94
- const action = actionMap[operation] || operation
95
-
96
- // Get subject from map or default to type name
97
- const subjectType = info.parentType.name
98
- const subjectName = subjectMap[subjectType] || subjectType
99
-
100
- if (debug) {
101
- console.debug(`Checking permission: ${action} on ${subjectName} at ${fieldPath}`)
102
- }
103
-
104
- // Check field-level permissions if defined
105
- if (fieldPermissions[fieldPath]) {
106
- const permissions = fieldPermissions[fieldPath]
107
- const allowed = permissions.some(permission =>
108
- // Don't pass resolver args here - only use them if the permission defines conditions
109
- checkPermission(context, permission.action, permission.subject, permission.conditions)
110
- )
111
-
112
- if (!allowed) {
113
- throw new GraphQLError(`Access denied for field ${fieldPath}`)
114
- }
115
- }
116
-
117
- // Check operation-level permission
118
- // Don't pass resolver args as conditions - they're not CASL conditions
119
- const allowed = checkPermission(context, action, subjectName)
120
-
121
- if (!allowed) {
122
- throw new GraphQLError(`Access denied for ${action} on ${subjectName}`)
123
- }
124
-
125
- // Additional checks for mutations with input fields
126
- if (operation === 'mutation' && args.input) {
127
- const querySource = info.operation.loc?.source.body
128
-
129
- if (querySource) {
130
- const accessedFields = extractAccessedFields(querySource)
131
-
132
- for (const field of accessedFields) {
133
- // Check field-level update permissions if needed
134
- const fieldAllowed = checkPermission(context, 'update', subjectName, { field })
135
-
136
- if (!fieldAllowed && debug) {
137
- console.warn(`Warning: Field ${field} update not explicitly allowed`)
138
- }
139
- }
140
- }
141
- }
142
-
143
- // Call the resolver
144
- return resolve(root, args, context, info)
145
- }
146
- }
147
-
148
- // Utility function to combine multiple middlewares
149
- export const combineMiddlewares = (...middlewares: any[]) => {
150
- return (resolve: ResolverFn, root: any, args: any, context: any, info: any) => {
151
- const chain = middlewares.reduceRight(
152
- (next, middleware) => () => middleware(next, root, args, context, info),
153
- () => resolve(root, args, context, info)
154
- )
155
- return chain()
156
- }
157
- }