Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth: add the missing fields for all SSO providers #83813

Merged
merged 14 commits into from Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 76 additions & 19 deletions public/app/features/auth-config/ProviderConfigForm.test.tsx
Expand Up @@ -77,24 +77,54 @@ describe('ProviderConfigForm', () => {
jest.clearAllMocks();
});

it('renders all fields correctly', async () => {
it('renders all general settings fields correctly', async () => {
setup(<ProviderConfigForm config={testConfig} provider={testConfig.provider} />);
expect(screen.getByRole('textbox', { name: /Client ID/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /Team IDs/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /Allowed organizations/i })).toBeInTheDocument();
expect(screen.getByRole('textbox', { name: /Client secret/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /Scopes/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Allow Sign Up/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Auto login/i })).toBeInTheDocument();
expect(screen.getByRole('textbox', { name: /Sign out redirect URL/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Save/i })).toBeInTheDocument();
expect(screen.getByRole('link', { name: /Discard/i })).toBeInTheDocument();
});

it('renders all user mapping fields correctly', async () => {
const { user } = setup(<ProviderConfigForm config={testConfig} provider={testConfig.provider} />);
await user.click(screen.getByText('User mapping'));
expect(screen.getByRole('textbox', { name: /Role attribute path/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Role attribute strict mode/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Skip organization role sync/i })).toBeInTheDocument();
});

it('renders all extra security fields correctly', async () => {
const { user } = setup(<ProviderConfigForm config={testConfig} provider={testConfig.provider} />);
await user.click(screen.getByText('Extra security measures'));
expect(screen.getByRole('combobox', { name: /Allowed organizations/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /Allowed domains/i })).toBeInTheDocument();
expect(screen.getByRole('combobox', { name: /Team Ids/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Use PKCE/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /Use refresh token/i })).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: /TLS skip verify/i })).toBeInTheDocument();
});

it('should save and enable on form submit', async () => {
const { user } = setup(<ProviderConfigForm config={emptyConfig} provider={emptyConfig.provider} />);

await user.type(screen.getByRole('textbox', { name: /Client ID/i }), 'test-client-id');
await user.type(screen.getByLabelText(/Client secret/i), 'test-client-secret');
// Type a team name and press enter to select it
await user.type(screen.getByRole('combobox', { name: /Team IDs/i }), '12324{enter}');
// Add two orgs
await user.type(screen.getByRole('combobox', { name: /Allowed organizations/i }), 'test-org1{enter}');
await user.type(screen.getByRole('combobox', { name: /Allowed organizations/i }), 'test-org2{enter}');
// Type a scope and press enter to select it
await user.type(screen.getByRole('combobox', { name: /Scopes/i }), 'user:email{enter}');
await user.click(screen.getByRole('checkbox', { name: /Auto login/i }));

await user.click(screen.getByText('User mapping'));
await user.type(screen.getByRole('textbox', { name: /Role attribute path/i }), 'new-attribute-path');
await user.click(screen.getByRole('checkbox', { name: /Role attribute strict mode/i }));

await user.click(screen.getByText('Extra security measures'));
await user.type(screen.getByRole('combobox', { name: /Allowed domains/i }), 'grafana.com{enter}');
await user.click(screen.getByRole('checkbox', { name: /Use PKCE/i }));

await user.click(screen.getByRole('button', { name: /Save and enable/i }));

await waitFor(() => {
Expand All @@ -104,12 +134,27 @@ describe('ProviderConfigForm', () => {
id: '300f9b7c-0488-40db-9763-a22ce8bf6b3e',
provider: 'github',
settings: {
name: 'GitHub',
allowedOrganizations: 'test-org1,test-org2',
allowAssignGrafanaAdmin: false,
allowSignUp: false,
allowedDomains: 'grafana.com',
allowedOrganizations: '',
autoLogin: true,
clientId: 'test-client-id',
clientSecret: 'test-client-secret',
teamIds: '12324',
enabled: true,
name: 'GitHub',
roleAttributePath: 'new-attribute-path',
roleAttributeStrict: true,
scopes: 'user:email',
signoutRedirectUrl: '',
skipOrgRoleSync: false,
teamIds: '',
tlsClientCa: '',
tlsClientCert: '',
tlsClientKey: '',
tlsSkipVerifyInsecure: false,
usePkce: true,
useRefreshToken: false,
},
},
{ showErrorAlert: false }
Expand All @@ -126,11 +171,9 @@ describe('ProviderConfigForm', () => {
const { user } = setup(<ProviderConfigForm config={emptyConfig} provider={emptyConfig.provider} />);
await user.type(screen.getByRole('textbox', { name: /Client ID/i }), 'test-client-id');
await user.type(screen.getByLabelText(/Client secret/i), 'test-client-secret');
// Type a team name and press enter to select it
await user.type(screen.getByRole('combobox', { name: /Team IDs/i }), '12324{enter}');
// Add two orgs
await user.type(screen.getByRole('combobox', { name: /Allowed organizations/i }), 'test-org1{enter}');
await user.type(screen.getByRole('combobox', { name: /Allowed organizations/i }), 'test-org2{enter}');
// Type a scope and press enter to select it
await user.type(screen.getByRole('combobox', { name: /Scopes/i }), 'user:email{enter}');
await user.click(screen.getByRole('checkbox', { name: /Auto login/i }));
await user.click(screen.getByText('Save'));

await waitFor(() => {
Expand All @@ -140,12 +183,26 @@ describe('ProviderConfigForm', () => {
id: '300f9b7c-0488-40db-9763-a22ce8bf6b3e',
provider: 'github',
settings: {
name: 'GitHub',
allowedOrganizations: 'test-org1,test-org2',
allowAssignGrafanaAdmin: false,
allowSignUp: false,
allowedDomains: '',
allowedOrganizations: '',
autoLogin: true,
clientId: 'test-client-id',
clientSecret: 'test-client-secret',
teamIds: '12324',
enabled: false,
name: 'GitHub',
roleAttributePath: '',
roleAttributeStrict: false,
scopes: 'user:email',
signoutRedirectUrl: '',
skipOrgRoleSync: false,
teamIds: '',
tlsClientCa: '',
tlsClientCert: '',
tlsClientKey: '',
usePkce: false,
useRefreshToken: false,
},
},
{ showErrorAlert: false }
Expand Down
74 changes: 26 additions & 48 deletions public/app/features/auth-config/ProviderConfigForm.tsx
Expand Up @@ -21,7 +21,7 @@ import { FormPrompt } from '../../core/components/FormPrompt/FormPrompt';
import { Page } from '../../core/components/Page/Page';

import { FieldRenderer } from './FieldRenderer';
import { fields, sectionFields } from './fields';
import { sectionFields } from './fields';
import { SSOProvider, SSOProviderDTO } from './types';
import { dataToDTO, dtoToData } from './utils/data';

Expand All @@ -45,7 +45,6 @@ export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConf
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];
Expand Down Expand Up @@ -155,55 +154,34 @@ export const ProviderConfigForm = ({ config, provider, isLoading }: ProviderConf
<Field label="Enabled" hidden={true}>
<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) => {
<Stack gap={2} direction={'column'}>
{sections
.filter((section) => !section.hidden)
.map((section, index) => {
return (
<FieldRenderer
key={field}
field={field}
control={control}
errors={errors}
setValue={setValue}
register={register}
watch={watch}
unregister={unregister}
provider={provider}
secretConfigured={!!config?.settings.clientSecret}
/>
<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>
<Box display={'flex'} gap={2} marginTop={5}>
<Stack alignItems={'center'} gap={2}>
<Button
Expand Down