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

feat: Copy ACS URL for SAML configurations to clipboard. Disable editing SAML configuration names #4494

Merged
merged 7 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions docs/docs/system-administration/authentication/01-SAML/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@ SAML tab, you'll be able to configure it.

In the UI, you will be able to configure the following fields.

**Name:** (**Required**) A short name for the organisation, used as the input when clicking "Single Sign-on" at login
(note this is unique across all tenants and will form part of the URL so should only be alphanumeric + '-,\_').
**Name:** (**Required**) A short name for the organisation, used as the input when clicking "Single Sign-On" at login.
This name must be unique across all Flagsmith organisations and forms part of the URL that your identity provider will
post SAML messages to during authentication.

**Frontend URL**: (**Required**) This should be the base URL of the Flagsmith dashboard.
**Frontend URL**: (**Required**) This should be the base URL of the Flagsmith dashboard. Users will be redirected here
after authenticating successfully.

**Allow IdP initiated**: This field determines whether logins can be initiated from the IdP.
**Allow IdP-initiated**: If enabled, users will be able to log in directly from your identity provider without needing
to visit the Flagsmith login page.

**IdP metadata xml**: The metadata from the IdP.
**IdP metadata XML**: The metadata from your identity provider.

Once you have configured your identity provider, you can download the service provider metadata XML document with the
button "Download Service Provider Metadata".

### Assertion Consumer Service URL
### Assertion consumer service URL

The assertion consumer service (ACS) URL, also known as single sign-on URL, for this SAML configuration will be at the
following path, replacing `flagsmith.example.com` with your Flagsmith API's domain:
Expand Down Expand Up @@ -66,12 +69,11 @@ Flagsmith also maps user attributes from the following claims in the SAML assert

| Flagsmith attribute | IdP claims |
| ------------------- | ---------------------------------------------------- |
| `email` | `mail`, `email` or `emailAddress` |
| `first_name` | `gn`, `givenName` or the first part of `displayName` |
| `last_name` | `sn`, `surname` or the second part of `displayName` |
| Email | `mail`, `email` or `emailAddress` |
| First name | `gn`, `givenName` or the first part of `displayName` |
| Last name | `sn`, `surname` or the second part of `displayName` |

You can override these mappings by adding the corresponding IdP attribute names to your SAML configuration from the
Django admin interface.
To add custom attribute mappings, edit your SAML configuration and open the Attribute Mappings tab.

## Permissions for SAML users

