fix: create api key permissions

This commit is contained in:
Eli Bosley
2025-01-28 13:45:25 -05:00
parent eb1c62d3d9
commit 14fe30e925
6 changed files with 39 additions and 40 deletions

View File

@@ -94,10 +94,8 @@ export function AccessUrlInputSchema(): z.ZodObject<Properties<AccessUrlInput>>
export function AddPermissionInputSchema(): z.ZodObject<Properties<AddPermissionInput>> { export function AddPermissionInputSchema(): z.ZodObject<Properties<AddPermissionInput>> {
return z.object({ return z.object({
action: z.string(), actions: z.array(z.string()),
possession: z.string(), resource: ResourceSchema
resource: ResourceSchema,
role: RoleSchema
}) })
} }
@@ -326,7 +324,8 @@ export function CreateApiKeyInputSchema(): z.ZodObject<Properties<CreateApiKeyIn
return z.object({ return z.object({
description: z.string().nullish(), description: z.string().nullish(),
name: z.string(), name: z.string(),
roles: z.array(RoleSchema) permissions: z.array(z.lazy(() => AddPermissionInputSchema())).nullish(),
roles: z.array(RoleSchema).nullish()
}) })
} }
@@ -1230,7 +1229,8 @@ export function VmDomainSchema(): z.ZodObject<Properties<VmDomain>> {
export function VmsSchema(): z.ZodObject<Properties<Vms>> { export function VmsSchema(): z.ZodObject<Properties<Vms>> {
return z.object({ return z.object({
__typename: z.literal('Vms').optional(), __typename: z.literal('Vms').optional(),
domain: z.array(VmDomainSchema()).nullish() domain: z.array(VmDomainSchema()).nullish(),
id: z.string()
}) })
} }

View File

@@ -40,10 +40,8 @@ export type AccessUrlInput = {
}; };
export type AddPermissionInput = { export type AddPermissionInput = {
action: Scalars['String']['input']; actions: Array<Scalars['String']['input']>;
possession: Scalars['String']['input'];
resource: Resource; resource: Resource;
role: Role;
}; };
export type AddRoleForApiKeyInput = { export type AddRoleForApiKeyInput = {
@@ -350,7 +348,8 @@ export enum ContainerState {
export type CreateApiKeyInput = { export type CreateApiKeyInput = {
description?: InputMaybe<Scalars['String']['input']>; description?: InputMaybe<Scalars['String']['input']>;
name: Scalars['String']['input']; name: Scalars['String']['input'];
roles: Array<Role>; permissions?: InputMaybe<Array<AddPermissionInput>>;
roles?: InputMaybe<Array<Role>>;
}; };
export type Devices = { export type Devices = {
@@ -1682,6 +1681,7 @@ export enum VmState {
export type Vms = { export type Vms = {
__typename?: 'Vms'; __typename?: 'Vms';
domain?: Maybe<Array<VmDomain>>; domain?: Maybe<Array<VmDomain>>;
id: Scalars['ID']['output'];
}; };
export enum WAN_ACCESS_TYPE { export enum WAN_ACCESS_TYPE {
@@ -3090,6 +3090,7 @@ export type VmDomainResolvers<ContextType = Context, ParentType extends Resolver
export type VmsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Vms'] = ResolversParentTypes['Vms']> = ResolversObject<{ export type VmsResolvers<ContextType = Context, ParentType extends ResolversParentTypes['Vms'] = ResolversParentTypes['Vms']> = ResolversObject<{
domain?: Resolver<Maybe<Array<ResolversTypes['VmDomain']>>, ParentType, ContextType>; domain?: Resolver<Maybe<Array<ResolversTypes['VmDomain']>>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
}>; }>;

View File

@@ -25,14 +25,13 @@ type ApiKeyWithSecret {
input CreateApiKeyInput { input CreateApiKeyInput {
name: String! name: String!
description: String description: String
roles: [Role!]! roles: [Role!]
permissions: [AddPermissionInput!]
} }
input AddPermissionInput { input AddPermissionInput {
role: Role!
resource: Resource! resource: Resource!
action: String! actions: [String!]!
possession: String!
} }
input AddRoleForUserInput { input AddRoleForUserInput {

View File

@@ -13,12 +13,12 @@ import { ZodError } from 'zod';
import { environment } from '@app/environment'; import { environment } from '@app/environment';
import { ApiKeySchema, ApiKeyWithSecretSchema } from '@app/graphql/generated/api/operations'; import { ApiKeySchema, ApiKeyWithSecretSchema } from '@app/graphql/generated/api/operations';
import { import {
AddPermissionInput,
ApiKey, ApiKey,
ApiKeyWithSecret, ApiKeyWithSecret,
Permission, Permission,
Resource, Resource,
Role, Role,
UserAccount,
} from '@app/graphql/generated/api/types'; } from '@app/graphql/generated/api/types';
import { getters, store } from '@app/store'; import { getters, store } from '@app/store';
import { updateUserConfig } from '@app/store/modules/config'; import { updateUserConfig } from '@app/store/modules/config';
@@ -107,7 +107,7 @@ export class ApiKeyService implements OnModuleInit {
name: string; name: string;
description: string | undefined; description: string | undefined;
roles?: Role[]; roles?: Role[];
permissions?: Permission[]; permissions?: Permission[] | AddPermissionInput[];
overwrite?: boolean; overwrite?: boolean;
}): Promise<ApiKeyWithSecret> { }): Promise<ApiKeyWithSecret> {
const trimmedName = name?.trim(); const trimmedName = name?.trim();

View File

@@ -1,28 +1,26 @@
import { import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
Catch, import { Catch } from '@nestjs/common';
type ArgumentsHost,
type ExceptionFilter,
} from '@nestjs/common';
import { GraphQLError } from 'graphql';
import { type FastifyReply } from 'fastify'; import { type FastifyReply } from 'fastify';
import { GraphQLError } from 'graphql';
@Catch(GraphQLError) @Catch(GraphQLError)
export class GraphQLExceptionsFilter<T extends GraphQLError> export class GraphQLExceptionsFilter<T extends GraphQLError> implements ExceptionFilter {
implements ExceptionFilter
{
catch(exception: T, host: ArgumentsHost) { catch(exception: T, host: ArgumentsHost) {
const ctx = host.switchToHttp(); const ctx = host.switchToHttp();
const response: FastifyReply<any> = ctx.getResponse<FastifyReply>(); const response: FastifyReply<any> = ctx.getResponse<FastifyReply>();
response.code(200).send({ if (response.code) {
data: null, response.code(200).send({
errors: [ data: null,
{ errors: [
message: exception.message, {
locations: exception.locations, message: exception.message,
path: exception.path, locations: exception.locations,
}, path: exception.path,
], },
}); ],
});
}
} }
} }

View File

@@ -56,11 +56,12 @@ export class AuthResolver {
@Args('input') @Args('input')
input: CreateApiKeyInput input: CreateApiKeyInput
): Promise<ApiKeyWithSecret> { ): Promise<ApiKeyWithSecret> {
const apiKey = await this.apiKeyService.create( const apiKey = await this.apiKeyService.create({
input.name, name: input.name,
input.description ?? undefined, description: input.description ?? undefined,
input.roles roles: input.roles ?? [],
); permissions: input.permissions ?? [],
});
await this.authService.syncApiKeyRoles(apiKey.id, apiKey.roles); await this.authService.syncApiKeyRoles(apiKey.id, apiKey.roles);