Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,632 changes: 2,210 additions & 422 deletions gen/go/authorizer/v1/admin.pb.go

Large diffs are not rendered by default.

919 changes: 883 additions & 36 deletions gen/go/authorizer/v1/admin.pb.gw.go

Large diffs are not rendered by default.

534 changes: 502 additions & 32 deletions gen/go/authorizer/v1/admin_grpc.pb.go

Large diffs are not rendered by default.

719 changes: 718 additions & 1 deletion gen/openapi/authorizer.swagger.json

Large diffs are not rendered by default.

161 changes: 161 additions & 0 deletions internal/grpcsrv/handlers/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,3 +439,164 @@ func (h *AdminHandler) FgaReset(ctx context.Context, _ *authorizerv1.FgaResetReq
}
return &authorizerv1.FgaResetResponse{Message: res.Message}, nil
}

// CreateServiceAccount delegates to service.CreateServiceAccount and returns the
// generated client secret exactly once (CreateServiceAccountResponse is the only
// admin message that carries a secret). Requires super-admin auth.
func (h *AdminHandler) CreateServiceAccount(ctx context.Context, req *authorizerv1.CreateServiceAccountRequest) (*authorizerv1.CreateServiceAccountResponse, error) {
res, _, err := h.Service.CreateServiceAccount(ctx, transport.MetaFromGRPC(ctx), &model.CreateServiceAccountRequest{
Name: req.GetName(),
Description: req.Description,
AllowedScopes: req.GetAllowedScopes(),
})
if err != nil {
return nil, err
}
return &authorizerv1.CreateServiceAccountResponse{
ServiceAccount: projectServiceAccount(res.ServiceAccount),
ClientSecret: res.ClientSecret,
}, nil
}

// UpdateServiceAccount delegates to service.UpdateServiceAccount. Optional proto
// fields map 1:1 onto the model's nullable pointers; the client secret is never
// touched. Requires super-admin auth.
func (h *AdminHandler) UpdateServiceAccount(ctx context.Context, req *authorizerv1.UpdateServiceAccountRequest) (*authorizerv1.UpdateServiceAccountResponse, error) {
res, _, err := h.Service.UpdateServiceAccount(ctx, transport.MetaFromGRPC(ctx), &model.UpdateServiceAccountRequest{
ID: req.GetId(),
Name: req.Name,
Description: req.Description,
AllowedScopes: req.GetAllowedScopes(),
IsActive: req.IsActive,
})
if err != nil {
return nil, err
}
return &authorizerv1.UpdateServiceAccountResponse{ServiceAccount: projectServiceAccount(res)}, nil
}

// DeleteServiceAccount delegates to service.DeleteServiceAccount, which cascades
// to the account's trusted issuers. Requires super-admin auth.
func (h *AdminHandler) DeleteServiceAccount(ctx context.Context, req *authorizerv1.DeleteServiceAccountRequest) (*authorizerv1.DeleteServiceAccountResponse, error) {
res, _, err := h.Service.DeleteServiceAccount(ctx, transport.MetaFromGRPC(ctx), &model.ServiceAccountRequest{
ID: req.GetId(),
})
if err != nil {
return nil, err
}
return &authorizerv1.DeleteServiceAccountResponse{Message: res.Message}, nil
}

// RotateServiceAccountSecret delegates to service.RotateServiceAccountSecret and
// returns the new client secret exactly once (reusing CreateServiceAccountResponse).
// Requires super-admin auth.
func (h *AdminHandler) RotateServiceAccountSecret(ctx context.Context, req *authorizerv1.RotateServiceAccountSecretRequest) (*authorizerv1.CreateServiceAccountResponse, error) {
res, _, err := h.Service.RotateServiceAccountSecret(ctx, transport.MetaFromGRPC(ctx), &model.ServiceAccountRequest{
ID: req.GetId(),
})
if err != nil {
return nil, err
}
return &authorizerv1.CreateServiceAccountResponse{
ServiceAccount: projectServiceAccount(res.ServiceAccount),
ClientSecret: res.ClientSecret,
}, nil
}

