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
pull/43519/head^2
Eric Leijonmarck 3 years ago committed by GitHub
parent c1a9a36bd2
commit 6409e761b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      pkg/models/org_user.go
  2. 9
      pkg/services/serviceaccounts/api/api.go
  3. 9
      pkg/services/serviceaccounts/database/database.go
  4. 1
      pkg/services/serviceaccounts/serviceaccounts.go
  5. 6
      pkg/services/serviceaccounts/tests/common.go
  6. 4
      pkg/services/sqlstore/org_users.go
  7. 1
      public/app/features/serviceaccounts/ServiceAccountsListPage.tsx
  8. 1
      public/app/features/serviceaccounts/ServiceAccountsTable.tsx
  9. 35
      public/app/types/serviceaccount.ts

@ -107,18 +107,20 @@ type UpdateOrgUserCommand struct {
// QUERIES
type GetOrgUsersQuery struct {
OrgId int64
Query string
Limit int
OrgId int64
Query string
Limit int
IsServiceAccount bool
Result []*OrgUserDTO
}
type SearchOrgUsersQuery struct {
OrgID int64
Query string
Page int
Limit int
OrgID int64
Query string
Page int
Limit int
IsServiceAccount bool
Result SearchOrgUsersQueryResult
}

@ -44,6 +44,7 @@ func (api *ServiceAccountsAPI) RegisterAPIEndpoints(
}
auth := acmiddleware.Middleware(api.accesscontrol)
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.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))
@ -83,3 +84,11 @@ func (api *ServiceAccountsAPI) UpgradeServiceAccounts(ctx *models.ReqContext) re
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
}
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 {
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
UpgradeServiceAccounts(ctx context.Context) error
}

@ -57,6 +57,7 @@ var _ serviceaccounts.Store = new(ServiceAccountsStoreMock)
type Calls struct {
CreateServiceAccount []interface{}
ListServiceAccounts []interface{}
DeleteServiceAccount []interface{}
UpgradeServiceAccounts []interface{}
}
@ -81,3 +82,8 @@ func (s *ServiceAccountsStoreMock) UpgradeServiceAccounts(ctx context.Context) e
s.Calls.DeleteServiceAccount = append(s.Calls.UpgradeServiceAccounts, []interface{}{ctx})
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
// 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 != "" {
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
// 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 != "" {
queryWithWildcards := "%" + query.Query + "%"

@ -42,6 +42,7 @@ export class ServiceAccountsListPage extends PureComponent<Props, State> {
return (
<VerticalGroup spacing="md">
<h1>Service Accounts</h1>
<ServiceAccountsTable
serviceAccounts={paginatedServiceAccounts}
onRoleChange={(role, serviceAccount) => this.onRoleChange(role, serviceAccount)}

@ -80,7 +80,6 @@ const ServiceAccountsTable: FC<Props> = (props) => {
{serviceAccount.name}
</span>
</td>
<td className="width-1">{serviceAccount.lastSeenAtAge}</td>
<td className="width-8">
{contextSrv.accessControlEnabled() ? (

@ -1,16 +1,15 @@
import { OrgRole, Unit } from '.';
import { SelectableValue } from '@grafana/data';
export interface OrgServiceAccount {
serviceAccountId: number;
avatarUrl: string;
email: string;
lastSeenAt: string;
lastSeenAtAge: string;
login: string;
name: string;
displayName: string;
orgId: number;
role: OrgRole;
serviceAccountId: number;
tokens: number[];
}
export interface ServiceAccount {
@ -20,6 +19,7 @@ export interface ServiceAccount {
login: string;
email: string;
name: string;
displayName: string;
orgId?: number;
}
@ -35,7 +35,6 @@ export interface ServiceAccountDTO {
authLabels?: string[];
avatarUrl?: string;
orgId?: number;
lastSeenAtAge?: string;
licensedRole?: string;
permissions?: string[];
teams?: Unit[];
@ -48,29 +47,3 @@ export interface ServiceAccountsState {
searchPage: number;
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;
}

Loading…
Cancel
Save