diff --git a/web/packages/teleport/src/Users/Users.story.tsx b/web/packages/teleport/src/Users/Users.story.tsx index 555f0e1fb1006..9488c39ad3a85 100644 --- a/web/packages/teleport/src/Users/Users.story.tsx +++ b/web/packages/teleport/src/Users/Users.story.tsx @@ -101,11 +101,13 @@ const sample = { user: null, } as any, inviteCollaboratorsOpen: false, + emailPasswordResetOpen: false, onStartCreate: () => null, onStartDelete: () => null, onStartEdit: () => null, onStartReset: () => null, onStartInviteCollaborators: () => null, + onStartEmailResetPassword: () => null, onClose: () => null, onCreate: () => null, onDelete: () => null, @@ -113,4 +115,6 @@ const sample = { onReset: () => null, onInviteCollaboratorsClose: () => null, InviteCollaborators: null, + onEmailPasswordResetClose: () => null, + EmailPasswordReset: null, }; diff --git a/web/packages/teleport/src/Users/Users.test.tsx b/web/packages/teleport/src/Users/Users.test.tsx index 8e88a81f3639d..c771766e2aad8 100644 --- a/web/packages/teleport/src/Users/Users.test.tsx +++ b/web/packages/teleport/src/Users/Users.test.tsx @@ -53,6 +53,8 @@ describe('invite collaborators integration', () => { onInviteCollaboratorsClose: () => undefined, InviteCollaborators: null, inviteCollaboratorsOpen: false, + onEmailPasswordResetClose: () => undefined, + EmailPasswordReset: null, }; }); @@ -100,3 +102,78 @@ describe('invite collaborators integration', () => { expect(screen.getByTestId('invite-collaborators')).toBeInTheDocument(); }); }); + +describe('email password reset integration', () => { + const ctx = createTeleportContext(); + + let props: State; + beforeEach(() => { + props = { + attempt: { + message: 'success', + isSuccess: true, + isProcessing: false, + isFailed: false, + }, + users: [], + roles: [], + operation: { + type: 'reset', + user: { name: 'alice@example.com', roles: ['foo'] }, + }, + + onStartCreate: () => undefined, + onStartDelete: () => undefined, + onStartEdit: () => undefined, + onStartReset: () => undefined, + onStartInviteCollaborators: () => undefined, + onClose: () => undefined, + onDelete: () => undefined, + onCreate: () => undefined, + onUpdate: () => undefined, + onReset: () => undefined, + onInviteCollaboratorsClose: () => undefined, + InviteCollaborators: null, + inviteCollaboratorsOpen: false, + onEmailPasswordResetClose: () => undefined, + EmailPasswordReset: null, + }; + }); + + test('displays the traditional reset UI when not configured', async () => { + render( + + + + + + ); + + expect(screen.getByText('Reset User Authentication?')).toBeInTheDocument(); + expect(screen.queryByText('New Reset UI')).not.toBeInTheDocument(); + }); + + test('displays the email-based UI when configured', async () => { + props = { + ...props, + InviteCollaborators: () => ( +
New Reset UI
+ ), + }; + + render( + + + + + + ); + + expect(screen.getByText('New Reset UI')).toBeInTheDocument(); + + // This will display regardless since the dialog display is managed by the + // dialog itself, and our mock above is trivial, but we can make sure it + // renders. + expect(screen.getByTestId('new-reset-ui')).toBeInTheDocument(); + }); +}); diff --git a/web/packages/teleport/src/Users/Users.tsx b/web/packages/teleport/src/Users/Users.tsx index de0c0cad35939..57e1f2cc9789f 100644 --- a/web/packages/teleport/src/Users/Users.tsx +++ b/web/packages/teleport/src/Users/Users.tsx @@ -53,6 +53,8 @@ export function Users(props: State) { onInviteCollaboratorsClose, inviteCollaboratorsOpen, InviteCollaborators, + EmailPasswordReset, + onEmailPasswordResetClose, } = props; return ( @@ -108,13 +110,19 @@ export function Users(props: State) { username={operation.user.name} /> )} - {operation.type === 'reset' && ( + {operation.type === 'reset' && !EmailPasswordReset && ( )} + {operation.type === 'reset' && EmailPasswordReset && ( + + )} {InviteCollaborators && ( { function fetchRoles() { if (ctx.getFeatureFlags().roles) { @@ -130,6 +137,8 @@ export default function useUsers({ InviteCollaborators }: UsersContainerProps) { onInviteCollaboratorsClose, InviteCollaborators, inviteCollaboratorsOpen, + onEmailPasswordResetClose, + EmailPasswordReset, }; } @@ -149,8 +158,14 @@ export interface InviteCollaboratorsDialogProps { open: boolean; } +export interface EmailPasswordResetDialogProps { + username: string; + onClose: () => void; +} + export type UsersContainerProps = { InviteCollaborators?: (props: InviteCollaboratorsDialogProps) => ReactElement; + EmailPasswordReset?: (props: EmailPasswordResetDialogProps) => ReactElement; }; export type State = ReturnType;