1 |
- {"version":3,"file":"ServiceAccountsPage.da5eb65dbd1ccd8bd708.js","mappings":"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","sources":["webpack://grafana/./public/app/features/serviceaccounts/ServiceAccountsListItem.tsx","webpack://grafana/./public/app/features/serviceaccounts/ServiceAccountsListPage.tsx"],"sourcesContent":["import { cx } from '@emotion/css';\nimport React, { memo } from 'react';\n\nimport { OrgRole } from '@grafana/data';\nimport { Button, Icon, useStyles2 } from '@grafana/ui';\nimport { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';\nimport { contextSrv } from 'app/core/core';\nimport { AccessControlAction, Role, ServiceAccountDTO } from 'app/types';\n\nimport { OrgRolePicker } from '../admin/OrgRolePicker';\n\nimport { getStyles } from './ServiceAccountsListPage';\n\ntype ServiceAccountListItemProps = {\n serviceAccount: ServiceAccountDTO;\n onRoleChange: (role: OrgRole, serviceAccount: ServiceAccountDTO) => void;\n roleOptions: Role[];\n builtInRoles: Record<string, Role[]>;\n onSetToRemove: (serviceAccount: ServiceAccountDTO) => void;\n};\n\nconst getServiceAccountsAriaLabel = (name: string) => {\n return `Edit service account's ${name} details`;\n};\nconst getServiceAccountsEnabledStatus = (disabled: boolean) => {\n return disabled ? 'Disabled' : 'Enabled';\n};\n\nconst ServiceAccountListItem = memo(\n ({ serviceAccount, onRoleChange, roleOptions, builtInRoles, onSetToRemove }: ServiceAccountListItemProps) => {\n const editUrl = `org/serviceaccounts/${serviceAccount.id}`;\n const styles = useStyles2(getStyles);\n const canUpdateRole = contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsWrite, serviceAccount);\n const displayRolePicker =\n contextSrv.hasPermission(AccessControlAction.ActionRolesList) &&\n contextSrv.hasPermission(AccessControlAction.ActionUserRolesList);\n const enableRolePicker = contextSrv.hasPermission(AccessControlAction.OrgUsersWrite) && canUpdateRole;\n\n return (\n <tr key={serviceAccount.id}>\n <td className=\"width-4 text-center link-td\">\n <a href={editUrl} aria-label={getServiceAccountsAriaLabel(serviceAccount.name)}>\n <img\n className=\"filter-table__avatar\"\n src={serviceAccount.avatarUrl}\n alt={`Avatar for user ${serviceAccount.name}`}\n />\n </a>\n </td>\n <td className=\"link-td max-width-10\">\n <a\n className=\"ellipsis\"\n href={editUrl}\n title={serviceAccount.name}\n aria-label={getServiceAccountsAriaLabel(serviceAccount.name)}\n >\n {serviceAccount.name}\n </a>\n </td>\n <td className=\"link-td max-width-10\">\n <a\n className=\"ellipsis\"\n href={editUrl}\n title={serviceAccount.login}\n aria-label={getServiceAccountsAriaLabel(serviceAccount.name)}\n >\n {serviceAccount.login}\n </a>\n </td>\n {contextSrv.licensedAccessControlEnabled() ? (\n <td className={cx('link-td', styles.iconRow)}>\n {displayRolePicker && (\n <UserRolePicker\n userId={serviceAccount.id}\n orgId={serviceAccount.orgId}\n builtInRole={serviceAccount.role}\n onBuiltinRoleChange={(newRole) => onRoleChange(newRole, serviceAccount)}\n roleOptions={roleOptions}\n builtInRoles={builtInRoles}\n disabled={!enableRolePicker}\n />\n )}\n </td>\n ) : (\n <td className={cx('link-td', styles.iconRow)}>\n <OrgRolePicker\n aria-label=\"Role\"\n value={serviceAccount.role}\n disabled={!canUpdateRole}\n onChange={(newRole) => onRoleChange(newRole, serviceAccount)}\n />\n </td>\n )}\n <td className=\"link-td max-width-10\">\n <a\n className=\"ellipsis\"\n href={editUrl}\n title={getServiceAccountsEnabledStatus(serviceAccount.isDisabled)}\n aria-label={getServiceAccountsAriaLabel(serviceAccount.name)}\n >\n {getServiceAccountsEnabledStatus(serviceAccount.isDisabled)}\n </a>\n </td>\n <td className=\"link-td max-width-10\">\n <a\n className=\"ellipsis\"\n href={editUrl}\n title=\"Tokens\"\n aria-label={getServiceAccountsAriaLabel(serviceAccount.name)}\n >\n <span>\n <Icon name={'key-skeleton-alt'}></Icon>\n </span>\n \n {serviceAccount.tokens}\n </a>\n </td>\n {contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsDelete, serviceAccount) && (\n <td>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n onClick={() => {\n onSetToRemove(serviceAccount);\n }}\n icon=\"times\"\n aria-label=\"Delete service account\"\n />\n </td>\n )}\n </tr>\n );\n }\n);\nServiceAccountListItem.displayName = 'ServiceAccountListItem';\n\nexport default ServiceAccountListItem;\n","import { css, cx } from '@emotion/css';\nimport pluralize from 'pluralize';\nimport React, { useEffect } from 'react';\nimport { connect, ConnectedProps } from 'react-redux';\n\nimport { GrafanaTheme2, OrgRole } from '@grafana/data';\nimport { ConfirmModal, FilterInput, LinkButton, RadioButtonGroup, useStyles2 } from '@grafana/ui';\nimport EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';\nimport Page from 'app/core/components/Page/Page';\nimport PageLoader from 'app/core/components/PageLoader/PageLoader';\nimport { contextSrv } from 'app/core/core';\nimport { getNavModel } from 'app/core/selectors/navModel';\nimport { StoreState, ServiceAccountDTO, AccessControlAction } from 'app/types';\n\nimport ServiceAccountListItem from './ServiceAccountsListItem';\nimport {\n changeFilter,\n changeQuery,\n fetchACOptions,\n fetchServiceAccounts,\n removeServiceAccount,\n updateServiceAccount,\n setServiceAccountToRemove,\n} from './state/actions';\n\ninterface OwnProps {}\n\ntype Props = OwnProps & ConnectedProps<typeof connector>;\n\nfunction mapStateToProps(state: StoreState) {\n return {\n navModel: getNavModel(state.navIndex, 'serviceaccounts'),\n ...state.serviceAccounts,\n };\n}\n\nconst mapDispatchToProps = {\n fetchServiceAccounts,\n fetchACOptions,\n updateServiceAccount,\n removeServiceAccount,\n setServiceAccountToRemove,\n changeFilter,\n changeQuery,\n};\n\nconst connector = connect(mapStateToProps, mapDispatchToProps);\n\nconst ServiceAccountsListPage = ({\n fetchServiceAccounts,\n removeServiceAccount,\n fetchACOptions,\n updateServiceAccount,\n setServiceAccountToRemove,\n navModel,\n serviceAccounts,\n isLoading,\n roleOptions,\n builtInRoles,\n changeFilter,\n changeQuery,\n query,\n filters,\n serviceAccountToRemove,\n}: Props): JSX.Element => {\n const styles = useStyles2(getStyles);\n\n useEffect(() => {\n const fetchData = async () => {\n await fetchServiceAccounts();\n if (contextSrv.licensedAccessControlEnabled()) {\n await fetchACOptions();\n }\n };\n fetchData();\n }, [fetchServiceAccounts, fetchACOptions]);\n\n const onRoleChange = async (role: OrgRole, serviceAccount: ServiceAccountDTO) => {\n const updatedServiceAccount = { ...serviceAccount, role: role };\n await updateServiceAccount(updatedServiceAccount);\n // need to refetch to display the new value in the list\n await fetchServiceAccounts();\n if (contextSrv.licensedAccessControlEnabled()) {\n fetchACOptions();\n }\n };\n\n return (\n <Page navModel={navModel}>\n <Page.Contents>\n <h2>Service accounts</h2>\n <div className=\"page-action-bar\" style={{ justifyContent: 'flex-end' }}>\n <FilterInput\n placeholder=\"Search service account by name.\"\n autoFocus={true}\n value={query}\n onChange={changeQuery}\n />\n <RadioButtonGroup\n options={[\n { label: 'All service accounts', value: false },\n { label: 'Expired tokens', value: true },\n ]}\n onChange={(value) => changeFilter({ name: 'expiredTokens', value })}\n value={filters.find((f) => f.name === 'expiredTokens')?.value}\n className={styles.filter}\n />\n {serviceAccounts.length !== 0 && contextSrv.hasPermission(AccessControlAction.ServiceAccountsCreate) && (\n <LinkButton href=\"org/serviceaccounts/create\" variant=\"primary\">\n Add service account\n </LinkButton>\n )}\n </div>\n {isLoading && <PageLoader />}\n {!isLoading && serviceAccounts.length === 0 && (\n <>\n <EmptyListCTA\n title=\"You haven't created any service accounts yet.\"\n buttonIcon=\"key-skeleton-alt\"\n buttonLink=\"org/serviceaccounts/create\"\n buttonTitle=\"Add service account\"\n buttonDisabled={!contextSrv.hasPermission(AccessControlAction.ServiceAccountsCreate)}\n proTip=\"Remember, you can provide specific permissions for API access to other applications.\"\n proTipLink=\"\"\n proTipLinkTitle=\"\"\n proTipTarget=\"_blank\"\n />\n </>\n )}\n {!isLoading && serviceAccounts.length !== 0 && (\n <>\n <div className={cx(styles.table, 'admin-list-table')}>\n <table className=\"filter-table form-inline filter-table--hover\">\n <thead>\n <tr>\n <th></th>\n <th>Account</th>\n <th>ID</th>\n <th>Roles</th>\n <th>Status</th>\n <th>Tokens</th>\n <th style={{ width: '34px' }} />\n </tr>\n </thead>\n <tbody>\n {serviceAccounts.map((serviceAccount: ServiceAccountDTO) => (\n <ServiceAccountListItem\n serviceAccount={serviceAccount}\n key={serviceAccount.id}\n builtInRoles={builtInRoles}\n roleOptions={roleOptions}\n onRoleChange={onRoleChange}\n onSetToRemove={setServiceAccountToRemove}\n />\n ))}\n </tbody>\n </table>\n </div>\n </>\n )}\n {serviceAccountToRemove && (\n <ConfirmModal\n body={\n <div>\n Are you sure you want to delete '{serviceAccountToRemove.name}'\n {Boolean(serviceAccountToRemove.tokens) &&\n ` and ${serviceAccountToRemove.tokens} accompanying ${pluralize(\n 'token',\n serviceAccountToRemove.tokens\n )}`}\n ?\n </div>\n }\n confirmText=\"Delete\"\n title=\"Delete service account\"\n onDismiss={() => {\n setServiceAccountToRemove(null);\n }}\n isOpen={true}\n onConfirm={() => {\n removeServiceAccount(serviceAccountToRemove.id);\n setServiceAccountToRemove(null);\n }}\n />\n )}\n </Page.Contents>\n </Page>\n );\n};\n\nexport const getStyles = (theme: GrafanaTheme2) => {\n return {\n table: css`\n margin-top: ${theme.spacing(3)};\n `,\n filter: css`\n margin: 0 ${theme.spacing(1)};\n `,\n iconRow: css`\n svg {\n margin-left: ${theme.spacing(0.5)};\n }\n `,\n row: css`\n display: flex;\n align-items: center;\n height: 100% !important;\n\n a {\n padding: ${theme.spacing(0.5)} 0 !important;\n }\n `,\n unitTooltip: css`\n display: flex;\n flex-direction: column;\n `,\n unitItem: css`\n cursor: pointer;\n padding: ${theme.spacing(0.5)} 0;\n margin-right: ${theme.spacing(1)};\n `,\n disabled: css`\n color: ${theme.colors.text.disabled};\n `,\n link: css`\n color: inherit;\n cursor: pointer;\n text-decoration: underline;\n `,\n };\n};\n\nexport default connector(ServiceAccountsListPage);\n"],"names":["getServiceAccountsAriaLabel","name","getServiceAccountsEnabledStatus","disabled","ServiceAccountListItem","memo","serviceAccount","onRoleChange","roleOptions","builtInRoles","onSetToRemove","editUrl","id","styles","useStyles2","getStyles","canUpdateRole","contextSrv","AccessControlAction","displayRolePicker","enableRolePicker","className","href","src","avatarUrl","alt","title","login","cx","iconRow","UserRolePicker","userId","orgId","builtInRole","role","onBuiltinRoleChange","newRole","OrgRolePicker","value","onChange","isDisabled","Icon","tokens","Button","size","variant","onClick","icon","displayName","mapDispatchToProps","fetchServiceAccounts","fetchACOptions","updateServiceAccount","removeServiceAccount","setServiceAccountToRemove","changeFilter","changeQuery","connector","connect","state","navModel","getNavModel","navIndex","serviceAccounts","theme","table","css","spacing","filter","row","unitTooltip","unitItem","colors","text","link","isLoading","query","filters","serviceAccountToRemove","useEffect","async","fetchData","updatedServiceAccount","Page","style","justifyContent","FilterInput","placeholder","autoFocus","RadioButtonGroup","options","label","find","f","length","LinkButton","PageLoader","EmptyListCTA","buttonIcon","buttonLink","buttonTitle","buttonDisabled","proTip","proTipLink","proTipLinkTitle","proTipTarget","width","map","ConfirmModal","body","Boolean","pluralize","confirmText","onDismiss","isOpen","onConfirm"],"sourceRoot":""}
|