grafana/public/app/features/auth-config/ProviderConfigForm.tsx

232 lines
7.4 KiB
TypeScript

import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { AppEvents } from '@grafana/data';
import { getAppEvents, getBackendSrv, isFetchError, locationService, reportInteraction } from '@grafana/runtime';
import { Box, Button, CollapsableSection, ConfirmModal, Field, LinkButton, Stack, Switch } from '@grafana/ui';
import { FormPrompt } from '../../core/components/FormPrompt/FormPrompt';
import { Page } from '../../core/components/Page/Page';
import { FieldRenderer } from './FieldRenderer';
import { fields, sectionFields } from './fields';
import { SSOProvider, SSOProviderDTO } from './types';
import { dataToDTO, dtoToData } from './utils/data';
const appEvents = getAppEvents();
interface ProviderConfigProps {
config?: SSOProvider;
isLoading?: boolean;
provider: string;
}
export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConfigProps) => {
const {
register,
handleSubmit,
control,
reset,
watch,
setValue,
unregister,
formState: { errors, dirtyFields, isSubmitted },
} = useForm({ defaultValues: dataToDTO(config), mode: 'onSubmit', reValidateMode: 'onChange' });
const [isSaving, setIsSaving] = useState(false);
const providerFields = fields[provider];
const [submitError, setSubmitError] = useState(false);
const dataSubmitted = isSubmitted && !submitError;
const sections = sectionFields[provider];
const [resetConfig, setResetConfig] = useState(false);
const onSubmit = async (data: SSOProviderDTO) => {
setIsSaving(true);
setSubmitError(false);
const requestData = dtoToData(data, provider);
try {
await getBackendSrv().put(
`/api/v1/sso-settings/${provider}`,
{
id: config?.id,
provider: config?.provider,
settings: { ...requestData },
},
{
showErrorAlert: false,
}
);
reportInteraction('grafana_authentication_ssosettings_saved', {
provider,
enabled: requestData.enabled,
});
appEvents.publish({
type: AppEvents.alertSuccess.name,
payload: ['Settings saved'],
});
reset(data);
// Delay redirect so the form state can update
setTimeout(() => {
locationService.push(`/admin/authentication`);
}, 300);
} catch (error) {
let message = '';
if (isFetchError(error)) {
message = error.data.message;
} else if (error instanceof Error) {
message = error.message;
}
appEvents.publish({
type: AppEvents.alertError.name,
payload: [message],
});
setSubmitError(true);
setIsSaving(false);
}
};
const onResetConfig = async () => {
try {
await getBackendSrv().delete(`/api/v1/sso-settings/${provider}`, undefined, { showSuccessAlert: false });
reportInteraction('grafana_authentication_ssosettings_removed', {
provider,
});
appEvents.publish({
type: AppEvents.alertSuccess.name,
payload: ['Settings reset to defaults'],
});
setTimeout(() => {
locationService.push(`/admin/authentication`);
});
} catch (error) {
let message = '';
if (isFetchError(error)) {
message = error.data.message;
} else if (error instanceof Error) {
message = error.message;
}
appEvents.publish({
type: AppEvents.alertError.name,
payload: [message],
});
}
};
return (
<Page.Contents isLoading={isLoading}>
<form onSubmit={handleSubmit(onSubmit)} style={{ maxWidth: '600px' }}>
<>
<FormPrompt
confirmRedirect={!!Object.keys(dirtyFields).length && !dataSubmitted}
onDiscard={() => {
reportInteraction('grafana_authentication_ssosettings_abandoned', {
provider,
});
reset();
}}
/>
<Field label="Enabled">
<Switch {...register('enabled')} id="enabled" label={'Enabled'} />
</Field>
{sections ? (
<Stack gap={2} direction={'column'}>
{sections
.filter((section) => !section.hidden)
.map((section, index) => {
return (
<CollapsableSection label={section.name} isOpen={index === 0} key={section.name}>
{section.fields
.filter((field) => (typeof field !== 'string' ? !field.hidden : true))
.map((field) => {
return (
<FieldRenderer
key={typeof field === 'string' ? field : field.name}
field={field}
control={control}
errors={errors}
setValue={setValue}
register={register}
watch={watch}
unregister={unregister}
provider={provider}
secretConfigured={!!config?.settings.clientSecret}
/>
);
})}
</CollapsableSection>
);
})}
</Stack>
) : (
<>
{providerFields.map((field) => {
return (
<FieldRenderer
key={field}
field={field}
control={control}
errors={errors}
setValue={setValue}
register={register}
watch={watch}
unregister={unregister}
provider={provider}
secretConfigured={!!config?.settings.clientSecret}
/>
);
})}
</>
)}
<Box display={'flex'} gap={2} marginTop={6}>
<Field>
<Button type={'submit'} disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</Button>
</Field>
<Field>
<LinkButton href={'/admin/authentication'} variant={'secondary'}>
Discard
</LinkButton>
</Field>
<Field>
<Button
variant={'secondary'}
hidden={config?.source === 'system'}
onClick={(event) => {
setResetConfig(true);
}}
>
Reset
</Button>
</Field>
</Box>
</>
</form>
{resetConfig && (
<ConfirmModal
isOpen
icon="trash-alt"
title="Reset"
body={
<Stack direction={'column'} gap={3}>
<span>Are you sure you want to reset this configuration?</span>
<small>
After resetting these settings Grafana will use the provider configuration from the system (config
file/environment variables) if any.
</small>
</Stack>
}
confirmText="Reset"
onDismiss={() => setResetConfig(false)}
onConfirm={async () => {
await onResetConfig();
setResetConfig(false);
}}
/>
)}
</Page.Contents>
);
};