mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
List serviceaccounts (#43672)
* Serviceaccounts: feat - tabview for serviceaccounts * WIP * feat: listing all service accounts * refactor: needed to remove showInvitees as not present in serviceaccounts * add token column in the list * add token to orgserviceaccount * Update pkg/services/serviceaccounts/api/api.go
This commit is contained in:
@@ -107,18 +107,20 @@ type UpdateOrgUserCommand struct {
|
|||||||
// QUERIES
|
// QUERIES
|
||||||
|
|
||||||
type GetOrgUsersQuery struct {
|
type GetOrgUsersQuery struct {
|
||||||
OrgId int64
|
OrgId int64
|
||||||
Query string
|
Query string
|
||||||
Limit int
|
Limit int
|
||||||
|
IsServiceAccount bool
|
||||||
|
|
||||||
Result []*OrgUserDTO
|
Result []*OrgUserDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchOrgUsersQuery struct {
|
type SearchOrgUsersQuery struct {
|
||||||
OrgID int64
|
OrgID int64
|
||||||
Query string
|
Query string
|
||||||
Page int
|
Page int
|
||||||
Limit int
|
Limit int
|
||||||
|
IsServiceAccount bool
|
||||||
|
|
||||||
Result SearchOrgUsersQueryResult
|
Result SearchOrgUsersQueryResult
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ func (api *ServiceAccountsAPI) RegisterAPIEndpoints(
|
|||||||
}
|
}
|
||||||
auth := acmiddleware.Middleware(api.accesscontrol)
|
auth := acmiddleware.Middleware(api.accesscontrol)
|
||||||
api.RouterRegister.Group("/api/serviceaccounts", func(serviceAccountsRoute routing.RouteRegister) {
|
api.RouterRegister.Group("/api/serviceaccounts", func(serviceAccountsRoute routing.RouteRegister) {
|
||||||
|
serviceAccountsRoute.Get("/", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionRead, serviceaccounts.ScopeAll)), routing.Wrap(api.ListServiceAccounts))
|
||||||
serviceAccountsRoute.Delete("/:serviceAccountId", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionDelete, serviceaccounts.ScopeID)), routing.Wrap(api.DeleteServiceAccount))
|
serviceAccountsRoute.Delete("/:serviceAccountId", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionDelete, serviceaccounts.ScopeID)), routing.Wrap(api.DeleteServiceAccount))
|
||||||
serviceAccountsRoute.Get("/upgrade", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionCreate, serviceaccounts.ScopeID)), routing.Wrap(api.UpgradeServiceAccounts))
|
serviceAccountsRoute.Get("/upgrade", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionCreate, serviceaccounts.ScopeID)), routing.Wrap(api.UpgradeServiceAccounts))
|
||||||
serviceAccountsRoute.Post("/", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionCreate, serviceaccounts.ScopeID)), routing.Wrap(api.CreateServiceAccount))
|
serviceAccountsRoute.Post("/", auth(middleware.ReqOrgAdmin, accesscontrol.EvalPermission(serviceaccounts.ActionCreate, serviceaccounts.ScopeID)), routing.Wrap(api.CreateServiceAccount))
|
||||||
@@ -83,3 +84,11 @@ func (api *ServiceAccountsAPI) UpgradeServiceAccounts(ctx *models.ReqContext) re
|
|||||||
return response.Error(500, "Internal server error", err)
|
return response.Error(500, "Internal server error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *ServiceAccountsAPI) ListServiceAccounts(ctx *models.ReqContext) response.Response {
|
||||||
|
serviceAccounts, err := api.store.ListServiceAccounts(ctx.Req.Context(), ctx.OrgId)
|
||||||
|
if err != nil {
|
||||||
|
return response.Error(http.StatusInternalServerError, "Failed to list roles", err)
|
||||||
|
}
|
||||||
|
return response.JSON(http.StatusOK, serviceAccounts)
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,3 +84,12 @@ func (s *ServiceAccountsStoreImpl) UpgradeServiceAccounts(ctx context.Context) e
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServiceAccountsStoreImpl) ListServiceAccounts(ctx context.Context, orgID int64) ([]*models.OrgUserDTO, error) {
|
||||||
|
query := models.GetOrgUsersQuery{OrgId: orgID, IsServiceAccount: true}
|
||||||
|
err := s.sqlStore.GetOrgUsers(ctx, &query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return query.Result, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type Service interface {
|
|||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
CreateServiceAccount(ctx context.Context, saForm *CreateServiceaccountForm) (*models.User, error)
|
CreateServiceAccount(ctx context.Context, saForm *CreateServiceaccountForm) (*models.User, error)
|
||||||
|
ListServiceAccounts(ctx context.Context, orgID int64) ([]*models.OrgUserDTO, error)
|
||||||
DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error
|
DeleteServiceAccount(ctx context.Context, orgID, serviceAccountID int64) error
|
||||||
UpgradeServiceAccounts(ctx context.Context) error
|
UpgradeServiceAccounts(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ var _ serviceaccounts.Store = new(ServiceAccountsStoreMock)
|
|||||||
|
|
||||||
type Calls struct {
|
type Calls struct {
|
||||||
CreateServiceAccount []interface{}
|
CreateServiceAccount []interface{}
|
||||||
|
ListServiceAccounts []interface{}
|
||||||
DeleteServiceAccount []interface{}
|
DeleteServiceAccount []interface{}
|
||||||
UpgradeServiceAccounts []interface{}
|
UpgradeServiceAccounts []interface{}
|
||||||
}
|
}
|
||||||
@@ -81,3 +82,8 @@ func (s *ServiceAccountsStoreMock) UpgradeServiceAccounts(ctx context.Context) e
|
|||||||
s.Calls.DeleteServiceAccount = append(s.Calls.UpgradeServiceAccounts, []interface{}{ctx})
|
s.Calls.DeleteServiceAccount = append(s.Calls.UpgradeServiceAccounts, []interface{}{ctx})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ServiceAccountsStoreMock) ListServiceAccounts(ctx context.Context, orgID int64) ([]*models.OrgUserDTO, error) {
|
||||||
|
s.Calls.ListServiceAccounts = append(s.Calls.ListServiceAccounts, []interface{}{ctx, orgID})
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ func (ss *SQLStore) GetOrgUsers(ctx context.Context, query *models.GetOrgUsersQu
|
|||||||
|
|
||||||
// TODO: add to chore, for cleaning up after we have created
|
// TODO: add to chore, for cleaning up after we have created
|
||||||
// service accounts table in the modelling
|
// service accounts table in the modelling
|
||||||
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = false", x.Dialect().Quote("user")))
|
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = %t", x.Dialect().Quote("user"), query.IsServiceAccount))
|
||||||
|
|
||||||
if query.Query != "" {
|
if query.Query != "" {
|
||||||
queryWithWildcards := "%" + query.Query + "%"
|
queryWithWildcards := "%" + query.Query + "%"
|
||||||
@@ -163,7 +163,7 @@ func (ss *SQLStore) SearchOrgUsers(ctx context.Context, query *models.SearchOrgU
|
|||||||
|
|
||||||
// TODO: add to chore, for cleaning up after we have created
|
// TODO: add to chore, for cleaning up after we have created
|
||||||
// service accounts table in the modelling
|
// service accounts table in the modelling
|
||||||
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = false", x.Dialect().Quote("user")))
|
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = %t", x.Dialect().Quote("user"), query.IsServiceAccount))
|
||||||
|
|
||||||
if query.Query != "" {
|
if query.Query != "" {
|
||||||
queryWithWildcards := "%" + query.Query + "%"
|
queryWithWildcards := "%" + query.Query + "%"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export class ServiceAccountsListPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalGroup spacing="md">
|
<VerticalGroup spacing="md">
|
||||||
|
<h1>Service Accounts</h1>
|
||||||
<ServiceAccountsTable
|
<ServiceAccountsTable
|
||||||
serviceAccounts={paginatedServiceAccounts}
|
serviceAccounts={paginatedServiceAccounts}
|
||||||
onRoleChange={(role, serviceAccount) => this.onRoleChange(role, serviceAccount)}
|
onRoleChange={(role, serviceAccount) => this.onRoleChange(role, serviceAccount)}
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ const ServiceAccountsTable: FC<Props> = (props) => {
|
|||||||
{serviceAccount.name}
|
{serviceAccount.name}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="width-1">{serviceAccount.lastSeenAtAge}</td>
|
|
||||||
|
|
||||||
<td className="width-8">
|
<td className="width-8">
|
||||||
{contextSrv.accessControlEnabled() ? (
|
{contextSrv.accessControlEnabled() ? (
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { OrgRole, Unit } from '.';
|
import { OrgRole, Unit } from '.';
|
||||||
import { SelectableValue } from '@grafana/data';
|
|
||||||
|
|
||||||
export interface OrgServiceAccount {
|
export interface OrgServiceAccount {
|
||||||
|
serviceAccountId: number;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
email: string;
|
email: string;
|
||||||
lastSeenAt: string;
|
|
||||||
lastSeenAtAge: string;
|
|
||||||
login: string;
|
login: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
displayName: string;
|
||||||
orgId: number;
|
orgId: number;
|
||||||
role: OrgRole;
|
role: OrgRole;
|
||||||
serviceAccountId: number;
|
tokens: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServiceAccount {
|
export interface ServiceAccount {
|
||||||
@@ -20,6 +19,7 @@ export interface ServiceAccount {
|
|||||||
login: string;
|
login: string;
|
||||||
email: string;
|
email: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
displayName: string;
|
||||||
orgId?: number;
|
orgId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,6 @@ export interface ServiceAccountDTO {
|
|||||||
authLabels?: string[];
|
authLabels?: string[];
|
||||||
avatarUrl?: string;
|
avatarUrl?: string;
|
||||||
orgId?: number;
|
orgId?: number;
|
||||||
lastSeenAtAge?: string;
|
|
||||||
licensedRole?: string;
|
licensedRole?: string;
|
||||||
permissions?: string[];
|
permissions?: string[];
|
||||||
teams?: Unit[];
|
teams?: Unit[];
|
||||||
@@ -48,29 +47,3 @@ export interface ServiceAccountsState {
|
|||||||
searchPage: number;
|
searchPage: number;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ServiceAccountSession {
|
|
||||||
id: number;
|
|
||||||
createdAt: string;
|
|
||||||
clientIp: string;
|
|
||||||
isActive: boolean;
|
|
||||||
seenAt: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ServiceAccountOrg {
|
|
||||||
name: string;
|
|
||||||
orgId: number;
|
|
||||||
role: OrgRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ServiceAccountFilter = Record<string, string | boolean | SelectableValue[]>;
|
|
||||||
export interface ServiceaccountListAdminState {
|
|
||||||
serviceaccounts: ServiceAccountDTO[];
|
|
||||||
query: string;
|
|
||||||
perPage: number;
|
|
||||||
page: number;
|
|
||||||
totalPages: number;
|
|
||||||
showPaging: boolean;
|
|
||||||
filters: ServiceAccountFilter[];
|
|
||||||
isLoading: boolean;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user