diff --git a/api/dev/Unraid.net/myservers.cfg b/api/dev/Unraid.net/myservers.cfg index 42c67a36b..1c664dad2 100644 --- a/api/dev/Unraid.net/myservers.cfg +++ b/api/dev/Unraid.net/myservers.cfg @@ -1,5 +1,5 @@ [api] -version="4.6.6" +version="4.4.1" extraOrigins="https://google.com,https://test.com" [local] sandbox="yes" diff --git a/api/generated-schema.graphql b/api/generated-schema.graphql index 6fd788d0f..b9e202d1f 100644 --- a/api/generated-schema.graphql +++ b/api/generated-schema.graphql @@ -2,6 +2,18 @@ # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) # ------------------------------------------------------ +"""Directive to document required permissions for fields""" +directive @usePermissions( + """The action verb required for access""" + action: AuthActionVerb + + """The resource required for access""" + resource: String + + """The possession type required for access""" + possession: AuthPossession +) on FIELD_DEFINITION + type ApiKeyResponse { valid: Boolean! error: String @@ -1412,6 +1424,8 @@ type Query { services: [Service!]! shares: [Share!]! vars: Vars! + + """Get information about all VMs on the system""" vms: Vms! parityHistory: [ParityCheck!]! array: UnraidArray! @@ -1421,6 +1435,7 @@ type Query { docker: Docker! disks: [Disk!]! disk(id: String!): Disk! + health: String! } type Mutation { @@ -1590,4 +1605,19 @@ type Subscription { serversSubscription: Server! parityHistorySubscription: ParityCheck! arraySubscription: UnraidArray! +} + +"""Available authentication action verbs""" +enum AuthActionVerb { + CREATE + UPDATE + DELETE + READ +} + +"""Available authentication possession types""" +enum AuthPossession { + ANY + OWN + OWN_ANY } \ No newline at end of file diff --git a/api/src/unraid-api/graph/directives/auth.directive.spec.ts b/api/src/unraid-api/graph/directives/auth.directive.spec.ts deleted file mode 100644 index 32c505ee0..000000000 --- a/api/src/unraid-api/graph/directives/auth.directive.spec.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { makeExecutableSchema } from '@graphql-tools/schema'; -import { Enforcer } from 'casbin'; -import { GraphQLResolveInfo, GraphQLSchema } from 'graphql'; -import { AuthActionVerb, AuthPossession, AuthZService, UsePermissions } from 'nest-authz'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -import { - authSchemaTransformer, - getAuthEnumTypeDefs, - transformResolvers, -} from '@app/unraid-api/graph/directives/auth.directive.js'; - -// Mock UsePermissions function -vi.mock('nest-authz', () => ({ - AuthActionVerb: { - READ: 'READ', - CREATE: 'CREATE', - UPDATE: 'UPDATE', - DELETE: 'DELETE', - }, - AuthPossession: { - OWN: 'OWN', - ANY: 'ANY', - }, - UsePermissions: vi.fn(), -})); - -describe.skip('Auth Directive', () => { - let schema: GraphQLSchema; - - const typeDefs = ` - ${getAuthEnumTypeDefs()} - - type Query { - protectedField: String @auth(action: READ, resource: "USER", possession: OWN) - unprotectedField: String - } - `; - - const resolvers = { - Query: { - protectedField: () => 'protected data', - unprotectedField: () => 'public data', - }, - }; - - beforeEach(() => { - const authZService = new AuthZService({} as Enforcer); - // Create schema for each test - schema = makeExecutableSchema({ - typeDefs, - resolvers: transformResolvers(resolvers, authZService), - }); - - // Apply our auth schema transformer - schema = authSchemaTransformer(schema); - - // Reset all mocks - vi.clearAllMocks(); - }); - - describe('authSchemaTransformer', () => { - it('should add permission information to field description', () => { - const queryType = schema.getQueryType(); - if (!queryType) throw new Error('Query type not found in schema'); - const protectedField = queryType.getFields().protectedField; - - expect(protectedField.description).toContain('Required Permissions'); - expect(protectedField.description).toContain('Action: **READ**'); - expect(protectedField.description).toContain('Resource: **USER**'); - expect(protectedField.description).toContain('Possession: **OWN**'); - }); - - it('should store permission requirements in field extensions', () => { - const queryType = schema.getQueryType(); - if (!queryType) throw new Error('Query type not found in schema'); - const protectedField = queryType.getFields().protectedField; - - expect(protectedField.extensions).toBeDefined(); - expect(protectedField.extensions.requiredPermissions).toEqual({ - action: 'READ', - resource: 'USER', - possession: 'OWN', - }); - }); - - it('should not modify fields without auth directive', () => { - const queryType = schema.getQueryType(); - if (!queryType) throw new Error('Query type not found in schema'); - const unprotectedField = queryType.getFields().unprotectedField; - - expect(unprotectedField.extensions?.requiredPermissions).toBeUndefined(); - expect(unprotectedField.description).toBeFalsy(); - }); - }); - - describe('transformResolvers', () => { - it('should wrap resolvers to check permissions before execution', async () => { - const queryType = schema.getQueryType(); - if (!queryType) throw new Error('Query type not found in schema'); - const protectedField = queryType.getFields().protectedField; - - const mockSource = {}; - const mockArgs = {}; - const mockContext: { requiredPermissions?: any } = {}; - - // Instead of mocking GraphQLResolveInfo, we can invoke the wrapped resolver directly - // Create a simple function to extract the resolver and call it with our mock objects - const testResolver = async () => { - if (!schema.getQueryType()) return; - - // Get the schema fields - const queryFields = schema.getQueryType()!.getFields(); - - // Call the manually wrapped resolver - if (queryFields.protectedField && queryFields.protectedField.resolve) { - const result = await queryFields.protectedField.resolve( - mockSource, - mockArgs, - mockContext, - { - fieldName: 'protectedField', - parentType: { name: 'Query' }, - schema, - // We need these fields, but they aren't actually used in the auth directive code - fieldNodes: [] as any, - returnType: {} as any, - path: {} as any, - fragments: {} as any, - rootValue: null as any, - operation: {} as any, - variableValues: {} as any, - } as unknown as GraphQLResolveInfo - ); - return result; - } - return; - }; - - await testResolver(); - - // Check that permissions were set in context - expect(mockContext).toHaveProperty('requiredPermissions'); - expect(mockContext.requiredPermissions).toEqual({ - action: 'READ', - resource: 'USER', - possession: 'OWN', - }); - - // Check that UsePermissions was called with the right params - expect(UsePermissions).toHaveBeenCalledWith({ - action: 'READ', - resource: 'USER', - possession: 'OWN', - }); - }); - - it('should not apply permissions for unprotected fields', async () => { - const queryType = schema.getQueryType(); - if (!queryType) throw new Error('Query type not found in schema'); - const unprotectedField = queryType.getFields().unprotectedField; - - const mockSource = {}; - const mockArgs = {}; - const mockContext: { requiredPermissions?: any } = {}; - - // Instead of mocking GraphQLResolveInfo, we can invoke the wrapped resolver directly - const testResolver = async () => { - if (!schema.getQueryType()) return; - - // Get the schema fields - const queryFields = schema.getQueryType()!.getFields(); - - // Call the manually wrapped resolver - if (queryFields.unprotectedField && queryFields.unprotectedField.resolve) { - const result = await queryFields.unprotectedField.resolve( - mockSource, - mockArgs, - mockContext, - { - fieldName: 'unprotectedField', - parentType: { name: 'Query' }, - schema, - // We need these fields, but they aren't actually used in the auth directive code - fieldNodes: [] as any, - returnType: {} as any, - path: {} as any, - fragments: {} as any, - rootValue: null as any, - operation: {} as any, - variableValues: {} as any, - } as unknown as GraphQLResolveInfo - ); - return result; - } - return; - }; - - await testResolver(); - - // Check that permissions were not set or checked - expect(mockContext.requiredPermissions).toBeUndefined(); - expect(UsePermissions).not.toHaveBeenCalled(); - }); - - it('should handle an array of resolvers', () => { - const authZService = new AuthZService({} as Enforcer); - const resolversArray = [ - { Query: { field1: () => 'data' } }, - { Mutation: { field2: () => 'data' } }, - ] as any; // Type assertion to avoid complex IResolvers typing - - const transformed = transformResolvers(resolversArray, authZService); - expect(Array.isArray(transformed)).toBe(true); - expect(transformed).toHaveLength(2); - }); - }); - - describe('getAuthEnumTypeDefs', () => { - it('should generate valid SDL for auth enums', () => { - const typeDefs = getAuthEnumTypeDefs(); - - expect(typeDefs).toContain('enum AuthActionVerb'); - expect(typeDefs).toContain('enum AuthPossession'); - expect(typeDefs).toContain('directive @auth'); - - // Check for enum values - Object.keys(AuthActionVerb) - .filter((key) => isNaN(Number(key))) - .forEach((key) => { - expect(typeDefs).toContain(key); - }); - - Object.keys(AuthPossession) - .filter((key) => isNaN(Number(key))) - .forEach((key) => { - expect(typeDefs).toContain(key); - }); - }); - }); -}); diff --git a/api/src/unraid-api/graph/directives/auth.directive.ts b/api/src/unraid-api/graph/directives/auth.directive.ts deleted file mode 100644 index 98686cdf9..000000000 --- a/api/src/unraid-api/graph/directives/auth.directive.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { UnauthorizedException } from '@nestjs/common'; - -import { getDirective, IResolvers, MapperKind, mapSchema } from '@graphql-tools/utils'; -import { GraphQLEnumType, GraphQLSchema } from 'graphql'; -import { AuthActionVerb, AuthPossession, AuthZService, BatchApproval } from 'nest-authz'; - -import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; - -/** - * @wip : This function does not correctly apply permission to every field. - * @todo : Once we've determined how to fix the transformResolvers function, uncomment this. - */ -export function transformResolvers( - resolvers: IResolvers | IResolvers[], - authZService: AuthZService -): IResolvers | IResolvers[] { - if (Array.isArray(resolvers)) { - return resolvers.map((r) => transformResolvers(r, authZService)) as IResolvers[]; - } - - // Iterate over each type in the resolvers object - Object.keys(resolvers).forEach((typeName) => { - const typeResolvers = resolvers[typeName]; - // Iterate over each field within the type - Object.keys(typeResolvers).forEach((fieldName) => { - const fieldResolver = typeResolvers[fieldName]; - // Skip non-function resolvers (or if it's not defined) - - if (typeof fieldResolver !== 'function') { - return; - } - // Check if this field has permission metadata in its extensions property - // We need to wrap the resolver in a function that checks if the user has the required permissions - - const originalResolver = fieldResolver; - - // Create a wrapped resolver that will extract permissions from info - typeResolvers[fieldName] = async (source, args, context, info) => { - // Access the extensions from the field definition in the schema - console.log( - 'resolving', - info.fieldName, - info.parentType.name, - info.schema.getType(info.parentType.name).getFields()[info.fieldName].extensions - ); - console.log('user', context?.req?.user); - const fieldExtensions = info.schema.getType(info.parentType.name).getFields()[ - info.fieldName - ].extensions; - if (fieldExtensions?.requiredPermissions && context?.req?.user) { - const { action, resource, possession } = fieldExtensions.requiredPermissions; - - if (context) { - // Handle OWN_ANY possession by checking both ANY and OWN permissions - if (possession === AuthPossession.OWN_ANY) { - context.requiredPermissions = [ - { - action: action.toUpperCase(), - resource: resource.toUpperCase(), - possession: AuthPossession.ANY, - }, - { - action: action.toUpperCase(), - resource: resource.toUpperCase(), - possession: AuthPossession.OWN, - }, - ]; - // For OWN_ANY, we want to check both ANY and OWN permissions - // If either check passes, the user has permission - const hasPermission = await authZService.enforce( - context.user, - resource.toUpperCase(), - action.toUpperCase(), - BatchApproval.ANY - ); - - if (!hasPermission) { - throw new UnauthorizedException( - 'Unauthorized: User does not have required permissions' - ); - } - } else { - context.requiredPermissions = { - action: action.toUpperCase(), - resource: resource.toUpperCase(), - possession: possession.toUpperCase(), - }; - - // For regular permissions, we check the specific possession type - const hasPermission = await authZService.enforce( - context.user, - resource.toUpperCase(), - `${action.toUpperCase()}:${possession.toUpperCase()}` - ); - - if (!hasPermission) { - throw new UnauthorizedException( - 'Unauthorized: User does not have required permissions' - ); - } - } - } - } - - // Call the original resolver after permission check - return await originalResolver(source, args, context, info); - }; - }); - }); - - return resolvers; -} - -export function authSchemaTransformer(schema: GraphQLSchema): GraphQLSchema { - return mapSchema(schema, { - [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => { - const authDirective = getDirective(schema, fieldConfig, 'auth')?.[0]; - - if (authDirective) { - const { - action: actionValue, - resource: resourceValue, - possession: possessionValue, - } = authDirective; - - if (!actionValue || !resourceValue || !possessionValue) { - console.warn( - `Auth directive on ${typeName}.${fieldName} is missing required arguments.` - ); - return fieldConfig; - } - - // Append permission information to the field description - const permissionDoc = ` - -#### Required Permissions: - -- Action: **${actionValue}** -- Resource: **${resourceValue}** -- Possession: **${possessionValue}**`; - const newDescription = fieldConfig.description - ? `${fieldConfig.description}${permissionDoc}` - : permissionDoc; - fieldConfig.description = newDescription; - - // Store the required permissions in the field config extensions - fieldConfig.extensions = { - ...fieldConfig.extensions, - requiredPermissions: { - action: actionValue.toUpperCase() as AuthActionVerb, - resource: resourceValue.toUpperCase() as Resource, - possession: possessionValue.toUpperCase() as AuthPossession, - }, - }; - } - - return fieldConfig; - }, - }); -} - -/** - * Generates GraphQL SDL strings for the authentication enums. - */ -export function getAuthEnumTypeDefs(): string { - // Helper to generate enum values string part with descriptions - const getEnumValues = (tsEnum: Record): string => { - return Object.entries(tsEnum) - .filter(([key]) => isNaN(Number(key))) // Filter out numeric keys - .map(([key]) => ` ${key}`) - .join('\n'); - }; - - return `""" -Available authentication action verbs -""" -enum AuthActionVerb { -${getEnumValues(AuthActionVerb)} -} - -""" -Available authentication possession types -""" -enum AuthPossession { -${getEnumValues(AuthPossession)} -} - -directive @auth( - action: AuthActionVerb!, - resource: String!, - possession: AuthPossession! -) on FIELD_DEFINITION -`; -} - -/** - * Generic function to convert TypeScript enums to GraphQL enums - * (Kept for potential other uses, but not used for Auth enums in schema generation anymore) - */ -export function createGraphQLEnumFromTSEnum( - tsEnum: Record, - name: string, - description: string -): GraphQLEnumType { - const enumValues = {}; - - Object.keys(tsEnum).forEach((key) => { - if (isNaN(Number(key))) { - // Skip numeric keys (enum in TS has both string and number keys) - enumValues[key] = { value: tsEnum[key] }; - } - }); - - return new GraphQLEnumType({ - name, - description, - values: enumValues, - }); -} diff --git a/api/src/unraid-api/graph/directives/use-permissions.directive.ts b/api/src/unraid-api/graph/directives/use-permissions.directive.ts new file mode 100644 index 000000000..70ce9fe49 --- /dev/null +++ b/api/src/unraid-api/graph/directives/use-permissions.directive.ts @@ -0,0 +1,126 @@ +import { Directive } from '@nestjs/graphql'; + +import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'; +import { + DirectiveLocation, + GraphQLDirective, + GraphQLEnumType, + GraphQLSchema, + GraphQLString, +} from 'graphql'; +import { AuthActionVerb, AuthPossession, UsePermissions as NestAuthzUsePermissions } from 'nest-authz'; + +// Re-export the types from nest-authz +export { AuthActionVerb, AuthPossession }; + +const buildGraphQLEnum = ( + enumObj: Record, + name: string, + description: string +) => { + const values = Object.entries(enumObj) + .filter(([key]) => isNaN(Number(key))) + .reduce( + (acc, [key]) => { + acc[key] = { value: key }; + return acc; + }, + {} as Record + ); + + return new GraphQLEnumType({ name, description, values }); +}; + +// Create GraphQL enum types for auth action verbs and possessions +const AuthActionVerbEnum = buildGraphQLEnum( + AuthActionVerb, + 'AuthActionVerb', + 'Available authentication action verbs' +); + +const AuthPossessionEnum = buildGraphQLEnum( + AuthPossession, + 'AuthPossession', + 'Available authentication possession types' +); + +// Create the auth directive +export const UsePermissionsDirective = new GraphQLDirective({ + name: 'usePermissions', + description: 'Directive to document required permissions for fields', + locations: [DirectiveLocation.FIELD_DEFINITION], + args: { + action: { + type: AuthActionVerbEnum, + description: 'The action verb required for access', + }, + resource: { + type: GraphQLString, + description: 'The resource required for access', + }, + possession: { + type: AuthPossessionEnum, + description: 'The possession type required for access', + }, + }, +}); + +// Create a decorator that combines both the GraphQL directive and UsePermissions +export const UsePermissions = (permissions: { + action: AuthActionVerb; + resource: string; + possession: AuthPossession; +}) => { + return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { + // Apply UsePermissions for actual authorization + NestAuthzUsePermissions(permissions)(target, propertyKey, descriptor); + + // Apply GraphQL directive using NestJS's @Directive decorator + Directive( + `@usePermissions(action: ${permissions.action.toUpperCase()}, resource: "${permissions.resource}", possession: ${permissions.possession.toUpperCase()})` + )(target, propertyKey, descriptor); + + return descriptor; + }; +}; + +// Schema transformer to add permission documentation +export function usePermissionsSchemaTransformer(schema: GraphQLSchema) { + return mapSchema(schema, { + [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => { + const usePermissionsDirective = getDirective(schema, fieldConfig, 'usePermissions')?.[0]; + if (usePermissionsDirective) { + const { + action: actionValue, + resource: resourceValue, + possession: possessionValue, + } = usePermissionsDirective; + + if (!actionValue || !resourceValue || !possessionValue) { + console.warn( + `UsePermissions directive on ${typeName}.${fieldName} is missing required arguments.` + ); + return fieldConfig; + } + + // Append permission information to the field description + const permissionDoc = ` +#### Required Permissions: + +- Action: **${actionValue}** +- Resource: **${resourceValue}** +- Possession: **${possessionValue}**`; + const descriptionDoc = fieldConfig.description + ? ` + +#### Description: + +${fieldConfig.description}` + : ''; + fieldConfig.description = permissionDoc + descriptionDoc; + } + + return fieldConfig; + }, + }); +} diff --git a/api/src/unraid-api/graph/graph.module.ts b/api/src/unraid-api/graph/graph.module.ts index aeaa6f375..31e96cf22 100644 --- a/api/src/unraid-api/graph/graph.module.ts +++ b/api/src/unraid-api/graph/graph.module.ts @@ -9,6 +9,10 @@ import { JSONResolver, URLResolver } from 'graphql-scalars'; import { ENVIRONMENT } from '@app/environment.js'; import { GraphQLLong } from '@app/graphql/resolvers/graphql-type-long.js'; import { getters } from '@app/store/index.js'; +import { + UsePermissionsDirective, + usePermissionsSchemaTransformer, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { idPrefixPlugin } from '@app/unraid-api/graph/id-prefix-plugin.js'; import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js'; import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js'; @@ -50,7 +54,9 @@ import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js'; }, buildSchemaOptions: { dateScalarMode: 'isoDate', + directives: [UsePermissionsDirective], }, + transformSchema: usePermissionsSchemaTransformer, validationRules: [NoUnusedVariablesRule], }; }, diff --git a/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts index f71f5fb61..685da705d 100644 --- a/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts @@ -1,9 +1,12 @@ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { ApiKeyService } from '@app/unraid-api/auth/api-key.service.js'; import { AuthService } from '@app/unraid-api/auth/auth.service.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { AddRoleForApiKeyInput, ApiKey, diff --git a/api/src/unraid-api/graph/resolvers/array/array.mutations.resolver.ts b/api/src/unraid-api/graph/resolvers/array/array.mutations.resolver.ts index dde7d1dda..919df86e9 100644 --- a/api/src/unraid-api/graph/resolvers/array/array.mutations.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/array/array.mutations.resolver.ts @@ -1,8 +1,11 @@ import { BadRequestException } from '@nestjs/common'; import { Args, Mutation, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { ArrayDisk, ArrayDiskInput, diff --git a/api/src/unraid-api/graph/resolvers/array/array.resolver.ts b/api/src/unraid-api/graph/resolvers/array/array.resolver.ts index 502184c79..03df8a449 100644 --- a/api/src/unraid-api/graph/resolvers/array/array.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/array/array.resolver.ts @@ -1,10 +1,11 @@ import { Query, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - -import { getArrayData } from '@app/core/modules/array/get-array-data.js'; import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; -import { store } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { UnraidArray } from '@app/unraid-api/graph/resolvers/array/array.model.js'; import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/array/parity.mutations.resolver.ts b/api/src/unraid-api/graph/resolvers/array/parity.mutations.resolver.ts index bac72602d..df924bc57 100644 --- a/api/src/unraid-api/graph/resolvers/array/parity.mutations.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/array/parity.mutations.resolver.ts @@ -1,8 +1,12 @@ import { Args, Mutation, ResolveField, Resolver } from '@nestjs/graphql'; import { GraphQLJSON } from 'graphql-scalars'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { ParityService } from '@app/unraid-api/graph/resolvers/array/parity.service.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { ParityCheckMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/array/parity.resolver.ts b/api/src/unraid-api/graph/resolvers/array/parity.resolver.ts index 8386b39d9..52692b100 100644 --- a/api/src/unraid-api/graph/resolvers/array/parity.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/array/parity.resolver.ts @@ -1,10 +1,13 @@ -import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql'; +import { Query, Resolver, Subscription } from '@nestjs/graphql'; -import { GraphQLJSON } from 'graphql-scalars'; import { PubSub } from 'graphql-subscriptions'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; import { PUBSUB_CHANNEL } from '@app/core/pubsub.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { ArrayService } from '@app/unraid-api/graph/resolvers/array/array.service.js'; import { ParityCheck } from '@app/unraid-api/graph/resolvers/array/parity.model.js'; import { ParityService } from '@app/unraid-api/graph/resolvers/array/parity.service.js'; diff --git a/api/src/unraid-api/graph/resolvers/cloud/cloud.resolver.ts b/api/src/unraid-api/graph/resolvers/cloud/cloud.resolver.ts index 3bd175ef9..327d6a8a6 100644 --- a/api/src/unraid-api/graph/resolvers/cloud/cloud.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/cloud/cloud.resolver.ts @@ -1,11 +1,14 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getAllowedOrigins } from '@app/common/allowed-origins.js'; import { checkApi } from '@app/graphql/resolvers/query/cloud/check-api.js'; import { checkCloud } from '@app/graphql/resolvers/query/cloud/check-cloud.js'; import { checkMinigraphql } from '@app/graphql/resolvers/query/cloud/check-minigraphql.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Cloud } from '@app/unraid-api/graph/resolvers/cloud/cloud.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/config/config.resolver.ts b/api/src/unraid-api/graph/resolvers/config/config.resolver.ts index e9084d461..4345e1a90 100644 --- a/api/src/unraid-api/graph/resolvers/config/config.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/config/config.resolver.ts @@ -1,8 +1,11 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getters } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Config } from '@app/unraid-api/graph/resolvers/config/config.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/connect/connect-settings.resolver.ts b/api/src/unraid-api/graph/resolvers/connect/connect-settings.resolver.ts index eefda05dc..c0d6ef72e 100644 --- a/api/src/unraid-api/graph/resolvers/connect/connect-settings.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/connect/connect-settings.resolver.ts @@ -3,11 +3,15 @@ import { Args, ID, Mutation, Query, ResolveField, Resolver } from '@nestjs/graph import { Layout } from '@jsonforms/core'; import { GraphQLJSON, GraphQLJSONObject } from 'graphql-scalars'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; import { getAllowedOrigins } from '@app/common/allowed-origins.js'; import { setupRemoteAccessThunk } from '@app/store/actions/setup-remote-access.js'; import { logoutUser, updateAllowedOrigins } from '@app/store/modules/config.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { ConnectSettingsService } from '@app/unraid-api/graph/resolvers/connect/connect-settings.service.js'; import { diff --git a/api/src/unraid-api/graph/resolvers/connect/connect.resolver.ts b/api/src/unraid-api/graph/resolvers/connect/connect.resolver.ts index 4a818f57c..124a734d2 100644 --- a/api/src/unraid-api/graph/resolvers/connect/connect.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/connect/connect.resolver.ts @@ -1,9 +1,12 @@ import { Logger } from '@nestjs/common'; import { Query, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { store } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Connect, diff --git a/api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts b/api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts index e995cdee4..bb864650b 100644 --- a/api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts @@ -1,7 +1,10 @@ import { Args, Int, Parent, Query, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Disk } from '@app/unraid-api/graph/resolvers/disks/disks.model.js'; import { DisksService } from '@app/unraid-api/graph/resolvers/disks/disks.service.js'; diff --git a/api/src/unraid-api/graph/resolvers/display/display.resolver.ts b/api/src/unraid-api/graph/resolvers/display/display.resolver.ts index f6771d85d..479923f81 100644 --- a/api/src/unraid-api/graph/resolvers/display/display.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/display/display.resolver.ts @@ -3,10 +3,13 @@ import { existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; import { getters } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Display } from '@app/unraid-api/graph/resolvers/info/info.model.js'; @@ -60,12 +63,12 @@ const states = { @Resolver(() => Display) export class DisplayResolver { - @Query(() => Display) @UsePermissions({ action: AuthActionVerb.READ, resource: Resource.DISPLAY, possession: AuthPossession.ANY, }) + @Query(() => Display) public async display(): Promise { /** * This is deprecated, remove it eventually diff --git a/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts b/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts index eb66e221c..ebbfd36d9 100644 --- a/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/docker/docker.mutations.resolver.ts @@ -1,7 +1,10 @@ import { Args, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { DockerContainer } from '@app/unraid-api/graph/resolvers/docker/docker.model.js'; import { DockerService } from '@app/unraid-api/graph/resolvers/docker/docker.service.js'; diff --git a/api/src/unraid-api/graph/resolvers/docker/docker.resolver.ts b/api/src/unraid-api/graph/resolvers/docker/docker.resolver.ts index 7b4ff47fd..50e885e86 100644 --- a/api/src/unraid-api/graph/resolvers/docker/docker.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/docker/docker.resolver.ts @@ -1,7 +1,10 @@ import { Query, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Docker, diff --git a/api/src/unraid-api/graph/resolvers/flash/flash.resolver.ts b/api/src/unraid-api/graph/resolvers/flash/flash.resolver.ts index c266524e3..b686f044c 100644 --- a/api/src/unraid-api/graph/resolvers/flash/flash.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/flash/flash.resolver.ts @@ -1,8 +1,11 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getters } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Flash } from '@app/unraid-api/graph/resolvers/flash/flash.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/info/info.resolver.ts b/api/src/unraid-api/graph/resolvers/info/info.resolver.ts index 23ae2ec45..d6e188bdc 100644 --- a/api/src/unraid-api/graph/resolvers/info/info.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/info/info.resolver.ts @@ -1,6 +1,5 @@ import { Query, ResolveField, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; import { baseboard as getBaseboard, system as getSystem } from 'systeminformation'; import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; @@ -14,6 +13,11 @@ import { generateOs, generateVersions, } from '@app/graphql/resolvers/query/info.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Baseboard, diff --git a/api/src/unraid-api/graph/resolvers/logs/logs.resolver.ts b/api/src/unraid-api/graph/resolvers/logs/logs.resolver.ts index e9f200cc4..6a4efe8bc 100644 --- a/api/src/unraid-api/graph/resolvers/logs/logs.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/logs/logs.resolver.ts @@ -1,8 +1,11 @@ import { Args, Int, Query, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { LogFile, LogFileContent } from '@app/unraid-api/graph/resolvers/logs/logs.model.js'; import { LogsService } from '@app/unraid-api/graph/resolvers/logs/logs.service.js'; diff --git a/api/src/unraid-api/graph/resolvers/network/network.resolver.ts b/api/src/unraid-api/graph/resolvers/network/network.resolver.ts index 32593b2fd..7d57b45ac 100644 --- a/api/src/unraid-api/graph/resolvers/network/network.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/network/network.resolver.ts @@ -1,8 +1,11 @@ import { Query, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getServerIps } from '@app/graphql/resolvers/subscription/network.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { AccessUrl, Network } from '@app/unraid-api/graph/resolvers/connect/connect.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/notifications/notifications.resolver.ts b/api/src/unraid-api/graph/resolvers/notifications/notifications.resolver.ts index d76ff3536..d9bf3f038 100644 --- a/api/src/unraid-api/graph/resolvers/notifications/notifications.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/notifications/notifications.resolver.ts @@ -1,9 +1,12 @@ import { Args, Mutation, Query, ResolveField, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { AppError } from '@app/core/errors/app-error.js'; import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Notification, diff --git a/api/src/unraid-api/graph/resolvers/online/online.resolver.ts b/api/src/unraid-api/graph/resolvers/online/online.resolver.ts index aa38a3501..292a1a280 100644 --- a/api/src/unraid-api/graph/resolvers/online/online.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/online/online.resolver.ts @@ -1,7 +1,10 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Online } from '@app/unraid-api/graph/resolvers/online/online.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/owner/owner.resolver.ts b/api/src/unraid-api/graph/resolvers/owner/owner.resolver.ts index c8ad40def..db7db51df 100644 --- a/api/src/unraid-api/graph/resolvers/owner/owner.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/owner/owner.resolver.ts @@ -1,9 +1,12 @@ import { Query, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; import { getters } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Owner } from '@app/unraid-api/graph/resolvers/owner/owner.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/registration/registration.resolver.ts b/api/src/unraid-api/graph/resolvers/registration/registration.resolver.ts index ff761d855..f4fef52e7 100644 --- a/api/src/unraid-api/graph/resolvers/registration/registration.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/registration/registration.resolver.ts @@ -1,11 +1,14 @@ import { Query, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; import { getKeyFile } from '@app/core/utils/misc/get-key-file.js'; import { getters } from '@app/store/index.js'; import { FileLoadStatus } from '@app/store/types.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Registration, diff --git a/api/src/unraid-api/graph/resolvers/servers/server.resolver.ts b/api/src/unraid-api/graph/resolvers/servers/server.resolver.ts index c69f40b7c..a01d333b8 100644 --- a/api/src/unraid-api/graph/resolvers/servers/server.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/servers/server.resolver.ts @@ -1,9 +1,12 @@ import { Query, Resolver, Subscription } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { createSubscription, PUBSUB_CHANNEL } from '@app/core/pubsub.js'; import { getLocalServer } from '@app/graphql/schema/utils.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Server as ServerModel } from '@app/unraid-api/graph/resolvers/servers/server.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/vars/vars.resolver.ts b/api/src/unraid-api/graph/resolvers/vars/vars.resolver.ts index e1d328720..ba6a01a4c 100644 --- a/api/src/unraid-api/graph/resolvers/vars/vars.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/vars/vars.resolver.ts @@ -1,8 +1,11 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getters } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { Vars } from '@app/unraid-api/graph/resolvers/vars/vars.model.js'; diff --git a/api/src/unraid-api/graph/resolvers/vms/vms.mutations.resolver.ts b/api/src/unraid-api/graph/resolvers/vms/vms.mutations.resolver.ts index ba5a5d7ce..3cb287d20 100644 --- a/api/src/unraid-api/graph/resolvers/vms/vms.mutations.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/vms/vms.mutations.resolver.ts @@ -1,7 +1,10 @@ import { Args, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { VmMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js'; import { VmsService } from '@app/unraid-api/graph/resolvers/vms/vms.service.js'; diff --git a/api/src/unraid-api/graph/resolvers/vms/vms.resolver.ts b/api/src/unraid-api/graph/resolvers/vms/vms.resolver.ts index fc9223f1d..7252f208f 100644 --- a/api/src/unraid-api/graph/resolvers/vms/vms.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/vms/vms.resolver.ts @@ -1,7 +1,10 @@ import { Query, ResolveField, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { VmDomain, Vms } from '@app/unraid-api/graph/resolvers/vms/vms.model.js'; import { VmsService } from '@app/unraid-api/graph/resolvers/vms/vms.service.js'; @@ -10,7 +13,7 @@ import { VmsService } from '@app/unraid-api/graph/resolvers/vms/vms.service.js'; export class VmsResolver { constructor(private readonly vmsService: VmsService) {} - @Query(() => Vms) + @Query(() => Vms, { description: 'Get information about all VMs on the system' }) @UsePermissions({ action: AuthActionVerb.READ, resource: Resource.VMS, @@ -35,12 +38,6 @@ export class VmsResolver { @ResolveField(() => [VmDomain]) public async domain(): Promise> { - try { - return await this.vmsService.getDomains(); - } catch (error) { - throw new Error( - `Failed to retrieve VM domains: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } + return this.domains(); } } diff --git a/api/src/unraid-api/graph/services/services.resolver.ts b/api/src/unraid-api/graph/services/services.resolver.ts index 0e02c4942..6b348eb0e 100644 --- a/api/src/unraid-api/graph/services/services.resolver.ts +++ b/api/src/unraid-api/graph/services/services.resolver.ts @@ -1,10 +1,13 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { bootTimestamp } from '@app/common/dashboard/boot-timestamp.js'; import { API_VERSION } from '@app/environment.js'; import { store } from '@app/store/index.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { DynamicRemoteAccessType } from '@app/unraid-api/graph/resolvers/connect/connect.model.js'; import { Service } from '@app/unraid-api/graph/services/service.model.js'; diff --git a/api/src/unraid-api/graph/shares/shares.resolver.ts b/api/src/unraid-api/graph/shares/shares.resolver.ts index c03f5ddba..e9ae45d1c 100644 --- a/api/src/unraid-api/graph/shares/shares.resolver.ts +++ b/api/src/unraid-api/graph/shares/shares.resolver.ts @@ -1,8 +1,11 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { getShares } from '@app/core/utils/shares/get-shares.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Share } from '@app/unraid-api/graph/resolvers/array/array.model.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; diff --git a/api/src/unraid-api/graph/user/user.resolver.ts b/api/src/unraid-api/graph/user/user.resolver.ts index 3eeb22d1f..f6ef6f3a3 100644 --- a/api/src/unraid-api/graph/user/user.resolver.ts +++ b/api/src/unraid-api/graph/user/user.resolver.ts @@ -1,8 +1,11 @@ import { Query, Resolver } from '@nestjs/graphql'; -import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; - import { GraphqlUser } from '@app/unraid-api/auth/user.decorator.js'; +import { + AuthActionVerb, + AuthPossession, + UsePermissions, +} from '@app/unraid-api/graph/directives/use-permissions.directive.js'; import { Resource } from '@app/unraid-api/graph/resolvers/base.model.js'; import { UserAccount } from '@app/unraid-api/graph/user/user.model.js'; diff --git a/api/src/unraid-api/graph/utils/auth-enum.utils.ts b/api/src/unraid-api/graph/utils/auth-enum.utils.ts deleted file mode 100644 index ea192c4d6..000000000 --- a/api/src/unraid-api/graph/utils/auth-enum.utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AuthPossession } from 'nest-authz'; -import { AuthActionVerb } from 'nest-authz/dist/src/types.js'; - -/** - * Generates GraphQL SDL strings for the authentication enums. - */ -export function getAuthEnumTypeDefs(): string { - // Helper to generate enum values string part with descriptions - const getEnumValues = (tsEnum: Record): string => { - return Object.entries(tsEnum) - .filter(([key]) => isNaN(Number(key))) // Filter out numeric keys - .map(([key]) => ` ${key}`) - .join('\n'); - }; - - return `""" -Available authentication action verbs -""" -enum AuthActionVerb { -${getEnumValues(AuthActionVerb)} -} - -""" -Available authentication possession types -""" -enum AuthPossession { -${getEnumValues(AuthPossession)} -} - -directive @auth( - action: AuthActionVerb!, - resource: String!, - possession: AuthPossession! -) on FIELD_DEFINITION -`; -}