Auth: Add access token to in-proc communication and ServiceIdentity (#98926)
Use fake access token for in-proc grpc and add ServiceIdentity --------- Co-authored-by: gamab <gabriel.mabille@grafana.com> Co-authored-by: Karl Persson <23356117+kalleep@users.noreply.github.com>
This commit is contained in:
co-authored by
gamab
Karl Persson
parent
eb2d276a42
commit
437b7a565d
@@ -5,12 +5,11 @@ import (
|
||||
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -71,18 +70,11 @@ func folderTreeCollector(folderService folder.Service) legacyTupleCollector {
|
||||
ctx, span := tracer.Start(ctx, "accesscontrol.migrator.folderTreeCollector")
|
||||
defer span.End()
|
||||
|
||||
user := &user.SignedInUser{
|
||||
Login: "folder-tree-collector",
|
||||
OrgRole: "Admin",
|
||||
IsGrafanaAdmin: true,
|
||||
IsServiceAccount: true,
|
||||
Permissions: map[int64]map[string][]string{orgID: {dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll}}},
|
||||
OrgID: orgID,
|
||||
}
|
||||
ctx, ident := identity.WithServiceIdentitiy(ctx, orgID)
|
||||
|
||||
q := folder.GetFoldersQuery{
|
||||
OrgID: orgID,
|
||||
SignedInUser: user,
|
||||
SignedInUser: ident,
|
||||
}
|
||||
|
||||
folders, err := folderService.GetFolders(ctx, q)
|
||||
|
||||
@@ -22,7 +22,6 @@ func NewInProcGrpcAuthenticator() *authnlib.GrpcAuthenticator {
|
||||
// In proc grpc ID token signature verification can be skipped
|
||||
return authnlib.NewUnsafeGrpcAuthenticator(
|
||||
&authnlib.GrpcAuthenticatorConfig{},
|
||||
authnlib.WithDisableAccessTokenAuthOption(),
|
||||
authnlib.WithIDTokenAuthOption(false),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package grpcutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/types"
|
||||
)
|
||||
|
||||
type inProcExchanger struct {
|
||||
tokenResponse *authn.TokenExchangeResponse
|
||||
}
|
||||
|
||||
func ProvideInProcExchanger() *inProcExchanger {
|
||||
tokenResponse, err := createInProcToken()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &inProcExchanger{tokenResponse}
|
||||
}
|
||||
|
||||
func (e *inProcExchanger) Exchange(ctx context.Context, r authn.TokenExchangeRequest) (*authn.TokenExchangeResponse, error) {
|
||||
return e.tokenResponse, nil
|
||||
}
|
||||
|
||||
func createInProcToken() (*authn.TokenExchangeResponse, error) {
|
||||
claims := authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Audience: []string{"resourceStore"},
|
||||
Issuer: "grafana",
|
||||
Subject: types.NewTypeID(types.TypeAccessPolicy, "grafana"),
|
||||
},
|
||||
Rest: authn.AccessTokenClaims{
|
||||
Namespace: "*",
|
||||
Permissions: []string{"folder.grafana.app:*", "dashboard.grafana.app:*"},
|
||||
DelegatedPermissions: []string{"folder.grafana.app:*", "dashboard.grafana.app:*"},
|
||||
},
|
||||
}
|
||||
|
||||
header, err := json.Marshal(map[string]string{
|
||||
"alg": "none",
|
||||
"typ": authn.TokenTypeAccess,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &authn.TokenExchangeResponse{
|
||||
Token: fmt.Sprintf("%s.%s.", base64.RawURLEncoding.EncodeToString(header), base64.RawURLEncoding.EncodeToString(payload)),
|
||||
}, nil
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
@@ -54,14 +53,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
provisionerPermissions = []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersCreate, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionDashboardsWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: datasources.ActionRead, Scope: datasources.ScopeAll},
|
||||
}
|
||||
// DashboardServiceImpl implements the DashboardService interface
|
||||
_ dashboards.DashboardService = (*DashboardServiceImpl)(nil)
|
||||
_ dashboards.DashboardProvisioningService = (*DashboardServiceImpl)(nil)
|
||||
@@ -164,7 +155,7 @@ func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.Sc
|
||||
|
||||
total := int64(0)
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(org.ID))
|
||||
ctx, _ := identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
orgDashboards, err := dr.CountDashboardsInOrg(ctx, org.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -228,21 +219,6 @@ func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
||||
return limits, nil
|
||||
}
|
||||
|
||||
func getDashboardBackgroundRequester(orgId int64) *identity.StaticRequester {
|
||||
return &identity.StaticRequester{
|
||||
Type: claims.TypeServiceAccount,
|
||||
UserID: 1,
|
||||
OrgID: orgId,
|
||||
Name: "dashboard-background",
|
||||
Login: "dashboard-background",
|
||||
Permissions: map[int64]map[string][]string{
|
||||
orgId: {
|
||||
"*": {"*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context, name string) ([]*dashboards.DashboardProvisioning, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesCliDashboards) {
|
||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||
@@ -581,6 +557,7 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
ctx, _ := identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
// find all dashboards in the org that have a file repo set that is not in the given readers list
|
||||
foundDashs, err := dr.searchProvisionedDashboardsThroughK8s(ctx, dashboards.FindPersistedDashboardsQuery{
|
||||
ProvisionedReposNotIn: cmd.ReaderNames,
|
||||
@@ -592,7 +569,6 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
|
||||
|
||||
// delete them
|
||||
for _, foundDash := range foundDashs {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(org.ID))
|
||||
if err = dr.deleteDashboard(ctx, foundDash.DashboardID, foundDash.DashboardUID, org.ID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -682,8 +658,8 @@ func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dt
|
||||
dto.Dashboard.Data.Set("refresh", dr.cfg.MinRefreshInterval)
|
||||
}
|
||||
|
||||
dto.User = accesscontrol.BackgroundUser("dashboard_provisioning", dto.OrgID, org.RoleAdmin, provisionerPermissions)
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(dto.OrgID))
|
||||
ctx, ident := identity.WithServiceIdentitiy(ctx, dto.OrgID)
|
||||
dto.User = ident
|
||||
|
||||
cmd, err := dr.BuildSaveDashboardCommand(ctx, dto, false)
|
||||
if err != nil {
|
||||
@@ -722,8 +698,8 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.SaveFolderForProvisionedDashboards")
|
||||
defer span.End()
|
||||
|
||||
dto.SignedInUser = accesscontrol.BackgroundUser("dashboard_provisioning", dto.OrgID, org.RoleAdmin, provisionerPermissions)
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(dto.OrgID))
|
||||
ctx, ident := identity.WithServiceIdentitiy(ctx, dto.OrgID)
|
||||
dto.SignedInUser = ident
|
||||
|
||||
f, err := dr.folderService.Create(ctx, dto)
|
||||
if err != nil {
|
||||
@@ -867,7 +843,7 @@ func (dr *DashboardServiceImpl) GetDashboardByPublicUid(ctx context.Context, das
|
||||
|
||||
// DeleteProvisionedDashboard removes dashboard from the DB even if it is provisioned.
|
||||
func (dr *DashboardServiceImpl) DeleteProvisionedDashboard(ctx context.Context, dashboardId int64, orgId int64) error {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(orgId))
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, orgId)
|
||||
return dr.deleteDashboard(ctx, dashboardId, "", orgId, false)
|
||||
}
|
||||
|
||||
@@ -949,7 +925,7 @@ func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashbo
|
||||
}
|
||||
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(org.ID))
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
dash, err := dr.getDashboardThroughK8s(ctx, &dashboards.GetDashboardQuery{OrgID: org.ID, ID: dashboardId})
|
||||
if err != nil {
|
||||
// if we can't find it in this org, try the next one
|
||||
@@ -1720,7 +1696,7 @@ type dashboardProvisioningWithUID struct {
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx context.Context, query dashboards.FindPersistedDashboardsQuery) ([]*dashboardProvisioningWithUID, error) {
|
||||
ctx = identity.WithRequester(ctx, getDashboardBackgroundRequester(query.OrgId))
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, query.OrgId)
|
||||
|
||||
if query.ProvisionedRepo != "" {
|
||||
query.ProvisionedRepo = provisionedFileNameWithPrefix(query.ProvisionedRepo)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
@@ -45,7 +44,7 @@ type sqlStatsService struct {
|
||||
func (ss *sqlStatsService) getDashboardCount(ctx context.Context, orgs []*org.OrgDTO) (int64, error) {
|
||||
count := int64(0)
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getStatsRequester(org.ID))
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
dashsCount, err := ss.dashSvc.CountDashboardsInOrg(ctx, org.ID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -59,7 +58,7 @@ func (ss *sqlStatsService) getDashboardCount(ctx context.Context, orgs []*org.Or
|
||||
func (ss *sqlStatsService) getTagCount(ctx context.Context, orgs []*org.OrgDTO) (int64, error) {
|
||||
total := 0
|
||||
for _, org := range orgs {
|
||||
ctx = identity.WithRequester(ctx, getStatsRequester(org.ID))
|
||||
ctx, _ = identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
tags, err := ss.dashSvc.GetDashboardTags(ctx, &dashboards.GetDashboardTagsQuery{
|
||||
OrgID: org.ID,
|
||||
})
|
||||
@@ -75,11 +74,10 @@ func (ss *sqlStatsService) getTagCount(ctx context.Context, orgs []*org.OrgDTO)
|
||||
func (ss *sqlStatsService) getFolderCount(ctx context.Context, orgs []*org.OrgDTO) (int64, error) {
|
||||
total := 0
|
||||
for _, org := range orgs {
|
||||
backgroundUser := getStatsRequester(org.ID)
|
||||
ctx = identity.WithRequester(ctx, backgroundUser)
|
||||
ctx, ident := identity.WithServiceIdentitiy(ctx, org.ID)
|
||||
folders, err := ss.folderSvc.GetFolders(ctx, folder.GetFoldersQuery{
|
||||
OrgID: org.ID,
|
||||
SignedInUser: backgroundUser,
|
||||
SignedInUser: ident,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -438,18 +436,3 @@ func addToStats(base stats.UserStats, role org.RoleType, count int64) stats.User
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
func getStatsRequester(orgId int64) *identity.StaticRequester {
|
||||
return &identity.StaticRequester{
|
||||
Type: claims.TypeServiceAccount,
|
||||
UserID: 1,
|
||||
OrgID: orgId,
|
||||
Name: "stats-requester",
|
||||
Login: "stats-requester",
|
||||
Permissions: map[int64]map[string][]string{
|
||||
orgId: {
|
||||
"*": {"*"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user