mirror of
https://github.com/grafana/grafana.git
synced 2025-01-24 15:27:01 -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:
|
||||
return response.Error(http.StatusInternalServerError, "Failed to create service account", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusCreated, user)
|
||||
result := models.UserIdDTO{
|
||||
Message: "Service account created",
|
||||
Id: user.Id,
|
||||
}
|
||||
return response.JSON(http.StatusCreated, result)
|
||||
}
|
||||
|
||||
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) {
|
||||
// create a new service account - "user" with empty permissions
|
||||
generatedLogin := "Service-Account-" + uuid.New().String()
|
||||
cmd := models.CreateUserCommand{
|
||||
Login: "Service-Account-" + uuid.New().String(),
|
||||
Name: sa.Name + "-Service-Account-" + uuid.New().String(),
|
||||
Login: generatedLogin,
|
||||
Name: sa.Name,
|
||||
OrgId: sa.OrgID,
|
||||
IsServiceAccount: true,
|
||||
}
|
||||
|
@ -19,8 +19,6 @@ type ServiceAccount struct {
|
||||
}
|
||||
|
||||
type CreateServiceaccountForm struct {
|
||||
OrgID int64 `json:"-"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
OrgID int64 `json:"-"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
}
|
||||
|
@ -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 (
|
||||
<>
|
||||
<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">
|
||||
<table className="filter-table form-inline">
|
||||
|
@ -1,15 +1,16 @@
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { LinkButton, useStyles2 } from '@grafana/ui';
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
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 { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getServiceAccounts, getServiceAccountsSearchPage, getServiceAccountsSearchQuery } from './state/selectors';
|
||||
import PageLoader from 'app/core/components/PageLoader/PageLoader';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
export type Props = ConnectedProps<typeof connector>;
|
||||
|
||||
export interface State {}
|
||||
@ -41,6 +42,14 @@ const ServiceAccountsListPage: React.FC<Props> = ({ loadServiceAccounts, navMode
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<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 ? (
|
||||
<PageLoader />
|
||||
) : (
|
||||
@ -50,7 +59,7 @@ const ServiceAccountsListPage: React.FC<Props> = ({ loadServiceAccounts, navMode
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Account</th>
|
||||
<th>Display name</th>
|
||||
<th>ID</th>
|
||||
<th>Roles</th>
|
||||
<th>Tokens</th>
|
||||
@ -97,20 +106,20 @@ const ServiceAccountListItem = memo(({ serviceaccount }: ServiceAccountListItemP
|
||||
<a
|
||||
className="ellipsis"
|
||||
href={editUrl}
|
||||
title={serviceaccount.login}
|
||||
title={serviceaccount.name}
|
||||
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
||||
>
|
||||
{serviceaccount.login}
|
||||
{serviceaccount.name}
|
||||
</a>
|
||||
</td>
|
||||
<td className="link-td max-width-10">
|
||||
<a
|
||||
className="ellipsis"
|
||||
href={editUrl}
|
||||
title={serviceaccount.name}
|
||||
title={serviceaccount.login}
|
||||
aria-label={getServiceAccountsAriaLabel(serviceaccount.name)}
|
||||
>
|
||||
{serviceaccount.name}
|
||||
{serviceaccount.login}
|
||||
</a>
|
||||
</td>
|
||||
<td className={cx('link-td', styles.iconRow)}>
|
||||
|
@ -201,6 +201,15 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
import(/* webpackChunkName: "ServiceAccountsPage" */ 'app/features/serviceaccounts/ServiceAccountsListPage')
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/org/serviceaccounts/create',
|
||||
component: SafeDynamicImport(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "ServiceAccountCreatePage" */ 'app/features/serviceaccounts/ServiceAccountCreatePage'
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
path: '/org/serviceaccounts/:id',
|
||||
component: ServiceAccountPage,
|
||||
|
@ -25,6 +25,8 @@ export enum AccessControlAction {
|
||||
UsersQuotasList = 'users.quotas:list',
|
||||
UsersQuotasUpdate = 'users.quotas:update',
|
||||
|
||||
ServiceAccountsCreate = 'serviceaccounts:create',
|
||||
|
||||
OrgsRead = 'orgs:read',
|
||||
OrgsPreferencesRead = 'orgs.preferences:read',
|
||||
OrgsWrite = 'orgs:write',
|
||||
|
Loading…
Reference in New Issue
Block a user