// GetServiceAccount delegates to service.ServiceAccount and projects the result.
// The client secret is never surfaced. Requires super-admin auth.
func (h *AdminHandler) GetServiceAccount(ctx context.Context, req *authorizerv1.GetServiceAccountRequest) (*authorizerv1.GetServiceAccountResponse, error) {
res, _, err := h.Service.ServiceAccount(ctx, transport.MetaFromGRPC(ctx), &model.ServiceAccountRequest{
ID: req.GetId(),
})
if err != nil {
return nil, err
}
return &authorizerv1.GetServiceAccountResponse{ServiceAccount: projectServiceAccount(res)}, nil
}

// ServiceAccounts delegates to service.ServiceAccounts and projects the
// paginated result. Client secrets are never surfaced. Requires super-admin auth.
func (h *AdminHandler) ServiceAccounts(ctx context.Context, req *authorizerv1.ServiceAccountsRequest) (*authorizerv1.ServiceAccountsResponse, error) {
res, _, err := h.Service.ServiceAccounts(ctx, transport.MetaFromGRPC(ctx), &model.ListServiceAccountsRequest{
Pagination: modelPaginatedRequest(req.GetPagination()),
})
if err != nil {
return nil, err
}
return projectServiceAccounts(res), nil
}

// AddTrustedIssuer delegates to service.AddTrustedIssuer. subject_claim defaults
// to "sub" in the service layer when unset. Requires super-admin auth.
func (h *AdminHandler) AddTrustedIssuer(ctx context.Context, req *authorizerv1.AddTrustedIssuerRequest) (*authorizerv1.AddTrustedIssuerResponse, error) {
res, _, err := h.Service.AddTrustedIssuer(ctx, transport.MetaFromGRPC(ctx), &model.AddTrustedIssuerRequest{
ServiceAccountID: req.GetServiceAccountId(),
Name: req.GetName(),
IssuerURL: req.GetIssuerUrl(),
KeySourceType: req.GetKeySourceType(),
JwksURL: req.JwksUrl,
ExpectedAud: req.GetExpectedAud(),
SubjectClaim: req.SubjectClaim,
IssuerType: req.GetIssuerType(),
SpiffeRefreshHintSeconds: req.SpiffeRefreshHintSeconds,
})
if err != nil {
return nil, err
}
return &authorizerv1.AddTrustedIssuerResponse{TrustedIssuer: projectTrustedIssuer(res)}, nil
}

// UpdateTrustedIssuer delegates to service.UpdateTrustedIssuer. Optional proto
// fields map 1:1 onto the model's nullable pointers. Requires super-admin auth.
func (h *AdminHandler) UpdateTrustedIssuer(ctx context.Context, req *authorizerv1.UpdateTrustedIssuerRequest) (*authorizerv1.UpdateTrustedIssuerResponse, error) {
res, _, err := h.Service.UpdateTrustedIssuer(ctx, transport.MetaFromGRPC(ctx), &model.UpdateTrustedIssuerRequest{
ID: req.GetId(),
Name: req.Name,
JwksURL: req.JwksUrl,
ExpectedAud: req.ExpectedAud,
IsActive: req.IsActive,
SpiffeRefreshHintSeconds: req.SpiffeRefreshHintSeconds,
})
if err != nil {
return nil, err
}
return &authorizerv1.UpdateTrustedIssuerResponse{TrustedIssuer: projectTrustedIssuer(res)}, nil
}

// DeleteTrustedIssuer delegates to service.DeleteTrustedIssuer. Requires
// super-admin auth.
func (h *AdminHandler) DeleteTrustedIssuer(ctx context.Context, req *authorizerv1.DeleteTrustedIssuerRequest) (*authorizerv1.DeleteTrustedIssuerResponse, error) {
res, _, err := h.Service.DeleteTrustedIssuer(ctx, transport.MetaFromGRPC(ctx), &model.TrustedIssuerRequest{
ID: req.GetId(),
})
if err != nil {
return nil, err
}
return &authorizerv1.DeleteTrustedIssuerResponse{Message: res.Message}, nil
}

// GetTrustedIssuer delegates to service.TrustedIssuer and projects the result.
// Requires super-admin auth.
func (h *AdminHandler) GetTrustedIssuer(ctx context.Context, req *authorizerv1.GetTrustedIssuerRequest) (*authorizerv1.GetTrustedIssuerResponse, error) {
res, _, err := h.Service.TrustedIssuer(ctx, transport.MetaFromGRPC(ctx), &model.TrustedIssuerRequest{
ID: req.GetId(),
})
if err != nil {
return nil, err
}
return &authorizerv1.GetTrustedIssuerResponse{TrustedIssuer: projectTrustedIssuer(res)}, nil
}

