Access Control: Add fine-grained access control to ldap handlers (#35525)

* Add new accesscontrol action for ldap config reload

* Update ldapAdminEditRole with new ldap config reload permission

* wrap /ldap/reload with accesscontrol authorize middleware

* document new action and update fixed:ldap:admin:edit with said action

* add fake accesscontrol implementation for tests

* Add accesscontrol tests for ldap handlers

Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com>
This commit is contained in:
Karl Persson
2021-06-11 15:58:18 +02:00
committed by GitHub
parent 6707b61434
commit 36c997a625
7 changed files with 194 additions and 6 deletions

View File

@ -4,8 +4,11 @@ import (
"errors"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/bus"
@ -573,3 +576,142 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
assert.JSONEq(t, expected, sc.resp.Body.String())
}
// ***
// Access control tests for ldap endpoints
// ***
type ldapAccessControlTestCase struct {
expectedCode int
desc string
url string
method string
permissions []*accesscontrol.Permission
}
func TestLDAP_AccessControl(t *testing.T) {
tests := []ldapAccessControlTestCase{
{
url: "/api/admin/ldap/reload",
method: http.MethodPost,
desc: "ReloadLDAPCfg should return 200 for user with correct permissions",
expectedCode: http.StatusOK,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionLDAPConfigReload},
},
},
{
url: "/api/admin/ldap/reload",
method: http.MethodPost,
desc: "ReloadLDAPCfg should return 403 for user without required permissions",
expectedCode: http.StatusForbidden,
permissions: []*accesscontrol.Permission{
{Action: "wrong"},
},
},
{
url: "/api/admin/ldap/status",
method: http.MethodGet,
desc: "GetLDAPStatus should return 200 for user without required permissions",
expectedCode: http.StatusOK,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionLDAPStatusRead},
},
},
{
url: "/api/admin/ldap/status",
method: http.MethodGet,
desc: "GetLDAPStatus should return 200 for user without required permissions",
expectedCode: http.StatusForbidden,
permissions: []*accesscontrol.Permission{
{Action: "wrong"},
},
},
{
url: "/api/admin/ldap/test",
method: http.MethodGet,
desc: "GetUserFromLDAP should return 200 for user with required permissions",
expectedCode: http.StatusOK,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionLDAPUsersRead},
},
},
{
url: "/api/admin/ldap/test",
method: http.MethodGet,
desc: "GetUserFromLDAP should return 403 for user without required permissions",
expectedCode: http.StatusForbidden,
permissions: []*accesscontrol.Permission{
{Action: "wrong"},
},
},
{
url: "/api/admin/ldap/sync/test",
method: http.MethodPost,
desc: "PostSyncUserWithLDAP should return 200 for user without required permissions",
expectedCode: http.StatusOK,
permissions: []*accesscontrol.Permission{
{Action: accesscontrol.ActionLDAPUsersSync},
},
},
{
url: "/api/admin/ldap/sync/test",
method: http.MethodPost,
desc: "PostSyncUserWithLDAP should return 200 for user without required permissions",
expectedCode: http.StatusForbidden,
permissions: []*accesscontrol.Permission{
{Action: "wrong"},
},
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
t.Helper()
enabled := setting.LDAPEnabled
configFile := setting.LDAPConfigFile
t.Cleanup(func() {
setting.LDAPEnabled = enabled
setting.LDAPConfigFile = configFile
})
setting.LDAPEnabled = true
path, err := filepath.Abs("../../conf/ldap.toml")
assert.NoError(t, err)
setting.LDAPConfigFile = path
cfg := setting.NewCfg()
cfg.LDAPEnabled = true
sc := setupAccessControlScenarioContext(t, cfg, test.url, test.permissions)
sc.resp = httptest.NewRecorder()
sc.req, err = http.NewRequest(test.method, test.url, nil)
assert.NoError(t, err)
// Add minimal setup to pass handler
userSearchResult = &models.ExternalUserInfo{}
userSearchError = nil
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
q.Result = &models.User{}
return nil
})
bus.AddHandler("test", func(q *models.GetAuthInfoQuery) error {
return nil
})
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
return nil
})
sc.exec()
assert.Equal(t, test.expectedCode, sc.resp.Code)
})
}
}