From 908e583c69a0837ada0b01ae77171e1cc4606430 Mon Sep 17 00:00:00 2001 From: mabashian Date: Thu, 4 Jun 2020 17:29:07 -0400 Subject: [PATCH 01/10] Display prompt on launch passwords properly --- .../Credential/CredentialDetail/CredentialDetail.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx index 2e8809955f..b297c679ef 100644 --- a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx @@ -75,7 +75,7 @@ function CredentialDetail({ i18n, credential }) { const { error, dismissError } = useDismissableError(deleteError); - const renderDetail = ({ id, label, type }) => { + const renderDetail = ({ id, label, type, ask_at_runtime }) => { let detail; if (type === 'boolean') { @@ -96,6 +96,10 @@ function CredentialDetail({ i18n, credential }) { isEncrypted={isEncrypted} /> ); + } else if (ask_at_runtime && inputs[id] === 'ASK') { + detail = ( + + ); } else { detail = ; } From e7cd9bbb9805f17d94934422541382f338394dd4 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 8 Jun 2020 11:00:35 -0400 Subject: [PATCH 02/10] Display fields that have plugins configured. --- .../CredentialDetail/CredentialDetail.jsx | 134 +++- .../CredentialDetail.test.jsx | 44 ++ .../Credential/shared/data.credentials.json | 746 +++++++++--------- 3 files changed, 528 insertions(+), 396 deletions(-) diff --git a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx index b297c679ef..f66389f3a1 100644 --- a/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialDetail/CredentialDetail.jsx @@ -1,9 +1,9 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { Fragment, useState, useEffect, useCallback } from 'react'; import { Link, useHistory } from 'react-router-dom'; import { withI18n } from '@lingui/react'; -import { t } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; import { shape } from 'prop-types'; - +import styled from 'styled-components'; import { Button, List, ListItem } from '@patternfly/react-core'; import AlertModal from '../../../components/AlertModal'; import { CardBody, CardActionsRow } from '../../../components/Card'; @@ -11,15 +11,26 @@ import ContentError from '../../../components/ContentError'; import ContentLoading from '../../../components/ContentLoading'; import DeleteButton from '../../../components/DeleteButton'; import { - DetailList, Detail, + DetailList, UserDateDetail, } from '../../../components/DetailList'; +import ChipGroup from '../../../components/ChipGroup'; +import CodeMirrorInput from '../../../components/CodeMirrorInput'; +import CredentialChip from '../../../components/CredentialChip'; import ErrorDetail from '../../../components/ErrorDetail'; import { CredentialsAPI, CredentialTypesAPI } from '../../../api'; import { Credential } from '../../../types'; import useRequest, { useDismissableError } from '../../../util/useRequest'; +const PluginInputMetadata = styled(CodeMirrorInput)` + grid-column: 1 / -1; +`; + +const PluginFieldText = styled.p` + margin-top: 10px; +`; + function CredentialDetail({ i18n, credential }) { const { id: credentialId, @@ -38,6 +49,7 @@ function CredentialDetail({ i18n, credential }) { } = credential; const [fields, setFields] = useState([]); + const [inputSources, setInputSources] = useState({}); const [managedByTower, setManagedByTower] = useState([]); const [contentError, setContentError] = useState(null); const [hasContentLoading, setHasContentLoading] = useState(true); @@ -48,19 +60,33 @@ function CredentialDetail({ i18n, credential }) { setContentError(null); setHasContentLoading(true); try { - const { - data: { inputs: credentialTypeInputs, managed_by_tower }, - } = await CredentialTypesAPI.readDetail(credential_type.id); + const [ + { + data: { inputs: credentialTypeInputs, managed_by_tower }, + }, + { + data: { results: loadedInputSources }, + }, + ] = await Promise.all([ + CredentialTypesAPI.readDetail(credential_type.id), + CredentialsAPI.readInputSources(credentialId, { page_size: 200 }), + ]); setFields(credentialTypeInputs.fields || []); setManagedByTower(managed_by_tower); + setInputSources( + loadedInputSources.reduce((inputSourcesMap, inputSource) => { + inputSourcesMap[inputSource.input_field_name] = inputSource; + return inputSourcesMap; + }, {}) + ); } catch (error) { setContentError(error); } finally { setHasContentLoading(false); } })(); - }, [credential_type]); + }, [credential_type, credentialId]); const { request: deleteCredential, @@ -76,35 +102,77 @@ function CredentialDetail({ i18n, credential }) { const { error, dismissError } = useDismissableError(deleteError); const renderDetail = ({ id, label, type, ask_at_runtime }) => { - let detail; + if (inputSources[id]) { + return ( + + {label} *} + value={ + + + + } + /> + {}} + rows={5} + hasErrors={false} + /> + + ); + } if (type === 'boolean') { - detail = ( + return ( {inputs[id] && {label}}} /> ); - } else if (inputs[id] === '$encrypted$') { - const isEncrypted = true; - detail = ( + } + + if (inputs[id] === '$encrypted$') { + return ( ); - } else if (ask_at_runtime && inputs[id] === 'ASK') { - detail = ( - - ); - } else { - detail = ; } - return detail; + if (ask_at_runtime && inputs[id] === 'ASK') { + return ( + + ); + } + + return ( + + ); }; if (hasContentLoading) { @@ -118,10 +186,19 @@ function CredentialDetail({ i18n, credential }) { return ( - - + + {organization && ( @@ -131,6 +208,7 @@ function CredentialDetail({ i18n, credential }) { /> )} renderDetail(field))} + {Object.keys(inputSources).length > 0 && ( + + + * This field will be retrieved from an external secret management + system using the specified credential. + + + )} {user_capabilities.edit && (