Expand Down
4 changes: 2 additions & 2 deletions frontend/web/components/SAMLAttributeMappingTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ const SAMLAttributeMappingTable: FC<SAMLAttributeMappingTableType> = ({
header={
<Row className='table-header'>
<Flex className='table-column px-3'>
<div className='font-weight-medium'>SAML Attribute Name</div>
<div className='font-weight-medium'>SAML attribute name</div>
</Flex>
<Flex className='table-column px-3'>
<div className='table-column' style={{ width: '375px' }}>
IDP Attribute Name
IdP attribute name
</div>
</Flex>
</Row>
Expand Down
32 changes: 28 additions & 4 deletions frontend/web/components/SamlTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import { SAMLConfiguration } from 'common/types/responses'
export type SamlTabType = {
organisationId: number
}

const copyAcsUrl = async (configurationName: string) => {
await navigator.clipboard.writeText(
`${Project.flagsmithClientAPI}auth/saml/${configurationName}/response/`,
)
toast('Copied to clipboard')
}
const SamlTab: FC<SamlTabType> = ({ organisationId }) => {
const { data } = useGetSamlConfigurationsQuery({
organisation_id: organisationId,
Expand All @@ -36,15 +43,15 @@ const SamlTab: FC<SamlTabType> = ({ organisationId }) => {
return (
<div className='mt-3'>
<PageTitle
title={'SAML Configuration'}
title={'SAML Configurations'}
cta={
<Button
className='text-right'
onClick={() => {
openCreateSAML('Create SAML configuration', organisationId)
}}
>
{'Create a SAML Configuration'}
{'Create a SAML configuration'}
</Button>
}
/>
Expand All @@ -62,10 +69,13 @@ const SamlTab: FC<SamlTabType> = ({ organisationId }) => {
header={
<Row className='table-header'>
<Flex className='table-column px-3'>
<div className='font-weight-medium'>SAML Name</div>
<div className='font-weight-medium'>Configuration name</div>
</Flex>
<div className='table-column' style={{ width: '305px' }}>
Assertion Consumer Service (ACS) URL
</div>
<div className='table-column' style={{ width: '205px' }}>
Allow IDP Initiated
Allow IdP-initiated
</div>
</Row>
}
Expand All @@ -86,6 +96,20 @@ const SamlTab: FC<SamlTabType> = ({ organisationId }) => {
<Flex className='table-column px-3'>
<div className='font-weight-medium mb-1'>{samlConf.name}</div>
</Flex>
<div className='table-column' style={{ width: '300px' }}>
<Button
onClick={async (e) => {
e.stopPropagation()
await copyAcsUrl(samlConf.name)
}}
theme='text'
>
<div className='flex flex-row space-4'>
<div>Copy to clipboard</div>
<Icon name='copy' />
</div>
</Button>
</div>
<div className='table-column' style={{ width: '95px' }}>
<Switch checked={samlConf.allow_idp_initiated} />
</div>
Expand Down
21 changes: 10 additions & 11 deletions frontend/web/components/modals/CreateSAML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,10 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
className='mt-2'
title='Name*'
data-test='saml-name'
tooltip='A short name for the organization, used as the input when clicking "Single Sign-on" at login, should only consist of alphanumeric characters, plus (+), underscore (_), and hyphen (-).'
tooltip='An URL-friendly name for this configuration, used as the input when selecting "Single Sign-On" at login. It determines the Assertion Consumer Service (ACS) URL that your identity provider must post SAML responses to. This cannot be changed after the SAML configuration is created.'
tooltipPlace='right'
value={name}
disabled={isEdit}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const newName = Utils.safeParseEventValue(event).replace(/ /g, '_')
if (validateName(newName)) {
Expand All @@ -121,7 +122,7 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
className='mt-2 mb-4'
title='Frontend URL*'
data-test='frontend-url'
tooltip='The base URL of the Flagsmith dashboard'
tooltip='The base URL of the Flagsmith dashboard. Users will be redirected here after authenticating successfully.'
tooltipPlace='right'
value={data?.frontend_url || frontendUrl}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -136,8 +137,8 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
/>
<InputGroup
className='mt-2 mb-4'
title='Allow IDP initiated'
tooltip='Determines whether logins can be initiated from the IDP'
title='Allow IdP-initiated logins'
tooltip="Enable this to allow logins initiated by your identity provider. If disabled, users can only log in from Flagsmith's login page."
tooltipPlace='right'
component={
<Switch
Expand All @@ -151,7 +152,7 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
<FormGroup className='mb-1'>
<div className='mt-2 p-0'>
<Row>
<label className='form-label'>IDP Metadata XML</label>
<label className='form-label'>IdP metadata XML</label>
{data?.idp_metadata_xml && (
<div className='ml-2 clickable' onClick={downloadIDPMetadata}>
<Tooltip
Expand Down Expand Up @@ -273,13 +274,11 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
return (
<div className='create-feature-tab mt-3'>
<InputGroup
title={'SAML Attribute Name*'}
tooltip='This is the attribute name where you want to store the information received from the SAML identity provider'
tooltipPlace='right'
title={'Flagsmith user attribute'}
component={
<Select
value={djangoAttributeName}
placeholder='Select a SAML attribute name'
placeholder='Select a Flagsmith user attribute'
options={samlAttributes}
onChange={(m: samlAttributeType) => {
setDjangoAttributeName(m)
Expand All @@ -290,9 +289,9 @@ const CreateSAML: FC<CreateSAML> = ({ organisationId, samlName }) => {
/>
<InputGroup
className='mt-2'
title='IDP Attribute Name*'
title='IdP attribute name*'
data-test='attribute-name'
tooltip='This is the specific value of the attribute sent by the SAML identity provider'
tooltip='The value(s) of this SAML attribute from your identity provider will be saved to the selected Flagsmith user attribute.'
tooltipPlace='right'
value={ipdAttributeName}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
Expand Down
Loading