diff --git a/api/dev/sessions/sess_mock-user-session b/api/dev/sessions/sess_mockusersession similarity index 100% rename from api/dev/sessions/sess_mock-user-session rename to api/dev/sessions/sess_mockusersession diff --git a/api/dev/states/myservers.cfg b/api/dev/states/myservers.cfg index 3eb97f13b..e43bef8c1 100644 --- a/api/dev/states/myservers.cfg +++ b/api/dev/states/myservers.cfg @@ -19,5 +19,5 @@ dynamicRemoteAccessType="DISABLED" ssoSubIds="" allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com" [connectionStatus] -minigraph="PRE_INIT" +minigraph="ERROR_RETRYING" upnpStatus="" diff --git a/api/src/graphql/generated/api/operations.ts b/api/src/graphql/generated/api/operations.ts index 0021fef98..d4a193d92 100755 --- a/api/src/graphql/generated/api/operations.ts +++ b/api/src/graphql/generated/api/operations.ts @@ -323,7 +323,9 @@ export function ContainerPortSchema(): z.ZodObject> { export function CreateApiKeyInputSchema(): z.ZodObject> { return z.object({ description: z.string().nullish(), + memory: z.boolean().nullish(), name: z.string(), + overwrite: z.boolean().nullish(), permissions: z.array(z.lazy(() => AddPermissionInputSchema())).nullish(), roles: z.array(RoleSchema).nullish() }) diff --git a/api/src/graphql/generated/api/types.ts b/api/src/graphql/generated/api/types.ts index 409040334..8137395b7 100644 --- a/api/src/graphql/generated/api/types.ts +++ b/api/src/graphql/generated/api/types.ts @@ -347,7 +347,11 @@ export enum ContainerState { export type CreateApiKeyInput = { description?: InputMaybe; + /** Whether to create the key in memory only (true), or on disk (false) - memory only keys will not persist through reboots of the API */ + memory?: InputMaybe; name: Scalars['String']['input']; + /** This will replace the existing key if one already exists with the same name, otherwise returns the existing key */ + overwrite?: InputMaybe; permissions?: InputMaybe>; roles?: InputMaybe>; }; diff --git a/api/src/graphql/schema/types/auth/auth.graphql b/api/src/graphql/schema/types/api-key/api-key.graphql similarity index 79% rename from api/src/graphql/schema/types/auth/auth.graphql rename to api/src/graphql/schema/types/api-key/api-key.graphql index a498c48c8..141fc8b8d 100644 --- a/api/src/graphql/schema/types/auth/auth.graphql +++ b/api/src/graphql/schema/types/api-key/api-key.graphql @@ -27,6 +27,10 @@ input CreateApiKeyInput { description: String roles: [Role!] permissions: [AddPermissionInput!] + """ This will replace the existing key if one already exists with the same name, otherwise returns the existing key """ + overwrite: Boolean + """ Whether to create the key in memory only (true), or on disk (false) - memory only keys will not persist through reboots of the API """ + memory: Boolean } input AddPermissionInput { diff --git a/api/src/graphql/schema/types/auth/roles.graphql b/api/src/graphql/schema/types/api-key/roles.graphql similarity index 100% rename from api/src/graphql/schema/types/auth/roles.graphql rename to api/src/graphql/schema/types/api-key/roles.graphql diff --git a/api/src/unraid-api/auth/api-key.service.ts b/api/src/unraid-api/auth/api-key.service.ts index 9c54084f0..e8bd34d40 100644 --- a/api/src/unraid-api/auth/api-key.service.ts +++ b/api/src/unraid-api/auth/api-key.service.ts @@ -103,12 +103,14 @@ export class ApiKeyService implements OnModuleInit { roles, permissions, overwrite = false, + memory = false, }: { name: string; description: string | undefined; roles?: Role[]; permissions?: Permission[] | AddPermissionInput[]; overwrite?: boolean; + memory?: boolean; }): Promise { const trimmedName = name?.trim(); const sanitizedName = this.sanitizeName(trimmedName); @@ -127,7 +129,7 @@ export class ApiKeyService implements OnModuleInit { const existingKey = this.findByField('name', sanitizedName); if (!overwrite && existingKey) { - throw new GraphQLError('API key name already exists, use overwrite flag to update'); + return existingKey; } const apiKey: Partial = { id: uuidv4(), @@ -142,7 +144,11 @@ export class ApiKeyService implements OnModuleInit { // Update createdAt date apiKey.createdAt = new Date().toISOString(); - await this.saveApiKey(apiKey as ApiKeyWithSecret); + if (memory) { + this.memoryApiKeys.push(apiKey as ApiKeyWithSecret) + } else { + await this.saveApiKey(apiKey as ApiKeyWithSecret); + } return apiKey as ApiKeyWithSecret; } diff --git a/api/src/unraid-api/graph/resolvers/auth/auth.resolver.spec.ts b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.spec.ts similarity index 96% rename from api/src/unraid-api/graph/resolvers/auth/auth.resolver.spec.ts rename to api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.spec.ts index 23052c366..4c4fc29cb 100644 --- a/api/src/unraid-api/graph/resolvers/auth/auth.resolver.spec.ts +++ b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.spec.ts @@ -8,10 +8,10 @@ import { ApiKeyService } from '@app/unraid-api/auth/api-key.service'; import { AuthService } from '@app/unraid-api/auth/auth.service'; import { CookieService } from '@app/unraid-api/auth/cookie.service'; -import { AuthResolver } from './auth.resolver'; +import { ApiKeyResolver } from './api-key.resolver'; -describe('AuthResolver', () => { - let resolver: AuthResolver; +describe('ApiKeyResolver', () => { + let resolver: ApiKeyResolver; let authService: AuthService; let apiKeyService: ApiKeyService; let authzService: AuthZService; @@ -45,7 +45,7 @@ describe('AuthResolver', () => { authzService = new AuthZService(enforcer); cookieService = new CookieService(); authService = new AuthService(cookieService, apiKeyService, authzService); - resolver = new AuthResolver(authService, apiKeyService); + resolver = new ApiKeyResolver(authService, apiKeyService); }); describe('apiKeys', () => { diff --git a/api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts similarity index 95% rename from api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts rename to api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts index 74b21a636..912104887 100644 --- a/api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts +++ b/api/src/unraid-api/graph/resolvers/api-key/api-key.resolver.ts @@ -6,7 +6,6 @@ import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz'; import type { AddRoleForApiKeyInput, - AddRoleForUserInput, ApiKey, ApiKeyWithSecret, CreateApiKeyInput, @@ -17,10 +16,10 @@ import { ApiKeyService } from '@app/unraid-api/auth/api-key.service'; import { GraphqlAuthGuard } from '@app/unraid-api/auth/auth.guard'; import { AuthService } from '@app/unraid-api/auth/auth.service'; -@Resolver('Auth') +@Resolver('ApiKey') @UseGuards(GraphqlAuthGuard) @Throttle({ default: { limit: 100, ttl: 60000 } }) // 100 requests per minute -export class AuthResolver { +export class ApiKeyResolver { constructor( private authService: AuthService, private apiKeyService: ApiKeyService @@ -61,6 +60,8 @@ export class AuthResolver { description: input.description ?? undefined, roles: input.roles ?? [], permissions: input.permissions ?? [], + memory: input.memory ?? false, + overwrite: input.overwrite ?? false }); await this.authService.syncApiKeyRoles(apiKey.id, apiKey.roles); diff --git a/api/src/unraid-api/graph/resolvers/resolvers.module.ts b/api/src/unraid-api/graph/resolvers/resolvers.module.ts index b0b954d5d..b69ec06f3 100644 --- a/api/src/unraid-api/graph/resolvers/resolvers.module.ts +++ b/api/src/unraid-api/graph/resolvers/resolvers.module.ts @@ -4,13 +4,14 @@ import { AuthModule } from '@app/unraid-api/auth/auth.module'; import { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver'; import { DockerResolver } from '@app/unraid-api/graph/resolvers/docker/docker.resolver'; -import { AuthResolver } from './auth/auth.resolver'; +import { ApiKeyResolver } from './api-key/api-key.resolver'; import { CloudResolver } from './cloud/cloud.resolver'; import { ConfigResolver } from './config/config.resolver'; import { DisksResolver } from './disks/disks.resolver'; import { DisplayResolver } from './display/display.resolver'; import { FlashResolver } from './flash/flash.resolver'; import { InfoResolver } from './info/info.resolver'; +import { MeResolver } from './me/me.resolver'; import { NotificationsResolver } from './notifications/notifications.resolver'; import { NotificationsService } from './notifications/notifications.service'; import { OnlineResolver } from './online/online.resolver'; @@ -19,13 +20,12 @@ import { RegistrationResolver } from './registration/registration.resolver'; import { ServerResolver } from './servers/server.resolver'; import { VarsResolver } from './vars/vars.resolver'; import { VmsResolver } from './vms/vms.resolver'; -import { MeResolver } from './me/me.resolver'; @Module({ imports: [AuthModule], providers: [ ArrayResolver, - AuthResolver, + ApiKeyResolver, CloudResolver, ConfigResolver, DisksResolver, @@ -43,6 +43,6 @@ import { MeResolver } from './me/me.resolver'; NotificationsService, MeResolver, ], - exports: [AuthModule, AuthResolver], + exports: [AuthModule, ApiKeyResolver], }) export class ResolversModule {}