-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(auth): add okta settings * feat(auth): handle login with okta fix(auth): update following new design * refactor: improve addValuesToState util refactor(util): write tests * feat(auth): handle okta invitation fix(invitation): add error message * feat: add translations fix: codegen fix: linter fix: translations fix: translations * fix(access): update premium check to include integrations * fix: codegen * fix(Yup): add method for domain * fix(Settings): add permission for auth page
- Loading branch information
Showing
27 changed files
with
1,939 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -262,6 +262,44 @@ | |
"text_649c54823c9089006247625a": "Can’t be prorated due to {{chargeModel}} charge model defined above.", | ||
"text_649c49bcebd91c0082d84446": "Full charge", | ||
"text_649c54823c90890062476259": "Based on the event timestamp and end date of the billing period.", | ||
"text_664c732c264d7eed1c74fd96": "Authentication", | ||
"text_664c732c264d7eed1c74fd9c": "Manage how to authenticate to your Lago organization.", | ||
"text_664c732c264d7eed1c74fda2": "Okta", | ||
"text_664c732c264d7eed1c74fda8": "Allows logins using SSO by connecting Lago and Okta.", | ||
"text_664c732c264d7eed1c74fdb4": "{{integration}} integration successfully deleted", | ||
"text_664c732c264d7eed1c74fd88": "Connect Lago and Okta", | ||
"text_664c732c264d7eed1c74fd8e": "To connect to Okta, please enter the following information", | ||
"text_664c732c264d7eed1c74fd94": "Domain name", | ||
"text_664c732c264d7eed1c74fd9a": "Type your domain name", | ||
"text_664c732c264d7eed1c74fda0": "e.g. acme.com", | ||
"text_664c732c264d7eed1c74fda6": "Okta public key", | ||
"text_664c732c264d7eed1c74fdac": "Type your Okta public key", | ||
"text_664c732c264d7eed1c74fdb2": "Okta private key", | ||
"text_664c732c264d7eed1c74fdb7": "Type your Okta private key", | ||
"text_664c732c264d7eed1c74fdbb": "Okta organization name", | ||
"text_664c732c264d7eed1c74fdbf": "Type your organization name", | ||
"text_664c732c264d7eed1c74fdc3": ".okta.com", | ||
"text_664c732c264d7eed1c74fdcb": "Connect to Okta", | ||
"text_664c732c264d7eed1c74fdaa": "Edit connection", | ||
"text_664c732c264d7eed1c74fdb0": "Delete connection", | ||
"text_664c732c264d7eed1c74fdbd": "Identity provider", | ||
"text_664c732c264d7eed1c74fdc5": "Connection details", | ||
"text_664c732c264d7eed1c74fde6": "{{integration}} integration successfully connected", | ||
"text_664c732c264d7eed1c74fde8": "{{integration}} integration successfully edited", | ||
"text_664c732c264d7eed1c74fe03": "Please fill this input with a domain format to move forward", | ||
"text_664c8fa719b5e7ad81c86018": "Edit Okta connection", | ||
"text_664c8fa719b5e7ad81c86019": "To edit Okta connection, please edit the following information", | ||
"text_664c900d2d312a01546bd84b": "Delete connection to Okta", | ||
"text_664c900d2d312a01546bd84c": "By deleting the connection, it will not be used anymore and you’ll be not able to access to your Lago organization via Okta SSO. Are you sure?", | ||
"text_664c90c9b2b6c2012aa50bce": "Log In with Okta", | ||
"text_664c90c9b2b6c2012aa50bd0": "Please log in with your enterprise account", | ||
"text_664c90c9b2b6c2012aa50bd6": "Okta provider was not assigned for this domain, please contact an admin.", | ||
"text_664c90c9b2b6c2012aa50bda": "Log In with another method? <a data-text=\"Log In\" href=\"{{linkLogin}}\">-</a>", | ||
"text_664c90c9b2b6c2012aa50bcd": "Join {{orgnisationName}}", | ||
"text_664c90c9b2b6c2012aa50bd1": "Oops! Looks like your Okta provider was not assigned for this domain, please contact an admin.", | ||
"text_664c90c9b2b6c2012aa50bd3": "Join with Google", | ||
"text_664c90c9b2b6c2012aa50bd5": "Join with Okta", | ||
"text_664c98989d08a3f733357f73": "There was an error while fetching user info from Okta. Please try again.", | ||
"text_66141e30699a0631f0b2ec7f": "There is no invoices with dispute lost", | ||
"text_66141e30699a0631f0b2ec87": "Disputed lost invoices occur when a customer chooses not to pay, requests a dispute, and demands a chargeback.", | ||
"text_66141e30699a0631f0b2ec59": "Mark this invoice as disputed", | ||
|
@@ -946,7 +984,7 @@ | |
"text_63208bfc99e69a28211ec7fd": "Show more invitations", | ||
"text_63208c711ce25db78140755d": "Member successfully removed", | ||
"text_63208c701ce25db78140748f": "Invite members", | ||
"text_63208c701ce25db78140749b": "To generate a link for inviting a member to this organization, please enter the new member’s email.", | ||
"text_63208c701ce25db78140749b": "To generate a link for inviting a member to this organization, please enter the member’s email and select a role.", | ||
"text_63208c701ce25db7814074ab": "Email address", | ||
"text_63208c711ce25db7814074c1": "[email protected]", | ||
"text_63208c711ce25db7814074cd": "Cancel", | ||
|
@@ -968,7 +1006,6 @@ | |
"text_6321a076b94bd1b32494e9ee": "Something went wrong", | ||
"text_6321a076b94bd1b32494e9f0": "We can’t fetch the members list, please refresh the section or contact us if the error persists.", | ||
"text_6321a076b94bd1b32494e9f2": "Refresh the section", | ||
"text_63246f875e2228ab7b63dcd0": "Sign up to {{orgnisationName}}", | ||
"text_63246f875e2228ab7b63dcd4": "Create your account and access to this organization.", | ||
"text_63246f875e2228ab7b63dcdc": "Email address", | ||
"text_63246f875e2228ab7b63dce9": "Set your password", | ||
|
@@ -991,7 +1028,6 @@ | |
"text_660bf95c75dd928ced0ecb21": "Continue with Google", | ||
"text_660bf95c75dd928ced0ecb33": "Type your organization name", | ||
"text_660bf95c75dd928ced0ecb2b": "Oops! Looks like your Google account email doesn't match the invitation. Give it another shot!", | ||
"text_660bf95c75dd928ced0ecb37": "Sign up with Google", | ||
"text_660bfaa2cbc95800a63f48b1": "Sorry, it seems there are no accounts associated with the provided credentials.", | ||
"text_642707b0da1753a9bb6672b5": "Forgot password", | ||
"text_642707b0da1753a9bb66728e": "Password reset email sent!", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
src/components/settings/authentication/AddOktaDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { InputAdornment, Stack } from '@mui/material' | ||
import { forwardRef, RefObject, useImperativeHandle, useRef, useState } from 'react' | ||
import styled from 'styled-components' | ||
|
||
import { Button, Dialog, DialogRef } from '~/components/designSystem' | ||
import { TextInputField } from '~/components/form' | ||
import { DeleteOktaIntegrationDialogRef } from '~/components/settings/authentication/DeleteOktaIntegrationDialog' | ||
import { | ||
useOktaIntegration, | ||
UseOktaIntegrationProps, | ||
} from '~/components/settings/authentication/useOktaIntegration' | ||
import { AddOktaIntegrationDialogFragment } from '~/generated/graphql' | ||
import { useInternationalization } from '~/hooks/core/useInternationalization' | ||
import { theme } from '~/styles' | ||
|
||
type AddOktaDialogProps = Partial<{ | ||
integration: AddOktaIntegrationDialogFragment | ||
deleteModalRef: RefObject<DeleteOktaIntegrationDialogRef> | ||
deleteDialogCallback: Function | ||
callback?: UseOktaIntegrationProps['onSubmit'] | ||
}> | ||
|
||
export interface AddOktaDialogRef { | ||
openDialog: (props?: AddOktaDialogProps) => unknown | ||
closeDialog: () => unknown | ||
} | ||
|
||
export const AddOktaDialog = forwardRef<AddOktaDialogRef>((_, ref) => { | ||
const { translate } = useInternationalization() | ||
|
||
const dialogRef = useRef<DialogRef>(null) | ||
const [localData, setLocalData] = useState<AddOktaDialogProps | undefined>() | ||
|
||
const integration = localData?.integration | ||
const isEdition = !!integration | ||
|
||
const { formikProps } = useOktaIntegration({ | ||
initialValues: integration, | ||
onSubmit: (id) => { | ||
localData?.callback?.(id) | ||
dialogRef.current?.closeDialog() | ||
}, | ||
}) | ||
|
||
useImperativeHandle(ref, () => ({ | ||
openDialog: (data) => { | ||
setLocalData(data) | ||
dialogRef.current?.openDialog() | ||
}, | ||
closeDialog: () => dialogRef.current?.closeDialog(), | ||
})) | ||
|
||
return ( | ||
<> | ||
<Dialog | ||
ref={dialogRef} | ||
title={translate( | ||
isEdition ? 'text_664c8fa719b5e7ad81c86018' : 'text_664c732c264d7eed1c74fd88', | ||
)} | ||
description={translate( | ||
isEdition ? 'text_664c8fa719b5e7ad81c86019' : 'text_664c732c264d7eed1c74fd8e', | ||
)} | ||
onClose={formikProps.resetForm} | ||
actions={({ closeDialog }) => ( | ||
<Stack | ||
direction="row" | ||
justifyContent="space-between" | ||
alignItems="center" | ||
width={isEdition ? '100%' : 'inherit'} | ||
spacing={3} | ||
> | ||
{isEdition && localData?.deleteDialogCallback && ( | ||
<Button | ||
danger | ||
variant="quaternary" | ||
onClick={() => { | ||
closeDialog() | ||
localData?.deleteModalRef?.current?.openDialog({ | ||
integration, | ||
callback: localData.deleteDialogCallback, | ||
}) | ||
}} | ||
> | ||
{translate('text_65845f35d7d69c3ab4793dad')} | ||
</Button> | ||
)} | ||
|
||
<Stack direction="row" spacing={3} alignItems="center" marginLeft="auto !important"> | ||
<Button variant="quaternary" onClick={closeDialog}> | ||
{translate('text_63eba8c65a6c8043feee2a14')} | ||
</Button> | ||
<Button | ||
variant="primary" | ||
disabled={!formikProps.isValid || !formikProps.dirty} | ||
onClick={formikProps.submitForm} | ||
> | ||
{translate( | ||
isEdition ? 'text_664c732c264d7eed1c74fdaa' : 'text_664c732c264d7eed1c74fdcb', | ||
)} | ||
</Button> | ||
</Stack> | ||
</Stack> | ||
)} | ||
> | ||
<Content> | ||
<TextInputField | ||
// eslint-disable-next-line jsx-a11y/no-autofocus | ||
autoFocus | ||
formikProps={formikProps} | ||
name="domain" | ||
label={translate('text_664c732c264d7eed1c74fd94')} | ||
placeholder={translate('text_664c732c264d7eed1c74fd9a')} | ||
helperText={translate('text_664c732c264d7eed1c74fda0')} | ||
/> | ||
<TextInputField | ||
formikProps={formikProps} | ||
name="clientId" | ||
label={translate('text_664c732c264d7eed1c74fda6')} | ||
placeholder={translate('text_664c732c264d7eed1c74fdac')} | ||
/> | ||
<TextInputField | ||
formikProps={formikProps} | ||
name="clientSecret" | ||
label={translate('text_664c732c264d7eed1c74fdb2')} | ||
placeholder={translate('text_664c732c264d7eed1c74fdb7')} | ||
/> | ||
<TextInputField | ||
formikProps={formikProps} | ||
name="organizationName" | ||
label={translate('text_664c732c264d7eed1c74fdbb')} | ||
placeholder={translate('text_664c732c264d7eed1c74fdbf')} | ||
InputProps={{ | ||
endAdornment: ( | ||
<InputAdornment position="end"> | ||
{translate('text_664c732c264d7eed1c74fdc3')} | ||
</InputAdornment> | ||
), | ||
}} | ||
/> | ||
</Content> | ||
</Dialog> | ||
</> | ||
) | ||
}) | ||
|
||
const Content = styled.div` | ||
margin-bottom: ${theme.spacing(8)}; | ||
> *:not(:last-child) { | ||
margin-bottom: ${theme.spacing(6)}; | ||
} | ||
` | ||
|
||
AddOktaDialog.displayName = 'AddOktaDialog' |
89 changes: 89 additions & 0 deletions
89
src/components/settings/authentication/DeleteOktaIntegrationDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { gql } from '@apollo/client' | ||
import { forwardRef, useImperativeHandle, useRef, useState } from 'react' | ||
|
||
import { WarningDialog, WarningDialogRef } from '~/components/WarningDialog' | ||
import { addToast } from '~/core/apolloClient' | ||
import { | ||
DeleteOktaIntegrationDialogFragment, | ||
useDestroyIntegrationMutation, | ||
} from '~/generated/graphql' | ||
import { useInternationalization } from '~/hooks/core/useInternationalization' | ||
|
||
gql` | ||
fragment DeleteOktaIntegrationDialog on OktaIntegration { | ||
id | ||
name | ||
} | ||
mutation DestroyIntegration($input: DestroyIntegrationInput!) { | ||
destroyIntegration(input: $input) { | ||
id | ||
} | ||
} | ||
` | ||
|
||
type DeleteOktaIntegrationDialogProps = { | ||
integration: DeleteOktaIntegrationDialogFragment | undefined | ||
callback?: Function | ||
} | ||
|
||
export interface DeleteOktaIntegrationDialogRef { | ||
openDialog: ({ integration, callback }: DeleteOktaIntegrationDialogProps) => unknown | ||
closeDialog: () => unknown | ||
} | ||
|
||
export const DeleteOktaIntegrationDialog = forwardRef<DeleteOktaIntegrationDialogRef>((_, ref) => { | ||
const { translate } = useInternationalization() | ||
|
||
const dialogRef = useRef<WarningDialogRef>(null) | ||
const [localData, setLocalData] = useState<DeleteOktaIntegrationDialogProps>() | ||
|
||
const integration = localData?.integration | ||
|
||
const [deleteIntegration] = useDestroyIntegrationMutation({ | ||
onCompleted(data) { | ||
if (data && data.destroyIntegration) { | ||
dialogRef.current?.closeDialog() | ||
localData?.callback?.() | ||
|
||
addToast({ | ||
message: translate('text_664c732c264d7eed1c74fdb4', { | ||
integration: translate('text_664c732c264d7eed1c74fda2'), | ||
}), | ||
severity: 'success', | ||
}) | ||
} | ||
}, | ||
update(cache) { | ||
cache.evict({ id: `OktaIntegration:${integration?.id}` }) | ||
}, | ||
}) | ||
|
||
useImperativeHandle(ref, () => ({ | ||
openDialog: (data) => { | ||
setLocalData(data) | ||
dialogRef.current?.openDialog() | ||
}, | ||
closeDialog: () => dialogRef.current?.closeDialog(), | ||
})) | ||
|
||
return ( | ||
<WarningDialog | ||
ref={dialogRef} | ||
title={translate('text_664c900d2d312a01546bd84b')} | ||
description={translate('text_664c900d2d312a01546bd84c')} | ||
onContinue={async () => | ||
await deleteIntegration({ | ||
variables: { | ||
input: { | ||
id: integration?.id ?? '', | ||
}, | ||
}, | ||
}) | ||
} | ||
continueText={translate('text_645d071272418a14c1c76a81')} | ||
/> | ||
) | ||
}) | ||
|
||
DeleteOktaIntegrationDialog.displayName = 'DeleteOktaIntegrationDialog' |
Oops, something went wrong.