mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-09 11:12:21 +00:00
Compare commits
1 Commits
test/uplot
...
feat/sso-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a20aeaba4 |
@@ -4,12 +4,15 @@ import { Button, Form, Modal } from 'antd';
|
||||
import put from 'api/v1/domains/id/put';
|
||||
import post from 'api/v1/domains/post';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useErrorModal } from 'providers/ErrorModalProvider';
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import APIError from 'types/api/error';
|
||||
import { GettableAuthDomain } from 'types/api/v1/domains/list';
|
||||
import {
|
||||
GettableAuthDomain,
|
||||
GoogleAuthConfig,
|
||||
RoleMapping,
|
||||
} from 'types/api/v1/domains/list';
|
||||
import { PostableAuthDomain } from 'types/api/v1/domains/post';
|
||||
|
||||
import AuthnProviderSelector from './AuthnProviderSelector';
|
||||
@@ -17,6 +20,133 @@ import ConfigureGoogleAuthAuthnProvider from './Providers/AuthnGoogleAuth';
|
||||
import ConfigureOIDCAuthnProvider from './Providers/AuthnOIDC';
|
||||
import ConfigureSAMLAuthnProvider from './Providers/AuthnSAML';
|
||||
|
||||
interface GroupMappingItem {
|
||||
group: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface DomainAdminEmailItem {
|
||||
domain: string;
|
||||
adminEmail: string;
|
||||
}
|
||||
|
||||
// Convert groupMappings object to array format for form
|
||||
function groupMappingsToList(
|
||||
groupMappings?: Record<string, string>,
|
||||
): GroupMappingItem[] {
|
||||
if (!groupMappings) return [];
|
||||
return Object.entries(groupMappings).map(([group, role]) => ({
|
||||
group,
|
||||
role,
|
||||
}));
|
||||
}
|
||||
|
||||
// Convert array format back to groupMappings object for API
|
||||
function listToGroupMappings(
|
||||
list?: GroupMappingItem[],
|
||||
): Record<string, string> {
|
||||
if (!list || list.length === 0) return {};
|
||||
return list.reduce<Record<string, string>>((acc, { group, role }) => {
|
||||
if (group && role) {
|
||||
acc[group] = role;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// Convert domainToAdminEmail object to array format for form
|
||||
function domainToAdminEmailToList(
|
||||
domainToAdminEmail?: Record<string, string>,
|
||||
): DomainAdminEmailItem[] {
|
||||
if (!domainToAdminEmail) return [];
|
||||
return Object.entries(domainToAdminEmail).map(([domain, adminEmail]) => ({
|
||||
domain,
|
||||
adminEmail,
|
||||
}));
|
||||
}
|
||||
|
||||
// Convert array format back to domainToAdminEmail object for API
|
||||
function listToDomainToAdminEmail(
|
||||
list?: DomainAdminEmailItem[],
|
||||
): Record<string, string> {
|
||||
if (!list || list.length === 0) return {};
|
||||
return list.reduce<Record<string, string>>((acc, { domain, adminEmail }) => {
|
||||
if (domain && adminEmail) {
|
||||
acc[domain] = adminEmail;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
// Build roleMapping object for API submission
|
||||
function buildRoleMapping(formRoleMapping?: {
|
||||
defaultRole?: string;
|
||||
useRoleAttribute?: boolean;
|
||||
groupMappingsList?: GroupMappingItem[];
|
||||
}): RoleMapping | undefined {
|
||||
if (!formRoleMapping) return undefined;
|
||||
|
||||
const { defaultRole, useRoleAttribute, groupMappingsList } = formRoleMapping;
|
||||
const groupMappings = listToGroupMappings(groupMappingsList);
|
||||
|
||||
// Only include roleMapping if there's actually something configured
|
||||
if (
|
||||
!defaultRole &&
|
||||
!useRoleAttribute &&
|
||||
Object.keys(groupMappings).length === 0
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
defaultRole: defaultRole || '',
|
||||
useRoleAttribute: useRoleAttribute || false,
|
||||
groupMappings,
|
||||
};
|
||||
}
|
||||
|
||||
// Build googleAuthConfig for API submission
|
||||
function buildGoogleAuthConfig(formGoogleAuthConfig?: {
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
redirectURI?: string;
|
||||
fetchGroups?: boolean;
|
||||
serviceAccountJson?: string;
|
||||
domainToAdminEmailList?: DomainAdminEmailItem[];
|
||||
fetchTransitiveGroupMembership?: boolean;
|
||||
allowedGroupsList?: string[];
|
||||
insecureSkipEmailVerified?: boolean;
|
||||
}): GoogleAuthConfig | undefined {
|
||||
if (!formGoogleAuthConfig) return undefined;
|
||||
|
||||
const {
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectURI,
|
||||
fetchGroups,
|
||||
serviceAccountJson,
|
||||
domainToAdminEmailList,
|
||||
fetchTransitiveGroupMembership,
|
||||
allowedGroupsList,
|
||||
insecureSkipEmailVerified,
|
||||
} = formGoogleAuthConfig;
|
||||
|
||||
// Return undefined if required fields are missing
|
||||
if (!clientId || !clientSecret) return undefined;
|
||||
|
||||
return {
|
||||
clientId,
|
||||
clientSecret,
|
||||
redirectURI: redirectURI || '',
|
||||
fetchGroups: fetchGroups || false,
|
||||
serviceAccountJson,
|
||||
domainToAdminEmail: listToDomainToAdminEmail(domainToAdminEmailList),
|
||||
fetchTransitiveGroupMembership,
|
||||
allowedGroups: allowedGroupsList,
|
||||
insecureSkipEmailVerified: insecureSkipEmailVerified || false,
|
||||
};
|
||||
}
|
||||
|
||||
interface CreateOrEditProps {
|
||||
isCreate: boolean;
|
||||
onClose: () => void;
|
||||
@@ -51,11 +181,46 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
const samlEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.SSO)?.active || false;
|
||||
|
||||
// Transform record for form initial values (convert objects to list format for forms)
|
||||
const initialValues = useMemo(() => {
|
||||
if (!record) {
|
||||
return {
|
||||
name: '',
|
||||
ssoEnabled: false,
|
||||
ssoType: '',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...record,
|
||||
roleMapping: record.roleMapping
|
||||
? {
|
||||
defaultRole: record.roleMapping.defaultRole,
|
||||
useRoleAttribute: record.roleMapping.useRoleAttribute,
|
||||
groupMappingsList: groupMappingsToList(record.roleMapping.groupMappings),
|
||||
}
|
||||
: undefined,
|
||||
googleAuthConfig: record.googleAuthConfig
|
||||
? {
|
||||
...record.googleAuthConfig,
|
||||
domainToAdminEmailList: domainToAdminEmailToList(
|
||||
record.googleAuthConfig.domainToAdminEmail,
|
||||
),
|
||||
allowedGroupsList: record.googleAuthConfig.allowedGroups,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}, [record]);
|
||||
|
||||
const onSubmitHandler = async (): Promise<void> => {
|
||||
const name = form.getFieldValue('name');
|
||||
const googleAuthConfig = form.getFieldValue('googleAuthConfig');
|
||||
const formGoogleAuthConfig = form.getFieldValue('googleAuthConfig');
|
||||
const samlConfig = form.getFieldValue('samlConfig');
|
||||
const oidcConfig = form.getFieldValue('oidcConfig');
|
||||
const formRoleMapping = form.getFieldValue('roleMapping');
|
||||
|
||||
const googleAuthConfig = buildGoogleAuthConfig(formGoogleAuthConfig);
|
||||
const roleMapping = buildRoleMapping(formRoleMapping);
|
||||
|
||||
try {
|
||||
if (isCreate) {
|
||||
@@ -67,6 +232,7 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
googleAuthConfig,
|
||||
samlConfig,
|
||||
oidcConfig,
|
||||
roleMapping,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
@@ -78,6 +244,7 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
googleAuthConfig,
|
||||
samlConfig,
|
||||
oidcConfig,
|
||||
roleMapping,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -96,11 +263,7 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
<Modal open footer={null} onCancel={onClose}>
|
||||
<Form
|
||||
name="auth-domain"
|
||||
initialValues={defaultTo(record, {
|
||||
name: '',
|
||||
ssoEnabled: false,
|
||||
ssoType: '',
|
||||
})}
|
||||
initialValues={initialValues}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
import './Providers.styles.scss';
|
||||
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Callout } from '@signozhq/callout';
|
||||
import { Form, Input, Typography } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Collapse,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
|
||||
import RoleMappingSection from './RoleMappingSection';
|
||||
|
||||
function ConfigureGoogleAuthAuthnProvider({
|
||||
isCreate,
|
||||
}: {
|
||||
isCreate: boolean;
|
||||
}): JSX.Element {
|
||||
const fetchGroups = Form.useWatch(['googleAuthConfig', 'fetchGroups']);
|
||||
|
||||
return (
|
||||
<div className="google-auth">
|
||||
<section className="header">
|
||||
@@ -62,11 +76,168 @@ function ConfigureGoogleAuthAuthnProvider({
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Skip Email Verification"
|
||||
name={['googleAuthConfig', 'insecureSkipEmailVerified']}
|
||||
valuePropName="checked"
|
||||
className="field"
|
||||
tooltip={{
|
||||
title: `Whether to skip email verification. Defaults to "false"`,
|
||||
}}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<Collapse
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: 'groupFetching',
|
||||
label: (
|
||||
<Typography.Text strong>
|
||||
Google Workspace Groups (Advanced)
|
||||
</Typography.Text>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>
|
||||
Enable group fetching to retrieve user groups from Google Workspace.
|
||||
This requires a Google Service Account with domain-wide delegation and
|
||||
the Admin SDK Directory API enabled.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.Item
|
||||
label="Fetch Groups"
|
||||
name={['googleAuthConfig', 'fetchGroups']}
|
||||
valuePropName="checked"
|
||||
className="field"
|
||||
tooltip={{
|
||||
title: `Enable fetching Google Workspace groups for the user. Requires service account configuration.`,
|
||||
}}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
{fetchGroups && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="Service Account JSON"
|
||||
name={['googleAuthConfig', 'serviceAccountJson']}
|
||||
tooltip={{
|
||||
title: `The JSON content of the Google Service Account credentials file. Required for group fetching.`,
|
||||
}}
|
||||
rules={[
|
||||
{
|
||||
required: fetchGroups,
|
||||
message:
|
||||
'Service Account JSON is required when Fetch Groups is enabled',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder='{"type": "service_account", ...}'
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Typography.Text
|
||||
strong
|
||||
style={{ display: 'block', marginBottom: 8, marginTop: 16 }}
|
||||
>
|
||||
Domain to Admin Email Mapping
|
||||
</Typography.Text>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
|
||||
Map workspace domains to admin emails for service account
|
||||
impersonation. Use "*" as a wildcard for any domain.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.List name={['googleAuthConfig', 'domainToAdminEmailList']}>
|
||||
{(fields, { add, remove }): JSX.Element => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<Space
|
||||
key={key}
|
||||
style={{ display: 'flex', marginBottom: 8 }}
|
||||
align="baseline"
|
||||
>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'domain']}
|
||||
rules={[{ required: true, message: 'Domain required' }]}
|
||||
>
|
||||
<Input
|
||||
placeholder="Domain (e.g., example.com or *)"
|
||||
style={{ width: 180 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'adminEmail']}
|
||||
rules={[
|
||||
{ required: true, message: 'Admin email required' },
|
||||
{ type: 'email', message: 'Must be a valid email' },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="Admin Email" style={{ width: 200 }} />
|
||||
</Form.Item>
|
||||
<MinusCircleOutlined onClick={(): void => remove(name)} />
|
||||
</Space>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={(): void => add()}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Add Domain Mapping
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<Form.Item
|
||||
label="Fetch Transitive Group Membership"
|
||||
name={['googleAuthConfig', 'fetchTransitiveGroupMembership']}
|
||||
valuePropName="checked"
|
||||
className="field"
|
||||
tooltip={{
|
||||
title: `If enabled, recursively fetch groups that contain other groups (transitive membership).`,
|
||||
}}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Allowed Groups"
|
||||
name={['googleAuthConfig', 'allowedGroupsList']}
|
||||
tooltip={{
|
||||
title: `Optional list of allowed groups. If configured, only users belonging to one of these groups will be allowed to login.`,
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="Enter group emails and press Enter"
|
||||
style={{ width: '100%' }}
|
||||
tokenSeparators={[',']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<RoleMappingSection />
|
||||
|
||||
<Callout
|
||||
type="warning"
|
||||
size="small"
|
||||
showIcon
|
||||
description="Google OAuth2 won’t be enabled unless you enter all the attributes above"
|
||||
description="Google OAuth2 won't be enabled unless you enter all the required attributes above"
|
||||
className="callout"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import './Providers.styles.scss';
|
||||
|
||||
import { Callout } from '@signozhq/callout';
|
||||
import { Checkbox, Form, Input, Typography } from 'antd';
|
||||
import { Checkbox, Collapse, Form, Input, Typography } from 'antd';
|
||||
|
||||
import RoleMappingSection from './RoleMappingSection';
|
||||
|
||||
function ConfigureOIDCAuthnProvider({
|
||||
isCreate,
|
||||
@@ -64,16 +66,6 @@ function ConfigureOIDCAuthnProvider({
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Email Claim Mapping"
|
||||
name={['oidcConfig', 'claimMapping', 'email']}
|
||||
tooltip={{
|
||||
title: `Mapping of email claims to the corresponding email field in the token.`,
|
||||
}}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Skip Email Verification"
|
||||
name={['oidcConfig', 'insecureSkipEmailVerified']}
|
||||
@@ -98,11 +90,71 @@ function ConfigureOIDCAuthnProvider({
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<Collapse
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: 'claimMapping',
|
||||
label: <Typography.Text strong>Claim Mapping (Advanced)</Typography.Text>,
|
||||
children: (
|
||||
<>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>
|
||||
Configure how claims from your Identity Provider map to SigNoz user
|
||||
attributes. Leave empty to use default values.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.Item
|
||||
label="Email Claim"
|
||||
name={['oidcConfig', 'claimMapping', 'email']}
|
||||
tooltip={{
|
||||
title: `The claim key that contains the user's email address. Default: "email"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="email" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Name Claim"
|
||||
name={['oidcConfig', 'claimMapping', 'name']}
|
||||
tooltip={{
|
||||
title: `The claim key that contains the user's display name. Default: "name"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="name" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Groups Claim"
|
||||
name={['oidcConfig', 'claimMapping', 'groups']}
|
||||
tooltip={{
|
||||
title: `The claim key that contains the user's group memberships. Used for role mapping. Default: "groups"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="groups" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Role Claim"
|
||||
name={['oidcConfig', 'claimMapping', 'role']}
|
||||
tooltip={{
|
||||
title: `The claim key that contains the user's role directly from the IDP. Default: "role"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="role" />
|
||||
</Form.Item>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<RoleMappingSection />
|
||||
|
||||
<Callout
|
||||
type="warning"
|
||||
size="small"
|
||||
showIcon
|
||||
description="OIDC won’t be enabled unless you enter all the attributes above"
|
||||
description="OIDC won't be enabled unless you enter all the required attributes above"
|
||||
className="callout"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import './Providers.styles.scss';
|
||||
|
||||
import { Callout } from '@signozhq/callout';
|
||||
import { Checkbox, Form, Input, Typography } from 'antd';
|
||||
import { Checkbox, Collapse, Form, Input, Typography } from 'antd';
|
||||
|
||||
import RoleMappingSection from './RoleMappingSection';
|
||||
|
||||
function ConfigureSAMLAuthnProvider({
|
||||
isCreate,
|
||||
@@ -70,11 +72,64 @@ function ConfigureSAMLAuthnProvider({
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<Collapse
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: 'attributeMapping',
|
||||
label: (
|
||||
<Typography.Text strong>Attribute Mapping (Advanced)</Typography.Text>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>
|
||||
Configure how SAML assertion attributes from your Identity Provider map
|
||||
to SigNoz user attributes. Leave empty to use default values. Note:
|
||||
Email is always extracted from the NameID assertion.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.Item
|
||||
label="Name Attribute"
|
||||
name={['samlConfig', 'attributeMapping', 'name']}
|
||||
tooltip={{
|
||||
title: `The SAML attribute key that contains the user's display name. Default: "name"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="name" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Groups Attribute"
|
||||
name={['samlConfig', 'attributeMapping', 'groups']}
|
||||
tooltip={{
|
||||
title: `The SAML attribute key that contains the user's group memberships. Used for role mapping. Default: "groups"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="groups" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Role Attribute"
|
||||
name={['samlConfig', 'attributeMapping', 'role']}
|
||||
tooltip={{
|
||||
title: `The SAML attribute key that contains the user's role directly from the IDP. Default: "role"`,
|
||||
}}
|
||||
>
|
||||
<Input placeholder="role" />
|
||||
</Form.Item>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<RoleMappingSection />
|
||||
|
||||
<Callout
|
||||
type="warning"
|
||||
size="small"
|
||||
showIcon
|
||||
description="SAML won’t be enabled unless you enter all the attributes above"
|
||||
description="SAML won't be enabled unless you enter all the required attributes above"
|
||||
className="callout"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import './Providers.styles.scss';
|
||||
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Collapse,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Space,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
|
||||
const ROLE_OPTIONS = [
|
||||
{ label: 'Viewer', value: 'VIEWER' },
|
||||
{ label: 'Editor', value: 'EDITOR' },
|
||||
{ label: 'Admin', value: 'ADMIN' },
|
||||
];
|
||||
|
||||
function RoleMappingSection(): JSX.Element {
|
||||
return (
|
||||
<Collapse
|
||||
ghost
|
||||
items={[
|
||||
{
|
||||
key: 'roleMapping',
|
||||
label: <Typography.Text strong>Role Mapping (Advanced)</Typography.Text>,
|
||||
children: (
|
||||
<>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>
|
||||
Configure how user roles are determined from your Identity Provider. You
|
||||
can either use a direct role attribute or map IDP groups to SigNoz
|
||||
roles.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.Item
|
||||
label="Default Role"
|
||||
name={['roleMapping', 'defaultRole']}
|
||||
tooltip={{
|
||||
title: `The default role assigned to new SSO users if no other role mapping applies. Default: "VIEWER"`,
|
||||
}}
|
||||
>
|
||||
<Select placeholder="VIEWER" options={ROLE_OPTIONS} allowClear />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Use Role Attribute Directly"
|
||||
name={['roleMapping', 'useRoleAttribute']}
|
||||
valuePropName="checked"
|
||||
className="field"
|
||||
tooltip={{
|
||||
title: `If enabled, the role claim/attribute from the IDP will be used directly instead of group mappings. The role value must match a SigNoz role (VIEWER, EDITOR, or ADMIN).`,
|
||||
}}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<Typography.Text strong style={{ display: 'block', marginBottom: 8 }}>
|
||||
Group to Role Mappings
|
||||
</Typography.Text>
|
||||
<Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
|
||||
Map IDP group names to SigNoz roles. If a user belongs to multiple
|
||||
groups, the highest privilege role will be assigned.
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Form.List name={['roleMapping', 'groupMappingsList']}>
|
||||
{(fields, { add, remove }): JSX.Element => (
|
||||
<>
|
||||
{fields.map(({ key, name, ...restField }) => (
|
||||
<Space
|
||||
key={key}
|
||||
style={{ display: 'flex', marginBottom: 8 }}
|
||||
align="baseline"
|
||||
>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'group']}
|
||||
rules={[{ required: true, message: 'Group name required' }]}
|
||||
>
|
||||
<Input placeholder="IDP Group Name" style={{ width: 200 }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'role']}
|
||||
rules={[{ required: true, message: 'Role required' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="SigNoz Role"
|
||||
options={ROLE_OPTIONS}
|
||||
style={{ width: 120 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<MinusCircleOutlined onClick={(): void => remove(name)} />
|
||||
</Space>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={(): void => add()}
|
||||
block
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Add Group Mapping
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoleMappingSection;
|
||||
@@ -15,6 +15,7 @@ export interface GettableAuthDomain {
|
||||
samlConfig?: SAMLConfig;
|
||||
googleAuthConfig?: GoogleAuthConfig;
|
||||
oidcConfig?: OIDCConfig;
|
||||
roleMapping?: RoleMapping;
|
||||
}
|
||||
|
||||
export interface SAMLConfig {
|
||||
@@ -22,12 +23,19 @@ export interface SAMLConfig {
|
||||
samlIdp: string;
|
||||
samlCert: string;
|
||||
insecureSkipAuthNRequestsSigned: boolean;
|
||||
attributeMapping?: AttributeMapping;
|
||||
}
|
||||
|
||||
export interface GoogleAuthConfig {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
redirectURI: string;
|
||||
fetchGroups: boolean;
|
||||
serviceAccountJson?: string;
|
||||
domainToAdminEmail?: Record<string, string>;
|
||||
fetchTransitiveGroupMembership?: boolean;
|
||||
allowedGroups?: string[];
|
||||
insecureSkipEmailVerified: boolean;
|
||||
}
|
||||
|
||||
export interface OIDCConfig {
|
||||
@@ -35,13 +43,22 @@ export interface OIDCConfig {
|
||||
issuerAlias: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
claimMapping: ClaimMapping;
|
||||
claimMapping?: AttributeMapping;
|
||||
insecureSkipEmailVerified: boolean;
|
||||
getUserInfo: boolean;
|
||||
}
|
||||
|
||||
export interface ClaimMapping {
|
||||
export interface AttributeMapping {
|
||||
email: string;
|
||||
name: string;
|
||||
groups: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface RoleMapping {
|
||||
defaultRole: string;
|
||||
groupMappings: Record<string, string>;
|
||||
useRoleAttribute: boolean;
|
||||
}
|
||||
|
||||
export interface AuthNProviderInfo {
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface Config {
|
||||
samlConfig?: SAMLConfig;
|
||||
googleAuthConfig?: GoogleAuthConfig;
|
||||
oidcConfig?: OIDCConfig;
|
||||
roleMapping?: RoleMapping;
|
||||
}
|
||||
|
||||
export interface SAMLConfig {
|
||||
@@ -16,12 +17,19 @@ export interface SAMLConfig {
|
||||
samlIdp: string;
|
||||
samlCert: string;
|
||||
insecureSkipAuthNRequestsSigned: boolean;
|
||||
attributeMapping?: AttributeMapping;
|
||||
}
|
||||
|
||||
export interface GoogleAuthConfig {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
redirectURI: string;
|
||||
fetchGroups: boolean;
|
||||
serviceAccountJson?: string;
|
||||
domainToAdminEmail?: Record<string, string>;
|
||||
fetchTransitiveGroupMembership?: boolean;
|
||||
allowedGroups?: string[];
|
||||
insecureSkipEmailVerified: boolean;
|
||||
}
|
||||
|
||||
export interface OIDCConfig {
|
||||
@@ -29,11 +37,20 @@ export interface OIDCConfig {
|
||||
issuerAlias: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
claimMapping: ClaimMapping;
|
||||
claimMapping?: AttributeMapping;
|
||||
insecureSkipEmailVerified: boolean;
|
||||
getUserInfo: boolean;
|
||||
}
|
||||
|
||||
export interface ClaimMapping {
|
||||
export interface AttributeMapping {
|
||||
email: string;
|
||||
name: string;
|
||||
groups: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface RoleMapping {
|
||||
defaultRole: string;
|
||||
groupMappings: Record<string, string>;
|
||||
useRoleAttribute: boolean;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export interface UpdatableAuthDomain {
|
||||
samlConfig?: SAMLConfig;
|
||||
googleAuthConfig?: GoogleAuthConfig;
|
||||
oidcConfig?: OIDCConfig;
|
||||
roleMapping?: RoleMapping;
|
||||
};
|
||||
id: string;
|
||||
}
|
||||
@@ -14,12 +15,19 @@ export interface SAMLConfig {
|
||||
samlIdp: string;
|
||||
samlCert: string;
|
||||
insecureSkipAuthNRequestsSigned: boolean;
|
||||
attributeMapping?: AttributeMapping;
|
||||
}
|
||||
|
||||
export interface GoogleAuthConfig {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
redirectURI: string;
|
||||
fetchGroups: boolean;
|
||||
serviceAccountJson?: string;
|
||||
domainToAdminEmail?: Record<string, string>;
|
||||
fetchTransitiveGroupMembership?: boolean;
|
||||
allowedGroups?: string[];
|
||||
insecureSkipEmailVerified: boolean;
|
||||
}
|
||||
|
||||
export interface OIDCConfig {
|
||||
@@ -27,11 +35,20 @@ export interface OIDCConfig {
|
||||
issuerAlias: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
claimMapping: ClaimMapping;
|
||||
claimMapping?: AttributeMapping;
|
||||
insecureSkipEmailVerified: boolean;
|
||||
getUserInfo: boolean;
|
||||
}
|
||||
|
||||
export interface ClaimMapping {
|
||||
export interface AttributeMapping {
|
||||
email: string;
|
||||
name: string;
|
||||
groups: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface RoleMapping {
|
||||
defaultRole: string;
|
||||
groupMappings: Record<string, string>;
|
||||
useRoleAttribute: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user