@stonecrop/casl-middleware 0.7.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 (66) hide show
  1. package/README.md +149 -0
  2. package/dist/casl-middleware.d.ts +158 -0
  3. package/dist/casl-middleware.js +40571 -0
  4. package/dist/casl-middleware.js.map +1 -0
  5. package/dist/casl_middleware.tsbuildinfo +1 -0
  6. package/dist/src/index.d.ts +6 -0
  7. package/dist/src/index.d.ts.map +1 -0
  8. package/dist/src/index.js +3 -0
  9. package/dist/src/middleware/ability.d.ts +55 -0
  10. package/dist/src/middleware/ability.d.ts.map +1 -0
  11. package/dist/src/middleware/ability.js +139 -0
  12. package/dist/src/middleware/graphql.d.ts +11 -0
  13. package/dist/src/middleware/graphql.d.ts.map +1 -0
  14. package/dist/src/middleware/graphql.js +120 -0
  15. package/dist/src/middleware/introspection.d.ts +71 -0
  16. package/dist/src/middleware/introspection.d.ts.map +1 -0
  17. package/dist/src/middleware/introspection.js +169 -0
  18. package/dist/src/middleware/jwt.d.ts +114 -0
  19. package/dist/src/middleware/jwt.d.ts.map +1 -0
  20. package/dist/src/middleware/jwt.js +291 -0
  21. package/dist/src/middleware/postgraphile.d.ts +7 -0
  22. package/dist/src/middleware/postgraphile.d.ts.map +1 -0
  23. package/dist/src/middleware/postgraphile.js +80 -0
  24. package/dist/src/middleware/yoga.d.ts +15 -0
  25. package/dist/src/middleware/yoga.d.ts.map +1 -0
  26. package/dist/src/middleware/yoga.js +32 -0
  27. package/dist/src/tsdoc-metadata.json +11 -0
  28. package/dist/src/types/index.d.ts +114 -0
  29. package/dist/src/types/index.d.ts.map +1 -0
  30. package/dist/src/types/index.js +0 -0
  31. package/dist/tests/ability.test.d.ts +2 -0
  32. package/dist/tests/ability.test.d.ts.map +1 -0
  33. package/dist/tests/ability.test.js +125 -0
  34. package/dist/tests/helpers/test-utils.d.ts +46 -0
  35. package/dist/tests/helpers/test-utils.d.ts.map +1 -0
  36. package/dist/tests/helpers/test-utils.js +92 -0
  37. package/dist/tests/introspection.test.d.ts +2 -0
  38. package/dist/tests/introspection.test.d.ts.map +1 -0
  39. package/dist/tests/introspection.test.js +368 -0
  40. package/dist/tests/jwt.test.d.ts +2 -0
  41. package/dist/tests/jwt.test.d.ts.map +1 -0
  42. package/dist/tests/jwt.test.js +371 -0
  43. package/dist/tests/middleware.test.d.ts +2 -0
  44. package/dist/tests/middleware.test.d.ts.map +1 -0
  45. package/dist/tests/middleware.test.js +184 -0
  46. package/dist/tests/postgraphile-plugin.test.d.ts +2 -0
  47. package/dist/tests/postgraphile-plugin.test.d.ts.map +1 -0
  48. package/dist/tests/postgraphile-plugin.test.js +56 -0
  49. package/dist/tests/setup.d.ts +2 -0
  50. package/dist/tests/setup.d.ts.map +1 -0
  51. package/dist/tests/setup.js +11 -0
  52. package/dist/tests/user-roles.test.d.ts +2 -0
  53. package/dist/tests/user-roles.test.d.ts.map +1 -0
  54. package/dist/tests/user-roles.test.js +157 -0
  55. package/dist/tests/yoga-plugin.test.d.ts +2 -0
  56. package/dist/tests/yoga-plugin.test.d.ts.map +1 -0
  57. package/dist/tests/yoga-plugin.test.js +47 -0
  58. package/package.json +91 -0
  59. package/src/index.ts +15 -0
  60. package/src/middleware/ability.ts +191 -0
  61. package/src/middleware/graphql.ts +157 -0
  62. package/src/middleware/introspection.ts +258 -0
  63. package/src/middleware/jwt.ts +394 -0
  64. package/src/middleware/postgraphile.ts +93 -0
  65. package/src/middleware/yoga.ts +39 -0
  66. package/src/types/index.ts +133 -0
