mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Update API Keys UI to adjust based on users permissions (#47802)
* Update API Keys UI to adjust based on users permissions Since API Keys support now RBAC we need to ensure that UI is adjusted based on the user permissions. * Applying PR suggestions
This commit is contained in:
parent
1588cd393a
commit
cbd2d09d70
@ -174,19 +174,15 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
DisplayName: "APIKeys writer",
|
||||
Description: "Gives access to add and delete api keys.",
|
||||
Group: "API Keys",
|
||||
Permissions: []ac.Permission{
|
||||
Permissions: ac.ConcatPermissions(apikeyReaderRole.Role.Permissions, []ac.Permission{
|
||||
{
|
||||
Action: ac.ActionAPIKeyCreate,
|
||||
},
|
||||
{
|
||||
Action: ac.ActionAPIKeyRead,
|
||||
Scope: ac.ScopeAPIKeysAll,
|
||||
},
|
||||
{
|
||||
Action: ac.ActionAPIKeyDelete,
|
||||
Scope: ac.ScopeAPIKeysAll,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
Grants: []string{string(models.ROLE_ADMIN)},
|
||||
}
|
||||
|
@ -31,11 +31,7 @@ func RegisterRoles(ac accesscontrol.AccessControl) error {
|
||||
DisplayName: "Service accounts writer",
|
||||
Description: "Create, delete, read, or query service accounts.",
|
||||
Group: "Service accounts",
|
||||
Permissions: []accesscontrol.Permission{
|
||||
{
|
||||
Action: serviceaccounts.ActionRead,
|
||||
Scope: serviceaccounts.ScopeAll,
|
||||
},
|
||||
Permissions: accesscontrol.ConcatPermissions(saReader.Role.Permissions, []accesscontrol.Permission{
|
||||
{
|
||||
Action: serviceaccounts.ActionWrite,
|
||||
Scope: serviceaccounts.ScopeAll,
|
||||
@ -47,7 +43,7 @@ func RegisterRoles(ac accesscontrol.AccessControl) error {
|
||||
Action: serviceaccounts.ActionDelete,
|
||||
Scope: serviceaccounts.ScopeAll,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
Grants: []string{string(models.ROLE_ADMIN)},
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ interface Props {
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
onKeyAdded: (apiKey: NewApiKey) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
function isValidInterval(value: string): boolean {
|
||||
@ -40,7 +41,7 @@ const timeRangeValidationEvents: ValidationEvents = {
|
||||
const tooltipText =
|
||||
'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';
|
||||
|
||||
export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
||||
export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded, disabled }) => {
|
||||
const [name, setName] = useState<string>('');
|
||||
const [role, setRole] = useState<OrgRole>(OrgRole.Viewer);
|
||||
const [secondsToLive, setSecondsToLive] = useState<string>('');
|
||||
@ -102,7 +103,7 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
||||
</InlineField>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<Button>Add</Button>
|
||||
<Button disabled={disabled}>Add</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -37,6 +37,9 @@ const setup = (propOverrides: Partial<Props>) => {
|
||||
includeExpired: false,
|
||||
includeExpiredDisabled: false,
|
||||
toggleIncludeExpired: toggleIncludeExpiredMock,
|
||||
canRead: true,
|
||||
canCreate: true,
|
||||
canDelete: true,
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
// Utils
|
||||
import { ApiKey, NewApiKey, StoreState } from 'app/types';
|
||||
import { AccessControlAction, ApiKey, NewApiKey, StoreState } from 'app/types';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getApiKeys, getApiKeysCount, getIncludeExpired, getIncludeExpiredDisabled } from './state/selectors';
|
||||
import { addApiKey, deleteApiKey, loadApiKeys, toggleIncludeExpired } from './state/actions';
|
||||
@ -19,8 +19,13 @@ import { ApiKeysActionBar } from './ApiKeysActionBar';
|
||||
import { ApiKeysTable } from './ApiKeysTable';
|
||||
import { ApiKeysController } from './ApiKeysController';
|
||||
import { ShowModalReactEvent } from 'app/types/events';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
|
||||
function mapStateToProps(state: StoreState) {
|
||||
const canRead = contextSrv.hasAccess(AccessControlAction.ActionAPIKeysRead, true);
|
||||
const canCreate = contextSrv.hasAccess(AccessControlAction.ActionAPIKeysCreate, true);
|
||||
const canDelete = contextSrv.hasAccess(AccessControlAction.ActionAPIKeysDelete, true);
|
||||
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, 'apikeys'),
|
||||
apiKeys: getApiKeys(state.apiKeys),
|
||||
@ -30,6 +35,9 @@ function mapStateToProps(state: StoreState) {
|
||||
timeZone: getTimeZone(state.user),
|
||||
includeExpired: getIncludeExpired(state.apiKeys),
|
||||
includeExpiredDisabled: getIncludeExpiredDisabled(state.apiKeys),
|
||||
canRead: canRead,
|
||||
canCreate: canCreate,
|
||||
canDelete: canDelete,
|
||||
};
|
||||
}
|
||||
|
||||
@ -120,6 +128,9 @@ export class ApiKeysPageUnconnected extends PureComponent<Props, State> {
|
||||
timeZone,
|
||||
includeExpired,
|
||||
includeExpiredDisabled,
|
||||
canRead,
|
||||
canCreate,
|
||||
canDelete,
|
||||
} = this.props;
|
||||
|
||||
if (!hasFetched) {
|
||||
@ -146,23 +157,35 @@ export class ApiKeysPageUnconnected extends PureComponent<Props, State> {
|
||||
onClick={toggleIsAdding}
|
||||
buttonTitle="New API key"
|
||||
proTip="Remember, you can provide view-only API access to other applications."
|
||||
buttonDisabled={!canCreate}
|
||||
/>
|
||||
) : null}
|
||||
{showTable ? (
|
||||
<ApiKeysActionBar
|
||||
searchQuery={searchQuery}
|
||||
disabled={isAdding}
|
||||
disabled={isAdding || !canCreate}
|
||||
onAddClick={toggleIsAdding}
|
||||
onSearchChange={this.onSearchQueryChange}
|
||||
/>
|
||||
) : null}
|
||||
<ApiKeysForm show={isAdding} onClose={toggleIsAdding} onKeyAdded={this.onAddApiKey} />
|
||||
<ApiKeysForm
|
||||
show={isAdding}
|
||||
onClose={toggleIsAdding}
|
||||
onKeyAdded={this.onAddApiKey}
|
||||
disabled={!canCreate}
|
||||
/>
|
||||
{showTable ? (
|
||||
<VerticalGroup>
|
||||
<InlineField disabled={includeExpiredDisabled} label="Include expired keys">
|
||||
<InlineSwitch id="showExpired" value={includeExpired} onChange={this.onIncludeExpiredChange} />
|
||||
</InlineField>
|
||||
<ApiKeysTable apiKeys={apiKeys} timeZone={timeZone} onDelete={this.onDeleteApiKey} />
|
||||
<ApiKeysTable
|
||||
apiKeys={apiKeys}
|
||||
timeZone={timeZone}
|
||||
onDelete={this.onDeleteApiKey}
|
||||
canRead={canRead}
|
||||
canDelete={canDelete}
|
||||
/>
|
||||
</VerticalGroup>
|
||||
) : null}
|
||||
</>
|
||||
|
@ -9,9 +9,11 @@ interface Props {
|
||||
apiKeys: ApiKey[];
|
||||
timeZone: TimeZone;
|
||||
onDelete: (apiKey: ApiKey) => void;
|
||||
canRead: boolean;
|
||||
canDelete: boolean;
|
||||
}
|
||||
|
||||
export const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete }) => {
|
||||
export const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete, canRead, canDelete }) => {
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
|
||||
@ -25,7 +27,7 @@ export const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete }) => {
|
||||
<th style={{ width: '34px' }} />
|
||||
</tr>
|
||||
</thead>
|
||||
{apiKeys.length > 0 ? (
|
||||
{canRead && apiKeys.length > 0 ? (
|
||||
<tbody>
|
||||
{apiKeys.map((key) => {
|
||||
const isExpired = Boolean(key.expiration && Date.now() > new Date(key.expiration).getTime());
|
||||
@ -44,7 +46,12 @@ export const ApiKeysTable: FC<Props> = ({ apiKeys, timeZone, onDelete }) => {
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
<DeleteButton aria-label="Delete API key" size="sm" onConfirm={() => onDelete(key)} />
|
||||
<DeleteButton
|
||||
aria-label="Delete API key"
|
||||
size="sm"
|
||||
onConfirm={() => onDelete(key)}
|
||||
disabled={!canDelete}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
@ -110,6 +110,8 @@ export enum AccessControlAction {
|
||||
AlertingNotificationsExternalRead = 'alert.notifications.external:read',
|
||||
|
||||
ActionAPIKeysRead = 'apikeys:read',
|
||||
ActionAPIKeysCreate = 'apikeys:create',
|
||||
ActionAPIKeysDelete = 'apikeys:delete',
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
|
Loading…
Reference in New Issue
Block a user