mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat: session issues
This commit is contained in:
@@ -19,5 +19,5 @@ dynamicRemoteAccessType="DISABLED"
|
|||||||
ssoSubIds=""
|
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"
|
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]
|
[connectionStatus]
|
||||||
minigraph="PRE_INIT"
|
minigraph="ERROR_RETRYING"
|
||||||
upnpStatus=""
|
upnpStatus=""
|
||||||
|
|||||||
@@ -323,7 +323,9 @@ export function ContainerPortSchema(): z.ZodObject<Properties<ContainerPort>> {
|
|||||||
export function CreateApiKeyInputSchema(): z.ZodObject<Properties<CreateApiKeyInput>> {
|
export function CreateApiKeyInputSchema(): z.ZodObject<Properties<CreateApiKeyInput>> {
|
||||||
return z.object({
|
return z.object({
|
||||||
description: z.string().nullish(),
|
description: z.string().nullish(),
|
||||||
|
memory: z.boolean().nullish(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
|
overwrite: z.boolean().nullish(),
|
||||||
permissions: z.array(z.lazy(() => AddPermissionInputSchema())).nullish(),
|
permissions: z.array(z.lazy(() => AddPermissionInputSchema())).nullish(),
|
||||||
roles: z.array(RoleSchema).nullish()
|
roles: z.array(RoleSchema).nullish()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -347,7 +347,11 @@ export enum ContainerState {
|
|||||||
|
|
||||||
export type CreateApiKeyInput = {
|
export type CreateApiKeyInput = {
|
||||||
description?: InputMaybe<Scalars['String']['input']>;
|
description?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
/** 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<Scalars['Boolean']['input']>;
|
||||||
name: Scalars['String']['input'];
|
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<Scalars['Boolean']['input']>;
|
||||||
permissions?: InputMaybe<Array<AddPermissionInput>>;
|
permissions?: InputMaybe<Array<AddPermissionInput>>;
|
||||||
roles?: InputMaybe<Array<Role>>;
|
roles?: InputMaybe<Array<Role>>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ input CreateApiKeyInput {
|
|||||||
description: String
|
description: String
|
||||||
roles: [Role!]
|
roles: [Role!]
|
||||||
permissions: [AddPermissionInput!]
|
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 {
|
input AddPermissionInput {
|
||||||
@@ -103,12 +103,14 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
roles,
|
roles,
|
||||||
permissions,
|
permissions,
|
||||||
overwrite = false,
|
overwrite = false,
|
||||||
|
memory = false,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
description: string | undefined;
|
description: string | undefined;
|
||||||
roles?: Role[];
|
roles?: Role[];
|
||||||
permissions?: Permission[] | AddPermissionInput[];
|
permissions?: Permission[] | AddPermissionInput[];
|
||||||
overwrite?: boolean;
|
overwrite?: boolean;
|
||||||
|
memory?: boolean;
|
||||||
}): Promise<ApiKeyWithSecret> {
|
}): Promise<ApiKeyWithSecret> {
|
||||||
const trimmedName = name?.trim();
|
const trimmedName = name?.trim();
|
||||||
const sanitizedName = this.sanitizeName(trimmedName);
|
const sanitizedName = this.sanitizeName(trimmedName);
|
||||||
@@ -127,7 +129,7 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
|
|
||||||
const existingKey = this.findByField('name', sanitizedName);
|
const existingKey = this.findByField('name', sanitizedName);
|
||||||
if (!overwrite && existingKey) {
|
if (!overwrite && existingKey) {
|
||||||
throw new GraphQLError('API key name already exists, use overwrite flag to update');
|
return existingKey;
|
||||||
}
|
}
|
||||||
const apiKey: Partial<ApiKeyWithSecret> = {
|
const apiKey: Partial<ApiKeyWithSecret> = {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
@@ -142,7 +144,11 @@ export class ApiKeyService implements OnModuleInit {
|
|||||||
// Update createdAt date
|
// Update createdAt date
|
||||||
apiKey.createdAt = new Date().toISOString();
|
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;
|
return apiKey as ApiKeyWithSecret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import { ApiKeyService } from '@app/unraid-api/auth/api-key.service';
|
|||||||
import { AuthService } from '@app/unraid-api/auth/auth.service';
|
import { AuthService } from '@app/unraid-api/auth/auth.service';
|
||||||
import { CookieService } from '@app/unraid-api/auth/cookie.service';
|
import { CookieService } from '@app/unraid-api/auth/cookie.service';
|
||||||
|
|
||||||
import { AuthResolver } from './auth.resolver';
|
import { ApiKeyResolver } from './api-key.resolver';
|
||||||
|
|
||||||
describe('AuthResolver', () => {
|
describe('ApiKeyResolver', () => {
|
||||||
let resolver: AuthResolver;
|
let resolver: ApiKeyResolver;
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
let apiKeyService: ApiKeyService;
|
let apiKeyService: ApiKeyService;
|
||||||
let authzService: AuthZService;
|
let authzService: AuthZService;
|
||||||
@@ -45,7 +45,7 @@ describe('AuthResolver', () => {
|
|||||||
authzService = new AuthZService(enforcer);
|
authzService = new AuthZService(enforcer);
|
||||||
cookieService = new CookieService();
|
cookieService = new CookieService();
|
||||||
authService = new AuthService(cookieService, apiKeyService, authzService);
|
authService = new AuthService(cookieService, apiKeyService, authzService);
|
||||||
resolver = new AuthResolver(authService, apiKeyService);
|
resolver = new ApiKeyResolver(authService, apiKeyService);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('apiKeys', () => {
|
describe('apiKeys', () => {
|
||||||
@@ -6,7 +6,6 @@ import { AuthActionVerb, AuthPossession, UsePermissions } from 'nest-authz';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
AddRoleForApiKeyInput,
|
AddRoleForApiKeyInput,
|
||||||
AddRoleForUserInput,
|
|
||||||
ApiKey,
|
ApiKey,
|
||||||
ApiKeyWithSecret,
|
ApiKeyWithSecret,
|
||||||
CreateApiKeyInput,
|
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 { GraphqlAuthGuard } from '@app/unraid-api/auth/auth.guard';
|
||||||
import { AuthService } from '@app/unraid-api/auth/auth.service';
|
import { AuthService } from '@app/unraid-api/auth/auth.service';
|
||||||
|
|
||||||
@Resolver('Auth')
|
@Resolver('ApiKey')
|
||||||
@UseGuards(GraphqlAuthGuard)
|
@UseGuards(GraphqlAuthGuard)
|
||||||
@Throttle({ default: { limit: 100, ttl: 60000 } }) // 100 requests per minute
|
@Throttle({ default: { limit: 100, ttl: 60000 } }) // 100 requests per minute
|
||||||
export class AuthResolver {
|
export class ApiKeyResolver {
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private apiKeyService: ApiKeyService
|
private apiKeyService: ApiKeyService
|
||||||
@@ -61,6 +60,8 @@ export class AuthResolver {
|
|||||||
description: input.description ?? undefined,
|
description: input.description ?? undefined,
|
||||||
roles: input.roles ?? [],
|
roles: input.roles ?? [],
|
||||||
permissions: input.permissions ?? [],
|
permissions: input.permissions ?? [],
|
||||||
|
memory: input.memory ?? false,
|
||||||
|
overwrite: input.overwrite ?? false
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.authService.syncApiKeyRoles(apiKey.id, apiKey.roles);
|
await this.authService.syncApiKeyRoles(apiKey.id, apiKey.roles);
|
||||||
@@ -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 { ArrayResolver } from '@app/unraid-api/graph/resolvers/array/array.resolver';
|
||||||
import { DockerResolver } from '@app/unraid-api/graph/resolvers/docker/docker.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 { CloudResolver } from './cloud/cloud.resolver';
|
||||||
import { ConfigResolver } from './config/config.resolver';
|
import { ConfigResolver } from './config/config.resolver';
|
||||||
import { DisksResolver } from './disks/disks.resolver';
|
import { DisksResolver } from './disks/disks.resolver';
|
||||||
import { DisplayResolver } from './display/display.resolver';
|
import { DisplayResolver } from './display/display.resolver';
|
||||||
import { FlashResolver } from './flash/flash.resolver';
|
import { FlashResolver } from './flash/flash.resolver';
|
||||||
import { InfoResolver } from './info/info.resolver';
|
import { InfoResolver } from './info/info.resolver';
|
||||||
|
import { MeResolver } from './me/me.resolver';
|
||||||
import { NotificationsResolver } from './notifications/notifications.resolver';
|
import { NotificationsResolver } from './notifications/notifications.resolver';
|
||||||
import { NotificationsService } from './notifications/notifications.service';
|
import { NotificationsService } from './notifications/notifications.service';
|
||||||
import { OnlineResolver } from './online/online.resolver';
|
import { OnlineResolver } from './online/online.resolver';
|
||||||
@@ -19,13 +20,12 @@ import { RegistrationResolver } from './registration/registration.resolver';
|
|||||||
import { ServerResolver } from './servers/server.resolver';
|
import { ServerResolver } from './servers/server.resolver';
|
||||||
import { VarsResolver } from './vars/vars.resolver';
|
import { VarsResolver } from './vars/vars.resolver';
|
||||||
import { VmsResolver } from './vms/vms.resolver';
|
import { VmsResolver } from './vms/vms.resolver';
|
||||||
import { MeResolver } from './me/me.resolver';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AuthModule],
|
imports: [AuthModule],
|
||||||
providers: [
|
providers: [
|
||||||
ArrayResolver,
|
ArrayResolver,
|
||||||
AuthResolver,
|
ApiKeyResolver,
|
||||||
CloudResolver,
|
CloudResolver,
|
||||||
ConfigResolver,
|
ConfigResolver,
|
||||||
DisksResolver,
|
DisksResolver,
|
||||||
@@ -43,6 +43,6 @@ import { MeResolver } from './me/me.resolver';
|
|||||||
NotificationsService,
|
NotificationsService,
|
||||||
MeResolver,
|
MeResolver,
|
||||||
],
|
],
|
||||||
exports: [AuthModule, AuthResolver],
|
exports: [AuthModule, ApiKeyResolver],
|
||||||
})
|
})
|
||||||
export class ResolversModule {}
|
export class ResolversModule {}
|
||||||
|
|||||||
Reference in New Issue
Block a user