|
|
|
@ -1,9 +1,11 @@ |
|
|
|
|
package api |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"bytes" |
|
|
|
|
"context" |
|
|
|
|
"encoding/json" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"net/http" |
|
|
|
|
"net/http/httptest" |
|
|
|
|
"testing" |
|
|
|
@ -19,6 +21,7 @@ import ( |
|
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore" |
|
|
|
|
"github.com/grafana/grafana/pkg/setting" |
|
|
|
|
"github.com/grafana/grafana/pkg/web" |
|
|
|
|
"github.com/stretchr/testify/assert" |
|
|
|
|
"github.com/stretchr/testify/require" |
|
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/services/serviceaccounts/database" |
|
|
|
@ -205,3 +208,129 @@ func TestServiceAccountsAPI_RetrieveServiceAccount(t *testing.T) { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func newString(s string) *string { |
|
|
|
|
return &s |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestServiceAccountsAPI_UpdateServiceAccount(t *testing.T) { |
|
|
|
|
store := sqlstore.InitTestDB(t) |
|
|
|
|
svcmock := tests.ServiceAccountMock{} |
|
|
|
|
type testUpdateSATestCase struct { |
|
|
|
|
desc string |
|
|
|
|
user *tests.TestUser |
|
|
|
|
expectedCode int |
|
|
|
|
acmock *accesscontrolmock.Mock |
|
|
|
|
body *serviceaccounts.UpdateServiceAccountForm |
|
|
|
|
Id int |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
role := models.ROLE_ADMIN |
|
|
|
|
var invalidRole models.RoleType = "InvalidRole" |
|
|
|
|
testCases := []testUpdateSATestCase{ |
|
|
|
|
{ |
|
|
|
|
desc: "should be ok to update serviceaccount with permissions", |
|
|
|
|
user: &tests.TestUser{Login: "servicetest1@admin", IsServiceAccount: true, Role: "Editor", Name: "Unaltered"}, |
|
|
|
|
body: &serviceaccounts.UpdateServiceAccountForm{Name: newString("New Name"), Role: &role}, |
|
|
|
|
acmock: tests.SetupMockAccesscontrol( |
|
|
|
|
t, |
|
|
|
|
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { |
|
|
|
|
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil |
|
|
|
|
}, |
|
|
|
|
false, |
|
|
|
|
), |
|
|
|
|
expectedCode: http.StatusOK, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
desc: "bad request when invalid role", |
|
|
|
|
user: &tests.TestUser{Login: "servicetest3@admin", IsServiceAccount: true, Role: "Invalid", Name: "Unaltered"}, |
|
|
|
|
body: &serviceaccounts.UpdateServiceAccountForm{Name: newString("NameB"), Role: &invalidRole}, |
|
|
|
|
acmock: tests.SetupMockAccesscontrol( |
|
|
|
|
t, |
|
|
|
|
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { |
|
|
|
|
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil |
|
|
|
|
}, |
|
|
|
|
false, |
|
|
|
|
), |
|
|
|
|
expectedCode: http.StatusBadRequest, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
desc: "should be forbidden to update serviceaccount if no permissions", |
|
|
|
|
user: &tests.TestUser{Login: "servicetest2@admin", IsServiceAccount: true}, |
|
|
|
|
body: nil, |
|
|
|
|
acmock: tests.SetupMockAccesscontrol( |
|
|
|
|
t, |
|
|
|
|
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { |
|
|
|
|
return []*accesscontrol.Permission{}, nil |
|
|
|
|
}, |
|
|
|
|
false, |
|
|
|
|
), |
|
|
|
|
expectedCode: http.StatusForbidden, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
desc: "should be not found when the user doesnt exist", |
|
|
|
|
user: nil, |
|
|
|
|
body: nil, |
|
|
|
|
Id: 12, |
|
|
|
|
acmock: tests.SetupMockAccesscontrol( |
|
|
|
|
t, |
|
|
|
|
func(c context.Context, siu *models.SignedInUser, _ accesscontrol.Options) ([]*accesscontrol.Permission, error) { |
|
|
|
|
return []*accesscontrol.Permission{{Action: serviceaccounts.ActionWrite, Scope: serviceaccounts.ScopeAll}}, nil |
|
|
|
|
}, |
|
|
|
|
false, |
|
|
|
|
), |
|
|
|
|
expectedCode: http.StatusNotFound, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var requestResponse = func(server *web.Mux, httpMethod, requestpath string, body io.Reader) *httptest.ResponseRecorder { |
|
|
|
|
req, err := http.NewRequest(httpMethod, requestpath, body) |
|
|
|
|
req.Header.Add("Content-Type", "application/json") |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
recorder := httptest.NewRecorder() |
|
|
|
|
server.ServeHTTP(recorder, req) |
|
|
|
|
return recorder |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, tc := range testCases { |
|
|
|
|
t.Run(tc.desc, func(t *testing.T) { |
|
|
|
|
serviceAccountRequestScenario(t, http.MethodPatch, serviceaccountIDPath, tc.user, func(httpmethod string, endpoint string, user *tests.TestUser) { |
|
|
|
|
scopeID := tc.Id |
|
|
|
|
if tc.user != nil { |
|
|
|
|
createdUser := tests.SetupUserServiceAccount(t, store, *tc.user) |
|
|
|
|
scopeID = int(createdUser.Id) |
|
|
|
|
} |
|
|
|
|
server := setupTestServer(t, &svcmock, routing.NewRouteRegister(), tc.acmock, store) |
|
|
|
|
|
|
|
|
|
var rawBody io.Reader = http.NoBody |
|
|
|
|
if tc.body != nil { |
|
|
|
|
body, err := json.Marshal(tc.body) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
rawBody = bytes.NewReader(body) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
actual := requestResponse(server, httpmethod, fmt.Sprintf(endpoint, scopeID), rawBody) |
|
|
|
|
|
|
|
|
|
actualCode := actual.Code |
|
|
|
|
require.Equal(t, tc.expectedCode, actualCode) |
|
|
|
|
|
|
|
|
|
if actualCode == http.StatusOK { |
|
|
|
|
actualBody := map[string]interface{}{} |
|
|
|
|
err := json.Unmarshal(actual.Body.Bytes(), &actualBody) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
assert.Equal(t, scopeID, int(actualBody["id"].(float64))) |
|
|
|
|
assert.Equal(t, string(*tc.body.Role), actualBody["role"].(string)) |
|
|
|
|
assert.Equal(t, *tc.body.Name, actualBody["name"].(string)) |
|
|
|
|
assert.Equal(t, tc.user.Login, actualBody["login"].(string)) |
|
|
|
|
|
|
|
|
|
// Ensure the user was updated in DB
|
|
|
|
|
query := models.GetOrgUsersQuery{UserID: int64(scopeID), OrgId: 1, IsServiceAccount: true} |
|
|
|
|
err = store.GetOrgUsers(context.Background(), &query) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
require.Equal(t, *tc.body.Name, query.Result[0].Name) |
|
|
|
|
require.Equal(t, string(*tc.body.Role), query.Result[0].Role) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|