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:
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"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"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||
@@ -27,141 +29,102 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
||||
|
||||
// Define test cases
|
||||
testCases := []struct {
|
||||
name string
|
||||
orgIDGetter accesscontrol.OrgIDGetter
|
||||
evaluator accesscontrol.Evaluator
|
||||
accessControl accesscontrol.AccessControl
|
||||
acService accesscontrol.Service
|
||||
userCache user.Service
|
||||
ctxSignedInUser *user.SignedInUser
|
||||
teamService team.Service
|
||||
expectedStatus int
|
||||
name string
|
||||
targetOrgId int64
|
||||
targerOrgPermissions []accesscontrol.Permission
|
||||
orgIDGetter accesscontrol.OrgIDGetter
|
||||
evaluator accesscontrol.Evaluator
|
||||
accessControl accesscontrol.AccessControl
|
||||
acService accesscontrol.Service
|
||||
userCache user.Service
|
||||
ctxSignedInUser *user.SignedInUser
|
||||
teamService team.Service
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "should authorize user with global org ID - fetch",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return accesscontrol.GlobalOrgID, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
acService: &actest.FakeService{
|
||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusOK,
|
||||
name: "should authorize user with global org ID - fetch",
|
||||
targetOrgId: accesscontrol.GlobalOrgID,
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "should authorize user with non-global org ID - no fetch",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return 1, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
acService: &actest.FakeService{},
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusOK,
|
||||
name: "should authorize user with non-global org ID - no fetch",
|
||||
targetOrgId: 1,
|
||||
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "should return 403 when user has no permissions for the org",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return 1, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
acService: &actest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
name: "should return 403 when user has no permissions for the org",
|
||||
targetOrgId: 1,
|
||||
targerOrgPermissions: []accesscontrol.Permission{},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "should return 200 when user has permissions for a global org",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return accesscontrol.GlobalOrgID, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
acService: &actest.FakeService{
|
||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
name: "should return 200 when user has permissions for a global org",
|
||||
targetOrgId: accesscontrol.GlobalOrgID,
|
||||
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "should return 403 when user has no permissions for a global org",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return accesscontrol.GlobalOrgID, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
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.StatusForbidden,
|
||||
name: "should return 403 when user has no permissions for a global org",
|
||||
targetOrgId: accesscontrol.GlobalOrgID,
|
||||
targerOrgPermissions: []accesscontrol.Permission{},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
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) {
|
||||
return 2, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{ExpectedError: fmt.Errorf("user not found")},
|
||||
acService: &actest.FakeService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
name: "should return 403 when user org ID doesn't match and user does not exist in org 2",
|
||||
targetOrgId: 2,
|
||||
targerOrgPermissions: []accesscontrol.Permission{},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
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:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "should return 403 early when api key org ID doesn't match",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return 2, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
acService: &actest.FakeService{},
|
||||
ctxSignedInUser: &user.SignedInUser{ApiKeyID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
name: "should return 403 early when api key org ID doesn't match",
|
||||
targetOrgId: 2,
|
||||
targerOrgPermissions: []accesscontrol.Permission{},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{ApiKeyID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "should fetch user permissions when they are empty",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return 1, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
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:*"}},
|
||||
},
|
||||
name: "should fetch user permissions when org ID doesn't match",
|
||||
targetOrgId: 2,
|
||||
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
teamService: &teamtest.FakeService{},
|
||||
userCache: &usertest.FakeUserService{
|
||||
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
|
||||
@@ -171,13 +134,12 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
||||
expectedStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "fails to 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{},
|
||||
name: "fails to fetch user permissions when org ID doesn't match",
|
||||
targetOrgId: 2,
|
||||
targerOrgPermissions: []accesscontrol.Permission{},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
teamService: &teamtest.FakeService{},
|
||||
acService: &actest.FakeService{
|
||||
ExpectedErr: fmt.Errorf("failed to get user permissions"),
|
||||
},
|
||||
@@ -196,22 +158,17 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
acService: &actest.FakeService{},
|
||||
userCache: &usertest.FakeUserService{},
|
||||
ctxSignedInUser: &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {"users:read": {"users:*"}}}},
|
||||
teamService: &teamtest.FakeService{},
|
||||
expectedStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
name: "should fetch global user permissions when user is not a member of the target org",
|
||||
orgIDGetter: func(c *contextmodel.ReqContext) (int64, error) {
|
||||
return 2, nil
|
||||
},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
acService: &actest.FakeService{
|
||||
ExpectedPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
},
|
||||
name: "should fetch global user permissions when user is not a member of the target org",
|
||||
targetOrgId: 2,
|
||||
targerOrgPermissions: []accesscontrol.Permission{{Action: "users:read", Scope: "users:*"}},
|
||||
evaluator: accesscontrol.EvalPermission("users:read", "users:*"),
|
||||
accessControl: ac,
|
||||
userCache: &usertest.FakeUserService{
|
||||
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
|
||||
@@ -228,15 +185,28 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) {
|
||||
// Create test context
|
||||
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
|
||||
middleware := accesscontrol.AuthorizeInOrgMiddleware(
|
||||
tc.accessControl,
|
||||
service,
|
||||
tc.userCache,
|
||||
tc.teamService,
|
||||
)(tc.orgIDGetter, tc.evaluator)
|
||||
middleware := accesscontrol.AuthorizeInOrgMiddleware(tc.accessControl, authnService)(orgIDGetter, tc.evaluator)
|
||||
|
||||
// Create test server
|
||||
server := web.New()
|
||||
|
||||
Reference in New Issue
Block a user