// TrustedIssuers delegates to service.TrustedIssuers and projects the paginated
// result. service_account_id is optional. Requires super-admin auth.
func (h *AdminHandler) TrustedIssuers(ctx context.Context, req *authorizerv1.TrustedIssuersRequest) (*authorizerv1.TrustedIssuersResponse, error) {
res, _, err := h.Service.TrustedIssuers(ctx, transport.MetaFromGRPC(ctx), &model.ListTrustedIssuersRequest{
ServiceAccountID: req.ServiceAccountId,
Pagination: modelPaginatedRequest(req.GetPagination()),
})
if err != nil {
return nil, err
}
return projectTrustedIssuers(res), nil
}
75 changes: 75 additions & 0 deletions internal/grpcsrv/handlers/admin_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,78 @@ func projectFgaExpandResponse(r *model.FgaExpandResponse) *authorizerv1.FgaExpan
}
return &authorizerv1.FgaExpandResponse{Tree: r.Tree}
}

// projectServiceAccount converts a single GraphQL ServiceAccount model into the
// proto message. There is deliberately NO client-secret field on the proto
// ServiceAccount: the plaintext secret is surfaced only by
// CreateServiceAccountResponse, so no get/list/update path can leak it.
func projectServiceAccount(s *model.ServiceAccount) *authorizerv1.ServiceAccount {
if s == nil {
return nil
}
return &authorizerv1.ServiceAccount{
Id: s.ID,
Name: s.Name,
Description: refs.StringValue(s.Description),
AllowedScopes: s.AllowedScopes,
IsActive: s.IsActive,
CreatedAt: refs.Int64Value(s.CreatedAt),
UpdatedAt: refs.Int64Value(s.UpdatedAt),
}
}

// projectServiceAccounts converts the GraphQL ServiceAccounts model (a page plus
// its pagination cursor) into the proto response.
func projectServiceAccounts(s *model.ServiceAccounts) *authorizerv1.ServiceAccountsResponse {
if s == nil {
return &authorizerv1.ServiceAccountsResponse{}
}
accounts := make([]*authorizerv1.ServiceAccount, 0, len(s.ServiceAccounts))
for _, item := range s.ServiceAccounts {
accounts = append(accounts, projectServiceAccount(item))
}
return &authorizerv1.ServiceAccountsResponse{
ServiceAccounts: accounts,
Pagination: projectPagination(s.Pagination),
}
}

// projectTrustedIssuer converts a single GraphQL TrustedIssuer model into the
// proto message. Optional pointer fields collapse to zero values; the issuer
// references its parent via service_account_id.
func projectTrustedIssuer(t *model.TrustedIssuer) *authorizerv1.TrustedIssuer {
if t == nil {
return nil
}
return &authorizerv1.TrustedIssuer{
Id: t.ID,
ServiceAccountId: t.ServiceAccountID,
Name: t.Name,
IssuerUrl: t.IssuerURL,
KeySourceType: t.KeySourceType,
JwksUrl: refs.StringValue(t.JwksURL),
ExpectedAud: t.ExpectedAud,
SubjectClaim: t.SubjectClaim,
IssuerType: t.IssuerType,
IsActive: t.IsActive,
SpiffeRefreshHintSeconds: refs.Int64Value(t.SpiffeRefreshHintSeconds),
CreatedAt: refs.Int64Value(t.CreatedAt),
UpdatedAt: refs.Int64Value(t.UpdatedAt),
}
}

// projectTrustedIssuers converts the GraphQL TrustedIssuers model (a page plus
// its pagination cursor) into the proto response.
func projectTrustedIssuers(t *model.TrustedIssuers) *authorizerv1.TrustedIssuersResponse {
if t == nil {
return &authorizerv1.TrustedIssuersResponse{}
}
issuers := make([]*authorizerv1.TrustedIssuer, 0, len(t.TrustedIssuers))
for _, item := range t.TrustedIssuers {
issuers = append(issuers, projectTrustedIssuer(item))
}
return &authorizerv1.TrustedIssuersResponse{
TrustedIssuers: issuers,
Pagination: projectPagination(t.Pagination),
}
}
Loading
Loading