mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
fix: parsing of ssoEnabled in state.php (#1455)
read `ssoSubIds` in state.php from `api.json` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a new query to check if Single Sign-On (SSO) is enabled. * Updated UI components to dynamically reflect SSO availability via live data. * **Refactor** * Streamlined internal handling of SSO status detection for improved reliability and maintainability. * **Tests** * Enhanced tests for SSO button behavior with mocked live data and added edge case coverage for SSO callback handling. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1666,6 +1666,7 @@ type Query {
|
||||
disk(id: PrefixedID!): Disk!
|
||||
rclone: RCloneBackupSettings!
|
||||
settings: Settings!
|
||||
isSSOEnabled: Boolean!
|
||||
|
||||
"""List all installed plugins with their metadata"""
|
||||
plugins: [Plugin!]!
|
||||
|
||||
@@ -13,6 +13,8 @@ import { GraphQLJSON } from 'graphql-scalars';
|
||||
|
||||
import { ENVIRONMENT } from '@app/environment.js';
|
||||
import { LifecycleService } from '@app/unraid-api/app/lifecycle.service.js';
|
||||
import { Public } from '@app/unraid-api/auth/public.decorator.js';
|
||||
import { SsoUserService } from '@app/unraid-api/auth/sso-user.service.js';
|
||||
import {
|
||||
Settings,
|
||||
UnifiedSettings,
|
||||
@@ -22,7 +24,10 @@ import { ApiSettings } from '@app/unraid-api/graph/resolvers/settings/settings.s
|
||||
|
||||
@Resolver(() => Settings)
|
||||
export class SettingsResolver {
|
||||
constructor(private readonly apiSettings: ApiSettings) {}
|
||||
constructor(
|
||||
private readonly apiSettings: ApiSettings,
|
||||
private readonly ssoUserService: SsoUserService
|
||||
) {}
|
||||
|
||||
@Query(() => Settings)
|
||||
async settings() {
|
||||
@@ -45,6 +50,12 @@ export class SettingsResolver {
|
||||
id: 'unified-settings',
|
||||
};
|
||||
}
|
||||
|
||||
@Query(() => Boolean)
|
||||
@Public()
|
||||
public async isSSOEnabled(): Promise<boolean> {
|
||||
return this.ssoUserService.getSsoUsers().then((users) => users.length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Resolver(() => UnifiedSettings)
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
*/
|
||||
class ApiConfig
|
||||
{
|
||||
/** Home of API-specific configuration files */
|
||||
public const CONFIG_DIR = '/boot/config/plugins/dynamix.my.servers/configs';
|
||||
|
||||
private static $scriptsDir = "/usr/local/share/dynamix.unraid.net/scripts";
|
||||
|
||||
/**
|
||||
@@ -27,9 +30,9 @@ class ApiConfig
|
||||
{
|
||||
$output = [];
|
||||
$exitCode = 0;
|
||||
|
||||
|
||||
exec($command, $output, $exitCode);
|
||||
|
||||
|
||||
return implode("\n", $output);
|
||||
}
|
||||
|
||||
@@ -45,7 +48,7 @@ class ApiConfig
|
||||
}
|
||||
|
||||
$apiUtilsScript = self::getApiUtilsScript();
|
||||
|
||||
|
||||
if (!is_executable($apiUtilsScript)) {
|
||||
return false;
|
||||
}
|
||||
@@ -53,10 +56,10 @@ class ApiConfig
|
||||
$escapedScript = escapeshellarg($apiUtilsScript);
|
||||
$escapedPlugin = escapeshellarg($pluginName);
|
||||
$command = "$escapedScript is_api_plugin_enabled $escapedPlugin 2>/dev/null";
|
||||
|
||||
|
||||
$exitCode = 0;
|
||||
self::executeCommand($command, $exitCode);
|
||||
|
||||
|
||||
return $exitCode === 0;
|
||||
}
|
||||
|
||||
@@ -76,22 +79,43 @@ class ApiConfig
|
||||
public static function getApiVersion()
|
||||
{
|
||||
$apiUtilsScript = self::getApiUtilsScript();
|
||||
|
||||
|
||||
if (!is_executable($apiUtilsScript)) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
$escapedScript = escapeshellarg($apiUtilsScript);
|
||||
$command = "$escapedScript get_api_version 2>/dev/null";
|
||||
|
||||
|
||||
$exitCode = 0;
|
||||
$output = self::executeCommand($command, $exitCode);
|
||||
|
||||
|
||||
if ($exitCode !== 0) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
|
||||
$version = trim($output);
|
||||
return !empty($version) ? $version : 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ApiUserConfig
|
||||
{
|
||||
public const CONFIG_PATH = ApiConfig::CONFIG_DIR . '/api.json';
|
||||
|
||||
public static function getConfig()
|
||||
{
|
||||
try {
|
||||
return json_decode(file_get_contents(self::CONFIG_PATH), true) ?? [];
|
||||
} catch (Throwable $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static function isSSOEnabled()
|
||||
{
|
||||
$config = self::getConfig();
|
||||
return !empty($config['ssoSubIds'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class ServerState
|
||||
$this->registered = !empty($connectConfig['apikey']) && $this->connectPluginInstalled;
|
||||
$this->registeredTime = $connectConfig['regWizTime'] ?? '';
|
||||
$this->username = $connectConfig['username'] ?? '';
|
||||
$this->ssoEnabled = !empty($connectConfig['ssoSubIds'] ?? '');
|
||||
$this->ssoEnabled = ApiUserConfig::isSSOEnabled();
|
||||
}
|
||||
|
||||
private function getConnectKnownOrigins()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* SsoButton Component Test Coverage
|
||||
*/
|
||||
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
import { flushPromises, mount } from '@vue/test-utils';
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
@@ -15,6 +16,11 @@ const BrandButtonStub = {
|
||||
props: ['disabled', 'variant', 'class'],
|
||||
};
|
||||
|
||||
// Mock the GraphQL composable
|
||||
vi.mock('@vue/apollo-composable', () => ({
|
||||
useQuery: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('~/helpers/urls', () => ({
|
||||
ACCOUNT: 'http://mock-account-url.net',
|
||||
}));
|
||||
@@ -60,10 +66,13 @@ const mockUsernameField = { value: '' };
|
||||
|
||||
describe('SsoButton.ce.vue', () => {
|
||||
let querySelectorSpy: MockInstance;
|
||||
let mockUseQuery: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
|
||||
mockUseQuery = useQuery as Mock;
|
||||
|
||||
(sessionStorage.getItem as Mock).mockReturnValue(null);
|
||||
(sessionStorage.setItem as Mock).mockClear();
|
||||
mockForm.requestSubmit.mockClear();
|
||||
@@ -73,6 +82,7 @@ describe('SsoButton.ce.vue', () => {
|
||||
mockLocation.search = '';
|
||||
mockLocation.href = '';
|
||||
(fetch as Mock).mockClear();
|
||||
mockUseQuery.mockClear();
|
||||
|
||||
// Spy on document.querySelector and provide mock implementation
|
||||
querySelectorSpy = vi.spyOn(document, 'querySelector');
|
||||
@@ -93,9 +103,12 @@ describe('SsoButton.ce.vue', () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('renders the button when ssoenabled prop is true (boolean)', () => {
|
||||
it('renders the button when SSO is enabled via GraphQL', () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoenabled: true },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -105,33 +118,12 @@ describe('SsoButton.ce.vue', () => {
|
||||
expect(wrapper.text()).toContain('Log In With Unraid.net');
|
||||
});
|
||||
|
||||
it('renders the button when ssoenabled prop is true (string)', () => {
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoenabled: 'true' },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
it('does not render the button when SSO is disabled via GraphQL', () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: false } },
|
||||
});
|
||||
expect(wrapper.findComponent(BrandButtonStub).exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('or');
|
||||
expect(wrapper.text()).toContain('Log In With Unraid.net');
|
||||
});
|
||||
|
||||
it('renders the button when ssoEnabled prop is true', () => {
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoEnabled: true },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
});
|
||||
expect(wrapper.findComponent(BrandButtonStub).exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('or');
|
||||
expect(wrapper.text()).toContain('Log In With Unraid.net');
|
||||
});
|
||||
|
||||
it('does not render the button when ssoenabled prop is false', () => {
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoenabled: false },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -140,9 +132,12 @@ describe('SsoButton.ce.vue', () => {
|
||||
expect(wrapper.text()).not.toContain('or');
|
||||
});
|
||||
|
||||
it('does not render the button when ssoEnabled prop is false', () => {
|
||||
it('does not render the button when GraphQL result is null/undefined', () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: null },
|
||||
});
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoEnabled: false },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -151,7 +146,11 @@ describe('SsoButton.ce.vue', () => {
|
||||
expect(wrapper.text()).not.toContain('or');
|
||||
});
|
||||
|
||||
it('does not render the button when props are not provided', () => {
|
||||
it('does not render the button when GraphQL result is undefined', () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: undefined },
|
||||
});
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
@@ -162,8 +161,11 @@ describe('SsoButton.ce.vue', () => {
|
||||
});
|
||||
|
||||
it('navigates to the external SSO URL on button click', async () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoenabled: true },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -186,6 +188,10 @@ describe('SsoButton.ce.vue', () => {
|
||||
});
|
||||
|
||||
it('handles SSO callback in onMounted hook successfully', async () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
const mockCode = 'mock_auth_code';
|
||||
const mockState = 'mock_session_state_value';
|
||||
const mockAccessToken = 'mock_access_token_123';
|
||||
@@ -199,7 +205,6 @@ describe('SsoButton.ce.vue', () => {
|
||||
|
||||
// Mount the component so that onMounted hook is called
|
||||
mount(SsoButton, {
|
||||
props: { ssoenabled: true },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -225,6 +230,10 @@ describe('SsoButton.ce.vue', () => {
|
||||
});
|
||||
|
||||
it('handles SSO callback error in onMounted hook', async () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
const mockCode = 'mock_auth_code_error';
|
||||
const mockState = 'mock_session_state_error';
|
||||
|
||||
@@ -235,7 +244,6 @@ describe('SsoButton.ce.vue', () => {
|
||||
(fetch as Mock).mockRejectedValueOnce(fetchError);
|
||||
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const wrapper = mount(SsoButton, {
|
||||
props: { ssoenabled: true },
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
@@ -261,4 +269,52 @@ describe('SsoButton.ce.vue', () => {
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('handles SSO callback when state does not match', async () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
const mockCode = 'mock_auth_code';
|
||||
const mockState = 'mock_session_state_value';
|
||||
const differentState = 'different_state_value';
|
||||
|
||||
mockLocation.search = `?code=${mockCode}&state=${mockState}`;
|
||||
(sessionStorage.getItem as Mock).mockReturnValue(differentState);
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// Should not make any fetch calls when state doesn't match
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
expect(mockForm.requestSubmit).not.toHaveBeenCalled();
|
||||
expect(wrapper.findComponent(BrandButtonStub).text()).toBe('Log In With Unraid.net');
|
||||
});
|
||||
|
||||
it('handles SSO callback when no code is present', async () => {
|
||||
mockUseQuery.mockReturnValue({
|
||||
result: { value: { isSSOEnabled: true } },
|
||||
});
|
||||
|
||||
mockLocation.search = '?state=some_state';
|
||||
(sessionStorage.getItem as Mock).mockReturnValue('some_state');
|
||||
|
||||
const wrapper = mount(SsoButton, {
|
||||
global: {
|
||||
stubs: { BrandButton: BrandButtonStub },
|
||||
},
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// Should not make any fetch calls when no code is present
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
expect(mockForm.requestSubmit).not.toHaveBeenCalled();
|
||||
expect(wrapper.findComponent(BrandButtonStub).text()).toBe('Log In With Unraid.net');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useQuery } from '@vue/apollo-composable';
|
||||
import { SSO_ENABLED } from '~/store/account.fragment';
|
||||
|
||||
import { BrandButton } from '@unraid/ui';
|
||||
import { ACCOUNT } from '~/helpers/urls';
|
||||
|
||||
export interface Props {
|
||||
ssoenabled?: boolean | string;
|
||||
ssoEnabled?: boolean;
|
||||
}
|
||||
const props = defineProps<Props>();
|
||||
|
||||
type CurrentState = 'loading' | 'idle' | 'error';
|
||||
|
||||
const currentState = ref<CurrentState>('idle');
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
const { result } = useQuery(SSO_ENABLED);
|
||||
|
||||
const isSsoEnabled = computed<boolean>(
|
||||
() => props['ssoenabled'] === true || props['ssoenabled'] === 'true' || props.ssoEnabled
|
||||
() => result.value?.isSSOEnabled ?? false
|
||||
);
|
||||
|
||||
const getInputFields = (): {
|
||||
|
||||
@@ -45,6 +45,7 @@ type Documents = {
|
||||
"\n query ListRCloneRemotes {\n rclone {\n remotes {\n name\n type\n parameters\n config\n }\n }\n }\n": typeof types.ListRCloneRemotesDocument,
|
||||
"\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n": typeof types.ConnectSignInDocument,
|
||||
"\n mutation SignOut {\n connectSignOut\n }\n": typeof types.SignOutDocument,
|
||||
"\n query IsSSOEnabled {\n isSSOEnabled\n }\n": typeof types.IsSsoEnabledDocument,
|
||||
"\n fragment PartialCloud on Cloud {\n error\n apiKey {\n valid\n error\n }\n cloud {\n status\n error\n }\n minigraphql {\n status\n error\n }\n relay {\n status\n error\n }\n }\n": typeof types.PartialCloudFragmentDoc,
|
||||
"\n query cloudState {\n cloud {\n ...PartialCloud\n }\n }\n": typeof types.CloudStateDocument,
|
||||
"\n query serverState {\n config {\n error\n valid\n }\n info {\n os {\n hostname\n }\n }\n owner {\n avatar\n username\n }\n registration {\n state\n expiration\n keyFile {\n contents\n }\n updateExpiration\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\n }\n": typeof types.ServerStateDocument,
|
||||
@@ -82,6 +83,7 @@ const documents: Documents = {
|
||||
"\n query ListRCloneRemotes {\n rclone {\n remotes {\n name\n type\n parameters\n config\n }\n }\n }\n": types.ListRCloneRemotesDocument,
|
||||
"\n mutation ConnectSignIn($input: ConnectSignInInput!) {\n connectSignIn(input: $input)\n }\n": types.ConnectSignInDocument,
|
||||
"\n mutation SignOut {\n connectSignOut\n }\n": types.SignOutDocument,
|
||||
"\n query IsSSOEnabled {\n isSSOEnabled\n }\n": types.IsSsoEnabledDocument,
|
||||
"\n fragment PartialCloud on Cloud {\n error\n apiKey {\n valid\n error\n }\n cloud {\n status\n error\n }\n minigraphql {\n status\n error\n }\n relay {\n status\n error\n }\n }\n": types.PartialCloudFragmentDoc,
|
||||
"\n query cloudState {\n cloud {\n ...PartialCloud\n }\n }\n": types.CloudStateDocument,
|
||||
"\n query serverState {\n config {\n error\n valid\n }\n info {\n os {\n hostname\n }\n }\n owner {\n avatar\n username\n }\n registration {\n state\n expiration\n keyFile {\n contents\n }\n updateExpiration\n }\n vars {\n regGen\n regState\n configError\n configValid\n }\n }\n": types.ServerStateDocument,
|
||||
@@ -226,6 +228,10 @@ export function graphql(source: "\n mutation ConnectSignIn($input: ConnectSignI
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation SignOut {\n connectSignOut\n }\n"): (typeof documents)["\n mutation SignOut {\n connectSignOut\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query IsSSOEnabled {\n isSSOEnabled\n }\n"): (typeof documents)["\n query IsSSOEnabled {\n isSSOEnabled\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -1264,6 +1264,7 @@ export type Query = {
|
||||
docker: Docker;
|
||||
flash: Flash;
|
||||
info: Info;
|
||||
isSSOEnabled: Scalars['Boolean']['output'];
|
||||
logFile: LogFileContent;
|
||||
logFiles: Array<LogFile>;
|
||||
me: UserAccount;
|
||||
@@ -2199,6 +2200,11 @@ export type SignOutMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type SignOutMutation = { __typename?: 'Mutation', connectSignOut: boolean };
|
||||
|
||||
export type IsSsoEnabledQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type IsSsoEnabledQuery = { __typename?: 'Query', isSSOEnabled: boolean };
|
||||
|
||||
export type PartialCloudFragment = { __typename?: 'Cloud', error?: string | null, apiKey: { __typename?: 'ApiKeyResponse', valid: boolean, error?: string | null }, cloud: { __typename?: 'CloudResponse', status: string, error?: string | null }, minigraphql: { __typename?: 'MinigraphqlResponse', status: MinigraphStatus, error?: string | null }, relay?: { __typename?: 'RelayResponse', status: string, error?: string | null } | null } & { ' $fragmentName'?: 'PartialCloudFragment' };
|
||||
|
||||
export type CloudStateQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
@@ -2251,6 +2257,7 @@ export const GetRCloneConfigFormDocument = {"kind":"Document","definitions":[{"k
|
||||
export const ListRCloneRemotesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListRCloneRemotes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"rclone"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"remotes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"parameters"}},{"kind":"Field","name":{"kind":"Name","value":"config"}}]}}]}}]}}]} as unknown as DocumentNode<ListRCloneRemotesQuery, ListRCloneRemotesQueryVariables>;
|
||||
export const ConnectSignInDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ConnectSignIn"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ConnectSignInInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignIn"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<ConnectSignInMutation, ConnectSignInMutationVariables>;
|
||||
export const SignOutDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SignOut"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"connectSignOut"}}]}}]} as unknown as DocumentNode<SignOutMutation, SignOutMutationVariables>;
|
||||
export const IsSsoEnabledDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"IsSSOEnabled"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isSSOEnabled"}}]}}]} as unknown as DocumentNode<IsSsoEnabledQuery, IsSsoEnabledQueryVariables>;
|
||||
export const CloudStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"cloudState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PartialCloud"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PartialCloud"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Cloud"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"minigraphql"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"relay"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode<CloudStateQuery, CloudStateQueryVariables>;
|
||||
export const ServerStateDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"serverState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"valid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"os"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hostname"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"owner"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"username"}}]}},{"kind":"Field","name":{"kind":"Name","value":"registration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"expiration"}},{"kind":"Field","name":{"kind":"Name","value":"keyFile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"contents"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updateExpiration"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vars"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regGen"}},{"kind":"Field","name":{"kind":"Name","value":"regState"}},{"kind":"Field","name":{"kind":"Name","value":"configError"}},{"kind":"Field","name":{"kind":"Name","value":"configValid"}}]}}]}}]} as unknown as DocumentNode<ServerStateQuery, ServerStateQueryVariables>;
|
||||
export const GetThemeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getTheme"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publicTheme"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"showBannerImage"}},{"kind":"Field","name":{"kind":"Name","value":"showBannerGradient"}},{"kind":"Field","name":{"kind":"Name","value":"headerBackgroundColor"}},{"kind":"Field","name":{"kind":"Name","value":"showHeaderDescription"}},{"kind":"Field","name":{"kind":"Name","value":"headerPrimaryTextColor"}},{"kind":"Field","name":{"kind":"Name","value":"headerSecondaryTextColor"}}]}}]}}]} as unknown as DocumentNode<GetThemeQuery, GetThemeQueryVariables>;
|
||||
@@ -187,7 +187,7 @@ watch(
|
||||
<div class="bg-background">
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h2 class="text-xl font-semibold font-mono">SSO Button Component</h2>
|
||||
<SsoButtonCe :ssoenabled="serverState.ssoEnabled" />
|
||||
<SsoButtonCe />
|
||||
</div>
|
||||
<div class="bg-background">
|
||||
<hr class="border-black dark:border-white" />
|
||||
|
||||
@@ -56,7 +56,7 @@ onBeforeMount(() => {
|
||||
<!-- uncomment to test modals <unraid-modals />-->
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h3 class="text-lg font-semibold font-mono">SSOSignInButtonCe</h3>
|
||||
<unraid-sso-button :ssoenabled="serverState.ssoEnabled" />
|
||||
<unraid-sso-button />
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h3 class="text-lg font-semibold font-mono">ApiKeyManagerCe</h3>
|
||||
<unraid-api-key-manager />
|
||||
|
||||
@@ -11,3 +11,9 @@ export const CONNECT_SIGN_OUT = graphql(/* GraphQL */`
|
||||
connectSignOut
|
||||
}
|
||||
`);
|
||||
|
||||
export const SSO_ENABLED = graphql(/* GraphQL */`
|
||||
query IsSSOEnabled {
|
||||
isSSOEnabled
|
||||
}
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user