@@ -0,0 +1,93 @@
1
+ import { extendSchema, gql } from 'postgraphile/utils'
2
+ import type { ExecutableStep } from 'postgraphile/grafast'
3
+ import { GraphileConfig } from 'postgraphile/graphile-build'
4
+
5
+ import { createAbility } from './ability'
6
+
7
+ /**
8
+ * PostGraphile plugin for CASL authorization
9
+ * @public
10
+ */
11
+ export const pglCaslPlugin: GraphileConfig.Plugin = extendSchema(build => {
12
+ const {
13
+ grafast: { constant, object, sideEffect },
14
+ } = build
15
+
16
+ return {
17
+ typeDefs: gql`
18
+ input CreateAbilityInput {
19
+ userId: String!
20
+ roles: [String!]
21
+ }
22
+
23
+ type AbilityResponse {
24
+ success: Boolean!
25
+ ability: JSON
26
+ message: String
27
+ }
28
+
29
+ type SecretData {
30
+ id: String!
31
+ content: String!
32
+ }
33
+
34
+ extend type Query {
35
+ getSecretData: SecretData
36
+ }
37
+
38
+ extend type Mutation {
39
+ createAbility(input: CreateAbilityInput!): AbilityResponse!
40
+ }
41
+ `,
42
+
43
+ objects: {
44
+ Query: {
45
+ plans: {
46
+ async getSecretData() {
47
+ // TODO: This should be protected by CASL
48
+ // const $ability = context<Context>().get('ability')
49
+ // if (!$ability.can('read', 'SecretData')) {
50
+ // throw new Error('Access denied')
51
+ // }
52
+
53
+ return object({
54
+ id: constant('123'),
55
+ content: constant('This is protected content'),
56
+ })
57
+ },
58
+ },
59
+ },
60
+
61
+ Mutation: {
62
+ plans: {
63
+ async createAbility(_plan: any, fieldArgs: any) {
64
+ const $input = fieldArgs.getRaw().input
65
+ const $userId = $input.userId
66
+ const $roles = $input.roles
67
+
68
+ return sideEffect(
69
+ [$userId as ExecutableStep, $roles as ExecutableStep],
70
+ async ([userId, roles]: [string, string[]]) => {
71
+ // Make this async
72
+ try {
73
+ const ability = await createAbility({ id: userId, roles }) // Await here
74
+ return {
75
+ success: true,
76
+ ability: ability.rules,
77
+ message: 'Ability created successfully',
78
+ }
79
+ } catch (error) {
80
+ return {
81
+ success: false,
82
+ ability: null,
83
+ message: error instanceof Error ? error.message : 'Unknown error occurred',
84
+ }
85
+ }
86
+ }
87
+ )
88
+ },
89
+ },
90
+ },
91
+ },
92
+ }
93
+ })
@@ -0,0 +1,39 @@
1
+ import type { Plugin } from 'graphql-yoga'
2
+
3
+ import { createAbility } from './ability'
4
+ import { createCaslMiddleware } from './graphql'
5
+ import type { Context, MiddlewareOptions } from '../types'
6
+
7
+ const getLoggedInUser = () => {
8
+ // Mock user for demonstration purposes
9
+ return { id: '1', roles: ['editor'] }
10
+ }
11
+
12
+ export const yogaCaslPlugin: Plugin<Context> = {
13
+ onContextBuilding: async ({ context, extendContext }) => {
14
+ // Make this async
15
+ const user = getLoggedInUser()
16
+ const ability = await createAbility(user) // Await here
17
+ extendContext({ ability, user })
18
+ },
19
+ }
20
+
21
+ /**
22
+ * Create a GraphQL Yoga plugin for CASL authorization
23
+ * Note: This is a placeholder for future implementation
24
+ *
25
+ * @param options - CASL middleware configuration options
26
+ * @returns Yoga plugin
27
+ * @public
28
+ */
29
+ export const createYogaPlugin = (options: MiddlewareOptions = {}) => {
30
+ const middleware = createCaslMiddleware(options)
31
+
32
+ return {
33
+ onExecute: async ({ args }: any) => {
34
+ // TODO: Implement Yoga plugin
35
+ // This would integrate with GraphQL Yoga's plugin system
36
+ console.log('Yoga plugin not yet implemented')
37
+ },
38
+ }
39
+ }
@@ -0,0 +1,133 @@
1
+ import { GraphQLResolveInfo } from 'graphql'
2
+
3
+ import type { AppAbility, AbilityBuilderFunction } from '../middleware/ability'
4
+
5
+ /**
6
+ * User information for authorization
7
+ * @public
8
+ */
9
+ export interface User {
10
+ /** Unique identifier for the user */
11
+ id: string
12
+ /** Array of role names assigned to the user */
13
+ roles?: string[]
14
+ /** Additional user properties */
15
+ [key: string]: any
16
+ }
17
+
18
+ /**
19
+ * GraphQL context with CASL ability and user information
20
+ * @public
21
+ */
22
+ export interface Context {
23
+ /** CASL ability instance for authorization checks */
24
+ ability?: AppAbility
25
+ /** Current authenticated user */
26
+ user?: User
27
+ /** Additional context properties */
28
+ [key: string]: any
29
+ }
30
+
31
+ // ... rest of the file stays the same
32
+
33
+ /**
34
+ * Configuration options for CASL middleware
35
+ * @public
36
+ */
37
+ export interface MiddlewareOptions {
38
+ /** Mapping of GraphQL types to authorization subjects */
39
+ subjectMap?: Record<string, string>
40
+ /** Mapping of GraphQL operations to CASL actions */
41
+ actionMap?: Record<string, string>
42
+ /** Field-level permission rules */
43
+ fieldPermissions?: Record<string, FieldPermission[]>
44
+ /** Custom function to build user abilities */
45
+ abilityBuilder?: AbilityBuilderFunction
46
+ /** Enable debug logging */
47
+ debug?: boolean
48
+ }
49
+
50
+ /**
51
+ * Field-level permission definition for fine-grained access control
52
+ * @public
53
+ */
54
+ export interface FieldPermission {
55
+ /** CASL action (e.g., 'read', 'write') */
56
+ action: string
57
+ /** Subject/resource type to apply permission to */
58
+ subject: string
59
+ /** Specific field name (optional) */
60
+ field?: string
61
+ /** Conditional rules for permission */
62
+ conditions?: any
63
+ }
64
+
65
+ /**
66
+ * GraphQL resolver function type
67
+ * @public
68
+ */
69
+ export type ResolverFn = (root: any, args: any, context: Context, info: GraphQLResolveInfo) => Promise<any> | any
70
+
71
+ /**
72
+ * Middleware function that wraps a GraphQL resolver with authorization logic
73
+ * @public
74
+ */
75
+ export type MiddlewareFn = (
76
+ resolve: ResolverFn,
77
+ root: any,
78
+ args: any,
79
+ context: Context,
80
+ info: GraphQLResolveInfo
81
+ ) => Promise<any> | any
82
+
83
+ /**
84
+ * Plugin configuration options for framework integrations
85
+ * @public
86
+ */
87
+ export interface PluginOptions extends MiddlewareOptions {
88
+ /** Custom function to build user abilities */
89
+ abilityBuilder?: AbilityBuilderFunction
90
+ /** Ability caching configuration */
91
+ cacheOptions?: {
92
+ /** Time-to-live in milliseconds */
93
+ ttl?: number
94
+ /** Function to generate cache key from user */
95
+ key?: (user?: User) => string
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Response type for ability creation mutations
101
+ * @public
102
+ */
103
+ export interface AbilityResponse {
104
+ /** Whether the ability was created successfully */
105
+ success: boolean
106
+ /** The created ability rules */
107
+ ability: any
108
+ /** Success or error message */
109
+ message: string
110
+ }
111
+
112
+ /**
113
+ * Input type for creating a new ability
114
+ * @public
115
+ */
116
+ export interface CreateAbilityInput {
117
+ /** User ID to create ability for */
118
+ userId: string
119
+ /** Array of role names to assign */
120
+ roles: string[]
121
+ }
122
+
123
+ // Rule definition for database storage
124
+ export interface AbilityRule {
125
+ id?: string
126
+ roleId?: string
127
+ userId?: string
128
+ action: string | string[]
129
+ subject: string
130
+ fields?: string[]
131
+ conditions?: any
132
+ inverted?: boolean
133
+ }