mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Access control: Use ResolveIdentity() for authorizing in org (#85549)
* Access control: Use ResolveIdentity() for authorizing in org * Fix tests * Fix middleware tests * Use ResolveIdentity in HasGlobalAccess() function * remove makeTmpUser * Cleanup * Fix linter errors * Fix test build * Remove GetUserPermissionsInOrg()
This commit is contained in:
parent
ebb4bb859e
commit
3127566a20
@ -60,7 +60,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
||||||
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
||||||
authorize := ac.Middleware(hs.AccessControl)
|
authorize := ac.Middleware(hs.AccessControl)
|
||||||
authorizeInOrg := ac.AuthorizeInOrgMiddleware(hs.AccessControl, hs.accesscontrolService, hs.userService, hs.teamService)
|
authorizeInOrg := ac.AuthorizeInOrgMiddleware(hs.AccessControl, hs.authnService)
|
||||||
quota := middleware.Quota(hs.QuotaService)
|
quota := middleware.Quota(hs.QuotaService)
|
||||||
|
|
||||||
r := hs.RouteRegister
|
r := hs.RouteRegister
|
||||||
|
@ -5,6 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -136,6 +139,11 @@ func TestAPIEndpoint_UpdateOrg(t *testing.T) {
|
|||||||
ExpectedSignedInUser: &user.SignedInUser{OrgID: tt.targetOrgID},
|
ExpectedSignedInUser: &user.SignedInUser{OrgID: tt.targetOrgID},
|
||||||
}
|
}
|
||||||
hs.accesscontrolService = actest.FakeService{}
|
hs.accesscontrolService = actest.FakeService{}
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: &authn.Identity{
|
||||||
|
OrgID: tt.targetOrgID,
|
||||||
|
},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, tt.path, strings.NewReader(tt.body)), userWithPermissions(1, tt.permission))
|
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, tt.path, strings.NewReader(tt.body)), userWithPermissions(1, tt.permission))
|
||||||
@ -209,11 +217,22 @@ func TestAPIEndpoint_DeleteOrgs(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
expectedIdentity := &authn.Identity{
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
1: accesscontrol.GroupScopesByAction(tt.permission),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
||||||
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
||||||
hs.accesscontrolService = actest.FakeService{ExpectedPermissions: tt.permission}
|
hs.accesscontrolService = actest.FakeService{ExpectedPermissions: tt.permission}
|
||||||
|
hs.authnService = &authntest.FakeService{}
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: expectedIdentity,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodDelete, "/api/orgs/1", nil), userWithPermissions(2, nil))
|
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodDelete, "/api/orgs/1", nil), userWithPermissions(2, nil))
|
||||||
@ -246,11 +265,23 @@ func TestAPIEndpoint_GetOrg(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
expectedIdentity := &authn.Identity{
|
||||||
|
ID: "user:1",
|
||||||
|
OrgID: 1,
|
||||||
|
Permissions: map[int64]map[string][]string{
|
||||||
|
0: accesscontrol.GroupScopesByAction(tt.permissions),
|
||||||
|
1: accesscontrol.GroupScopesByAction(tt.permissions),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
||||||
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
||||||
hs.accesscontrolService = &actest.FakeService{ExpectedPermissions: tt.permissions}
|
hs.accesscontrolService = &actest.FakeService{ExpectedPermissions: tt.permissions}
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: expectedIdentity,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
verify := func(path string) {
|
verify := func(path string) {
|
||||||
req := webtest.RequestWithSignedInUser(server.NewGetRequest(path), authedUserWithPermissions(1, 1, tt.permissions))
|
req := webtest.RequestWithSignedInUser(server.NewGetRequest(path), authedUserWithPermissions(1, 1, tt.permissions))
|
||||||
|
@ -539,7 +539,7 @@ func (hs *HTTPServer) hasPluginRequestedPermissions(c *contextmodel.ReqContext,
|
|||||||
|
|
||||||
hs.log.Debug("check installer's permissions, plugin wants to register an external service")
|
hs.log.Debug("check installer's permissions, plugin wants to register an external service")
|
||||||
evaluator := evalAllPermissions(plugin.JSONData.IAM.Permissions)
|
evaluator := evalAllPermissions(plugin.JSONData.IAM.Permissions)
|
||||||
hasAccess := ac.HasGlobalAccess(hs.AccessControl, hs.accesscontrolService, c)
|
hasAccess := ac.HasGlobalAccess(hs.AccessControl, hs.authnService, c)
|
||||||
if hs.Cfg.RBACSingleOrganization {
|
if hs.Cfg.RBACSingleOrganization {
|
||||||
// In a single organization setup, no need for a global check
|
// In a single organization setup, no need for a global check
|
||||||
hasAccess = ac.HasAccess(hs.AccessControl, c)
|
hasAccess = ac.HasAccess(hs.AccessControl, c)
|
||||||
|
@ -12,12 +12,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||||
@ -32,6 +32,8 @@ import (
|
|||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||||
@ -94,6 +96,16 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
|
|||||||
ID: pluginID,
|
ID: pluginID,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expectedIdentity := &authn.Identity{
|
||||||
|
OrgID: tc.permissionOrg,
|
||||||
|
Permissions: map[int64]map[string][]string{},
|
||||||
|
OrgRoles: map[int64]org.RoleType{},
|
||||||
|
}
|
||||||
|
expectedIdentity.Permissions[tc.permissionOrg] = ac.GroupScopesByAction(tc.permissions)
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: expectedIdentity,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run(testName("Install", tc), func(t *testing.T) {
|
t.Run(testName("Install", tc), func(t *testing.T) {
|
||||||
@ -734,6 +746,14 @@ func TestHTTPServer_hasPluginRequestedPermissions(t *testing.T) {
|
|||||||
hs.accesscontrolService = actest.FakeService{}
|
hs.accesscontrolService = actest.FakeService{}
|
||||||
hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg)
|
hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg)
|
||||||
|
|
||||||
|
expectedIdentity := &authn.Identity{
|
||||||
|
OrgID: tt.orgID,
|
||||||
|
Permissions: tt.permissions,
|
||||||
|
}
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: expectedIdentity,
|
||||||
|
}
|
||||||
|
|
||||||
c := &contextmodel.ReqContext{
|
c := &contextmodel.ReqContext{
|
||||||
Context: &web.Context{Req: httpReq},
|
Context: &web.Context{Req: httpReq},
|
||||||
SignedInUser: &user.SignedInUser{OrgID: tt.orgID, Permissions: tt.permissions},
|
SignedInUser: &user.SignedInUser{OrgID: tt.orgID, Permissions: tt.permissions},
|
||||||
|
@ -6,6 +6,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@ -67,6 +70,9 @@ func TestAPIEndpoint_GetOrgQuotas(t *testing.T) {
|
|||||||
hs.userService = &usertest.FakeUserService{
|
hs.userService = &usertest.FakeUserService{
|
||||||
ExpectedSignedInUser: &user.SignedInUser{OrgID: 2},
|
ExpectedSignedInUser: &user.SignedInUser{OrgID: 2},
|
||||||
}
|
}
|
||||||
|
hs.authnService = &authntest.FakeService{
|
||||||
|
ExpectedIdentity: &authn.Identity{OrgID: 1},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("AccessControl allows viewing another org quotas with correct permissions", func(t *testing.T) {
|
t.Run("AccessControl allows viewing another org quotas with correct permissions", func(t *testing.T) {
|
||||||
@ -96,60 +102,88 @@ func TestAPIEndpoint_GetOrgQuotas(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIEndpoint_PutOrgQuotas(t *testing.T) {
|
func TestAPIEndpoint_PutOrgQuotas(t *testing.T) {
|
||||||
cfg := setting.NewCfg()
|
type testCase struct {
|
||||||
cfg.Quota = setting.QuotaSettings{
|
desc string
|
||||||
Enabled: true,
|
userOrg int64
|
||||||
Global: setting.GlobalQuota{
|
targetOrg int64
|
||||||
Org: 5,
|
permissions map[int64][]accesscontrol.Permission
|
||||||
|
expectedCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testCase{
|
||||||
|
{
|
||||||
|
desc: "AccessControl allows updating another org quotas with correct permissions",
|
||||||
|
userOrg: 1,
|
||||||
|
targetOrg: 2,
|
||||||
|
permissions: map[int64][]accesscontrol.Permission{2: {{Action: accesscontrol.ActionOrgsQuotasWrite}}},
|
||||||
|
expectedCode: http.StatusOK,
|
||||||
},
|
},
|
||||||
Org: setting.OrgQuota{
|
{
|
||||||
User: 5,
|
desc: "AccessControl prevents updating another org quotas with correct permissions in another org",
|
||||||
|
userOrg: 1,
|
||||||
|
targetOrg: 2,
|
||||||
|
permissions: map[int64][]accesscontrol.Permission{1: {{Action: accesscontrol.ActionOrgsQuotasWrite}}},
|
||||||
|
expectedCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
User: setting.UserQuota{
|
{
|
||||||
Org: 5,
|
desc: "AccessControl prevents updating another org quotas with incorrect permissions",
|
||||||
|
userOrg: 2,
|
||||||
|
targetOrg: 2,
|
||||||
|
permissions: map[int64][]accesscontrol.Permission{2: {{Action: "orgs:invalid"}}},
|
||||||
|
expectedCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeACService := &actest.FakeService{}
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.accesscontrolService = fakeACService
|
|
||||||
hs.userService = &usertest.FakeUserService{
|
|
||||||
ExpectedSignedInUser: &user.SignedInUser{OrgID: 2},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
input := strings.NewReader(testUpdateOrgQuotaCmd)
|
for _, tt := range tests {
|
||||||
t.Run("AccessControl allows updating another org quotas with correct permissions", func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
user := userWithPermissions(2, []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}})
|
cfg := setting.NewCfg()
|
||||||
user.OrgID = 1
|
cfg.Quota = setting.QuotaSettings{
|
||||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}}
|
Enabled: true,
|
||||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
Global: setting.GlobalQuota{
|
||||||
response, err := server.SendJSON(req)
|
Org: 5,
|
||||||
require.NoError(t, err)
|
},
|
||||||
assert.Equal(t, http.StatusOK, response.StatusCode)
|
Org: setting.OrgQuota{
|
||||||
require.NoError(t, response.Body.Close())
|
User: 5,
|
||||||
})
|
},
|
||||||
|
User: setting.UserQuota{
|
||||||
|
Org: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fakeACService := &actest.FakeService{}
|
||||||
|
input := strings.NewReader(testUpdateOrgQuotaCmd)
|
||||||
|
expectedIdentity := &authn.Identity{
|
||||||
|
OrgID: tt.userOrg,
|
||||||
|
Permissions: map[int64]map[string][]string{},
|
||||||
|
}
|
||||||
|
for orgID, permissions := range tt.permissions {
|
||||||
|
expectedIdentity.Permissions[orgID] = accesscontrol.GroupScopesByAction(permissions)
|
||||||
|
}
|
||||||
|
|
||||||
input = strings.NewReader(testUpdateOrgQuotaCmd)
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
t.Run("AccessControl prevents updating another org quotas with correct permissions in another org", func(t *testing.T) {
|
hs.Cfg = cfg
|
||||||
user := userWithPermissions(1, []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}})
|
hs.accesscontrolService = fakeACService
|
||||||
user.Permissions[2] = nil
|
hs.userService = &usertest.FakeUserService{
|
||||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{}
|
ExpectedSignedInUser: &user.SignedInUser{OrgID: tt.userOrg},
|
||||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
}
|
||||||
response, err := server.SendJSON(req)
|
hs.authnService = &authntest.FakeService{
|
||||||
require.NoError(t, err)
|
ExpectedIdentity: expectedIdentity,
|
||||||
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
}
|
||||||
require.NoError(t, response.Body.Close())
|
})
|
||||||
})
|
|
||||||
|
|
||||||
input = strings.NewReader(testUpdateOrgQuotaCmd)
|
user := userWithPermissions(tt.userOrg, getFirstOrgPermissions(tt.permissions))
|
||||||
t.Run("AccessControl prevents updating another org quotas with incorrect permissions", func(t *testing.T) {
|
fakeACService.ExpectedPermissions = []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}}
|
||||||
user := userWithPermissions(2, []accesscontrol.Permission{{Action: "orgs:invalid"}})
|
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, tt.targetOrg, "org_user"), input), user)
|
||||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{}
|
response, err := server.SendJSON(req)
|
||||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
require.NoError(t, err)
|
||||||
response, err := server.SendJSON(req)
|
assert.Equal(t, tt.expectedCode, response.StatusCode)
|
||||||
require.NoError(t, err)
|
require.NoError(t, response.Body.Close())
|
||||||
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
})
|
||||||
require.NoError(t, response.Body.Close())
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
func getFirstOrgPermissions(p map[int64][]accesscontrol.Permission) []accesscontrol.Permission {
|
||||||
|
for _, permissions := range p {
|
||||||
|
return permissions
|
||||||
|
}
|
||||||
|
return []accesscontrol.Permission{}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@ -28,8 +29,6 @@ type Service interface {
|
|||||||
GetRoleByName(ctx context.Context, orgID int64, roleName string) (*RoleDTO, error)
|
GetRoleByName(ctx context.Context, orgID int64, roleName string) (*RoleDTO, error)
|
||||||
// GetUserPermissions returns user permissions with only action and scope fields set.
|
// GetUserPermissions returns user permissions with only action and scope fields set.
|
||||||
GetUserPermissions(ctx context.Context, user identity.Requester, options Options) ([]Permission, error)
|
GetUserPermissions(ctx context.Context, user identity.Requester, options Options) ([]Permission, error)
|
||||||
// GetUserPermissionsInOrg return user permission in a specific organization
|
|
||||||
GetUserPermissionsInOrg(ctx context.Context, user identity.Requester, orgID int64) ([]Permission, error)
|
|
||||||
// SearchUsersPermissions returns all users' permissions filtered by an action prefix
|
// SearchUsersPermissions returns all users' permissions filtered by an action prefix
|
||||||
SearchUsersPermissions(ctx context.Context, user identity.Requester, options SearchOptions) (map[int64][]Permission, error)
|
SearchUsersPermissions(ctx context.Context, user identity.Requester, options SearchOptions) (map[int64][]Permission, error)
|
||||||
// ClearUserPermissionCache removes the permission cache entry for the given user
|
// ClearUserPermissionCache removes the permission cache entry for the given user
|
||||||
@ -171,22 +170,26 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasGlobalAccess checks user access with globally assigned permissions only
|
// HasGlobalAccess checks user access with globally assigned permissions only
|
||||||
func HasGlobalAccess(ac AccessControl, service Service, c *contextmodel.ReqContext) func(evaluator Evaluator) bool {
|
func HasGlobalAccess(ac AccessControl, authnService authn.Service, c *contextmodel.ReqContext) func(evaluator Evaluator) bool {
|
||||||
return func(evaluator Evaluator) bool {
|
return func(evaluator Evaluator) bool {
|
||||||
var targetOrgID int64 = GlobalOrgID
|
var targetOrgID int64 = GlobalOrgID
|
||||||
tmpUser, err := makeTmpUser(c.Req.Context(), service, nil, nil, c.SignedInUser, targetOrgID)
|
orgUser, err := authnService.ResolveIdentity(c.Req.Context(), targetOrgID, c.SignedInUser.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
deny(c, nil, fmt.Errorf("failed to authenticate user in target org: %w", err))
|
deny(c, nil, fmt.Errorf("failed to authenticate user in target org: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccess, err := ac.Evaluate(c.Req.Context(), tmpUser, evaluator)
|
hasAccess, err := ac.Evaluate(c.Req.Context(), orgUser, evaluator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger.Error("Error from access control system", "error", err)
|
c.Logger.Error("Error from access control system", "error", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// guard against nil map
|
||||||
|
if c.SignedInUser.Permissions == nil {
|
||||||
|
c.SignedInUser.Permissions = make(map[int64]map[string][]string)
|
||||||
|
}
|
||||||
// set on user so we don't fetch global permissions every time this is called
|
// set on user so we don't fetch global permissions every time this is called
|
||||||
c.SignedInUser.Permissions[tmpUser.GetOrgID()] = tmpUser.GetPermissions()
|
c.SignedInUser.Permissions[orgUser.GetOrgID()] = orgUser.GetPermissions()
|
||||||
|
|
||||||
return hasAccess
|
return hasAccess
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/migrator"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/migrator"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/pluginutils"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/authn"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
@ -161,48 +160,6 @@ func (s *Service) getCachedUserPermissions(ctx context.Context, user identity.Re
|
|||||||
return permissions, nil
|
return permissions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) GetUserPermissionsInOrg(ctx context.Context, user identity.Requester, orgID int64) ([]accesscontrol.Permission, error) {
|
|
||||||
permissions := make([]accesscontrol.Permission, 0)
|
|
||||||
|
|
||||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
|
||||||
permissions = append(permissions, SharedWithMeFolderPermission)
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace, id := user.GetNamespacedID()
|
|
||||||
userID, err := identity.UserIdentifier(namespace, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get permissions for user's basic roles from RAM
|
|
||||||
roleList, err := s.store.GetUsersBasicRoles(ctx, []int64{userID}, orgID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not fetch basic roles for the user: %w", err)
|
|
||||||
}
|
|
||||||
var roles []string
|
|
||||||
var ok bool
|
|
||||||
if roles, ok = roleList[userID]; !ok {
|
|
||||||
return nil, fmt.Errorf("found no basic roles for user %d in organisation %d", userID, orgID)
|
|
||||||
}
|
|
||||||
for _, builtin := range roles {
|
|
||||||
if basicRole, ok := s.roles[builtin]; ok {
|
|
||||||
permissions = append(permissions, basicRole.Permissions...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dbPermissions, err := s.store.SearchUsersPermissions(ctx, orgID, accesscontrol.SearchOptions{
|
|
||||||
NamespacedID: authn.NamespacedID(namespace, userID),
|
|
||||||
// Query only basic, managed and plugin roles in OSS
|
|
||||||
RolePrefixes: OSSRolesPrefixes,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userPermissions := dbPermissions[userID]
|
|
||||||
return append(permissions, userPermissions...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) ClearUserPermissionCache(user identity.Requester) {
|
func (s *Service) ClearUserPermissionCache(user identity.Requester) {
|
||||||
s.cache.Delete(permissionCacheKey(user))
|
s.cache.Delete(permissionCacheKey(user))
|
||||||
}
|
}
|
||||||
|
@ -943,59 +943,3 @@ func TestService_DeleteExternalServiceRole(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_GetUserPermissionsInOrg(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
orgID int64
|
|
||||||
ramRoles map[string]*accesscontrol.RoleDTO // BasicRole => RBAC BasicRole
|
|
||||||
storedPerms map[int64][]accesscontrol.Permission // UserID => Permissions
|
|
||||||
storedRoles map[int64][]string // UserID => Roles
|
|
||||||
want []accesscontrol.Permission
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "should get correct permissions from another org",
|
|
||||||
orgID: 2,
|
|
||||||
ramRoles: map[string]*accesscontrol.RoleDTO{
|
|
||||||
string(roletype.RoleEditor): {Permissions: []accesscontrol.Permission{}},
|
|
||||||
string(roletype.RoleAdmin): {Permissions: []accesscontrol.Permission{
|
|
||||||
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
storedPerms: map[int64][]accesscontrol.Permission{
|
|
||||||
1: {
|
|
||||||
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"},
|
|
||||||
{Action: accesscontrol.ActionTeamsPermissionsRead, Scope: "teams:id:1"},
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
storedRoles: map[int64][]string{
|
|
||||||
1: {string(roletype.RoleAdmin)},
|
|
||||||
2: {string(roletype.RoleEditor)},
|
|
||||||
},
|
|
||||||
want: []accesscontrol.Permission{
|
|
||||||
{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:2"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
ac := setupTestEnv(t)
|
|
||||||
|
|
||||||
ac.roles = tt.ramRoles
|
|
||||||
ac.store = actest.FakeStore{
|
|
||||||
ExpectedUsersPermissions: tt.storedPerms,
|
|
||||||
ExpectedUsersRoles: tt.storedRoles,
|
|
||||||
}
|
|
||||||
user := &user.SignedInUser{OrgID: 1, UserID: 2}
|
|
||||||
|
|
||||||
got, err := ac.GetUserPermissionsInOrg(ctx, user, 2)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
assert.ElementsMatch(t, got, tt.want)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -27,10 +27,6 @@ func (f FakeService) GetUserPermissions(ctx context.Context, user identity.Reque
|
|||||||
return f.ExpectedPermissions, f.ExpectedErr
|
return f.ExpectedPermissions, f.ExpectedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FakeService) GetUserPermissionsInOrg(ctx context.Context, user identity.Requester, orgID int64) ([]accesscontrol.Permission, error) {
|
|
||||||
return f.ExpectedPermissions, f.ExpectedErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f FakeService) SearchUsersPermissions(ctx context.Context, user identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
|
func (f FakeService) SearchUsersPermissions(ctx context.Context, user identity.Requester, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
|
||||||
return f.ExpectedUsersPermissions, f.ExpectedErr
|
return f.ExpectedUsersPermissions, f.ExpectedErr
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||||
@ -27,141 +29,102 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
|||||||
|
|
||||||
// Define test cases
|
// Define test cases
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
orgIDGetter accesscontrol.OrgIDGetter
|
targetOrgId int64
|
||||||
evaluator accesscontrol.Evaluator
|
targerOrgPermissions []accesscontrol.Permission
|
||||||
accessControl accesscontrol.AccessControl
|
orgIDGetter accesscontrol.OrgIDGetter
|
||||||
acService accesscontrol.Service
|
evaluator accesscontrol.Evaluator
|
||||||
userCache user.Service
|
accessControl accesscontrol.AccessControl
|
||||||
ctxSignedInUser *user.SignedInUser
|
acService accesscontrol.Service
|
||||||
teamService team.Service
|
userCache user.Service
|
||||||
expectedStatus int
|
ctxSignedInUser *user.SignedInUser
|
||||||
|
teamService team.Service
|
||||||
|
expectedStatus int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "should authorize user with global org ID - fetch",
|
name: "should authorize user with global org ID - fetch",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: accesscontrol.GlobalOrgID,
|
||||||
return accesscontrol.GlobalOrgID, nil
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
},
|
accessControl: ac,
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
userCache: &usertest.FakeUserService{},
|
||||||
accessControl: ac,
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
userCache: &usertest.FakeUserService{},
|
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
teamService: &teamtest.FakeService{},
|
||||||
acService: &actest.FakeService{
|
expectedStatus: http.StatusOK,
|
||||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
|
||||||
},
|
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should authorize user with non-global org ID - no fetch",
|
name: "should authorize user with non-global org ID - no fetch",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 1,
|
||||||
return 1, nil
|
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{},
|
||||||
acService: &actest.FakeService{},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
userCache: &usertest.FakeUserService{},
|
teamService: &teamtest.FakeService{},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
expectedStatus: http.StatusOK,
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should return 403 when user has no permissions for the org",
|
name: "should return 403 when user has no permissions for the org",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 1,
|
||||||
return 1, nil
|
targerOrgPermissions: []accesscontrol.Permission{},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{},
|
||||||
userCache: &usertest.FakeUserService{},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{}},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{}},
|
teamService: &teamtest.FakeService{},
|
||||||
teamService: &teamtest.FakeService{},
|
expectedStatus: http.StatusForbidden,
|
||||||
acService: &actest.FakeService{},
|
|
||||||
expectedStatus: http.StatusForbidden,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should return 200 when user has permissions for a global org",
|
name: "should return 200 when user has permissions for a global org",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: accesscontrol.GlobalOrgID,
|
||||||
return accesscontrol.GlobalOrgID, nil
|
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{},
|
||||||
userCache: &usertest.FakeUserService{},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
teamService: &teamtest.FakeService{},
|
||||||
teamService: &teamtest.FakeService{},
|
expectedStatus: http.StatusOK,
|
||||||
acService: &actest.FakeService{
|
|
||||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
|
||||||
},
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should return 403 when user has no permissions for a global org",
|
name: "should return 403 when user has no permissions for a global org",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: accesscontrol.GlobalOrgID,
|
||||||
return accesscontrol.GlobalOrgID, nil
|
targerOrgPermissions: []accesscontrol.Permission{},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{},
|
||||||
userCache: &usertest.FakeUserService{},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
teamService: &teamtest.FakeService{},
|
||||||
teamService: &teamtest.FakeService{},
|
expectedStatus: http.StatusForbidden,
|
||||||
acService: &actest.FakeService{},
|
|
||||||
expectedStatus: http.StatusForbidden,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should return 403 when user org ID doesn't match and user does not exist in org 2",
|
name: "should return 403 when user org ID doesn't match and user does not exist in org 2",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 2,
|
||||||
return 2, nil
|
targerOrgPermissions: []accesscontrol.Permission{},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{ExpectedError: fmt.Errorf("user not found")},
|
||||||
userCache: &usertest.FakeUserService{ExpectedError: fmt.Errorf("user not found")},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
acService: &actest.FakeService{},
|
teamService: &teamtest.FakeService{},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
expectedStatus: http.StatusForbidden,
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
expectedStatus: http.StatusForbidden,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should return 403 early when api key org ID doesn't match",
|
name: "should return 403 early when api key org ID doesn't match",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 2,
|
||||||
return 2, nil
|
targerOrgPermissions: []accesscontrol.Permission{},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
userCache: &usertest.FakeUserService{},
|
||||||
userCache: &usertest.FakeUserService{},
|
ctxSignedInUser: &user.SignedInUser{ApiKeyID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
acService: &actest.FakeService{},
|
teamService: &teamtest.FakeService{},
|
||||||
ctxSignedInUser: &user.SignedInUser{ApiKeyID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
expectedStatus: http.StatusForbidden,
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
expectedStatus: http.StatusForbidden,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fetch user permissions when they are empty",
|
name: "should fetch user permissions when org ID doesn't match",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 2,
|
||||||
return 1, nil
|
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
teamService: &teamtest.FakeService{},
|
||||||
acService: &actest.FakeService{
|
|
||||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
|
||||||
},
|
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
userCache: &usertest.FakeUserService{
|
|
||||||
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
|
||||||
return &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: nil}, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: nil},
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "should fetch user permissions when org ID doesn't match",
|
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
|
||||||
return 2, nil
|
|
||||||
},
|
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
|
||||||
accessControl: ac,
|
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
acService: &actest.FakeService{
|
|
||||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
|
||||||
},
|
|
||||||
userCache: &usertest.FakeUserService{
|
userCache: &usertest.FakeUserService{
|
||||||
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
||||||
return &user.SignedInUser{UserID: 1, OrgID: 2, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}}, nil
|
return &user.SignedInUser{UserID: 1, OrgID: 2, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}}, nil
|
||||||
@ -171,13 +134,12 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
|||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fails to fetch user permissions when org ID doesn't match",
|
name: "fails to fetch user permissions when org ID doesn't match",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 2,
|
||||||
return 2, nil
|
targerOrgPermissions: []accesscontrol.Permission{},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
teamService: &teamtest.FakeService{},
|
||||||
teamService: &teamtest.FakeService{},
|
|
||||||
acService: &actest.FakeService{
|
acService: &actest.FakeService{
|
||||||
ExpectedErr: fmt.Errorf("failed to get user permissions"),
|
ExpectedErr: fmt.Errorf("failed to get user permissions"),
|
||||||
},
|
},
|
||||||
@ -196,22 +158,17 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
|||||||
},
|
},
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
accessControl: ac,
|
accessControl: ac,
|
||||||
acService: &actest.FakeService{},
|
|
||||||
userCache: &usertest.FakeUserService{},
|
userCache: &usertest.FakeUserService{},
|
||||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||||
teamService: &teamtest.FakeService{},
|
teamService: &teamtest.FakeService{},
|
||||||
expectedStatus: http.StatusForbidden,
|
expectedStatus: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fetch global user permissions when user is not a member of the target org",
|
name: "should fetch global user permissions when user is not a member of the target org",
|
||||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
targetOrgId: 2,
|
||||||
return 2, nil
|
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||||
},
|
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
accessControl: ac,
|
||||||
accessControl: ac,
|
|
||||||
acService: &actest.FakeService{
|
|
||||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
|
||||||
},
|
|
||||||
userCache: &usertest.FakeUserService{
|
userCache: &usertest.FakeUserService{
|
||||||
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
GetSignedInUserFn: func(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
||||||
return &user.SignedInUser{UserID: 1, OrgID: -1, Permissions: map[int64]map[string][]string{}}, nil
|
return &user.SignedInUser{UserID: 1, OrgID: -1, Permissions: map[int64]map[string][]string{}}, nil
|
||||||
@ -228,15 +185,28 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
|||||||
// Create test context
|
// Create test context
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/endpoint", nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/endpoint", nil)
|
||||||
|
|
||||||
service := tc.acService
|
expectedIdentity := &authn.Identity{
|
||||||
|
ID: fmt.Sprintf("user:%v", tc.ctxSignedInUser.UserID),
|
||||||
|
OrgID: tc.targetOrgId,
|
||||||
|
Permissions: map[int64]map[string][]string{},
|
||||||
|
}
|
||||||
|
expectedIdentity.Permissions[tc.targetOrgId] = accesscontrol.GroupScopesByAction(tc.targerOrgPermissions)
|
||||||
|
|
||||||
|
authnService := &authntest.FakeService{
|
||||||
|
ExpectedIdentity: expectedIdentity,
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgIDGetter accesscontrol.OrgIDGetter
|
||||||
|
if tc.orgIDGetter != nil {
|
||||||
|
orgIDGetter = tc.orgIDGetter
|
||||||
|
} else {
|
||||||
|
orgIDGetter = func(c *contextmodel.ReqContext) (int64, error) {
|
||||||
|
return tc.targetOrgId, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create middleware
|
// Create middleware
|
||||||
middleware := accesscontrol.AuthorizeInOrgMiddleware(
|
middleware := accesscontrol.AuthorizeInOrgMiddleware(tc.accessControl, authnService)(orgIDGetter, tc.evaluator)
|
||||||
tc.accessControl,
|
|
||||||
service,
|
|
||||||
tc.userCache,
|
|
||||||
tc.teamService,
|
|
||||||
)(tc.orgIDGetter, tc.evaluator)
|
|
||||||
|
|
||||||
// Create test server
|
// Create test server
|
||||||
server := web.New()
|
server := web.New()
|
||||||
|
@ -20,8 +20,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/authn"
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
@ -177,15 +175,7 @@ func newID() string {
|
|||||||
|
|
||||||
type OrgIDGetter func(c *contextmodel.ReqContext) (int64, error)
|
type OrgIDGetter func(c *contextmodel.ReqContext) (int64, error)
|
||||||
|
|
||||||
type userCache interface {
|
func AuthorizeInOrgMiddleware(ac AccessControl, authnService authn.Service) func(OrgIDGetter, Evaluator) web.Handler {
|
||||||
GetSignedInUserWithCacheCtx(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type teamService interface {
|
|
||||||
GetTeamIDsByUser(ctx context.Context, query *team.GetTeamIDsByUserQuery) ([]int64, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func AuthorizeInOrgMiddleware(ac AccessControl, service Service, userService userCache, teamService teamService) func(OrgIDGetter, Evaluator) web.Handler {
|
|
||||||
return func(getTargetOrg OrgIDGetter, evaluator Evaluator) web.Handler {
|
return func(getTargetOrg OrgIDGetter, evaluator Evaluator) web.Handler {
|
||||||
return func(c *contextmodel.ReqContext) {
|
return func(c *contextmodel.ReqContext) {
|
||||||
targetOrgID, err := getTargetOrg(c)
|
targetOrgID, err := getTargetOrg(c)
|
||||||
@ -194,104 +184,25 @@ func AuthorizeInOrgMiddleware(ac AccessControl, service Service, userService use
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpUser, err := makeTmpUser(c.Req.Context(), service, userService, teamService, c.SignedInUser, targetOrgID)
|
var orgUser identity.Requester = c.SignedInUser
|
||||||
if err != nil {
|
if targetOrgID != c.SignedInUser.GetOrgID() {
|
||||||
deny(c, nil, fmt.Errorf("failed to authenticate user in target org: %w", err))
|
orgUser, err = authnService.ResolveIdentity(c.Req.Context(), targetOrgID, c.SignedInUser.GetID())
|
||||||
return
|
if err != nil {
|
||||||
|
deny(c, nil, fmt.Errorf("failed to authenticate user in target org: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
authorize(c, ac, orgUser, evaluator)
|
||||||
authorize(c, ac, tmpUser, evaluator)
|
|
||||||
|
|
||||||
// guard against nil map
|
// guard against nil map
|
||||||
if c.SignedInUser.Permissions == nil {
|
if c.SignedInUser.Permissions == nil {
|
||||||
c.SignedInUser.Permissions = make(map[int64]map[string][]string)
|
c.SignedInUser.Permissions = make(map[int64]map[string][]string)
|
||||||
}
|
}
|
||||||
c.SignedInUser.Permissions[tmpUser.GetOrgID()] = tmpUser.GetPermissions()
|
c.SignedInUser.Permissions[orgUser.GetOrgID()] = orgUser.GetPermissions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeTmpUser creates a temporary user that can be used to evaluate access across orgs.
|
|
||||||
func makeTmpUser(ctx context.Context, service Service, cache userCache,
|
|
||||||
teamService teamService, reqUser identity.Requester, targetOrgID int64) (identity.Requester, error) {
|
|
||||||
tmpUser := &user.SignedInUser{
|
|
||||||
OrgID: reqUser.GetOrgID(),
|
|
||||||
OrgName: reqUser.GetOrgName(),
|
|
||||||
OrgRole: reqUser.GetOrgRole(),
|
|
||||||
IsGrafanaAdmin: reqUser.GetIsGrafanaAdmin(),
|
|
||||||
Login: reqUser.GetLogin(),
|
|
||||||
Teams: reqUser.GetTeams(),
|
|
||||||
Permissions: map[int64]map[string][]string{
|
|
||||||
reqUser.GetOrgID(): reqUser.GetPermissions(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace, identifier := reqUser.GetNamespacedID()
|
|
||||||
id, _ := identity.IntIdentifier(namespace, identifier)
|
|
||||||
switch namespace {
|
|
||||||
case identity.NamespaceUser:
|
|
||||||
tmpUser.UserID = id
|
|
||||||
case identity.NamespaceAPIKey:
|
|
||||||
tmpUser.ApiKeyID = id
|
|
||||||
if tmpUser.OrgID != targetOrgID {
|
|
||||||
return nil, errors.New("API key does not belong to target org")
|
|
||||||
}
|
|
||||||
case identity.NamespaceServiceAccount:
|
|
||||||
tmpUser.UserID = id
|
|
||||||
tmpUser.IsServiceAccount = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if tmpUser.OrgID != targetOrgID {
|
|
||||||
switch targetOrgID {
|
|
||||||
case GlobalOrgID:
|
|
||||||
tmpUser.OrgID = GlobalOrgID
|
|
||||||
tmpUser.OrgRole = org.RoleNone
|
|
||||||
tmpUser.OrgName = ""
|
|
||||||
tmpUser.Teams = []int64{}
|
|
||||||
default:
|
|
||||||
if cache == nil {
|
|
||||||
return nil, errors.New("user cache is nil")
|
|
||||||
}
|
|
||||||
query := user.GetSignedInUserQuery{UserID: tmpUser.UserID, OrgID: targetOrgID}
|
|
||||||
queryResult, err := cache.GetSignedInUserWithCacheCtx(ctx, &query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tmpUser.OrgID = queryResult.OrgID
|
|
||||||
tmpUser.OrgName = queryResult.OrgName
|
|
||||||
tmpUser.OrgRole = queryResult.OrgRole
|
|
||||||
|
|
||||||
// Only fetch the team membership is the user is a member of the organization
|
|
||||||
if queryResult.OrgID == targetOrgID {
|
|
||||||
if teamService != nil {
|
|
||||||
teamIDs, err := teamService.GetTeamIDsByUser(ctx, &team.GetTeamIDsByUserQuery{OrgID: targetOrgID, UserID: tmpUser.UserID})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tmpUser.Teams = teamIDs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user is not a member of the organization
|
|
||||||
// evaluation must happen based on global permissions.
|
|
||||||
evaluationOrg := targetOrgID
|
|
||||||
if tmpUser.OrgID == NoOrgID {
|
|
||||||
evaluationOrg = GlobalOrgID
|
|
||||||
}
|
|
||||||
if tmpUser.Permissions[evaluationOrg] == nil || len(tmpUser.Permissions[evaluationOrg]) == 0 {
|
|
||||||
permissions, err := service.GetUserPermissions(ctx, tmpUser, Options{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpUser.Permissions[evaluationOrg] = GroupScopesByAction(permissions)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmpUser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UseOrgFromContextParams(c *contextmodel.ReqContext) (int64, error) {
|
func UseOrgFromContextParams(c *contextmodel.ReqContext) (int64, error) {
|
||||||
orgID, err := strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64)
|
orgID, err := strconv.ParseInt(web.Params(c.Req)[":orgId"], 10, 64)
|
||||||
|
|
||||||
@ -339,7 +250,7 @@ func UseOrgFromRequestData(c *contextmodel.ReqContext) (int64, error) {
|
|||||||
|
|
||||||
// UseGlobalOrgFromRequestData returns global org if `global` flag is set or the org where user is logged in.
|
// UseGlobalOrgFromRequestData returns global org if `global` flag is set or the org where user is logged in.
|
||||||
// If RBACSingleOrganization is set, the org where user is logged in is returned - this is intended only for cloud workflows, where instances are limited to a single organization.
|
// If RBACSingleOrganization is set, the org where user is logged in is returned - this is intended only for cloud workflows, where instances are limited to a single organization.
|
||||||
func UseGlobalOrgFromRequestData(cfg *setting.Cfg) func(*contextmodel.ReqContext) (int64, error) {
|
func UseGlobalOrgFromRequestData(cfg *setting.Cfg) OrgIDGetter {
|
||||||
return func(c *contextmodel.ReqContext) (int64, error) {
|
return func(c *contextmodel.ReqContext) (int64, error) {
|
||||||
query, err := getOrgQueryFromRequest(c)
|
query, err := getOrgQueryFromRequest(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -22,7 +22,6 @@ type Calls struct {
|
|||||||
Evaluate []interface{}
|
Evaluate []interface{}
|
||||||
GetRoleByName []interface{}
|
GetRoleByName []interface{}
|
||||||
GetUserPermissions []interface{}
|
GetUserPermissions []interface{}
|
||||||
GetUserPermissionsInOrg []interface{}
|
|
||||||
ClearUserPermissionCache []interface{}
|
ClearUserPermissionCache []interface{}
|
||||||
DeclareFixedRoles []interface{}
|
DeclareFixedRoles []interface{}
|
||||||
DeclarePluginRoles []interface{}
|
DeclarePluginRoles []interface{}
|
||||||
@ -50,7 +49,6 @@ type Mock struct {
|
|||||||
EvaluateFunc func(context.Context, identity.Requester, accesscontrol.Evaluator) (bool, error)
|
EvaluateFunc func(context.Context, identity.Requester, accesscontrol.Evaluator) (bool, error)
|
||||||
GetRoleByNameFunc func(context.Context, int64, string) (*accesscontrol.RoleDTO, error)
|
GetRoleByNameFunc func(context.Context, int64, string) (*accesscontrol.RoleDTO, error)
|
||||||
GetUserPermissionsFunc func(context.Context, identity.Requester, accesscontrol.Options) ([]accesscontrol.Permission, error)
|
GetUserPermissionsFunc func(context.Context, identity.Requester, accesscontrol.Options) ([]accesscontrol.Permission, error)
|
||||||
GetUserPermissionsInOrgFunc func(context.Context, identity.Requester, int64) ([]accesscontrol.Permission, error)
|
|
||||||
ClearUserPermissionCacheFunc func(identity.Requester)
|
ClearUserPermissionCacheFunc func(identity.Requester)
|
||||||
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
|
DeclareFixedRolesFunc func(...accesscontrol.RoleRegistration) error
|
||||||
DeclarePluginRolesFunc func(context.Context, string, string, []plugins.RoleRegistration) error
|
DeclarePluginRolesFunc func(context.Context, string, string, []plugins.RoleRegistration) error
|
||||||
@ -152,16 +150,6 @@ func (m *Mock) GetUserPermissions(ctx context.Context, user identity.Requester,
|
|||||||
return m.permissions, nil
|
return m.permissions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mock) GetUserPermissionsInOrg(ctx context.Context, user identity.Requester, orgID int64) ([]accesscontrol.Permission, error) {
|
|
||||||
m.Calls.GetUserPermissionsInOrg = append(m.Calls.GetUserPermissionsInOrg, []interface{}{ctx, user, orgID})
|
|
||||||
// Use override if provided
|
|
||||||
if m.GetUserPermissionsInOrgFunc != nil {
|
|
||||||
return m.GetUserPermissionsInOrgFunc(ctx, user, orgID)
|
|
||||||
}
|
|
||||||
// Otherwise return the Permissions list
|
|
||||||
return m.permissions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mock) ClearUserPermissionCache(user identity.Requester) {
|
func (m *Mock) ClearUserPermissionCache(user identity.Requester) {
|
||||||
m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user})
|
m.Calls.ClearUserPermissionCache = append(m.Calls.ClearUserPermissionCache, []interface{}{user})
|
||||||
// Use override if provided
|
// Use override if provided
|
||||||
|
@ -73,7 +73,25 @@ func (f *FakeService) Logout(_ context.Context, _ identity.Requester, _ *usertok
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeService) ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*authn.Identity, error) {
|
func (f *FakeService) ResolveIdentity(ctx context.Context, orgID int64, namespaceID string) (*authn.Identity, error) {
|
||||||
panic("unimplemented")
|
if f.ExpectedIdentities != nil {
|
||||||
|
if f.CurrentIndex >= len(f.ExpectedIdentities) {
|
||||||
|
panic("ExpectedIdentities is empty")
|
||||||
|
}
|
||||||
|
if f.CurrentIndex >= len(f.ExpectedErrs) {
|
||||||
|
panic("ExpectedErrs is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
identity := f.ExpectedIdentities[f.CurrentIndex]
|
||||||
|
err := f.ExpectedErrs[f.CurrentIndex]
|
||||||
|
|
||||||
|
f.CurrentIndex += 1
|
||||||
|
|
||||||
|
return identity, err
|
||||||
|
}
|
||||||
|
|
||||||
|
identity := f.ExpectedIdentity
|
||||||
|
identity.OrgID = orgID
|
||||||
|
return identity, f.ExpectedErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeService) RegisterClient(c authn.Client) {}
|
func (f *FakeService) RegisterClient(c authn.Client) {}
|
||||||
|
@ -17,7 +17,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
|||||||
var configNodes []*navtree.NavLink
|
var configNodes []*navtree.NavLink
|
||||||
ctx := c.Req.Context()
|
ctx := c.Req.Context()
|
||||||
hasAccess := ac.HasAccess(s.accessControl, c)
|
hasAccess := ac.HasAccess(s.accessControl, c)
|
||||||
hasGlobalAccess := ac.HasGlobalAccess(s.accessControl, s.accesscontrolService, c)
|
hasGlobalAccess := ac.HasGlobalAccess(s.accessControl, s.authnService, c)
|
||||||
orgsAccessEvaluator := ac.EvalPermission(ac.ActionOrgsRead)
|
orgsAccessEvaluator := ac.EvalPermission(ac.ActionOrgsRead)
|
||||||
authConfigUIAvailable := s.license.FeatureEnabled(social.SAMLProviderName) || s.cfg.LDAPAuthEnabled
|
authConfigUIAvailable := s.license.FeatureEnabled(social.SAMLProviderName) || s.cfg.LDAPAuthEnabled
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/apikey"
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
|
"github.com/grafana/grafana/pkg/services/authn"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
@ -28,6 +29,7 @@ type ServiceImpl struct {
|
|||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
log log.Logger
|
log log.Logger
|
||||||
accessControl ac.AccessControl
|
accessControl ac.AccessControl
|
||||||
|
authnService authn.Service
|
||||||
pluginStore pluginstore.Store
|
pluginStore pluginstore.Store
|
||||||
pluginSettings pluginsettings.Service
|
pluginSettings pluginsettings.Service
|
||||||
starService star.Service
|
starService star.Service
|
||||||
@ -50,11 +52,14 @@ type NavigationAppConfig struct {
|
|||||||
Icon string
|
Icon string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service, features featuremgmt.FeatureToggles, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service, license licensing.Licensing) navtree.Service {
|
func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service,
|
||||||
|
features featuremgmt.FeatureToggles, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service,
|
||||||
|
license licensing.Licensing, authnService authn.Service) navtree.Service {
|
||||||
service := &ServiceImpl{
|
service := &ServiceImpl{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
log: log.New("navtree service"),
|
log: log.New("navtree service"),
|
||||||
accessControl: accessControl,
|
accessControl: accessControl,
|
||||||
|
authnService: authnService,
|
||||||
pluginStore: pluginStore,
|
pluginStore: pluginStore,
|
||||||
pluginSettings: pluginSettings,
|
pluginSettings: pluginSettings,
|
||||||
starService: starService,
|
starService: starService,
|
||||||
|
Loading…
Reference in New Issue
Block a user