1 |
- {"version":3,"file":"ApiKeysPage.a269349dc83fd8e96d37.js","mappings":"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","sources":["webpack://grafana/./public/app/features/api-keys/ApiKeysActionBar.tsx","webpack://grafana/./public/app/features/api-keys/ApiKeysAddedModal.tsx","webpack://grafana/./public/app/features/api-keys/ApiKeysController.tsx","webpack://grafana/./public/app/features/api-keys/ApiKeysForm.tsx","webpack://grafana/./public/app/features/api-keys/ApiKeysTable.tsx","webpack://grafana/./public/app/features/api-keys/state/actions.ts","webpack://grafana/./public/app/features/api-keys/state/selectors.ts","webpack://grafana/./public/app/features/api-keys/ApiKeysPage.tsx"],"sourcesContent":["import React, { FC } from 'react';\n\nimport { Button, FilterInput } from '@grafana/ui';\n\ninterface Props {\n searchQuery: string;\n disabled: boolean;\n onAddClick: () => void;\n onSearchChange: (value: string) => void;\n}\n\nexport const ApiKeysActionBar: FC<Props> = ({ searchQuery, disabled, onAddClick, onSearchChange }) => {\n return (\n <div className=\"page-action-bar\">\n <div className=\"gf-form gf-form--grow\">\n <FilterInput placeholder=\"Search keys\" value={searchQuery} onChange={onSearchChange} />\n </div>\n <Button className=\"pull-right\" onClick={onAddClick} disabled={disabled}>\n Add API key\n </Button>\n </div>\n );\n};\n","import { css } from '@emotion/css';\nimport React, { useCallback } from 'react';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { Alert, Field, Modal, useStyles2, Input, Icon, ClipboardButton } from '@grafana/ui';\n\nimport { notifyApp } from '../../core/actions';\nimport { createSuccessNotification } from '../../core/copy/appNotification';\nimport { dispatch } from '../../store/store';\n\nexport interface Props {\n onDismiss: () => void;\n apiKey: string;\n rootPath: string;\n}\n\nexport function ApiKeysAddedModal({ onDismiss, apiKey, rootPath }: Props): JSX.Element {\n const styles = useStyles2(getStyles);\n const getClipboardText = useCallback(() => apiKey, [apiKey]);\n const onClipboardCopy = () => {\n dispatch(notifyApp(createSuccessNotification('Content copied to clipboard')));\n };\n return (\n <Modal title=\"API Key Created\" onDismiss={onDismiss} onClickBackdrop={onDismiss} isOpen>\n <Field label=\"Key\">\n <Input\n id=\"Key\"\n value={apiKey}\n readOnly\n addonAfter={\n <ClipboardButton variant=\"primary\" getText={getClipboardText} onClipboardCopy={onClipboardCopy}>\n <Icon name=\"copy\" /> Copy\n </ClipboardButton>\n }\n />\n </Field>\n <Alert severity=\"info\" title=\"You will only be able to view this key here once!\">\n It is not stored in this form, so be sure to copy it now.\n </Alert>\n\n <p className=\"text-muted\">You can authenticate a request using the Authorization HTTP header, example:</p>\n <pre className={styles.small}>\n curl -H "Authorization: Bearer {apiKey}" {rootPath}/api/dashboards/home\n </pre>\n </Modal>\n );\n}\n\nfunction getStyles(theme: GrafanaTheme2) {\n return {\n label: css`\n padding: ${theme.spacing(1)};\n background-color: ${theme.colors.background.secondary};\n border-radius: ${theme.shape.borderRadius()};\n `,\n small: css`\n font-size: ${theme.typography.bodySmall.fontSize};\n font-weight: ${theme.typography.bodySmall.fontWeight};\n `,\n };\n}\n","import { FC, useCallback, useState } from 'react';\n\ninterface Api {\n isAdding: boolean;\n toggleIsAdding: () => void;\n}\n\ninterface Props {\n children: (props: Api) => JSX.Element;\n}\n\nexport const ApiKeysController: FC<Props> = ({ children }) => {\n const [isAdding, setIsAdding] = useState<boolean>(false);\n const toggleIsAdding = useCallback(() => {\n setIsAdding(!isAdding);\n }, [isAdding]);\n\n return children({ isAdding, toggleIsAdding });\n};\n","import React, { ChangeEvent, FC, FormEvent, useEffect, useState } from 'react';\n\nimport { rangeUtil, SelectableValue } from '@grafana/data';\nimport { EventsWithValidation, LegacyForms, ValidationEvents, Button, Select, InlineField } from '@grafana/ui';\nimport { CloseButton } from 'app/core/components/CloseButton/CloseButton';\n\nimport { SlideDown } from '../../core/components/Animations/SlideDown';\nimport { NewApiKey, OrgRole } from '../../types';\n\nconst { Input } = LegacyForms;\nconst ROLE_OPTIONS: Array<SelectableValue<OrgRole>> = Object.keys(OrgRole).map((role) => ({\n label: role,\n value: role as OrgRole,\n}));\n\ninterface Props {\n show: boolean;\n onClose: () => void;\n onKeyAdded: (apiKey: NewApiKey) => void;\n disabled: boolean;\n}\n\nfunction isValidInterval(value: string): boolean {\n if (!value) {\n return true;\n }\n try {\n rangeUtil.intervalToSeconds(value);\n return true;\n } catch {}\n return false;\n}\n\nconst timeRangeValidationEvents: ValidationEvents = {\n [EventsWithValidation.onBlur]: [\n {\n rule: isValidInterval,\n errorMessage: 'Not a valid duration',\n },\n ],\n};\n\nconst tooltipText =\n 'The API key life duration. For example, 1d if your key is going to last for one day. Supported units are: s,m,h,d,w,M,y';\n\nexport const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded, disabled }) => {\n const [name, setName] = useState<string>('');\n const [role, setRole] = useState<OrgRole>(OrgRole.Viewer);\n const [secondsToLive, setSecondsToLive] = useState<string>('');\n useEffect(() => {\n setName('');\n setRole(OrgRole.Viewer);\n setSecondsToLive('');\n }, [show]);\n\n const onSubmit = (event: FormEvent) => {\n event.preventDefault();\n if (isValidInterval(secondsToLive)) {\n onKeyAdded({ name, role, secondsToLive });\n onClose();\n }\n };\n const onNameChange = (event: ChangeEvent<HTMLInputElement>) => {\n setName(event.currentTarget.value);\n };\n const onRoleChange = (role: SelectableValue<OrgRole>) => {\n setRole(role.value!);\n };\n const onSecondsToLiveChange = (event: ChangeEvent<HTMLInputElement>) => {\n setSecondsToLive(event.currentTarget.value);\n };\n\n return (\n <SlideDown in={show}>\n <div className=\"gf-form-inline cta-form\">\n <CloseButton onClick={onClose} />\n <form className=\"gf-form-group\" onSubmit={onSubmit}>\n <h5>Add API Key</h5>\n <div className=\"gf-form-inline\">\n <div className=\"gf-form max-width-21\">\n <span className=\"gf-form-label\">Key name</span>\n <Input type=\"text\" className=\"gf-form-input\" value={name} placeholder=\"Name\" onChange={onNameChange} />\n </div>\n <div className=\"gf-form\">\n <InlineField label=\"Role\">\n <Select inputId=\"role-select\" value={role} onChange={onRoleChange} options={ROLE_OPTIONS} />\n </InlineField>\n </div>\n <div className=\"gf-form max-width-21\">\n <InlineField tooltip={tooltipText} label=\"Time to live\">\n <Input\n id=\"time-to-live-input\"\n type=\"text\"\n placeholder=\"1d\"\n validationEvents={timeRangeValidationEvents}\n value={secondsToLive}\n onChange={onSecondsToLiveChange}\n />\n </InlineField>\n </div>\n <div className=\"gf-form\">\n <Button type=\"submit\" disabled={disabled}>\n Add\n </Button>\n </div>\n </div>\n </form>\n </div>\n </SlideDown>\n );\n};\n","import { css } from '@emotion/css';\nimport React, { FC } from 'react';\n\nimport { dateTimeFormat, GrafanaTheme2, TimeZone } from '@grafana/data';\nimport { DeleteButton, Icon, IconName, Tooltip, useTheme2 } from '@grafana/ui';\nimport { contextSrv } from 'app/core/core';\nimport { AccessControlAction } from 'app/types';\n\nimport { ApiKey } from '../../types';\n\ninterface Props {\n apiKeys: ApiKey[];\n timeZone: TimeZone;\n onDelete: (apiKey: ApiKey) => void;\n}\n\nexport const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete }) => {\n const theme = useTheme2();\n const styles = getStyles(theme);\n\n return (\n <table className=\"filter-table\">\n <thead>\n <tr>\n <th>Name</th>\n <th>Role</th>\n <th>Expires</th>\n <th style={{ width: '34px' }} />\n </tr>\n </thead>\n {apiKeys.length > 0 ? (\n <tbody>\n {apiKeys.map((key) => {\n const isExpired = Boolean(key.expiration && Date.now() > new Date(key.expiration).getTime());\n return (\n <tr key={key.id} className={styles.tableRow(isExpired)}>\n <td>{key.name}</td>\n <td>{key.role}</td>\n <td>\n {formatDate(key.expiration, timeZone)}\n {isExpired && (\n <span className={styles.tooltipContainer}>\n <Tooltip content=\"This API key has expired.\">\n <Icon name={'exclamation-triangle' as IconName} />\n </Tooltip>\n </span>\n )}\n </td>\n <td>\n <DeleteButton\n aria-label=\"Delete API key\"\n size=\"sm\"\n onConfirm={() => onDelete(key)}\n disabled={!contextSrv.hasPermissionInMetadata(AccessControlAction.ActionAPIKeysDelete, key)}\n />\n </td>\n </tr>\n );\n })}\n </tbody>\n ) : null}\n </table>\n );\n};\n\nfunction formatDate(expiration: string | undefined, timeZone: TimeZone): string {\n if (!expiration) {\n return 'No expiration date';\n }\n return dateTimeFormat(expiration, { timeZone });\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n tableRow: (isExpired: boolean) => css`\n color: ${isExpired ? theme.colors.text.secondary : theme.colors.text.primary};\n `,\n tooltipContainer: css`\n margin-left: ${theme.spacing(1)};\n `,\n});\n","import { getBackendSrv } from 'app/core/services/backend_srv';\nimport { ApiKey, ThunkResult } from 'app/types';\n\nimport { apiKeysLoaded, includeExpiredToggled, isFetching, setSearchQuery } from './reducers';\n\nexport function addApiKey(apiKey: ApiKey, openModal: (key: string) => void): ThunkResult<void> {\n return async (dispatch) => {\n const result = await getBackendSrv().post('/api/auth/keys', apiKey);\n dispatch(setSearchQuery(''));\n dispatch(loadApiKeys());\n openModal(result.key);\n };\n}\n\nexport function loadApiKeys(): ThunkResult<void> {\n return async (dispatch) => {\n dispatch(isFetching());\n const [keys, keysIncludingExpired] = await Promise.all([\n getBackendSrv().get('/api/auth/keys?includeExpired=false&accesscontrol=true'),\n getBackendSrv().get('/api/auth/keys?includeExpired=true&accesscontrol=true'),\n ]);\n dispatch(apiKeysLoaded({ keys, keysIncludingExpired }));\n };\n}\n\nexport function deleteApiKey(id: number): ThunkResult<void> {\n return async (dispatch) => {\n getBackendSrv()\n .delete(`/api/auth/keys/${id}`)\n .then(() => dispatch(loadApiKeys()));\n };\n}\n\nexport function toggleIncludeExpired(): ThunkResult<void> {\n return (dispatch) => {\n dispatch(includeExpiredToggled());\n };\n}\n","import { ApiKeysState } from 'app/types';\n\nexport const getApiKeysCount = (state: ApiKeysState) =>\n state.includeExpired ? state.keysIncludingExpired.length : state.keys.length;\n\nexport const getApiKeys = (state: ApiKeysState) => {\n const regex = RegExp(state.searchQuery, 'i');\n const keysToFilter = state.includeExpired ? state.keysIncludingExpired : state.keys;\n\n return keysToFilter.filter((key) => {\n return regex.test(key.name) || regex.test(key.role);\n });\n};\n\nexport const getIncludeExpired = (state: ApiKeysState) => state.includeExpired;\n\nexport const getIncludeExpiredDisabled = (state: ApiKeysState) =>\n state.keys.length === 0 && state.keysIncludingExpired.length > 0;\n","import React, { PureComponent } from 'react';\nimport { connect, ConnectedProps } from 'react-redux';\n\n// Utils\nimport { rangeUtil } from '@grafana/data';\nimport { InlineField, InlineSwitch, VerticalGroup } from '@grafana/ui';\nimport appEvents from 'app/core/app_events';\nimport EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';\nimport Page from 'app/core/components/Page/Page';\nimport config from 'app/core/config';\nimport { contextSrv } from 'app/core/core';\nimport { getNavModel } from 'app/core/selectors/navModel';\nimport { getTimeZone } from 'app/features/profile/state/selectors';\nimport { AccessControlAction, ApiKey, NewApiKey, StoreState } from 'app/types';\nimport { ShowModalReactEvent } from 'app/types/events';\n\nimport { ApiKeysActionBar } from './ApiKeysActionBar';\nimport { ApiKeysAddedModal } from './ApiKeysAddedModal';\nimport { ApiKeysController } from './ApiKeysController';\nimport { ApiKeysForm } from './ApiKeysForm';\nimport { ApiKeysTable } from './ApiKeysTable';\nimport { addApiKey, deleteApiKey, loadApiKeys, toggleIncludeExpired } from './state/actions';\nimport { setSearchQuery } from './state/reducers';\nimport { getApiKeys, getApiKeysCount, getIncludeExpired, getIncludeExpiredDisabled } from './state/selectors';\n\nfunction mapStateToProps(state: StoreState) {\n const canCreate = contextSrv.hasAccess(AccessControlAction.ActionAPIKeysCreate, true);\n\n return {\n navModel: getNavModel(state.navIndex, 'apikeys'),\n apiKeys: getApiKeys(state.apiKeys),\n searchQuery: state.apiKeys.searchQuery,\n apiKeysCount: getApiKeysCount(state.apiKeys),\n hasFetched: state.apiKeys.hasFetched,\n timeZone: getTimeZone(state.user),\n includeExpired: getIncludeExpired(state.apiKeys),\n includeExpiredDisabled: getIncludeExpiredDisabled(state.apiKeys),\n canCreate: canCreate,\n };\n}\n\nconst mapDispatchToProps = {\n loadApiKeys,\n deleteApiKey,\n setSearchQuery,\n toggleIncludeExpired,\n addApiKey,\n};\n\nconst connector = connect(mapStateToProps, mapDispatchToProps);\n\ninterface OwnProps {}\n\nexport type Props = OwnProps & ConnectedProps<typeof connector>;\n\ninterface State {\n isAdding: boolean;\n}\n\nexport class ApiKeysPageUnconnected extends PureComponent<Props, State> {\n constructor(props: Props) {\n super(props);\n }\n\n componentDidMount() {\n this.fetchApiKeys();\n }\n\n async fetchApiKeys() {\n await this.props.loadApiKeys();\n }\n\n onDeleteApiKey = (key: ApiKey) => {\n this.props.deleteApiKey(key.id!);\n };\n\n onSearchQueryChange = (value: string) => {\n this.props.setSearchQuery(value);\n };\n\n onIncludeExpiredChange = (event: React.SyntheticEvent<HTMLInputElement>) => {\n this.props.toggleIncludeExpired();\n };\n\n onAddApiKey = (newApiKey: NewApiKey) => {\n const openModal = (apiKey: string) => {\n const rootPath = window.location.origin + config.appSubUrl;\n\n appEvents.publish(\n new ShowModalReactEvent({\n props: {\n apiKey,\n rootPath,\n },\n component: ApiKeysAddedModal,\n })\n );\n };\n\n const secondsToLive = newApiKey.secondsToLive;\n try {\n const secondsToLiveAsNumber = secondsToLive ? rangeUtil.intervalToSeconds(secondsToLive) : null;\n const apiKey: ApiKey = {\n ...newApiKey,\n secondsToLive: secondsToLiveAsNumber,\n };\n this.props.addApiKey(apiKey, openModal);\n this.setState((prevState: State) => {\n return {\n ...prevState,\n isAdding: false,\n };\n });\n } catch (err) {\n console.error(err);\n }\n };\n\n render() {\n const {\n hasFetched,\n navModel,\n apiKeysCount,\n apiKeys,\n searchQuery,\n timeZone,\n includeExpired,\n includeExpiredDisabled,\n canCreate,\n } = this.props;\n\n if (!hasFetched) {\n return (\n <Page navModel={navModel}>\n <Page.Contents isLoading={true}>{}</Page.Contents>\n </Page>\n );\n }\n\n return (\n <Page navModel={navModel}>\n <Page.Contents isLoading={false}>\n <ApiKeysController>\n {({ isAdding, toggleIsAdding }) => {\n const showCTA = !isAdding && apiKeysCount === 0;\n const showTable = apiKeysCount > 0;\n return (\n <>\n {showCTA ? (\n <EmptyListCTA\n title=\"You haven't added any API keys yet.\"\n buttonIcon=\"key-skeleton-alt\"\n onClick={toggleIsAdding}\n buttonTitle=\"New API key\"\n proTip=\"Remember, you can provide view-only API access to other applications.\"\n buttonDisabled={!canCreate}\n />\n ) : null}\n {showTable ? (\n <ApiKeysActionBar\n searchQuery={searchQuery}\n disabled={isAdding || !canCreate}\n onAddClick={toggleIsAdding}\n onSearchChange={this.onSearchQueryChange}\n />\n ) : null}\n <ApiKeysForm\n show={isAdding}\n onClose={toggleIsAdding}\n onKeyAdded={this.onAddApiKey}\n disabled={!canCreate}\n />\n {showTable ? (\n <VerticalGroup>\n <InlineField disabled={includeExpiredDisabled} label=\"Include expired keys\">\n <InlineSwitch id=\"showExpired\" value={includeExpired} onChange={this.onIncludeExpiredChange} />\n </InlineField>\n <ApiKeysTable apiKeys={apiKeys} timeZone={timeZone} onDelete={this.onDeleteApiKey} />\n </VerticalGroup>\n ) : null}\n </>\n );\n }}\n </ApiKeysController>\n </Page.Contents>\n </Page>\n );\n }\n}\n\nconst ApiKeysPage = connector(ApiKeysPageUnconnected);\nexport default ApiKeysPage;\n"],"names":["ApiKeysActionBar","searchQuery","disabled","onAddClick","onSearchChange","className","FilterInput","placeholder","value","onChange","Button","onClick","ApiKeysAddedModal","onDismiss","apiKey","rootPath","styles","useStyles2","getStyles","getClipboardText","useCallback","Modal","title","onClickBackdrop","isOpen","Field","label","Input","id","readOnly","addonAfter","ClipboardButton","variant","getText","onClipboardCopy","dispatch","notifyApp","createSuccessNotification","Icon","name","Alert","severity","small","theme","css","spacing","colors","background","secondary","shape","borderRadius","typography","bodySmall","fontSize","fontWeight","ApiKeysController","children","isAdding","setIsAdding","useState","toggleIsAdding","LegacyForms","ROLE_OPTIONS","Object","keys","OrgRole","map","role","isValidInterval","rangeUtil","timeRangeValidationEvents","EventsWithValidation","rule","errorMessage","ApiKeysForm","show","onClose","onKeyAdded","setName","setRole","secondsToLive","setSecondsToLive","useEffect","SlideDown","in","CloseButton","onSubmit","event","preventDefault","type","currentTarget","InlineField","Select","inputId","options","tooltip","validationEvents","ApiKeysTable","apiKeys","timeZone","onDelete","useTheme2","style","width","length","key","isExpired","Boolean","expiration","Date","now","getTime","tableRow","formatDate","tooltipContainer","Tooltip","content","DeleteButton","size","onConfirm","contextSrv","AccessControlAction","dateTimeFormat","text","primary","loadApiKeys","async","isFetching","keysIncludingExpired","Promise","all","getBackendSrv","get","apiKeysLoaded","getApiKeysCount","state","includeExpired","getApiKeys","regex","RegExp","filter","test","getIncludeExpired","getIncludeExpiredDisabled","mapDispatchToProps","deleteApiKey","delete","then","setSearchQuery","toggleIncludeExpired","includeExpiredToggled","addApiKey","openModal","result","post","connector","connect","canCreate","navModel","getNavModel","navIndex","apiKeysCount","hasFetched","getTimeZone","user","includeExpiredDisabled","ApiKeysPageUnconnected","PureComponent","constructor","props","super","this","newApiKey","window","location","origin","config","appEvents","ShowModalReactEvent","component","secondsToLiveAsNumber","setState","prevState","err","console","error","componentDidMount","fetchApiKeys","render","Page","isLoading","showCTA","showTable","EmptyListCTA","buttonIcon","buttonTitle","proTip","buttonDisabled","onSearchQueryChange","onAddApiKey","VerticalGroup","InlineSwitch","onIncludeExpiredChange","onDeleteApiKey"],"sourceRoot":""}
|