mirror of
https://github.com/quay/quay.git
synced 2026-01-26 06:21:37 +03:00
[redhat-3.16] fix(ui): consolidate credential modals and fix state management issues (PROJQUAY-9630) (#4477)
fix(ui): consolidate credential modals and fix state management issues (PROJQUAY-9630) - Rename ApplicationTokenCredentials to CredentialsModal for reusability - Add support for both application tokens and encrypted passwords - Fix memory leak by moving state cleanup to useEffect - Fix error handling to clear errors on successful responses - Add null checks for user loading state - Update data-testid naming for better specificity - Mock encrypted password API in Cypress tests - Simplify Cypress selectors for better reliability Co-authored-by: Brady Pratt <bpratt@redhat.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
821baa4d5c
commit
f1ff178e3b
@@ -3,8 +3,11 @@
|
||||
import {humanizeTimeForExpiry, parseTimeDuration} from 'src/libs/utils';
|
||||
|
||||
describe('Account Settings Page', () => {
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
cy.exec('npm run quay:seed');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit('/signin');
|
||||
cy.request('GET', `${Cypress.env('REACT_QUAY_APP_API_URL')}/csrf_token`)
|
||||
.then((response) => response.body.csrf_token)
|
||||
@@ -196,7 +199,27 @@ describe('Account Settings Page', () => {
|
||||
cy.get('#checkbox').should('be.checked');
|
||||
});
|
||||
|
||||
it('CLI Token', () => {
|
||||
it('CLI Token - Generate Encrypted Password with All Credential Formats', () => {
|
||||
// Mock the encrypted password API call - wrong password
|
||||
cy.intercept('POST', '/api/v1/user/clientkey', (req) => {
|
||||
if (req.body.password === 'wrongpassword') {
|
||||
req.reply({
|
||||
statusCode: 400,
|
||||
body: {
|
||||
error_message: 'Invalid Username or Password',
|
||||
error_type: 'invalid_auth',
|
||||
},
|
||||
});
|
||||
} else if (req.body.password === 'password') {
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
key: 'fake-encrypted-password-12345',
|
||||
},
|
||||
});
|
||||
}
|
||||
}).as('createClientKey');
|
||||
|
||||
cy.visit('/organization/user1?tab=Settings');
|
||||
|
||||
// navigate to CLI Tab
|
||||
@@ -208,13 +231,75 @@ describe('Account Settings Page', () => {
|
||||
// Wrong password
|
||||
cy.get('#delete-confirmation-input').type('wrongpassword');
|
||||
cy.get('#submit').click();
|
||||
cy.wait('@createClientKey');
|
||||
cy.contains('Invalid Username or Password');
|
||||
|
||||
// Correct password
|
||||
cy.get('#delete-confirmation-input').clear();
|
||||
cy.get('#delete-confirmation-input').type('password');
|
||||
cy.get('#submit').click();
|
||||
cy.contains('Your encrypted password is');
|
||||
cy.wait('@createClientKey');
|
||||
|
||||
// Should show credentials modal with all tabs
|
||||
cy.get('[data-testid="credentials-modal"]').should('be.visible');
|
||||
cy.contains('Credentials for user1').should('exist');
|
||||
|
||||
// Verify alert message for encrypted password
|
||||
cy.contains('Encrypted Password').should('exist');
|
||||
cy.contains('This encrypted password can be used for').should('exist');
|
||||
cy.contains('docker login').should('exist');
|
||||
|
||||
// Verify all tabs exist
|
||||
cy.contains('Encrypted Password').should('exist');
|
||||
cy.contains('Kubernetes Secret').should('exist');
|
||||
cy.contains('rkt Configuration').should('exist');
|
||||
cy.contains('Podman Login').should('exist');
|
||||
cy.contains('Docker Login').should('exist');
|
||||
cy.contains('Docker Configuration').should('exist');
|
||||
|
||||
// Verify Encrypted Password tab (default) shows username and password
|
||||
cy.get('[data-testid="credentials-modal-copy-username"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-username"]')
|
||||
.find('input[readonly]')
|
||||
.should('have.value', 'user1'); // Encrypted password uses actual username
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]').should('exist');
|
||||
|
||||
// Test Kubernetes Secret tab
|
||||
cy.contains('Kubernetes Secret').click();
|
||||
cy.contains('Step 1: Create secret YAML file').should('exist');
|
||||
cy.contains('apiVersion: v1').should('exist');
|
||||
cy.contains('kind: Secret').should('exist');
|
||||
cy.contains('user1-pull-secret').should('exist');
|
||||
|
||||
// Test Docker Login tab
|
||||
cy.contains('Docker Login').click();
|
||||
cy.contains('Run docker login command').should('exist');
|
||||
cy.contains('docker login').should('exist');
|
||||
// Verify username is user1 (not $app like application tokens)
|
||||
cy.contains('user1').should('exist'); // Username in command
|
||||
cy.contains('localhost').should('exist'); // Server hostname in command
|
||||
|
||||
// Test Podman Login tab
|
||||
cy.contains('Podman Login').click();
|
||||
cy.contains('Run podman login command').should('exist');
|
||||
cy.contains('podman login').should('exist');
|
||||
// Verify username is user1
|
||||
cy.contains('user1').should('exist'); // Username in command
|
||||
|
||||
// Test rkt Configuration tab
|
||||
cy.contains('rkt Configuration').click();
|
||||
cy.contains('Step 1: Create rkt configuration file').should('exist');
|
||||
cy.contains('rktKind').should('exist');
|
||||
|
||||
// Test Docker Configuration tab
|
||||
cy.contains('Docker Configuration').click();
|
||||
cy.contains('Step 1: Create Docker config file').should('exist');
|
||||
cy.contains('This will').should('exist');
|
||||
cy.contains('overwrite').should('exist');
|
||||
|
||||
// Close modal
|
||||
cy.get('[data-testid="credentials-modal-close"]').click();
|
||||
cy.get('[data-testid="credentials-modal"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('Avatar Display', () => {
|
||||
@@ -351,7 +436,9 @@ describe('Account Settings Page', () => {
|
||||
const mockNotification = function () {
|
||||
// Empty constructor for mocking purposes
|
||||
} as unknown as typeof Notification;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(mockNotification as any).permission = 'default';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(mockNotification as any).requestPermission = cy
|
||||
.stub()
|
||||
.resolves('granted');
|
||||
@@ -489,7 +576,8 @@ describe('Account Settings Page', () => {
|
||||
// Check section title
|
||||
cy.contains('Docker CLI and other Application Tokens').should('exist');
|
||||
|
||||
// Check tokens table
|
||||
// Check tokens table - wait for table to be populated
|
||||
cy.get('table').last().should('be.visible');
|
||||
cy.get('table')
|
||||
.last()
|
||||
.within(() => {
|
||||
@@ -555,13 +643,13 @@ describe('Account Settings Page', () => {
|
||||
cy.wait('@createToken');
|
||||
|
||||
// Should show success step with token
|
||||
cy.get('[data-testid="token-credentials-modal"]').within(() => {
|
||||
cy.get('[data-testid="credentials-modal"]').within(() => {
|
||||
cy.contains('Token Created Successfully').should('exist');
|
||||
cy.get('[data-testid="copy-token-button"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]').should('exist');
|
||||
});
|
||||
|
||||
cy.get('[data-testid="token-credentials-close"]').click();
|
||||
cy.get('[data-testid="token-credentials-modal"]').should('not.exist');
|
||||
cy.get('[data-testid="credentials-modal-close"]').click();
|
||||
cy.get('[data-testid="credentials-modal"]').should('not.exist');
|
||||
cy.wait('@getTokensAfterCreate');
|
||||
|
||||
// Verify new token appears in table
|
||||
@@ -724,7 +812,7 @@ describe('Account Settings Page', () => {
|
||||
cy.wait('@createToken');
|
||||
|
||||
// Should show success with tabs
|
||||
cy.get('[data-testid="token-credentials-modal"]').within(() => {
|
||||
cy.get('[data-testid="credentials-modal"]').within(() => {
|
||||
cy.contains('Token Created Successfully').should('exist');
|
||||
|
||||
// Check all tabs exist
|
||||
@@ -736,9 +824,9 @@ describe('Account Settings Page', () => {
|
||||
cy.contains('Docker Configuration').should('exist');
|
||||
|
||||
// Verify default tab (Application Token) shows username and token
|
||||
cy.get('[data-testid="copy-username"]').should('exist');
|
||||
cy.get('[data-testid="copy-token-button"]').should('exist');
|
||||
cy.get('[data-testid="copy-token-button"]')
|
||||
cy.get('[data-testid="credentials-modal-copy-username"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]')
|
||||
.find('input')
|
||||
.should('have.value', 'fake-token-code-12345');
|
||||
|
||||
@@ -757,11 +845,17 @@ describe('Account Settings Page', () => {
|
||||
cy.contains('Podman Login').click();
|
||||
cy.contains('Run podman login command').should('exist');
|
||||
cy.contains('podman login').should('exist');
|
||||
// Verify command contains username $app
|
||||
cy.contains('$app').should('exist'); // Username in command
|
||||
cy.contains('localhost').should('exist'); // Server hostname in command
|
||||
|
||||
// Test Docker Login tab
|
||||
cy.contains('Docker Login').click();
|
||||
cy.contains('Run docker login command').should('exist');
|
||||
cy.contains('docker login').should('exist');
|
||||
// Verify command contains username $app
|
||||
cy.contains('$app').should('exist'); // Username in command
|
||||
cy.contains('localhost').should('exist'); // Server hostname in command
|
||||
|
||||
// Test Docker Configuration tab
|
||||
cy.contains('Docker Configuration').click();
|
||||
@@ -770,8 +864,8 @@ describe('Account Settings Page', () => {
|
||||
cy.contains('overwrite').should('exist');
|
||||
});
|
||||
|
||||
cy.get('[data-testid="token-credentials-close"]').click();
|
||||
cy.get('[data-testid="token-credentials-modal"]').should('not.exist');
|
||||
cy.get('[data-testid="credentials-modal-close"]').click();
|
||||
cy.get('[data-testid="credentials-modal"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('Clickable Token Titles - View Token Details', () => {
|
||||
@@ -825,12 +919,12 @@ describe('Account Settings Page', () => {
|
||||
|
||||
// Verify modal shows token details with tabs
|
||||
cy.contains('Application Token').should('exist');
|
||||
cy.get('[data-testid="copy-username"]').should('exist');
|
||||
cy.get('[data-testid="copy-token-button"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-username"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]').should('exist');
|
||||
|
||||
// Close modal
|
||||
cy.contains('button', 'Done').click();
|
||||
cy.get('[data-testid="token-credentials-modal"]').should('not.exist');
|
||||
cy.get('[data-testid="credentials-modal"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('View Token Modal - Token Never Accessed', () => {
|
||||
@@ -876,13 +970,13 @@ describe('Account Settings Page', () => {
|
||||
cy.contains('Credentials for Unused Token').should('be.visible');
|
||||
|
||||
// Verify modal shows token credentials
|
||||
cy.get('[data-testid="token-credentials-modal"]').should('be.visible');
|
||||
cy.get('[data-testid="copy-username"]').should('exist');
|
||||
cy.get('[data-testid="copy-token-button"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal"]').should('be.visible');
|
||||
cy.get('[data-testid="credentials-modal-copy-username"]').should('exist');
|
||||
cy.get('[data-testid="credentials-modal-copy-password"]').should('exist');
|
||||
|
||||
// Close modal
|
||||
cy.contains('button', 'Done').click();
|
||||
cy.get('[data-testid="token-credentials-modal"]').should('not.exist');
|
||||
cy.get('[data-testid="credentials-modal"]').should('not.exist');
|
||||
});
|
||||
|
||||
it('Settings Tab Hidden in Read-Only Mode', () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@patternfly/react-core';
|
||||
import {useCreateApplicationToken} from 'src/hooks/UseApplicationTokens';
|
||||
import {IApplicationToken} from 'src/resources/UserResource';
|
||||
import ApplicationTokenCredentials from './ApplicationTokenCredentials';
|
||||
import CredentialsModal from './CredentialsModal';
|
||||
|
||||
interface CreateApplicationTokenModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -64,10 +64,15 @@ export default function CreateApplicationTokenModal({
|
||||
// If token was created successfully, show credentials
|
||||
if (createdToken) {
|
||||
return (
|
||||
<ApplicationTokenCredentials
|
||||
<CredentialsModal
|
||||
isOpen={isOpen}
|
||||
onClose={handleClose}
|
||||
token={createdToken}
|
||||
credentials={{
|
||||
username: '$app',
|
||||
password: createdToken.token_code,
|
||||
title: createdToken.title,
|
||||
}}
|
||||
type="token"
|
||||
isNewlyCreated={true}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -14,22 +14,31 @@ import {
|
||||
CodeBlockCode,
|
||||
Text,
|
||||
} from '@patternfly/react-core';
|
||||
import {IApplicationToken} from 'src/resources/UserResource';
|
||||
import {useQuayConfig} from 'src/hooks/UseQuayConfig';
|
||||
|
||||
interface ApplicationTokenCredentialsProps {
|
||||
export interface Credentials {
|
||||
username: string;
|
||||
password: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export type CredentialsType = 'token' | 'encrypted-password';
|
||||
|
||||
interface CredentialsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
token: IApplicationToken;
|
||||
credentials: Credentials;
|
||||
type: CredentialsType;
|
||||
isNewlyCreated?: boolean;
|
||||
}
|
||||
|
||||
export default function ApplicationTokenCredentials({
|
||||
export default function CredentialsModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
token,
|
||||
credentials,
|
||||
type,
|
||||
isNewlyCreated = false,
|
||||
}: ApplicationTokenCredentialsProps) {
|
||||
}: CredentialsModalProps) {
|
||||
const [activeTabKey, setActiveTabKey] = useState<string | number>(0);
|
||||
const quayConfig = useQuayConfig();
|
||||
|
||||
@@ -38,7 +47,9 @@ export default function ApplicationTokenCredentials({
|
||||
};
|
||||
|
||||
const getContainerLoginCommand = (runtime: 'docker' | 'podman') => {
|
||||
return `${runtime} login -u='$app' -p='${token?.token_code}' ${getServerHostname()}`;
|
||||
return `${runtime} login -u='${credentials.username}' -p='${
|
||||
credentials.password
|
||||
}' ${getServerHostname()}`;
|
||||
};
|
||||
|
||||
const kubernetesYaml = useMemo(() => {
|
||||
@@ -46,7 +57,7 @@ export default function ApplicationTokenCredentials({
|
||||
const dockerConfigJson = {
|
||||
auths: {
|
||||
[hostname]: {
|
||||
auth: btoa(`$app:${token?.token_code}`),
|
||||
auth: btoa(`${credentials.username}:${credentials.password}`),
|
||||
email: '',
|
||||
},
|
||||
},
|
||||
@@ -55,11 +66,16 @@ export default function ApplicationTokenCredentials({
|
||||
return `apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: ${token?.title}-pull-secret
|
||||
name: ${credentials.title}-pull-secret
|
||||
data:
|
||||
.dockerconfigjson: ${btoa(JSON.stringify(dockerConfigJson))}
|
||||
type: kubernetes.io/dockerconfigjson`;
|
||||
}, [token?.title, token?.token_code, quayConfig?.config?.SERVER_HOSTNAME]);
|
||||
}, [
|
||||
credentials.title,
|
||||
credentials.username,
|
||||
credentials.password,
|
||||
quayConfig?.config?.SERVER_HOSTNAME,
|
||||
]);
|
||||
|
||||
const rktConfig = useMemo(() => {
|
||||
const hostname = getServerHostname();
|
||||
@@ -69,43 +85,55 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
"domains": ["${hostname}"],
|
||||
"type": "basic",
|
||||
"credentials": {
|
||||
"user": "$app",
|
||||
"password": "${token?.token_code}"
|
||||
"user": "${credentials.username}",
|
||||
"password": "${credentials.password}"
|
||||
}
|
||||
}`;
|
||||
}, [token?.token_code, quayConfig?.config?.SERVER_HOSTNAME]);
|
||||
}, [
|
||||
credentials.username,
|
||||
credentials.password,
|
||||
quayConfig?.config?.SERVER_HOSTNAME,
|
||||
]);
|
||||
|
||||
const dockerConfig = useMemo(() => {
|
||||
const hostname = getServerHostname();
|
||||
return `{
|
||||
"auths": {
|
||||
"${hostname}": {
|
||||
"auth": "${btoa(`$app:${token?.token_code}`)}",
|
||||
"auth": "${btoa(`${credentials.username}:${credentials.password}`)}",
|
||||
"email": ""
|
||||
}
|
||||
}
|
||||
}`;
|
||||
}, [token?.token_code, quayConfig?.config?.SERVER_HOSTNAME]);
|
||||
}, [
|
||||
credentials.username,
|
||||
credentials.password,
|
||||
quayConfig?.config?.SERVER_HOSTNAME,
|
||||
]);
|
||||
|
||||
const isToken = type === 'token';
|
||||
const firstTabTitle = isToken ? 'Application Token' : 'Encrypted Password';
|
||||
const passwordFieldLabel = isToken ? 'Token' : 'Encrypted Password';
|
||||
|
||||
return (
|
||||
<Modal
|
||||
variant={ModalVariant.large}
|
||||
title={`Credentials for ${token.title}`}
|
||||
title={`Credentials for ${credentials.title}`}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
data-testid="token-credentials-modal"
|
||||
data-testid="credentials-modal"
|
||||
actions={[
|
||||
<Button
|
||||
key="done"
|
||||
variant="primary"
|
||||
onClick={onClose}
|
||||
data-testid="token-credentials-close"
|
||||
data-testid="credentials-modal-close"
|
||||
>
|
||||
Done
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
{isNewlyCreated ? (
|
||||
{isNewlyCreated && isToken && (
|
||||
<Alert
|
||||
variant="success"
|
||||
isInline
|
||||
@@ -116,7 +144,8 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
your password for Docker and other CLI commands. Make sure to copy and
|
||||
save it securely.
|
||||
</Alert>
|
||||
) : (
|
||||
)}
|
||||
{!isNewlyCreated && isToken && (
|
||||
<Alert
|
||||
variant="info"
|
||||
isInline
|
||||
@@ -127,15 +156,24 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
CLI commands. Keep it secure and do not share it.
|
||||
</Alert>
|
||||
)}
|
||||
{!isToken && (
|
||||
<Alert
|
||||
variant="info"
|
||||
isInline
|
||||
title="Encrypted Password"
|
||||
className="pf-v5-u-mb-md"
|
||||
>
|
||||
This encrypted password can be used for <code>docker login</code> and
|
||||
other CLI commands. It is recommended to use this instead of your
|
||||
plaintext password.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Tabs
|
||||
activeKey={activeTabKey}
|
||||
onSelect={(_event, tabIndex) => setActiveTabKey(tabIndex)}
|
||||
>
|
||||
<Tab
|
||||
eventKey={0}
|
||||
title={<TabTitleText>Application Token</TabTitleText>}
|
||||
>
|
||||
<Tab eventKey={0} title={<TabTitleText>{firstTabTitle}</TabTitleText>}>
|
||||
<Form className="pf-v5-u-p-md">
|
||||
<FormGroup
|
||||
label="Username"
|
||||
@@ -146,20 +184,20 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
hoverTip="Copy"
|
||||
clickTip="Copied"
|
||||
isReadOnly
|
||||
data-testid="copy-username"
|
||||
data-testid="credentials-modal-copy-username"
|
||||
>
|
||||
$app
|
||||
{credentials.username}
|
||||
</ClipboardCopy>
|
||||
</FormGroup>
|
||||
<FormGroup label="Token" fieldId="token-code">
|
||||
<FormGroup label={passwordFieldLabel} fieldId="password">
|
||||
<ClipboardCopy
|
||||
hoverTip="Copy"
|
||||
clickTip="Copied"
|
||||
variant="expansion"
|
||||
isReadOnly
|
||||
data-testid="copy-token-button"
|
||||
data-testid="credentials-modal-copy-password"
|
||||
>
|
||||
{token.token_code}
|
||||
{credentials.password}
|
||||
</ClipboardCopy>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
@@ -183,7 +221,7 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
className="pf-v5-u-mb-md"
|
||||
>
|
||||
<ClipboardCopy hoverTip="Copy" clickTip="Copied" isReadOnly>
|
||||
{`kubectl create -f ${token?.title}-pull-secret.yaml --namespace=NAMESPACE`}
|
||||
{`kubectl create -f ${credentials.title}-pull-secret.yaml --namespace=NAMESPACE`}
|
||||
</ClipboardCopy>
|
||||
</FormGroup>
|
||||
<FormGroup label="Step 3: Reference in pod spec">
|
||||
@@ -192,7 +230,7 @@ type: kubernetes.io/dockerconfigjson`;
|
||||
</Text>
|
||||
<CodeBlock>
|
||||
<CodeBlockCode>
|
||||
{`imagePullSecrets:\n - name: ${token?.title}-pull-secret`}
|
||||
{`imagePullSecrets:\n - name: ${credentials.title}-pull-secret`}
|
||||
</CodeBlockCode>
|
||||
</CodeBlock>
|
||||
</FormGroup>
|
||||
@@ -1,11 +1,14 @@
|
||||
import {Modal, ModalVariant, Button, TextInput} from '@patternfly/react-core';
|
||||
import {useState} from 'react';
|
||||
import {useState, useEffect} from 'react';
|
||||
import FormError from 'src/components/errors/FormError';
|
||||
import {addDisplayError} from 'src/resources/ErrorHandling';
|
||||
import {useCreateClientKey} from 'src/hooks/UseCreateClientKey';
|
||||
import {useCurrentUser} from 'src/hooks/UseCurrentUser';
|
||||
import CredentialsModal, {Credentials} from './CredentialsModal';
|
||||
|
||||
export function GenerateEncryptedPassword(props: ConfirmationModalProps) {
|
||||
const [err, setErr] = useState<string>();
|
||||
const {user} = useCurrentUser();
|
||||
|
||||
const [password, setPassword] = useState('');
|
||||
const [step, setStep] = useState(1);
|
||||
@@ -15,6 +18,7 @@ export function GenerateEncryptedPassword(props: ConfirmationModalProps) {
|
||||
setErr(addDisplayError('Error', error));
|
||||
},
|
||||
onSuccess: () => {
|
||||
setErr(undefined); // Clear any previous errors
|
||||
setStep(step + 1);
|
||||
},
|
||||
});
|
||||
@@ -23,53 +27,69 @@ export function GenerateEncryptedPassword(props: ConfirmationModalProps) {
|
||||
createClientKey(password);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
props.toggleModal();
|
||||
};
|
||||
|
||||
// Cleanup state when modal closes
|
||||
useEffect(() => {
|
||||
if (!props.modalOpen) {
|
||||
setStep(1);
|
||||
setPassword('');
|
||||
setErr(undefined);
|
||||
}
|
||||
}, [props.modalOpen]);
|
||||
|
||||
// Step 2: Show credentials modal with all credential formats
|
||||
// Only transition to step 2 if we have clientKey, user, and no error
|
||||
if (step === 2 && clientKey && user && !err) {
|
||||
const credentials: Credentials = {
|
||||
username: user.username,
|
||||
password: clientKey,
|
||||
title: user.username,
|
||||
};
|
||||
|
||||
return (
|
||||
<CredentialsModal
|
||||
isOpen={props.modalOpen}
|
||||
onClose={handleClose}
|
||||
credentials={credentials}
|
||||
type="encrypted-password"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Step 1: Password input
|
||||
return (
|
||||
<Modal
|
||||
variant={ModalVariant.small}
|
||||
title={props.title}
|
||||
isOpen={props.modalOpen}
|
||||
onClose={props.toggleModal}
|
||||
actions={
|
||||
step == 1
|
||||
? [
|
||||
<Button
|
||||
key="confirm"
|
||||
variant="primary"
|
||||
onClick={handleModalConfirm}
|
||||
id="submit"
|
||||
>
|
||||
{props.buttonText}
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={props.toggleModal}>
|
||||
Cancel
|
||||
</Button>,
|
||||
]
|
||||
: [
|
||||
<Button key="cancel" variant="link" onClick={props.toggleModal}>
|
||||
Done
|
||||
</Button>,
|
||||
]
|
||||
}
|
||||
onClose={handleClose}
|
||||
actions={[
|
||||
<Button
|
||||
key="confirm"
|
||||
variant="primary"
|
||||
onClick={handleModalConfirm}
|
||||
id="submit"
|
||||
>
|
||||
{props.buttonText}
|
||||
</Button>,
|
||||
<Button key="cancel" variant="link" onClick={handleClose}>
|
||||
Cancel
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
{step == 1 && (
|
||||
<>
|
||||
<FormError message={err} setErr={setErr} />
|
||||
<TextInput
|
||||
id="delete-confirmation-input"
|
||||
value={password}
|
||||
type="password"
|
||||
onChange={(_, value) => setPassword(value)}
|
||||
aria-label="text input example"
|
||||
label="Password"
|
||||
/>
|
||||
Please enter your password in order to generate
|
||||
</>
|
||||
)}
|
||||
{step == 2 && (
|
||||
<>
|
||||
Your encrypted password is: <br /> {clientKey}
|
||||
</>
|
||||
)}
|
||||
<FormError message={err} setErr={setErr} />
|
||||
<TextInput
|
||||
id="delete-confirmation-input"
|
||||
value={password}
|
||||
type="password"
|
||||
onChange={(_, value) => setPassword(value)}
|
||||
aria-label="text input example"
|
||||
label="Password"
|
||||
/>
|
||||
Please enter your password in order to generate
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import {EllipsisVIcon, KeyIcon} from '@patternfly/react-icons';
|
||||
import {GenerateEncryptedPassword} from 'src/components/modals/GenerateEncryptedPasswordModal';
|
||||
import CreateApplicationTokenModal from 'src/components/modals/CreateApplicationTokenModal';
|
||||
import RevokeTokenModal from 'src/components/modals/RevokeTokenModal';
|
||||
import ApplicationTokenCredentials from 'src/components/modals/ApplicationTokenCredentials';
|
||||
import CredentialsModal from 'src/components/modals/CredentialsModal';
|
||||
import {
|
||||
useApplicationTokens,
|
||||
useApplicationToken,
|
||||
@@ -353,10 +353,15 @@ export const CliConfiguration = () => {
|
||||
)}
|
||||
|
||||
{fetchedToken && !isFetchingToken && (
|
||||
<ApplicationTokenCredentials
|
||||
<CredentialsModal
|
||||
isOpen={viewTokenModalOpen}
|
||||
onClose={handleCloseViewModal}
|
||||
token={fetchedToken}
|
||||
credentials={{
|
||||
username: '$app',
|
||||
password: fetchedToken.token_code,
|
||||
title: fetchedToken.title,
|
||||
}}
|
||||
type="token"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user