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:
+1
-1
@@ -60,7 +60,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
||||
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
||||
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)
|
||||
|
||||
r := hs.RouteRegister
|
||||
|
||||
@@ -5,6 +5,9 @@ import (
|
||||
"strings"
|
||||
"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/require"
|
||||
|
||||
@@ -136,6 +139,11 @@ func TestAPIEndpoint_UpdateOrg(t *testing.T) {
|
||||
ExpectedSignedInUser: &user.SignedInUser{OrgID: tt.targetOrgID},
|
||||
}
|
||||
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))
|
||||
@@ -209,11 +217,22 @@ func TestAPIEndpoint_DeleteOrgs(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
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) {
|
||||
hs.Cfg = setting.NewCfg()
|
||||
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
||||
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
||||
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))
|
||||
@@ -246,11 +265,23 @@ func TestAPIEndpoint_GetOrg(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
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) {
|
||||
hs.Cfg = setting.NewCfg()
|
||||
hs.orgService = &orgtest.FakeOrgService{ExpectedOrg: &org.Org{}}
|
||||
hs.userService = &usertest.FakeUserService{ExpectedSignedInUser: &user.SignedInUser{OrgID: 1}}
|
||||
hs.accesscontrolService = &actest.FakeService{ExpectedPermissions: tt.permissions}
|
||||
hs.authnService = &authntest.FakeService{
|
||||
ExpectedIdentity: expectedIdentity,
|
||||
}
|
||||
})
|
||||
verify := func(path string) {
|
||||
req := webtest.RequestWithSignedInUser(server.NewGetRequest(path), authedUserWithPermissions(1, 1, tt.permissions))
|
||||
|
||||
+1
-1
@@ -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")
|
||||
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 {
|
||||
// In a single organization setup, no need for a global check
|
||||
hasAccess = ac.HasAccess(hs.AccessControl, c)
|
||||
|
||||
+21
-1
@@ -12,12 +12,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
@@ -32,6 +32,8 @@ import (
|
||||
ac "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/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgtest"
|
||||
@@ -94,6 +96,16 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
|
||||
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) {
|
||||
@@ -734,6 +746,14 @@ func TestHTTPServer_hasPluginRequestedPermissions(t *testing.T) {
|
||||
hs.accesscontrolService = actest.FakeService{}
|
||||
hs.AccessControl = acimpl.ProvideAccessControl(hs.Cfg)
|
||||
|
||||
expectedIdentity := &authn.Identity{
|
||||
OrgID: tt.orgID,
|
||||
Permissions: tt.permissions,
|
||||
}
|
||||
hs.authnService = &authntest.FakeService{
|
||||
ExpectedIdentity: expectedIdentity,
|
||||
}
|
||||
|
||||
c := &contextmodel.ReqContext{
|
||||
Context: &web.Context{Req: httpReq},
|
||||
SignedInUser: &user.SignedInUser{OrgID: tt.orgID, Permissions: tt.permissions},
|
||||
|
||||
+83
-49
@@ -6,6 +6,9 @@ import (
|
||||
"strings"
|
||||
"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/require"
|
||||
|
||||
@@ -67,6 +70,9 @@ func TestAPIEndpoint_GetOrgQuotas(t *testing.T) {
|
||||
hs.userService = &usertest.FakeUserService{
|
||||
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) {
|
||||
@@ -96,60 +102,88 @@ func TestAPIEndpoint_GetOrgQuotas(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAPIEndpoint_PutOrgQuotas(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Quota = setting.QuotaSettings{
|
||||
Enabled: true,
|
||||
Global: setting.GlobalQuota{
|
||||
Org: 5,
|
||||
type testCase struct {
|
||||
desc string
|
||||
userOrg int64
|
||||
targetOrg int64
|
||||
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)
|
||||
t.Run("AccessControl allows updating another org quotas with correct permissions", func(t *testing.T) {
|
||||
user := userWithPermissions(2, []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}})
|
||||
user.OrgID = 1
|
||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}}
|
||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
||||
response, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, response.StatusCode)
|
||||
require.NoError(t, response.Body.Close())
|
||||
})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Quota = setting.QuotaSettings{
|
||||
Enabled: true,
|
||||
Global: setting.GlobalQuota{
|
||||
Org: 5,
|
||||
},
|
||||
Org: setting.OrgQuota{
|
||||
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)
|
||||
t.Run("AccessControl prevents updating another org quotas with correct permissions in another org", func(t *testing.T) {
|
||||
user := userWithPermissions(1, []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}})
|
||||
user.Permissions[2] = nil
|
||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{}
|
||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
||||
response, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||
require.NoError(t, response.Body.Close())
|
||||
})
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
hs.accesscontrolService = fakeACService
|
||||
hs.userService = &usertest.FakeUserService{
|
||||
ExpectedSignedInUser: &user.SignedInUser{OrgID: tt.userOrg},
|
||||
}
|
||||
hs.authnService = &authntest.FakeService{
|
||||
ExpectedIdentity: expectedIdentity,
|
||||
}
|
||||
})
|
||||
|
||||
input = strings.NewReader(testUpdateOrgQuotaCmd)
|
||||
t.Run("AccessControl prevents updating another org quotas with incorrect permissions", func(t *testing.T) {
|
||||
user := userWithPermissions(2, []accesscontrol.Permission{{Action: "orgs:invalid"}})
|
||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{}
|
||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, 2, "org_user"), input), user)
|
||||
response, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, response.StatusCode)
|
||||
require.NoError(t, response.Body.Close())
|
||||
})
|
||||
user := userWithPermissions(tt.userOrg, getFirstOrgPermissions(tt.permissions))
|
||||
fakeACService.ExpectedPermissions = []accesscontrol.Permission{{Action: accesscontrol.ActionOrgsQuotasWrite}}
|
||||
req := webtest.RequestWithSignedInUser(server.NewRequest(http.MethodPut, fmt.Sprintf(putOrgsQuotasURL, tt.targetOrg, "org_user"), input), user)
|
||||
response, err := server.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedCode, 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{}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user