mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat: creation of service account (#44913)
* feat: creation of service account * added back button for the details page * refactor: remove unused file, renamed fields * oppps
This commit is contained in:
parent
a8e9369084
commit
334c29eebf
@ -71,8 +71,11 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *models.ReqContext) respon
|
|||||||
case err != nil:
|
case err != nil:
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to create service account", err)
|
return response.Error(http.StatusInternalServerError, "Failed to create service account", err)
|
||||||
}
|
}
|
||||||
|
result := models.UserIdDTO{
|
||||||
return response.JSON(http.StatusCreated, user)
|
Message: "Service account created",
|
||||||
|
Id: user.Id,
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusCreated, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ServiceAccountsAPI) DeleteServiceAccount(ctx *models.ReqContext) response.Response {
|
func (api *ServiceAccountsAPI) DeleteServiceAccount(ctx *models.ReqContext) response.Response {
|
||||||
|
@ -27,9 +27,10 @@ func NewServiceAccountsStore(store *sqlstore.SQLStore) *ServiceAccountsStoreImpl
|
|||||||
|
|
||||||
func (s *ServiceAccountsStoreImpl) CreateServiceAccount(ctx context.Context, sa *serviceaccounts.CreateServiceaccountForm) (user *models.User, err error) {
|
func (s *ServiceAccountsStoreImpl) CreateServiceAccount(ctx context.Context, sa *serviceaccounts.CreateServiceaccountForm) (user *models.User, err error) {
|
||||||
// create a new service account - "user" with empty permissions
|
// create a new service account - "user" with empty permissions
|
||||||
|
generatedLogin := "Service-Account-" + uuid.New().String()
|
||||||
cmd := models.CreateUserCommand{
|
cmd := models.CreateUserCommand{
|
||||||
Login: "Service-Account-" + uuid.New().String(),
|
Login: generatedLogin,
|
||||||
Name: sa.Name + "-Service-Account-" + uuid.New().String(),
|
Name: sa.Name,
|
||||||
OrgId: sa.OrgID,
|
OrgId: sa.OrgID,
|
||||||
IsServiceAccount: true,
|
IsServiceAccount: true,
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,6 @@ type ServiceAccount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreateServiceaccountForm struct {
|
type CreateServiceaccountForm struct {
|
||||||
OrgID int64 `json:"-"`
|
OrgID int64 `json:"-"`
|
||||||
Name string `json:"name" binding:"Required"`
|
Name string `json:"name" binding:"Required"`
|
||||||
DisplayName string `json:"displayName"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { Form, Button, Input, Field } from '@grafana/ui';
|
||||||
|
import { NavModel } from '@grafana/data';
|
||||||
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
|
import { StoreState } from '../../types';
|
||||||
|
import { getNavModel } from '../../core/selectors/navModel';
|
||||||
|
import Page from 'app/core/components/Page/Page';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface ServiceAccountCreatePageProps {
|
||||||
|
navModel: NavModel;
|
||||||
|
}
|
||||||
|
interface ServiceAccountDTO {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createServiceAccount = async (sa: ServiceAccountDTO) => getBackendSrv().post('/api/serviceaccounts/', sa);
|
||||||
|
|
||||||
|
const ServiceAccountCreatePage: React.FC<ServiceAccountCreatePageProps> = ({ navModel }) => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const onSubmit = useCallback(
|
||||||
|
async (data: ServiceAccountDTO) => {
|
||||||
|
await createServiceAccount(data);
|
||||||
|
history.push('/org/serviceaccounts/');
|
||||||
|
},
|
||||||
|
[history]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page navModel={navModel}>
|
||||||
|
<Page.Contents>
|
||||||
|
<h1>Add new service account</h1>
|
||||||
|
<Form onSubmit={onSubmit} validateOn="onBlur">
|
||||||
|
{({ register, errors }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Field
|
||||||
|
label="Display name"
|
||||||
|
required
|
||||||
|
invalid={!!errors.name}
|
||||||
|
error={errors.name ? 'Display name is required' : undefined}
|
||||||
|
>
|
||||||
|
<Input id="display-name-input" {...register('name', { required: true })} />
|
||||||
|
</Field>
|
||||||
|
<Button type="submit">Create Service account</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form>
|
||||||
|
</Page.Contents>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state: StoreState) => ({
|
||||||
|
navModel: getNavModel(state.navIndex, 'serviceaccounts'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(ServiceAccountCreatePage);
|
@ -58,6 +58,9 @@ export function ServiceAccountProfile({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3 className="page-heading">Service account information</h3>
|
<h3 className="page-heading">Service account information</h3>
|
||||||
|
<a href="org/serviceaccounts">
|
||||||
|
<Button variant="link" icon="backward" />
|
||||||
|
</a>
|
||||||
<div className="gf-form-group">
|
<div className="gf-form-group">
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<table className="filter-table form-inline">
|
<table className="filter-table form-inline">
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import React, { memo, useEffect } from 'react';
|
import React, { memo, useEffect } from 'react';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import { useStyles2 } from '@grafana/ui';
|
import { LinkButton, useStyles2 } from '@grafana/ui';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
|
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { StoreState, ServiceAccountDTO } from 'app/types';
|
import { StoreState, ServiceAccountDTO, AccessControlAction } from 'app/types';
|
||||||
import { loadServiceAccounts, removeServiceAccount, updateServiceAccount } from './state/actions';
|
import { loadServiceAccounts, removeServiceAccount, updateServiceAccount } from './state/actions';
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
import { getServiceAccounts, getServiceAccountsSearchPage, getServiceAccountsSearchQuery } from './state/selectors';
|
import { getServiceAccounts, getServiceAccountsSearchPage, getServiceAccountsSearchQuery } from './state/selectors';
|
||||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { contextSrv } from 'app/core/core';
|
||||||
export type Props = ConnectedProps<typeof connector>;
|
export type Props = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
export interface State {}
|
export interface State {}
|
||||||
@ -41,6 +42,14 @@ const ServiceAccountsListPage: React.FC<Props> = ({ loadServiceAccounts, navMode
|
|||||||
return (
|
return (
|
||||||
<Page navModel={navModel}>
|
<Page navModel={navModel}>
|
||||||
<Page.Contents>
|
<Page.Contents>
|
||||||
|
<h2>Service accounts</h2>
|
||||||
|
<div className="page-action-bar" style={{ justifyContent: 'flex-end' }}>
|
||||||
|
{contextSrv.hasPermission(AccessControlAction.ServiceAccountsCreate) && (
|
||||||
|
<LinkButton href="org/serviceaccounts/create" variant="primary">
|
||||||
|
New service account
|
||||||
|
</LinkButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<PageLoader />
|
<PageLoader />
|
||||||
) : (
|
) : (
|
||||||
@ -50,7 +59,7 @@ const ServiceAccountsListPage: React.FC<Props> = ({ loadServiceAccounts, navMode
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>Account</th>
|
<th>Display name</th>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Roles</th>
|
<th>Roles</th>
|
||||||
<th>Tokens</th>
|
<th>Tokens</th>
|
||||||
@ -97,20 +106,20 @@ const ServiceAccountListItem = memo(({ serviceaccount }: ServiceAccountListItemP
|
|||||||
<a
|
<a
|
||||||
className="ellipsis"
|
className="ellipsis"
|
||||||
href={editUrl}
|
href={editUrl}
|
||||||
title={serviceaccount.login}
|
title={serviceaccount.name}
|
||||||
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
||||||
>
|
>
|
||||||
{serviceaccount.login}
|
{serviceaccount.name}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td className="link-td max-width-10">
|
<td className="link-td max-width-10">
|
||||||
<a
|
<a
|
||||||
className="ellipsis"
|
className="ellipsis"
|
||||||
href={editUrl}
|
href={editUrl}
|
||||||
title={serviceaccount.name}
|
title={serviceaccount.login}
|
||||||
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
||||||
>
|
>
|
||||||
{serviceaccount.name}
|
{serviceaccount.login}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td className={cx('link-td', styles.iconRow)}>
|
<td className={cx('link-td', styles.iconRow)}>
|
||||||
|
@ -201,6 +201,15 @@ export function getAppRoutes(): RouteDescriptor[] {
|
|||||||
import(/* webpackChunkName: "ServiceAccountsPage" */ 'app/features/serviceaccounts/ServiceAccountsListPage')
|
import(/* webpackChunkName: "ServiceAccountsPage" */ 'app/features/serviceaccounts/ServiceAccountsListPage')
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/org/serviceaccounts/create',
|
||||||
|
component: SafeDynamicImport(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "ServiceAccountCreatePage" */ 'app/features/serviceaccounts/ServiceAccountCreatePage'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/org/serviceaccounts/:id',
|
path: '/org/serviceaccounts/:id',
|
||||||
component: ServiceAccountPage,
|
component: ServiceAccountPage,
|
||||||
|
@ -25,6 +25,8 @@ export enum AccessControlAction {
|
|||||||
UsersQuotasList = 'users.quotas:list',
|
UsersQuotasList = 'users.quotas:list',
|
||||||
UsersQuotasUpdate = 'users.quotas:update',
|
UsersQuotasUpdate = 'users.quotas:update',
|
||||||
|
|
||||||
|
ServiceAccountsCreate = 'serviceaccounts:create',
|
||||||
|
|
||||||
OrgsRead = 'orgs:read',
|
OrgsRead = 'orgs:read',
|
||||||
OrgsPreferencesRead = 'orgs.preferences:read',
|
OrgsPreferencesRead = 'orgs.preferences:read',
|
||||||
OrgsWrite = 'orgs:write',
|
OrgsWrite = 'orgs:write',
|
||||||
|
Loading…
Reference in New Issue
Block a user