From 0f169982bab11fd1abec4a012b356271b5dab181 Mon Sep 17 00:00:00 2001 From: shivaji-dgraph Date: Mon, 10 Apr 2023 12:04:58 +0530 Subject: [PATCH] test(acl): add upgrade tests for ACL --- dgraphtest/acl.go | 1 - dgraphtest/cluster.go | 21 +- dgraphtest/compose_cluster.go | 1 + dgraphtest/dgraph.go | 2 +- dgraphtest/image.go | 2 +- dgraphtest/paths.go | 6 + ee/acl/acl_curl_test.go | 92 +- ee/acl/acl_integration_test.go | 439 +++ ee/acl/acl_test.go | 2702 ++++++----------- ee/acl/integration_test.go | 38 + ee/acl/upgrade_test.go | 66 + .../incremental_restore_test.go | 9 +- systest/multi-tenancy/basic_test.go | 6 +- systest/multi-tenancy/upgrade_test.go | 8 +- 14 files changed, 1611 insertions(+), 1782 deletions(-) create mode 100644 ee/acl/acl_integration_test.go create mode 100644 ee/acl/integration_test.go create mode 100644 ee/acl/upgrade_test.go diff --git a/dgraphtest/acl.go b/dgraphtest/acl.go index f7058b6b15e..23ca22282be 100644 --- a/dgraphtest/acl.go +++ b/dgraphtest/acl.go @@ -124,7 +124,6 @@ func (hc *HTTPClient) CreateGroup(name string) (string, error) { if err != nil { return "", nil } - type Response struct { AddGroup struct { Group []struct { diff --git a/dgraphtest/cluster.go b/dgraphtest/cluster.go index a28ba7dd04d..86dea875065 100644 --- a/dgraphtest/cluster.go +++ b/dgraphtest/cluster.go @@ -246,11 +246,27 @@ func (hc *HTTPClient) RunGraphqlQuery(params GraphQLParams, admin bool) ([]byte, return nil, errors.Wrap(err, "error unmarshalling GQL response") } if len(gqlResp.Errors) > 0 { - return nil, errors.Wrapf(gqlResp.Errors, "error while running admin query") + return nil, errors.Wrapf(gqlResp.Errors, "error while running admin query, resp: %v", string(gqlResp.Data)) } return gqlResp.Data, nil } +func (hc *HTTPClient) HealthForInstance() ([]byte, error) { + const query = `query { + health { + instance + address + lastEcho + status + version + uptime + group + } + }` + params := GraphQLParams{Query: query} + return hc.RunGraphqlQuery(params, true) +} + // Backup creates a backup of dgraph at a given path func (hc *HTTPClient) Backup(c Cluster, forceFull bool, backupPath string) error { // backup API was made async in the commit d3bf7b7b2786bcb99f02e1641f3b656d0a98f7f4 @@ -531,13 +547,12 @@ func (gc *GrpcClient) DropPredicate(pred string) error { } // Mutate performs a given mutation in a txn -func (gc *GrpcClient) Mutate(rdfs string) (*api.Response, error) { +func (gc *GrpcClient) Mutate(mu *api.Mutation) (*api.Response, error) { txn := gc.NewTxn() defer func() { _ = txn.Discard(context.Background()) }() ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) defer cancel() - mu := &api.Mutation{SetNquads: []byte(rdfs), CommitNow: true} return txn.Mutate(ctx, mu) } diff --git a/dgraphtest/compose_cluster.go b/dgraphtest/compose_cluster.go index 992ba8c1502..f81e8ab1828 100644 --- a/dgraphtest/compose_cluster.go +++ b/dgraphtest/compose_cluster.go @@ -36,6 +36,7 @@ func (c *ComposeCluster) Client() (*GrpcClient, func(), error) { return &GrpcClient{Dgraph: client}, func() {}, nil } +// HTTPClient creates an HTTP client func (c *ComposeCluster) HTTPClient() (*HTTPClient, error) { adminUrl := "http://" + testutil.SockAddrHttp + "/admin" graphQLUrl := "http://" + testutil.SockAddrHttp + "/graphql" diff --git a/dgraphtest/dgraph.go b/dgraphtest/dgraph.go index 4931dc7d5db..da7ca05a68f 100644 --- a/dgraphtest/dgraph.go +++ b/dgraphtest/dgraph.go @@ -57,7 +57,7 @@ const ( localVersion = "local" waitDurBeforeRetry = time.Second - requestTimeout = 90 * time.Second + requestTimeout = 120 * time.Second ) var ( diff --git a/dgraphtest/image.go b/dgraphtest/image.go index f15fb5b4463..953ff94b813 100644 --- a/dgraphtest/image.go +++ b/dgraphtest/image.go @@ -130,7 +130,7 @@ func runGitCheckout(gitRef string) error { } func buildDgraphBinary(dir, binaryDir, version string) error { - log.Printf("[INFO] building dgraph binary") + log.Printf("[INFO] building dgraph binary for version [%v]", version) cmd := exec.Command("make", "dgraph") cmd.Dir = filepath.Join(dir, "dgraph") diff --git a/dgraphtest/paths.go b/dgraphtest/paths.go index 4dba9047aa7..643aa497788 100644 --- a/dgraphtest/paths.go +++ b/dgraphtest/paths.go @@ -56,4 +56,10 @@ func init() { binDir = filepath.Join(basePath, "binaries") encKeyPath = filepath.Join(basePath, "data", "enc-key") aclSecretPath = filepath.Join(basePath, "data", "hmac-secret") + + log.Printf("[INFO] baseRepoDir: %v", baseRepoDir) + log.Printf("[INFO] repoDir: %v", repoDir) + log.Printf("[INFO] binDir: %v", binDir) + log.Printf("[INFO] encKeyPath: %v", encKeyPath) + log.Printf("[INFO] aclSecretPath: %v", aclSecretPath) } diff --git a/ee/acl/acl_curl_test.go b/ee/acl/acl_curl_test.go index 46789e498b1..dab256cc878 100644 --- a/ee/acl/acl_curl_test.go +++ b/ee/acl/acl_curl_test.go @@ -14,6 +14,7 @@ package acl import ( + "context" "fmt" "testing" "time" @@ -21,33 +22,32 @@ import ( "github.com/golang/glog" "github.com/stretchr/testify/require" + "github.com/dgraph-io/dgraph/dgraphtest" "github.com/dgraph-io/dgraph/testutil" "github.com/dgraph-io/dgraph/x" ) -var adminEndpoint string - -func TestCurlAuthorization(t *testing.T) { +func (asuite *AclTestSuite) TestCurlAuthorization() { + t := asuite.T() if testing.Short() { t.Skip("skipping because -short=true") } - - glog.Infof("testing with port %s", testutil.SockAddr) - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - if err != nil { - t.Fatalf("Error while getting a dgraph client: %v", err) - } - createAccountAndData(t, dg) + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + createAccountAndData(t, gc, hc) // test query through curl - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: userid, - Passwd: userpassword, - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) // No ACL rules are specified, so query should return empty response, // alter and mutate should fail. queryArgs := func(jwt string) []string { @@ -55,7 +55,7 @@ func TestCurlAuthorization(t *testing.T) { "-H", "Content-Type: application/dql", "-d", query, testutil.SockAddrHttp + "/query"} } - testutil.VerifyCurlCmd(t, queryArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, queryArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: false, }) @@ -68,7 +68,7 @@ func TestCurlAuthorization(t *testing.T) { } - testutil.VerifyCurlCmd(t, mutateArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, mutateArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "PermissionDenied", }) @@ -77,7 +77,7 @@ func TestCurlAuthorization(t *testing.T) { return []string{"-H", fmt.Sprintf("X-Dgraph-AccessToken:%s", jwt), "-d", fmt.Sprintf(`%s: int .`, predicateToAlter), testutil.SockAddrHttp + "/alter"} } - testutil.VerifyCurlCmd(t, alterArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, alterArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "PermissionDenied", }) @@ -87,68 +87,64 @@ func TestCurlAuthorization(t *testing.T) { // JWT glog.Infof("Sleeping for accessJwt to expire") time.Sleep(expireJwtSleep) - testutil.VerifyCurlCmd(t, queryArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, queryArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "Token is expired", }) - testutil.VerifyCurlCmd(t, mutateArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, mutateArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "Token is expired", }) - testutil.VerifyCurlCmd(t, alterArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, alterArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "Token is expired", }) // login again using the refreshJwt - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - RefreshJwt: token.RefreshToken, - Namespace: x.GalaxyNamespace, - }) + require.NoError(t, hc.LoginUsingToken(x.GalaxyNamespace)) require.NoError(t, err, fmt.Sprintf("login through refresh httpToken failed: %v", err)) - - createGroupAndAcls(t, unusedGroup, false) + hcWithGroot, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hcWithGroot.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + createGroupAndAcls(t, unusedGroup, false, hcWithGroot) time.Sleep(expireJwtSleep) - testutil.VerifyCurlCmd(t, queryArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, queryArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "Token is expired", }) // refresh the jwts again - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - RefreshJwt: token.RefreshToken, - }) + require.NoError(t, hc.LoginUsingToken(x.GalaxyNamespace)) + require.NoError(t, err, fmt.Sprintf("login through refresh httpToken failed: %v", err)) // verify that with an ACL rule defined, all the operations except query should // does not have the required permissions be denied when the acsess JWT - testutil.VerifyCurlCmd(t, queryArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, queryArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: false, }) - testutil.VerifyCurlCmd(t, mutateArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, mutateArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "PermissionDenied", }) - testutil.VerifyCurlCmd(t, alterArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, alterArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: true, DgraphErrMsg: "PermissionDenied", }) - - createGroupAndAcls(t, devGroup, true) + require.NoError(t, hcWithGroot.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + createGroupAndAcls(t, devGroup, true, hcWithGroot) time.Sleep(defaultTimeToSleep) // refresh the jwts again - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - RefreshJwt: token.RefreshToken, - }) + require.NoError(t, hc.LoginUsingToken(x.GalaxyNamespace)) + require.NoError(t, err, fmt.Sprintf("login through refresh httpToken failed: %v", err)) // verify that the operations should be allowed again through the dev group - testutil.VerifyCurlCmd(t, queryArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, queryArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: false, }) - testutil.VerifyCurlCmd(t, mutateArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, mutateArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: false, }) - testutil.VerifyCurlCmd(t, alterArgs(token.AccessJwt), &testutil.CurlFailureConfig{ + testutil.VerifyCurlCmd(t, alterArgs(hc.AccessJwt), &testutil.CurlFailureConfig{ ShouldFail: false, }) } diff --git a/ee/acl/acl_integration_test.go b/ee/acl/acl_integration_test.go new file mode 100644 index 00000000000..03b5cde6430 --- /dev/null +++ b/ee/acl/acl_integration_test.go @@ -0,0 +1,439 @@ +//go:build integration + +/* + * Copyright 2023 Dgraph Labs, Inc. and Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package acl + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/dgraph-io/dgo/v230/protos/api" + "github.com/dgraph-io/dgraph/dgraphtest" + "github.com/dgraph-io/dgraph/x" +) + +func (asuite *AclTestSuite) TestInvalidGetUser() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + hc.HttpToken.AccessJwt = "invalid Token" + currentUser, err := hc.GetCurrentUser() + require.Contains(t, err.Error(), "couldn't rewrite query getCurrentUser because "+ + "unable to parse jwt token: token contains an invalid number of segments") + require.Equal(t, "", currentUser) +} + +func (asuite *AclTestSuite) TestPasswordReturn() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + query := dgraphtest.GraphQLParams{ + Query: ` + query { + getCurrentUser { + name + password + } + }`} + + _, err = hc.RunGraphqlQuery(query, true) + require.Contains(t, err.Error(), + `Cannot query field "password" on type "User". (Locations: [{Line: 5, Column: 4}])`) +} + +func (asuite *AclTestSuite) TestHealthForAcl() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + resetUser(t, hc) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) + gqlResp, err := hc.HealthForInstance() + require.Error(t, err) + // assert errors for non-guardians + assertNonGuardianFailure(t, "health", false, gqlResp, err) + + // assert data for guardians + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + resp, err := hc.HealthForInstance() + require.NoError(t, err, "health request failed") + + var guardianResp struct { + Health []struct { + Instance string + Address string + LastEcho int64 + Status string + Version string + UpTime int64 + Group string + } + } + require.NoError(t, json.Unmarshal(resp, &guardianResp)) + + // we have 9 instances of alphas/zeros in teamcity environment + require.Len(t, guardianResp.Health, 9) + for _, v := range guardianResp.Health { + t.Logf("Got health: %+v\n", v) + require.Contains(t, []string{"alpha", "zero"}, v.Instance) + require.NotEmpty(t, v.Address) + require.NotEmpty(t, v.LastEcho) + require.Equal(t, "healthy", v.Status) + require.NotEmpty(t, v.Version) + require.NotEmpty(t, v.UpTime) + require.NotEmpty(t, v.Group) + } +} + +func (asuite *AclTestSuite) TestGuardianOnlyAccessForAdminEndpoints() { + t := asuite.T() + tcases := []graphQLAdminEndpointTestCase{ + { + name: "backup has guardian auth", + query: ` + mutation { + backup(input: {destination: ""}) { + response { + code + message + } + } + }`, + queryName: "backup", + testGuardianAccess: true, + guardianErr: "you must specify a 'destination' value", + guardianData: `{"backup": null}`, + }, + { + name: "listBackups has guardian auth", + query: ` + query { + listBackups(input: {location: ""}) { + backupId + } + }`, + queryName: "listBackups", + respIsArray: true, + testGuardianAccess: true, + guardianErr: `The uri path: "" doesn't exist`, + guardianData: `{"listBackups": []}`, + }, + { + name: "config update has guardian auth", + query: ` + mutation { + config(input: {cacheMb: -1}) { + response { + code + message + } + } + }`, + queryName: "config", + testGuardianAccess: true, + guardianErr: "cache_mb must be non-negative", + guardianData: `{"config": null}`, + }, + { + name: "config get has guardian auth", + query: ` + query { + config { + cacheMb + } + }`, + queryName: "config", + testGuardianAccess: true, + guardianErr: "", + guardianData: "", + }, + { + name: "draining has guardian auth", + query: ` + mutation { + draining(enable: false) { + response { + code + message + } + } + }`, + queryName: "draining", + testGuardianAccess: true, + guardianErr: "", + guardianData: `{ + "draining": { + "response": { + "code": "Success", + "message": "draining mode has been set to false" + } + } + }`, + }, + { + name: "export has guardian auth", + query: ` + mutation { + export(input: {format: "invalid"}) { + response { + code + message + } + } + }`, + queryName: "export", + testGuardianAccess: true, + guardianErr: "invalid export format: invalid", + guardianData: `{"export": null}`, + }, + { + name: "restore has guardian auth", + query: ` + mutation { + restore(input: {location: "", backupId: "", encryptionKeyFile: ""}) { + code + } + }`, + queryName: "restore", + testGuardianAccess: true, + guardianErr: `The uri path: "" doesn't exist`, + guardianData: `{"restore": {"code": "Failure"}}`, + }, + { + name: "removeNode has guardian auth", + query: ` + mutation { + removeNode(input: {nodeId: 1, groupId: 2147483640}) { + response { + code + } + } + }`, + queryName: "removeNode", + testGuardianAccess: true, + guardianErr: "No group with groupId 2147483640 found", + guardianData: `{"removeNode": null}`, + }, + { + name: "moveTablet has guardian auth", + query: ` + mutation { + moveTablet(input: {tablet: "non_existent_pred", groupId: 2147483640}) { + response { + code + message + } + } + }`, + queryName: "moveTablet", + testGuardianAccess: true, + guardianErr: "group: [2147483640] is not a known group", + guardianData: `{"moveTablet": null}`, + }, + { + name: "assign has guardian auth", + query: ` + mutation { + assign(input: {what: UID, num: 0}) { + response { + startId + endId + readOnly + } + } + }`, + queryName: "assign", + testGuardianAccess: true, + guardianErr: "Nothing to be leased", + guardianData: `{"assign": null}`, + }, + { + name: "enterpriseLicense has guardian auth", + query: ` + mutation { + enterpriseLicense(input: {license: ""}) { + response { + code + } + } + }`, + queryName: "enterpriseLicense", + testGuardianAccess: true, + guardianErr: "while extracting enterprise details from the license: while decoding" + + " license file: EOF", + guardianData: `{"enterpriseLicense": null}`, + }, + { + name: "getGQLSchema has guardian auth", + query: ` + query { + getGQLSchema { + id + } + }`, + queryName: "getGQLSchema", + testGuardianAccess: true, + guardianErr: "", + guardianData: "", + }, + { + name: "updateGQLSchema has guardian auth", + query: ` + mutation { + updateGQLSchema(input: {set: {schema: ""}}) { + gqlSchema { + id + } + } + }`, + queryName: "updateGQLSchema", + testGuardianAccess: false, + guardianErr: "", + guardianData: "", + }, + { + name: "shutdown has guardian auth", + query: ` + mutation { + shutdown { + response { + code + message + } + } + }`, + queryName: "shutdown", + testGuardianAccess: false, + guardianErr: "", + guardianData: "", + }, + } + + for _, tcase := range tcases { + t.Run(tcase.name, func(t *testing.T) { + params := dgraphtest.GraphQLParams{Query: tcase.query} + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + resetUser(t, hc) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) + gqlResp, err := hc.RunGraphqlQuery(params, true) + require.Error(t, err) + // assert ACL error for non-guardians + assertNonGuardianFailure(t, tcase.queryName, !tcase.respIsArray, gqlResp, err) + + // for guardians, assert non-ACL error or success + if tcase.testGuardianAccess { + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + resp, err := hc.RunGraphqlQuery(params, true) + if tcase.guardianErr == "" { + require.NoError(t, err) + } else { + // require.Len(t, err, 1) + require.Contains(t, err.Error(), tcase.guardianErr) + } + + if tcase.guardianData != "" && err == nil { + require.JSONEq(t, tcase.guardianData, string(resp)) + } + } + }) + } +} + +func (asuite *AclTestSuite) TestFailedLogin() { + t := asuite.T() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + require.NoError(t, gc.DropAll()) + + client, _, err := asuite.dc.Client() + require.NoError(t, err) + + // User is not present + err = client.LoginIntoNamespace(ctx, userid, "simplepassword", x.GalaxyNamespace) + require.Error(t, err) + require.Contains(t, err.Error(), x.ErrorInvalidLogin.Error()) + + resetUser(t, hc) + // User is present + require.Error(t, client.LoginIntoNamespace(ctx, userid, "randomstring", x.GalaxyNamespace)) + require.Contains(t, err.Error(), x.ErrorInvalidLogin.Error()) +} + +func (asuite *AclTestSuite) TestWrongPermission() { + t := asuite.T() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + mu := &api.Mutation{SetNquads: []byte(` + _:dev "dgraph.type.Group" . + _:dev "dev" . + _:dev _:rule1 . + _:rule1 "name" . + _:rule1 "9" . + `), CommitNow: true} + _, err = gc.Mutate(mu) + + require.Error(t, err, "Setting permission to 9 should have returned error") + require.Contains(t, err.Error(), "Value for this predicate should be between 0 and 7") + + mu = &api.Mutation{SetNquads: []byte(` + _:dev "dgraph.type.Group" . + _:dev "dev" . + _:dev _:rule1 . + _:rule1 "name" . + _:rule1 "-1" . + `), CommitNow: true} + _, err = gc.Mutate(mu) + + require.Error(t, err, "Setting permission to -1 should have returned error") + require.Contains(t, err.Error(), "Value for this predicate should be between 0 and 7") +} diff --git a/ee/acl/acl_test.go b/ee/acl/acl_test.go index 503b4fe46ee..de5dd017cd9 100644 --- a/ee/acl/acl_test.go +++ b/ee/acl/acl_test.go @@ -1,5 +1,5 @@ -//go:build !oss && integration -// +build !oss,integration +//go:build (!oss && integration) || upgrade +// +build !oss,integration upgrade /* * Copyright 2023 Dgraph Labs, Inc. and Contributors @@ -16,18 +16,18 @@ package acl import ( "context" "encoding/json" - "errors" "fmt" "strconv" - "strings" "testing" "time" "github.com/golang/glog" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/dgraph-io/dgo/v230" "github.com/dgraph-io/dgo/v230/protos/api" + "github.com/dgraph-io/dgraph/dgraphtest" "github.com/dgraph-io/dgraph/testutil" "github.com/dgraph-io/dgraph/x" ) @@ -37,59 +37,6 @@ var ( userpassword = "simplepassword" ) -func makeRequestAndRefreshTokenIfNecessary(t *testing.T, token *testutil.HttpToken, - params testutil.GraphQLParams) *testutil.GraphQLResponse { - - resp := testutil.MakeGQLRequestWithAccessJwt(t, ¶ms, token.AccessJwt) - if len(resp.Errors) == 0 || !strings.Contains(resp.Errors.Error(), "Token is expired") { - return resp - } - var err error - newtoken, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: token.UserId, - Passwd: token.Password, - RefreshJwt: token.RefreshToken, - }) - require.NoError(t, err) - token.AccessJwt = newtoken.AccessJwt - token.RefreshToken = newtoken.RefreshToken - return testutil.MakeGQLRequestWithAccessJwt(t, ¶ms, token.AccessJwt) -} - -func createUser(t *testing.T, token *testutil.HttpToken, username, password string) *testutil.GraphQLResponse { - addUser := ` - mutation addUser($name: String!, $pass: String!) { - addUser(input: [{name: $name, password: $pass}]) { - user { - name - } - } - }` - - params := testutil.GraphQLParams{ - Query: addUser, - Variables: map[string]interface{}{ - "name": username, - "pass": password, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - return resp -} - -func getCurrentUser(t *testing.T, token *testutil.HttpToken) *testutil.GraphQLResponse { - query := ` - query { - getCurrentUser { - name - } - }` - - resp := makeRequestAndRefreshTokenIfNecessary(t, token, testutil.GraphQLParams{Query: query}) - return resp -} - func checkUserCount(t *testing.T, resp []byte, expected int) { type Response struct { AddUser struct { @@ -104,200 +51,129 @@ func checkUserCount(t *testing.T, resp []byte, expected int) { require.Equal(t, expected, len(r.AddUser.User)) } -func deleteUser(t *testing.T, token *testutil.HttpToken, username string, - confirmDeletion bool) *testutil.GraphQLResponse { - - delUser := ` - mutation deleteUser($name: String!) { - deleteUser(filter: {name: {eq: $name}}) { - msg - numUids - } - }` - - params := testutil.GraphQLParams{ - Query: delUser, - Variables: map[string]interface{}{ - "name": username, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - - if confirmDeletion { - resp.RequireNoGraphQLErrors(t) - require.JSONEq(t, `{"deleteUser":{"msg":"Deleted","numUids":1}}`, string(resp.Data)) - } - return resp -} - -func deleteGroup(t *testing.T, token *testutil.HttpToken, name string, confirmDeletion bool) *testutil.GraphQLResponse { - delGroup := ` - mutation deleteGroup($name: String!) { - deleteGroup(filter: {name: {eq: $name}}) { - msg - numUids +func checkGroupCount(t *testing.T, resp []byte, expected int) { + type Response struct { + AddGroup struct { + Group []struct { + Name string + } } - }` - - params := testutil.GraphQLParams{ - Query: delGroup, - Variables: map[string]interface{}{ - "name": name, - }, } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - if confirmDeletion { - resp.RequireNoGraphQLErrors(t) - require.JSONEq(t, `{"deleteGroup":{"msg":"Deleted","numUids":1}}`, string(resp.Data)) - } - return resp -} - -func deleteUsingNQuad(userClient *dgo.Dgraph, sub, pred, val string) (*api.Response, error) { - ctx := context.Background() - txn := userClient.NewTxn() - mutString := fmt.Sprintf("%s %s %s .", sub, pred, val) - mutation := &api.Mutation{ - DelNquads: []byte(mutString), - CommitNow: true, - } - return txn.Mutate(ctx, mutation) -} - -func TestInvalidGetUser(t *testing.T) { - currentUser := getCurrentUser(t, &testutil.HttpToken{AccessJwt: "invalid Token"}) - require.Equal(t, `{"getCurrentUser":null}`, string(currentUser.Data)) - require.Equal(t, x.GqlErrorList{{ - Message: "couldn't rewrite query getCurrentUser because unable to parse jwt token: token" + - " contains an invalid number of segments", - Path: []interface{}{"getCurrentUser"}, - }}, currentUser.Errors) -} - -func TestPasswordReturn(t *testing.T) { - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - query := ` - query { - getCurrentUser { - name - password - } - }` - - resp := makeRequestAndRefreshTokenIfNecessary(t, token, testutil.GraphQLParams{Query: query}) - require.Equal(t, resp.Errors, x.GqlErrorList{{ - Message: `Cannot query field "password" on type "User".`, - Locations: []x.Location{{ - Line: 5, - Column: 4, - }}, - }}) + var r Response + require.NoError(t, json.Unmarshal(resp, &r)) + require.Equal(t, expected, len(r.AddGroup.Group)) } -func TestGetCurrentUser(t *testing.T) { - token := testutil.GrootHttpLogin(adminEndpoint) - - currentUser := getCurrentUser(t, token) - currentUser.RequireNoGraphQLErrors(t) - require.Equal(t, string(currentUser.Data), `{"getCurrentUser":{"name":"groot"}}`) - +func (asuite *AclTestSuite) TestGetCurrentUser() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace), "login failed") + currentUser, err := hc.GetCurrentUser() + require.NoError(t, err) + require.Equal(t, currentUser, "groot") // clean up the user to allow repeated running of this test userid := "hamilton" - deleteUserResp := deleteUser(t, token, userid, false) - deleteUserResp.RequireNoGraphQLErrors(t) + require.NoError(t, hc.DeleteUser(userid), "error while deleteing user") glog.Infof("cleaned up db user state") - resp := createUser(t, token, userid, userpassword) - resp.RequireNoGraphQLErrors(t) - checkUserCount(t, resp.Data, 1) + user, err := hc.CreateUser(userid, userpassword) + require.NoError(t, err) + require.Equal(t, userid, user) - newToken, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: userid, - Passwd: userpassword, - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - currentUser = getCurrentUser(t, newToken) - currentUser.RequireNoGraphQLErrors(t) - require.Equal(t, string(currentUser.Data), `{"getCurrentUser":{"name":"hamilton"}}`) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, + x.GalaxyNamespace), "login failed") + currentUser, err = hc.GetCurrentUser() + require.NoError(t, err) + require.Equal(t, currentUser, "hamilton") } -func TestCreateAndDeleteUsers(t *testing.T) { - resetUser(t) - +func (asuite *AclTestSuite) TestCreateAndDeleteUsers() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + resetUser(t, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) // adding the user again should fail - token := testutil.GrootHttpLogin(adminEndpoint) - resp := createUser(t, token, userid, userpassword) - require.Equal(t, 1, len(resp.Errors)) - require.Equal(t, "couldn't rewrite mutation addUser because failed to rewrite mutation payload because id"+ - " alice already exists for field name inside type User", resp.Errors[0].Message) - checkUserCount(t, resp.Data, 0) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + user, err := hc.CreateUser(userid, userpassword) + require.Error(t, err) + require.Contains(t, err.Error(), "couldn't rewrite mutation addUser because failed to rewrite "+ + "mutation payload because id alice already exists for field name inside type User") + require.Equal(t, "", user) // delete the user - _ = deleteUser(t, token, userid, true) - - resp = createUser(t, token, userid, userpassword) - resp.RequireNoGraphQLErrors(t) - // now we should be able to create the user again - checkUserCount(t, resp.Data, 1) + require.NoError(t, hc.DeleteUser(userid), "error while deleteing user") + user, err = hc.CreateUser(userid, userpassword) + require.NoError(t, err) + require.Equal(t, userid, user) } -func resetUser(t *testing.T) { - token := testutil.GrootHttpLogin(adminEndpoint) +func resetUser(t *testing.T, hc *dgraphtest.HTTPClient) { + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // clean up the user to allow repeated running of this test - deleteUserResp := deleteUser(t, token, userid, false) - deleteUserResp.RequireNoGraphQLErrors(t) + require.NoError(t, hc.DeleteUser(userid), "error while deleteing user") glog.Infof("deleted user") - resp := createUser(t, token, userid, userpassword) - resp.RequireNoGraphQLErrors(t) - checkUserCount(t, resp.Data, 1) + user, err := hc.CreateUser(userid, userpassword) + require.NoError(t, err) + require.Equal(t, userid, user) glog.Infof("created user") } -func TestPreDefinedPredicates(t *testing.T) { +func (asuite *AclTestSuite) TestPreDefinedPredicates() { + t := asuite.T() // This test uses the groot account to ensure that pre-defined predicates // cannot be altered even if the permissions allow it. - dg1, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err, "Error while getting a dgraph client") - - alterPreDefinedPredicates(t, dg1) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + ctx := context.Background() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + alterPreDefinedPredicates(t, gc.Dgraph) } -func TestPreDefinedTypes(t *testing.T) { +func (asuite *AclTestSuite) TestPreDefinedTypes() { + t := asuite.T() // This test uses the groot account to ensure that pre-defined types // cannot be altered even if the permissions allow it. - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err, "Error while getting a dgraph client") + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + ctx := context.Background() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - alterPreDefinedTypes(t, dg) + alterPreDefinedTypes(t, gc.Dgraph) } -func TestAuthorization(t *testing.T) { +func (asuite *AclTestSuite) TestAuthorization() { + t := asuite.T() if testing.Short() { t.Skip("skipping because -short=true") } - glog.Infof("testing with port 9180") - dg1, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - if err != nil { - t.Fatalf("Error while getting a dgraph client: %v", err) - } - testAuthorization(t, dg1) + glog.Infof("testing with port 9080") + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + testAuthorization(t, gc, hc, asuite) glog.Infof("done") } -func getGrootAndGuardiansUid(t *testing.T, dg *dgo.Dgraph) (string, string) { - ctx := context.Background() - txn := dg.NewTxn() +func getGrootAndGuardiansUid(t *testing.T, gc *dgraphtest.GrpcClient) (string, string) { grootUserQuery := ` { grootUser(func:eq(dgraph.xid, "groot")){ @@ -314,7 +190,7 @@ func getGrootAndGuardiansUid(t *testing.T, dg *dgo.Dgraph) (string, string) { GrootUser []userNode `json:"grootUser"` } - resp, err := txn.Query(ctx, grootUserQuery) + resp, err := gc.Query(grootUserQuery) require.NoError(t, err, "groot user query failed") var userResp userQryResp @@ -323,7 +199,6 @@ func getGrootAndGuardiansUid(t *testing.T, dg *dgo.Dgraph) (string, string) { } grootUserUid := userResp.GrootUser[0].Uid - txn = dg.NewTxn() guardiansGroupQuery := ` { guardiansGroup(func:eq(dgraph.xid, "guardians")){ @@ -340,7 +215,7 @@ func getGrootAndGuardiansUid(t *testing.T, dg *dgo.Dgraph) (string, string) { GuardiansGroup []groupNode `json:"guardiansGroup"` } - resp, err = txn.Query(ctx, guardiansGroupQuery) + resp, err = gc.Query(guardiansGroupQuery) require.NoError(t, err, "guardians group query failed") var groupResp groupQryResp @@ -358,45 +233,51 @@ const timeout = 5 * time.Second const expireJwtSleep = 21 * time.Second -func testAuthorization(t *testing.T, dg *dgo.Dgraph) { - createAccountAndData(t, dg) - ctx := context.Background() - if err := dg.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace); err != nil { - t.Fatalf("unable to login using the account %v", userid) - } +func testAuthorization(t *testing.T, gc *dgraphtest.GrpcClient, hc *dgraphtest.HTTPClient, suite *AclTestSuite) { + createAccountAndData(t, gc, hc) + suite.Upgrade() + gc, cleanup, err := suite.dc.Client() + defer cleanup() + require.NoError(t, err) + hc, err = suite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + ctx := context.Background() + require.NoError(t, gc.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) // initially the query should return empty result, mutate and alter // operations should all fail when there are no rules defined on the predicates - queryPredicateWithUserAccount(t, dg, false) - mutatePredicateWithUserAccount(t, dg, true) - alterPredicateWithUserAccount(t, dg, true) - createGroupAndAcls(t, unusedGroup, false) + queryWithShouldFail(t, gc, false, query) + mutatePredicateWithUserAccount(t, gc, true) + alterPredicateWithUserAccount(t, gc, true) + createGroupAndAcls(t, unusedGroup, false, hc) // wait for 5 seconds to ensure the new acl have reached all acl caches glog.Infof("Sleeping for acl caches to be refreshed") time.Sleep(defaultTimeToSleep) // now all these operations except query should fail since // there are rules defined on the unusedGroup - queryPredicateWithUserAccount(t, dg, false) - mutatePredicateWithUserAccount(t, dg, true) - alterPredicateWithUserAccount(t, dg, true) + queryWithShouldFail(t, gc, false, query) + mutatePredicateWithUserAccount(t, gc, true) + alterPredicateWithUserAccount(t, gc, true) // create the dev group and add the user to it - createGroupAndAcls(t, devGroup, true) + createGroupAndAcls(t, devGroup, true, hc) // wait for 5 seconds to ensure the new acl have reached all acl caches glog.Infof("Sleeping for acl caches to be refreshed") time.Sleep(defaultTimeToSleep) // now the operations should succeed again through the devGroup - queryPredicateWithUserAccount(t, dg, false) + queryWithShouldFail(t, gc, false, query) // sleep long enough (10s per the docker-compose.yml) // for the accessJwt to expire in order to test auto login through refresh jwt glog.Infof("Sleeping for accessJwt to expire") time.Sleep(expireJwtSleep) - mutatePredicateWithUserAccount(t, dg, false) + mutatePredicateWithUserAccount(t, gc, false) glog.Infof("Sleeping for accessJwt to expire") time.Sleep(expireJwtSleep) - alterPredicateWithUserAccount(t, dg, false) + alterPredicateWithUserAccount(t, gc, false) } var predicateToRead = "predicate_to_read" @@ -482,10 +363,8 @@ func alterPreDefinedTypes(t *testing.T, dg *dgo.Dgraph) { "type dgraph.type.Group is pre-defined and is not allowed to be dropped") } -func queryPredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool) { - ctx := context.Background() - txn := dg.NewTxn() - _, err := txn.Query(ctx, query) +func queryWithShouldFail(t *testing.T, gc *dgraphtest.GrpcClient, shouldFail bool, query string) { + _, err := gc.Query(query) if shouldFail { require.Error(t, err, "the query should have failed") } else { @@ -493,26 +372,9 @@ func queryPredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool } } -func querySchemaWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool) { - ctx := context.Background() - txn := dg.NewTxn() - _, err := txn.Query(ctx, schemaQuery) - - if shouldFail { - require.Error(t, err, "the query should have failed") - } else { - require.NoError(t, err, "the query should have succeeded") - } -} - -func mutatePredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool) { - ctx := context.Background() - txn := dg.NewTxn() - _, err := txn.Mutate(ctx, &api.Mutation{ - CommitNow: true, - SetNquads: []byte(fmt.Sprintf(`_:a <%s> "string" .`, predicateToWrite)), - }) - +func mutatePredicateWithUserAccount(t *testing.T, gc *dgraphtest.GrpcClient, shouldFail bool) { + mu := &api.Mutation{SetNquads: []byte(fmt.Sprintf(`_:a <%s> "string" .`, predicateToWrite)), CommitNow: true} + _, err := gc.Mutate(mu) if shouldFail { require.Error(t, err, "the mutation should have failed") } else { @@ -520,9 +382,9 @@ func mutatePredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail boo } } -func alterPredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool) { +func alterPredicateWithUserAccount(t *testing.T, gc *dgraphtest.GrpcClient, shouldFail bool) { ctx := context.Background() - err := dg.Alter(ctx, &api.Operation{ + err := gc.Alter(ctx, &api.Operation{ Schema: fmt.Sprintf(`%s: int .`, predicateToAlter), }) if shouldFail { @@ -532,19 +394,14 @@ func alterPredicateWithUserAccount(t *testing.T, dg *dgo.Dgraph, shouldFail bool } } -func createAccountAndData(t *testing.T, dg *dgo.Dgraph) { +func createAccountAndData(t *testing.T, gc *dgraphtest.GrpcClient, hc *dgraphtest.HTTPClient) { // use the groot account to clean the database ctx := context.Background() - if err := dg.LoginIntoNamespace(ctx, x.GrootId, "password", x.GalaxyNamespace); err != nil { - t.Fatalf("unable to login using the groot account:%v", err) - } - op := api.Operation{ - DropAll: true, - } - if err := dg.Alter(ctx, &op); err != nil { - t.Fatalf("Unable to cleanup db:%v", err) - } - require.NoError(t, dg.Alter(ctx, &api.Operation{ + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + require.NoError(t, gc.DropAll(), "Unable to cleanup db") + require.NoError(t, gc.Alter(ctx, &api.Operation{ Schema: fmt.Sprintf(`%s: string @index(exact) .`, predicateToRead), })) // wait for 5 seconds to ensure the new acl have reached all acl caches @@ -552,203 +409,11 @@ func createAccountAndData(t *testing.T, dg *dgo.Dgraph) { time.Sleep(defaultTimeToSleep) // create some data, e.g. user with name alice - resetUser(t) + resetUser(t, hc) - txn := dg.NewTxn() - _, err := txn.Mutate(ctx, &api.Mutation{ - SetNquads: []byte(fmt.Sprintf("_:a <%s> \"SF\" .", predicateToRead)), - }) + mu := &api.Mutation{SetNquads: []byte(fmt.Sprintf("_:a <%s> \"SF\" .", predicateToRead)), CommitNow: true} + _, err := gc.Mutate(mu) require.NoError(t, err) - require.NoError(t, txn.Commit(ctx)) -} - -func createGroup(t *testing.T, token *testutil.HttpToken, name string) []byte { - addGroup := ` - mutation addGroup($name: String!) { - addGroup(input: [{name: $name}]) { - group { - name - } - } - }` - - params := testutil.GraphQLParams{ - Query: addGroup, - Variables: map[string]interface{}{ - "name": name, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - return resp.Data -} - -func createGroupWithRules(t *testing.T, token *testutil.HttpToken, name string, rules []rule) *group { - queryParams := testutil.GraphQLParams{ - Query: ` - mutation addGroup($name: String!, $rules: [RuleRef]){ - addGroup(input: [ - { - name: $name - rules: $rules - } - ]) { - group { - name - rules { - predicate - permission - } - } - } - }`, - Variables: map[string]interface{}{ - "name": name, - "rules": rules, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, queryParams) - resp.RequireNoGraphQLErrors(t) - - var addGroupResp struct { - AddGroup struct { - Group []group - } - } - require.NoError(t, json.Unmarshal(resp.Data, &addGroupResp)) - require.Len(t, addGroupResp.AddGroup.Group, 1) - - return &addGroupResp.AddGroup.Group[0] -} - -func updateGroup(t *testing.T, token *testutil.HttpToken, name string, setRules []rule, - removeRules []string) *group { - queryParams := testutil.GraphQLParams{ - Query: ` - mutation updateGroup($name: String!, $set: SetGroupPatch, $remove: RemoveGroupPatch){ - updateGroup(input: { - filter: { - name: { - eq: $name - } - } - set: $set - remove: $remove - }) { - group { - name - rules { - predicate - permission - } - } - } - }`, - Variables: map[string]interface{}{ - "name": name, - "set": nil, - "remove": nil, - }, - } - if len(setRules) != 0 { - queryParams.Variables["set"] = map[string]interface{}{ - "rules": setRules, - } - } - if len(removeRules) != 0 { - queryParams.Variables["remove"] = map[string]interface{}{ - "rules": removeRules, - } - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, queryParams) - resp.RequireNoGraphQLErrors(t) - - var result struct { - UpdateGroup struct { - Group []group - } - } - require.NoError(t, json.Unmarshal(resp.Data, &result)) - require.Len(t, result.UpdateGroup.Group, 1) - - return &result.UpdateGroup.Group[0] -} - -func checkGroupCount(t *testing.T, resp []byte, expected int) { - type Response struct { - AddGroup struct { - Group []struct { - Name string - } - } - } - - var r Response - require.NoError(t, json.Unmarshal(resp, &r)) - require.Equal(t, expected, len(r.AddGroup.Group)) -} - -func addToGroup(t *testing.T, token *testutil.HttpToken, userName, group string) { - addUserToGroup := `mutation updateUser($name: String!, $group: String!) { - updateUser(input: { - filter: { - name: { - eq: $name - } - }, - set: { - groups: [ - { name: $group } - ] - } - }) { - user { - name - groups { - name - } - } - } - }` - - params := testutil.GraphQLParams{ - Query: addUserToGroup, - Variables: map[string]interface{}{ - "name": userName, - "group": group, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - - var result struct { - UpdateUser struct { - User []struct { - Name string - Groups []struct { - Name string - } - } - Name string - } - } - require.NoError(t, json.Unmarshal(resp.Data, &result)) - - // There should be a user in response. - require.Len(t, result.UpdateUser.User, 1) - // User's name must be - require.Equal(t, userName, result.UpdateUser.User[0].Name) - - var foundGroup bool - for _, usr := range result.UpdateUser.User { - for _, grp := range usr.Groups { - if grp.Name == group { - foundGroup = true - break - } - } - } - require.True(t, foundGroup) } type rule struct { @@ -761,82 +426,29 @@ type group struct { Rules []rule `json:"rules"` } -func addRulesToGroup(t *testing.T, token *testutil.HttpToken, group string, rules []rule) { - addRuleToGroup := `mutation updateGroup($name: String!, $rules: [RuleRef!]!) { - updateGroup(input: { - filter: { - name: { - eq: $name - } - }, - set: { - rules: $rules - } - }) { - group { - name - rules { - predicate - permission - } - } - } - }` - - params := testutil.GraphQLParams{ - Query: addRuleToGroup, - Variables: map[string]interface{}{ - "name": group, - "rules": rules, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - rulesb, err := json.Marshal(rules) - require.NoError(t, err) - expectedOutput := fmt.Sprintf(`{ - "updateGroup": { - "group": [ - { - "name": "%s", - "rules": %s - } - ] - } - }`, group, rulesb) - testutil.CompareJSON(t, expectedOutput, string(resp.Data)) -} - -func createGroupAndAcls(t *testing.T, group string, addUserToGroup bool) { - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - +func createGroupAndAcls(t *testing.T, group string, addUserToGroup bool, hc *dgraphtest.HTTPClient) { // create a new group - resp := createGroup(t, token, group) - checkGroupCount(t, resp, 1) + createdGroup, err := hc.CreateGroup(group) + require.NoError(t, err) + require.Equal(t, group, createdGroup) // add the user to the group if addUserToGroup { - addToGroup(t, token, userid, group) + require.NoError(t, hc.AddUserToGroup(userid, group)) } - rules := []rule{ + rules := []dgraphtest.AclRule{ { - predicateToRead, Read.Code, + Predicate: predicateToRead, Permission: Read.Code, }, { - queryAttr, Read.Code, + Predicate: queryAttr, Permission: Read.Code, }, { - predicateToWrite, Write.Code, + Predicate: predicateToWrite, Permission: Write.Code, }, { - predicateToAlter, Modify.Code, + Predicate: predicateToAlter, Permission: Modify.Code, }, } @@ -844,33 +456,50 @@ func createGroupAndAcls(t *testing.T, group string, addUserToGroup bool) { // also add read permission to the attribute queryAttr, which is used inside the query block // add WRITE permission on the predicateToWrite // add MODIFY permission on the predicateToAlter - addRulesToGroup(t, token, group, rules) + require.NoError(t, hc.AddRulesToGroup(group, rules, true)) } -func TestPredicatePermission(t *testing.T) { +func (asuite *AclTestSuite) TestPredicatePermission() { + t := asuite.T() if testing.Short() { t.Skip("skipping because -short=true") } + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + glog.Infof("testing with port 9080") + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - glog.Infof("testing with port 9180") - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - if err != nil { - t.Fatalf("Error while getting a dgraph client: %v", err) - } - createAccountAndData(t, dg) - ctx := context.Background() - err = dg.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace) - require.NoError(t, err, "Logging in with the current password should have succeeded") + createAccountAndData(t, gc, hc) + asuite.Upgrade() + gc, cleanup, err = asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - // Schema query is allowed to all logged in users. - querySchemaWithUserAccount(t, dg, false) + require.NoError(t, gc.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace), + "Logging in with the current password should have succeeded") + // Schema query is allowed to all logged in users. + queryWithShouldFail(t, gc, false, schemaQuery) // The query should return emptry response, alter and mutation // should be blocked when no rule is defined. - queryPredicateWithUserAccount(t, dg, false) - mutatePredicateWithUserAccount(t, dg, true) - alterPredicateWithUserAccount(t, dg, true) - createGroupAndAcls(t, unusedGroup, false) + queryWithShouldFail(t, gc, false, query) + mutatePredicateWithUserAccount(t, gc, true) + alterPredicateWithUserAccount(t, gc, true) + createGroupAndAcls(t, unusedGroup, false, hc) // Wait for 5 seconds to ensure the new acl have reached all acl caches. t.Logf("Sleeping for acl caches to be refreshed") @@ -878,120 +507,142 @@ func TestPredicatePermission(t *testing.T) { // The operations except query should fail when there is a rule defined, but the // current user is not allowed. - queryPredicateWithUserAccount(t, dg, false) - mutatePredicateWithUserAccount(t, dg, true) - alterPredicateWithUserAccount(t, dg, true) + queryWithShouldFail(t, gc, false, query) + mutatePredicateWithUserAccount(t, gc, true) + alterPredicateWithUserAccount(t, gc, true) // Schema queries should still succeed since they are not tied to specific predicates. - querySchemaWithUserAccount(t, dg, false) + queryWithShouldFail(t, gc, false, schemaQuery) } -func TestAccessWithoutLoggingIn(t *testing.T) { - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) +func (asuite *AclTestSuite) TestAccessWithoutLoggingIn() { + t := asuite.T() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - createAccountAndData(t, dg) - dg, err = testutil.DgraphClient(testutil.SockAddr) + createAccountAndData(t, gc, hc) + asuite.Upgrade() + gc, cleanup, err = asuite.dc.Client() require.NoError(t, err) + defer cleanup() // Without logging in, the anonymous user should be evaluated as if the user does not // belong to any group, and access should not be granted if there is no ACL rule defined // for a predicate. - queryPredicateWithUserAccount(t, dg, true) - mutatePredicateWithUserAccount(t, dg, true) - alterPredicateWithUserAccount(t, dg, true) + queryWithShouldFail(t, gc, true, query) + mutatePredicateWithUserAccount(t, gc, true) + alterPredicateWithUserAccount(t, gc, true) // Schema queries should fail if the user has not logged in. - querySchemaWithUserAccount(t, dg, true) + queryWithShouldFail(t, gc, true, schemaQuery) } -func TestUnauthorizedDeletion(t *testing.T) { +func (asuite *AclTestSuite) TestUnauthorizedDeletion() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() unAuthPred := "unauthorizedPredicate" - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - op := api.Operation{ - DropAll: true, - } - require.NoError(t, dg.Alter(ctx, &op)) + require.NoError(t, gc.DropAll()) - op = api.Operation{ + op := api.Operation{ Schema: fmt.Sprintf("%s: string @index(exact) .", unAuthPred), } - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - createGroup(t, token, devGroup) + require.NoError(t, gc.Alter(ctx, &op)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - addToGroup(t, token, userid, devGroup) + resetUser(t, hc) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(fmt.Sprintf("_:a <%s> \"testdata\" .", unAuthPred)), - CommitNow: true, - } - resp, err := txn.Mutate(ctx, mutation) + createdGroup, err := hc.CreateGroup(devGroup) require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) + mu := &api.Mutation{SetNquads: []byte(fmt.Sprintf("_:a <%s> \"testdata\" .", unAuthPred)), CommitNow: true} + resp, err := gc.Mutate(mu) + require.NoError(t, err) nodeUID, ok := resp.Uids["a"] require.True(t, ok) - addRulesToGroup(t, token, devGroup, []rule{{unAuthPred, 0}}) - - userClient, err := testutil.DgraphClient(testutil.SockAddr) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: unAuthPred, Permission: 0}}, true)) + asuite.Upgrade() + userClient, cleanup, err := asuite.dc.Client() require.NoError(t, err) + defer cleanup() time.Sleep(defaultTimeToSleep) - require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) - - _, err = deleteUsingNQuad(userClient, "<"+nodeUID+">", "<"+unAuthPred+">", "*") + mu = &api.Mutation{ + DelNquads: []byte(fmt.Sprintf("%s %s %s .", "<"+nodeUID+">", "<"+unAuthPred+">", "*")), + CommitNow: true, + } + _, err = userClient.Mutate(mu) require.Error(t, err) require.Contains(t, err.Error(), "PermissionDenied") } -func TestGuardianAccess(t *testing.T) { +func (asuite *AclTestSuite) TestGuardianAccess() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) + defer cleanup() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: "unauthpred: string @index(exact) ."} - require.NoError(t, dg.Alter(ctx, &op)) + require.NoError(t, gc.Dgraph.Alter(ctx, &op)) - addNewUserToGroup(t, "guardian", "guardianpass", "guardians") + user, err := hc.CreateUser("guardian", "guardianpass") + require.NoError(t, err) + require.Equal(t, "guardian", user) + require.NoError(t, hc.AddUserToGroup("guardian", "guardians")) - mutation := &api.Mutation{ + mu := &api.Mutation{ SetNquads: []byte("_:a \"testdata\" ."), CommitNow: true, } - resp, err := dg.NewTxn().Mutate(ctx, mutation) + resp, err := gc.Mutate(mu) require.NoError(t, err) nodeUID, ok := resp.Uids["a"] require.True(t, ok) time.Sleep(defaultTimeToSleep) - gClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + gClient, cleanup, err := asuite.dc.Client() require.NoError(t, err, "Error while creating client") require.NoError(t, gClient.LoginIntoNamespace(ctx, "guardian", "guardianpass", x.GalaxyNamespace)) - - mutString := fmt.Sprintf("<%s> \"testdata\" .", nodeUID) - mutation = &api.Mutation{SetNquads: []byte(mutString), CommitNow: true} - _, err = gClient.NewTxn().Mutate(ctx, mutation) + mu = &api.Mutation{ + SetNquads: []byte(fmt.Sprintf("<%s> \"testdata\" .", nodeUID)), + CommitNow: true, + } + _, err = gClient.Mutate(mu) require.NoError(t, err, "Error while mutating unauthorized predicate") query := ` @@ -1001,86 +652,40 @@ func TestGuardianAccess(t *testing.T) { } }` - resp, err = gClient.NewTxn().Query(ctx, query) + resp, err = gClient.Query(query) require.NoError(t, err, "Error while querying unauthorized predicate") require.Contains(t, string(resp.GetJson()), "uid") op = api.Operation{Schema: "unauthpred: int ."} require.NoError(t, gClient.Alter(ctx, &op), "Error while altering unauthorized predicate") + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - gqlResp := removeUserFromGroup(t, "guardian", "guardians") - gqlResp.RequireNoGraphQLErrors(t) - expectedOutput := `{"updateUser":{"user":[{"name":"guardian","groups":[]}]}}` - require.JSONEq(t, expectedOutput, string(gqlResp.Data)) + require.NoError(t, hc.RemoveUserFromGroup("guardian", "guardians")) _, err = gClient.NewTxn().Query(ctx, query) require.Error(t, err, "Query succeeded. It should have failed.") } -func addNewUserToGroup(t *testing.T, userName, password, groupName string) { - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - - resp := createUser(t, token, userName, password) - resp.RequireNoGraphQLErrors(t) - checkUserCount(t, resp.Data, 1) - - addToGroup(t, token, userName, groupName) -} - -func removeUserFromGroup(t *testing.T, userName, groupName string) *testutil.GraphQLResponse { - removeUserGroups := `mutation updateUser($name: String!, $groupName: String!) { - updateUser(input: { - filter: { - name: { - eq: $name - } - }, - remove: { - groups: [{ name: $groupName }] - } - }) { - user { - name - groups { - name - } - } - } - }` - - params := testutil.GraphQLParams{ - Query: removeUserGroups, - Variables: map[string]interface{}{ - "name": userName, - "groupName": groupName, - }, - } - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - return resp -} - -func TestQueryRemoveUnauthorizedPred(t *testing.T) { +func (asuite *AclTestSuite) TestQueryRemoveUnauthorizedPred() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + + require.NoError(t, gc.DropAll()) - testutil.DropAll(t, dg) op := api.Operation{Schema: ` name : string @index(exact) . nickname : string @index(exact) . @@ -1090,40 +695,34 @@ func TestQueryRemoveUnauthorizedPred(t *testing.T) { age: int } `} - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - createGroup(t, token, devGroup) - addToGroup(t, token, userid, devGroup) + require.NoError(t, gc.Alter(ctx, &op)) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "TypeName" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "TypeName" . - `), - CommitNow: true, - } - _, err = txn.Mutate(ctx, mutation) + resetUser(t, hc) + createdGroup, err := hc.CreateGroup(devGroup) require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) - // give read access of to alice - addRulesToGroup(t, token, devGroup, []rule{{"name", Read.Code}}) + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "TypeName" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "TypeName" . + `), CommitNow: true} - userClient, err := testutil.DgraphClient(testutil.SockAddr) + _, err = gc.Mutate(mu) + require.NoError(t, err) + + // give read access of to alice + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}}, true)) + asuite.Upgrade() + userClient, cleanup, err := asuite.dc.Client() + // defer cleanup() require.NoError(t, err) time.Sleep(defaultTimeToSleep) @@ -1211,19 +810,28 @@ func TestQueryRemoveUnauthorizedPred(t *testing.T) { for _, tc := range tests { tc := tc // capture range variable t.Run(tc.description, func(t *testing.T) { - t.Parallel() - testutil.PollTillPassOrTimeout(t, userClient, tc.input, tc.output, timeout) + // testify does not support subtests running in parallel with suite package + // t.Parallel() + testutil.PollTillPassOrTimeout(t, userClient.Dgraph, tc.input, tc.output, timeout) }) } } -func TestExpandQueryWithACLPermissions(t *testing.T) { +func (asuite *AclTestSuite) TestExpandQueryWithACLPermissions() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . @@ -1235,112 +843,101 @@ func TestExpandQueryWithACLPermissions(t *testing.T) { age: int } `} - require.NoError(t, dg.Alter(ctx, &op)) + require.NoError(t, gc.Alter(ctx, &op)) - resetUser(t) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + resetUser(t, hc) - createGroup(t, token, devGroup) - createGroup(t, token, sreGroup) + createdGroup, err := hc.CreateGroup(devGroup) + require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + createdGroup, err = hc.CreateGroup(sreGroup) + require.NoError(t, err) + require.Equal(t, sreGroup, createdGroup) + require.NoError(t, hc.AddRulesToGroup(sreGroup, []dgraphtest.AclRule{{Predicate: "age", Permission: Read.Code}, + {Predicate: "name", Permission: Write.Code}}, true)) - addRulesToGroup(t, token, sreGroup, []rule{{"age", Read.Code}, {"name", Write.Code}}) - addToGroup(t, token, userid, devGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "TypeName" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "TypeName" . - `), - CommitNow: true, - } - _, err = txn.Mutate(ctx, mutation) + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "TypeName" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "TypeName" . + `), CommitNow: true} + _, err = gc.Mutate(mu) require.NoError(t, err) query := "{me(func: has(name)){expand(_all_)}}" // Test that groot has access to all the predicates - resp, err := dg.NewReadOnlyTxn().Query(ctx, query) + resp, err := gc.Query(query) require.NoError(t, err, "Error while querying data") testutil.CompareJSON(t, `{"me":[{"name":"RandomGuy","age":23, "nickname":"RG"},{"name":"RandomGuy2","age":25, "nickname":"RG2"}]}`, string(resp.GetJson())) - userClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() + require.NoError(t, err) + hc, err = asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) time.Sleep(defaultTimeToSleep) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) // Query via user when user has no permissions - testutil.PollTillPassOrTimeout(t, userClient, query, `{}`, timeout) - - // Login to groot to modify accesses (1) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + testutil.PollTillPassOrTimeout(t, userClient.Dgraph, query, `{}`, timeout) // Give read access of , write access of to dev - addRulesToGroup(t, token, devGroup, []rule{{"age", Write.Code}, {"name", Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: "age", Permission: Write.Code}, + {Predicate: "name", Permission: Read.Code}}, true)) - testutil.PollTillPassOrTimeout(t, userClient, query, + testutil.PollTillPassOrTimeout(t, userClient.Dgraph, query, `{"me":[{"name":"RandomGuy"},{"name":"RandomGuy2"}]}`, timeout) // Login to groot to modify accesses (2) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Add alice to sre group which has read access to and write access to - addToGroup(t, token, userid, sreGroup) + require.NoError(t, hc.AddUserToGroup(userid, sreGroup)) - testutil.PollTillPassOrTimeout(t, userClient, query, + testutil.PollTillPassOrTimeout(t, userClient.Dgraph, query, `{"me":[{"name":"RandomGuy","age":23},{"name":"RandomGuy2","age":25}]}`, timeout) // Login to groot to modify accesses (3) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Give read access of and , write access of to dev - addRulesToGroup(t, token, devGroup, []rule{{"age", Write.Code}, {"name", Read.Code}, {"nickname", Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: "age", Permission: Write.Code}, + {Predicate: "name", Permission: Read.Code}, {Predicate: "nickname", Permission: Read.Code}}, true)) - testutil.PollTillPassOrTimeout(t, userClient, query, + testutil.PollTillPassOrTimeout(t, userClient.Dgraph, query, `{"me":[{"name":"RandomGuy","age":23, "nickname":"RG"},{"name":"RandomGuy2","age":25, "nickname":"RG2"}]}`, timeout) - } -func TestDeleteQueryWithACLPermissions(t *testing.T) { + +func (asuite *AclTestSuite) TestDeleteQueryWithACLPermissions() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . @@ -1352,37 +949,26 @@ func TestDeleteQueryWithACLPermissions(t *testing.T) { age: int } `} - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) + require.NoError(t, gc.Alter(ctx, &op)) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - - createGroup(t, token, devGroup) + resetUser(t, hc) + createdGroup, err := hc.CreateGroup(devGroup) + require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) - addToGroup(t, token, userid, devGroup) + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "Person" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "Person" . + `), CommitNow: true} - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "Person" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "Person" . - `), - CommitNow: true, - } - resp, err := txn.Mutate(ctx, mutation) + resp, err := gc.Mutate(mu) require.NoError(t, err) nodeUID := resp.Uids["a"] @@ -1391,7 +977,7 @@ func TestDeleteQueryWithACLPermissions(t *testing.T) { }}` // Test that groot has access to all the predicates - resp, err = dg.NewReadOnlyTxn().Query(ctx, query) + resp, err = gc.Query(query) require.NoError(t, err, "Error while querying data") testutil.CompareJSON( t, @@ -1400,55 +986,68 @@ func TestDeleteQueryWithACLPermissions(t *testing.T) { ) // Give Write Access to alice for name and age predicate - addRulesToGroup(t, token, devGroup, []rule{{"name", Write.Code}, {"age", Write.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: "name", Permission: Write.Code}, + {Predicate: "age", Permission: Write.Code}}, true)) + asuite.Upgrade() + gc, _, err = asuite.dc.Client() + require.NoError(t, err) + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - userClient, err := testutil.DgraphClient(testutil.SockAddr) + userClient, _, err := asuite.dc.Client() require.NoError(t, err) time.Sleep(defaultTimeToSleep) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) + mu = &api.Mutation{DelNquads: []byte(fmt.Sprintf("%s %s %s .", "<"+nodeUID+">", "*", "*")), CommitNow: true} // delete S * * (user now has permission to name and age) - _, err = deleteUsingNQuad(userClient, "<"+nodeUID+">", "*", "*") + _, err = userClient.Mutate(mu) require.NoError(t, err) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - resp, err = dg.NewReadOnlyTxn().Query(ctx, query) + resp, err = gc.Query(query) require.NoError(t, err, "Error while querying data") // Only name and age predicates got deleted via user - alice testutil.CompareJSON(t, `{"q1":[{"nickname": "RG"},{"name":"RandomGuy2","age":25, "nickname": "RG2"}]}`, string(resp.GetJson())) // Give write access of to dev - addRulesToGroup(t, token, devGroup, []rule{{"name", Write.Code}, {"age", Write.Code}, {"dgraph.type", Write.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: "name", Permission: Write.Code}, + {Predicate: "age", Permission: Write.Code}, {Predicate: "dgraph.type", Permission: Write.Code}}, true)) time.Sleep(defaultTimeToSleep) // delete S * * (user now has permission to name, age and dgraph.type) - _, err = deleteUsingNQuad(userClient, "<"+nodeUID+">", "*", "*") + mu = &api.Mutation{DelNquads: []byte(fmt.Sprintf("%s %s %s .", "<"+nodeUID+">", "*", "*")), CommitNow: true} + _, err = userClient.Mutate(mu) require.NoError(t, err) - resp, err = dg.NewReadOnlyTxn().Query(ctx, query) + resp, err = gc.Query(query) require.NoError(t, err, "Error while querying data") // Because alise had permission to dgraph.type the node reference has been deleted testutil.CompareJSON(t, `{"q1":[{"name":"RandomGuy2","age":25, "nickname": "RG2"}]}`, string(resp.GetJson())) - } -func TestValQueryWithACLPermissions(t *testing.T) { +func (asuite *AclTestSuite) TestValQueryWithACLPermissions() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . @@ -1460,39 +1059,28 @@ func TestValQueryWithACLPermissions(t *testing.T) { age: int } `} - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, gc.Alter(ctx, &op)) - createGroup(t, token, devGroup) + resetUser(t, hc) + createdGroup, err := hc.CreateGroup(devGroup) + require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) // createGroup(t, accessJwt, sreGroup) - // addRulesToGroup(t, accessJwt, sreGroup, []rule{{"age", Read.Code}, {"name", Write.Code}}) - addToGroup(t, token, userid, devGroup) + // addRulesToGroup( accessJwt, sreGroup, []rule{{"age", Read.Code}, {"name", Write.Code}, true}) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "TypeName" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "TypeName" . - `), - CommitNow: true, - } - _, err = txn.Mutate(ctx, mutation) + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "TypeName" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "TypeName" . + `), CommitNow: true} + _, err = gc.Mutate(mu) require.NoError(t, err) query := `{q1(func: has(name)){ @@ -1505,7 +1093,7 @@ func TestValQueryWithACLPermissions(t *testing.T) { }}` // Test that groot has access to all the predicates - resp, err := dg.NewReadOnlyTxn().Query(ctx, query) + resp, err := gc.Query(query) require.NoError(t, err, "Error while querying data") testutil.CompareJSON(t, `{"q1":[{"name":"RandomGuy","age":23},{"name":"RandomGuy2","age":25}],`+ @@ -1608,8 +1196,13 @@ func TestValQueryWithACLPermissions(t *testing.T) { }, } - userClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() + require.NoError(t, err) + hc, err = asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) time.Sleep(defaultTimeToSleep) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) @@ -1618,51 +1211,39 @@ func TestValQueryWithACLPermissions(t *testing.T) { for _, tc := range tests { desc := tc.descriptionNoPerm t.Run(desc, func(t *testing.T) { - resp, err := userClient.NewTxn().Query(ctx, tc.input) + resp, err := userClient.Query(tc.input) require.NoError(t, err) testutil.CompareJSON(t, tc.outputNoPerm, string(resp.Json)) }) } - // Login to groot to modify accesses (1) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - // Give read access of to dev - addRulesToGroup(t, token, devGroup, []rule{{"name", Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}}, true)) time.Sleep(defaultTimeToSleep) for _, tc := range tests { desc := tc.descriptionNamePerm t.Run(desc, func(t *testing.T) { - resp, err := userClient.NewTxn().Query(ctx, tc.input) + resp, err := userClient.Query(tc.input) require.NoError(t, err) testutil.CompareJSON(t, tc.outputNamePerm, string(resp.Json)) }) } // Login to groot to modify accesses (1) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Give read access of and to dev - addRulesToGroup(t, token, devGroup, []rule{{"name", Read.Code}, {"age", Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}, + {Predicate: "age", Permission: Read.Code}}, true)) time.Sleep(defaultTimeToSleep) for _, tc := range tests { desc := tc.descriptionNameAgePerm t.Run(desc, func(t *testing.T) { - resp, err := userClient.NewTxn().Query(ctx, tc.input) + resp, err := userClient.Query(tc.input) require.NoError(t, err) testutil.CompareJSON(t, tc.outputNameAgePerm, string(resp.Json)) }) @@ -1670,13 +1251,21 @@ func TestValQueryWithACLPermissions(t *testing.T) { } -func TestAllPredsPermission(t *testing.T) { +func (asuite *AclTestSuite) TestAllPredsPermission() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . @@ -1688,36 +1277,26 @@ func TestAllPredsPermission(t *testing.T) { age: int } `} - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, gc.Alter(ctx, &op)) - createGroup(t, token, devGroup) - addToGroup(t, token, userid, devGroup) + resetUser(t, hc) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "TypeName" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "TypeName" . - `), - CommitNow: true, - } - _, err = txn.Mutate(ctx, mutation) + createdGroup, err := hc.CreateGroup(devGroup) + require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) + + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "TypeName" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "TypeName" . + `), CommitNow: true} + _, err = gc.Mutate(mu) require.NoError(t, err) query := `{q1(func: has(name)){ @@ -1730,7 +1309,7 @@ func TestAllPredsPermission(t *testing.T) { }}` // Test that groot has access to all the predicates - resp, err := dg.NewReadOnlyTxn().Query(ctx, query) + resp, err := gc.Query(query) require.NoError(t, err, "Error while querying data") testutil.CompareJSON(t, `{"q1":[{"name":"RandomGuy","age":23},{"name":"RandomGuy2","age":25}],`+ @@ -1770,7 +1349,8 @@ func TestAllPredsPermission(t *testing.T) { }, } - userClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() require.NoError(t, err) time.Sleep(defaultTimeToSleep) @@ -1780,65 +1360,69 @@ func TestAllPredsPermission(t *testing.T) { for _, tc := range tests { desc := tc.descriptionNoPerm t.Run(desc, func(t *testing.T) { - resp, err := userClient.NewTxn().Query(ctx, tc.input) + resp, err := userClient.Query(tc.input) require.NoError(t, err) testutil.CompareJSON(t, tc.outputNoPerm, string(resp.Json)) }) } // Login to groot to modify accesses (1) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Give read access of all predicates to dev - addRulesToGroup(t, token, devGroup, []rule{{"dgraph.all", Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "dgraph.all", Permission: Read.Code}}, true)) time.Sleep(defaultTimeToSleep) for _, tc := range tests { desc := tc.descriptionNameAgePerm t.Run(desc, func(t *testing.T) { - resp, err := userClient.NewTxn().Query(ctx, tc.input) + resp, err := userClient.Query(tc.input) require.NoError(t, err) testutil.CompareJSON(t, tc.outputNameAgePerm, string(resp.Json)) }) } // Mutation shall fail. - mutation = &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "TypeName" . - `), - CommitNow: true, - } - txn = userClient.NewTxn() - _, err = txn.Mutate(ctx, mutation) + + mu = &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "TypeName" . + `), CommitNow: true} + + _, err = userClient.Mutate(mu) require.Error(t, err) require.Contains(t, err.Error(), "unauthorized to mutate") // Give write access of all predicates to dev. Now mutation should succeed. - addRulesToGroup(t, token, devGroup, []rule{{"dgraph.all", Write.Code | Read.Code}}) + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "dgraph.all", Permission: Write.Code | Read.Code}}, true)) time.Sleep(defaultTimeToSleep) - txn = userClient.NewTxn() - _, err = txn.Mutate(ctx, mutation) + + _, err = userClient.Mutate(mu) require.NoError(t, err) } -func TestNewACLPredicates(t *testing.T) { +func (asuite *AclTestSuite) TestNewACLPredicates() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - userClient, err := testutil.DgraphClient(testutil.SockAddr) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() require.NoError(t, err) time.Sleep(defaultTimeToSleep) @@ -1878,7 +1462,8 @@ func TestNewACLPredicates(t *testing.T) { for _, tc := range queryTests { tc := tc // capture range variable t.Run(tc.description, func(t *testing.T) { - t.Parallel() + // testify does not support subtests running in parallel with suite package + // t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() @@ -1909,10 +1494,7 @@ func TestNewACLPredicates(t *testing.T) { } for _, tc := range mutationTests { t.Run(tc.description, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) - defer cancel() - - _, err := userClient.NewTxn().Mutate(ctx, &api.Mutation{ + _, err := userClient.Mutate(&api.Mutation{ SetNquads: []byte(tc.input), CommitNow: true, }) @@ -1921,85 +1503,56 @@ func TestNewACLPredicates(t *testing.T) { } } -func removeRuleFromGroup(t *testing.T, token *testutil.HttpToken, group string, - rulePredicate string) *testutil.GraphQLResponse { - removeRuleFromGroup := `mutation updateGroup($name: String!, $rules: [String!]!) { - updateGroup(input: { - filter: { - name: { - eq: $name - } - }, - remove: { - rules: $rules - } - }) { - group { - name - rules { - predicate - permission - } - } - } - }` - - params := testutil.GraphQLParams{ - Query: removeRuleFromGroup, - Variables: map[string]interface{}{ - "name": group, - "rules": []string{rulePredicate}, - }, - } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - return resp -} - -func TestDeleteRule(t *testing.T) { +func (asuite *AclTestSuite) TestDeleteRule() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - _ = addDataAndRules(ctx, t, dg) - - userClient, err := testutil.DgraphClient(testutil.SockAddr) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + userClient, _, err := asuite.dc.Client() require.NoError(t, err) time.Sleep(defaultTimeToSleep) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) queryName := "{me(func: has(name)) {name}}" - resp, err := userClient.NewReadOnlyTxn().Query(ctx, queryName) + resp, err := userClient.Query(queryName) require.NoError(t, err, "Error while querying data") testutil.CompareJSON(t, `{"me":[{"name":"RandomGuy"},{"name":"RandomGuy2"}]}`, string(resp.GetJson())) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - removeRuleFromGroup(t, token, devGroup, "name") + require.NoError(t, hc.RemovePredicateFromGroup(devGroup, "name")) time.Sleep(defaultTimeToSleep) - resp, err = userClient.NewReadOnlyTxn().Query(ctx, queryName) + resp, err = userClient.Query(queryName) require.NoError(t, err, "Error while querying data") testutil.CompareJSON(t, string(resp.GetJson()), `{}`) } -func addDataAndRules(ctx context.Context, t *testing.T, dg *dgo.Dgraph) map[string]string { - testutil.DropAll(t, dg) +func addDataAndRules(ctx context.Context, t *testing.T, gc *dgraphtest.GrpcClient, hc *dgraphtest.HTTPClient) { + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . nickname : string @index(exact) . `} - require.NoError(t, dg.Alter(ctx, &op)) + require.NoError(t, gc.Alter(ctx, &op)) - resetUser(t) + resetUser(t, hc) // TODO - We should be adding this data using the GraphQL API. // We create three groups here, dev, dev-a and dev-b and add alice to two of them. @@ -2019,10 +1572,8 @@ func addDataAndRules(ctx context.Context, t *testing.T, dg *dgo.Dgraph) map[stri _:r2 "nickname" . _:r2 "2" . ` - resp, err := dg.NewTxn().Mutate(ctx, &api.Mutation{ - SetNquads: []byte(devGroupMut), - CommitNow: true, - }) + mu := &api.Mutation{SetNquads: []byte(devGroupMut), CommitNow: true} + _, err := gc.Mutate(mu) require.NoError(t, err, "Error adding group and permissions") idQuery := fmt.Sprintf(` @@ -2036,7 +1587,7 @@ func addDataAndRules(ctx context.Context, t *testing.T, dg *dgo.Dgraph) map[stri Predicate: "dgraph.user.group", ObjectId: "uid(gid)", } - _, err = dg.NewTxn().Do(ctx, &api.Request{ + _, err = gc.NewTxn().Do(ctx, &api.Request{ CommitNow: true, Query: idQuery, Mutations: []*api.Mutation{ @@ -2047,55 +1598,56 @@ func addDataAndRules(ctx context.Context, t *testing.T, dg *dgo.Dgraph) map[stri }) require.NoError(t, err, "Error adding user to dev group") - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "RG" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - `), - CommitNow: true, - } - _, err = dg.NewTxn().Mutate(ctx, mutation) + mu = &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "RG" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + `), CommitNow: true} + + _, err = gc.Mutate(mu) require.NoError(t, err) - return resp.GetUids() } -func TestNonExistentGroup(t *testing.T) { +func (asuite *AclTestSuite) TestNonExistentGroup() { + t := asuite.T() t.Skip() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() // This test won't return an error anymore as if an update in a GraphQL mutation doesn't find // anything to update then it just returns an empty result. - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err) - - testutil.DropAll(t, dg) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - addRulesToGroup(t, token, devGroup, []rule{{"name", Read.Code}}) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.DropAll()) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}}, true)) } -func TestQueryUserInfo(t *testing.T) { +func (asuite *AclTestSuite) TestQueryUserInfo() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: userid, - Passwd: userpassword, - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) gqlQuery := ` query { @@ -2114,13 +1666,11 @@ func TestQueryUserInfo(t *testing.T) { } } ` - - params := testutil.GraphQLParams{ + params := dgraphtest.GraphQLParams{ Query: gqlQuery, } - gqlResp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - gqlResp.RequireNoGraphQLErrors(t) - + gqlResp, err := hc.RunGraphqlQuery(params, true) + require.NoError(t, err) testutil.CompareJSON(t, ` { "queryUser": [ @@ -2157,7 +1707,7 @@ func TestQueryUserInfo(t *testing.T) { ] } ] - }`, string(gqlResp.Data)) + }`, string(gqlResp)) query := ` { @@ -2174,11 +1724,14 @@ func TestQueryUserInfo(t *testing.T) { } ` - userClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() require.NoError(t, err) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) - - resp, err := userClient.NewReadOnlyTxn().Query(ctx, query) + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) + resp, err := userClient.Query(query) require.NoError(t, err, "Error while querying ACL") testutil.CompareJSON(t, `{"me":[]}`, string(resp.GetJson())) @@ -2198,11 +1751,11 @@ func TestQueryUserInfo(t *testing.T) { } ` - params = testutil.GraphQLParams{ + params = dgraphtest.GraphQLParams{ Query: gqlQuery, } - gqlResp = makeRequestAndRefreshTokenIfNecessary(t, token, params) - gqlResp.RequireNoGraphQLErrors(t) + gqlResp, err = hc.RunGraphqlQuery(params, true) + require.NoError(t, err) // The user should only be able to see their group dev and themselves as the user. testutil.CompareJSON(t, `{ "queryGroup": [ @@ -2235,7 +1788,7 @@ func TestQueryUserInfo(t *testing.T) { } ] - }`, string(gqlResp.Data)) + }`, string(gqlResp)) gqlQuery = ` query { @@ -2252,55 +1805,55 @@ func TestQueryUserInfo(t *testing.T) { } ` - params = testutil.GraphQLParams{ + params = dgraphtest.GraphQLParams{ Query: gqlQuery, } - gqlResp = makeRequestAndRefreshTokenIfNecessary(t, token, params) - gqlResp.RequireNoGraphQLErrors(t) - testutil.CompareJSON(t, `{"getGroup": null}`, string(gqlResp.Data)) + gqlResp, err = hc.RunGraphqlQuery(params, true) + require.NoError(t, err) + testutil.CompareJSON(t, `{"getGroup": null}`, string(gqlResp)) } -func TestQueriesWithUserAndGroupOfSameName(t *testing.T) { +func (asuite *AclTestSuite) TestQueriesWithUserAndGroupOfSameName() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) // Creates a user -- alice - resetUser(t) + resetUser(t, hc) - txn := dg.NewTxn() - mutation := &api.Mutation{ - SetNquads: []byte(` - _:a "RandomGuy" . - _:a "23" . - _:a "RG" . - _:a "TypeName" . - _:b "RandomGuy2" . - _:b "25" . - _:b "RG2" . - _:b "TypeName" . - `), - CommitNow: true, - } - _, err = txn.Mutate(ctx, mutation) - require.NoError(t, err) + mu := &api.Mutation{SetNquads: []byte(` + _:a "RandomGuy" . + _:a "23" . + _:a "RG" . + _:a "TypeName" . + _:b "RandomGuy2" . + _:b "25" . + _:b "RG2" . + _:b "TypeName" . + `), CommitNow: true} - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + _, err = gc.Mutate(mu) + require.NoError(t, err) - createGroup(t, token, "alice") - addToGroup(t, token, userid, "alice") + createdGroup, err := hc.CreateGroup("alice") + require.NoError(t, err) + require.Equal(t, "alice", createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, "alice")) // add rules to groups - addRulesToGroup(t, token, "alice", []rule{{Predicate: "name", Permission: Read.Code}}) + require.NoError(t, hc.AddRulesToGroup("alice", + []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}}, true)) query := ` { @@ -2310,22 +1863,27 @@ func TestQueriesWithUserAndGroupOfSameName(t *testing.T) { } } ` + asuite.Upgrade() + dc, cleanup, err := asuite.dc.Client() + defer cleanup() + require.NoError(t, dc.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) - dc := testutil.DgClientWithLogin(t, userid, userpassword, x.GalaxyNamespace) - testutil.PollTillPassOrTimeout(t, dc, query, `{"q":[{"name":"RandomGuy"},{"name":"RandomGuy2"}]}`, timeout) + testutil.PollTillPassOrTimeout(t, dc.Dgraph, query, `{"q":[{"name":"RandomGuy"},{"name":"RandomGuy2"}]}`, timeout) } -func TestQueriesForNonGuardianUserWithoutGroup(t *testing.T) { +func (asuite *AclTestSuite) TestQueriesForNonGuardianUserWithoutGroup() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Create a new user without any groups, queryGroup should return an empty result. - resetUser(t) + resetUser(t, hc) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: userid, - Passwd: userpassword, - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(userid, userpassword, x.GalaxyNamespace)) gqlQuery := ` query { @@ -2338,12 +1896,12 @@ func TestQueriesForNonGuardianUserWithoutGroup(t *testing.T) { } ` - params := testutil.GraphQLParams{ + params := dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - testutil.CompareJSON(t, `{"queryGroup": []}`, string(resp.Data)) + gqlResp, err := hc.RunGraphqlQuery(params, true) + require.NoError(t, err) + testutil.CompareJSON(t, `{"queryGroup": []}`, string(gqlResp)) gqlQuery = ` query { @@ -2356,15 +1914,18 @@ func TestQueriesForNonGuardianUserWithoutGroup(t *testing.T) { } ` - params = testutil.GraphQLParams{ + params = dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp = makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - testutil.CompareJSON(t, `{"queryUser": [{ "groups": [], "name": "alice"}]}`, string(resp.Data)) + gqlResp, err = hc.RunGraphqlQuery(params, true) + require.NoError(t, err) + testutil.CompareJSON(t, `{"queryUser": [{ "groups": [], "name": "alice"}]}`, string(gqlResp)) } -func TestSchemaQueryWithACL(t *testing.T) { +func (asuite *AclTestSuite) TestSchemaQueryWithACL() { + t := asuite.T() + ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) + defer cancel() schemaQuery := "schema{}" grootSchema := `{ "schema": [ @@ -2532,47 +2093,58 @@ func TestSchemaQueryWithACL(t *testing.T) { }` // guardian user should be able to view full schema - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) - testutil.DropAll(t, dg) - resp, err := dg.NewReadOnlyTxn().Query(context.Background(), schemaQuery) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.DropAll()) + resp, err := gc.Query(schemaQuery) require.NoError(t, err) require.JSONEq(t, grootSchema, string(resp.GetJson())) // add another user and some data for that user with permissions on predicates - resetUser(t) - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) - defer cancel() - addDataAndRules(ctx, t, dg) - time.Sleep(defaultTimeToSleep) // wait for ACL cache to refresh, otherwise it will be flaky test + resetUser(t, hc) + addDataAndRules(ctx, t, gc, hc) + time.Sleep(defaultTimeToSleep) // wait for ACL cache to refresh, otherwise it will be flaky test + asuite.Upgrade() // the other user should be able to view only the part of schema for which it has read access - dg, err = testutil.DgraphClient(testutil.SockAddr) + gc, _, err = asuite.dc.Client() require.NoError(t, err) - require.NoError(t, dg.LoginIntoNamespace(context.Background(), userid, userpassword, x.GalaxyNamespace)) - resp, err = dg.NewReadOnlyTxn().Query(context.Background(), schemaQuery) + require.NoError(t, gc.LoginIntoNamespace(context.Background(), userid, userpassword, x.GalaxyNamespace)) + resp, err = gc.Query(schemaQuery) require.NoError(t, err) require.JSONEq(t, aliceSchema, string(resp.GetJson())) } -func TestDeleteUserShouldDeleteUserFromGroup(t *testing.T) { - resetUser(t) - +func (asuite *AclTestSuite) TestDeleteUserShouldDeleteUserFromGroup() { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + t := asuite.T() + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) - addDataAndRules(ctx, t, dg) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + resetUser(t, hc) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: x.GrootId, - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - _ = deleteUser(t, token, userid, true) + require.NoError(t, hc.DeleteUser(userid)) gqlQuery := ` query { @@ -2582,12 +2154,12 @@ func TestDeleteUserShouldDeleteUserFromGroup(t *testing.T) { } ` - params := testutil.GraphQLParams{ + params := dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - require.JSONEq(t, `{"queryUser":[{"name":"groot"}]}`, string(resp.Data)) + gqlResp, err := hc.RunGraphqlQuery(params, true) + require.NoError(t, err) + require.JSONEq(t, `{"queryUser":[{"name":"groot"}]}`, string(gqlResp)) // The user should also be deleted from the dev group. gqlQuery = ` @@ -2601,11 +2173,11 @@ func TestDeleteUserShouldDeleteUserFromGroup(t *testing.T) { } ` - params = testutil.GraphQLParams{ + params = dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp = makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) + gqlResp, err = hc.RunGraphqlQuery(params, true) + require.NoError(t, err) testutil.CompareJSON(t, `{ "queryGroup": [ { @@ -2629,27 +2201,33 @@ func TestDeleteUserShouldDeleteUserFromGroup(t *testing.T) { "users": [] } ] - }`, string(resp.Data)) + }`, string(gqlResp)) } -func TestGroupDeleteShouldDeleteGroupFromUser(t *testing.T) { - resetUser(t) - +func (asuite *AclTestSuite) TestGroupDeleteShouldDeleteGroupFromUser() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) - addDataAndRules(ctx, t, dg) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + resetUser(t, hc) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: x.GrootId, - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - _ = deleteGroup(t, token, "dev-a", true) + require.NoError(t, hc.DeleteGroup("dev-a")) gqlQuery := ` query { @@ -2659,11 +2237,11 @@ func TestGroupDeleteShouldDeleteGroupFromUser(t *testing.T) { } ` - params := testutil.GraphQLParams{ + params := dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) + gqlResp, err := hc.RunGraphqlQuery(params, true) + require.NoError(t, err) testutil.CompareJSON(t, `{ "queryGroup": [ { @@ -2676,7 +2254,7 @@ func TestGroupDeleteShouldDeleteGroupFromUser(t *testing.T) { "name": "dev-b" } ] - }`, string(resp.Data)) + }`, string(gqlResp)) gqlQuery = ` query { @@ -2689,11 +2267,11 @@ func TestGroupDeleteShouldDeleteGroupFromUser(t *testing.T) { } ` - params = testutil.GraphQLParams{ + params = dgraphtest.GraphQLParams{ Query: gqlQuery, } - resp = makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) + gqlResp, err = hc.RunGraphqlQuery(params, true) + require.NoError(t, err) testutil.CompareJSON(t, `{ "getUser": { "name": "alice", @@ -2703,125 +2281,20 @@ func TestGroupDeleteShouldDeleteGroupFromUser(t *testing.T) { } ] } - }`, string(resp.Data)) -} - -func TestWrongPermission(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) - defer cancel() - - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err) - - ruleMutation := ` - _:dev "dgraph.type.Group" . - _:dev "dev" . - _:dev _:rule1 . - _:rule1 "name" . - _:rule1 "9" . - ` - - _, err = dg.NewTxn().Mutate(ctx, &api.Mutation{ - SetNquads: []byte(ruleMutation), - CommitNow: true, - }) - - require.Error(t, err, "Setting permission to 9 should have returned error") - require.Contains(t, err.Error(), "Value for this predicate should be between 0 and 7") - - ruleMutation = ` - _:dev "dgraph.type.Group" . - _:dev "dev" . - _:dev _:rule1 . - _:rule1 "name" . - _:rule1 "-1" . - ` - - _, err = dg.NewTxn().Mutate(ctx, &api.Mutation{ - SetNquads: []byte(ruleMutation), - CommitNow: true, - }) - - require.Error(t, err, "Setting permission to -1 should have returned error") - require.Contains(t, err.Error(), "Value for this predicate should be between 0 and 7") -} - -func TestHealthForAcl(t *testing.T) { - params := testutil.GraphQLParams{ - Query: ` - query { - health { - instance - address - lastEcho - status - version - uptime - group - } - }`, - } - - // assert errors for non-guardians - assertNonGuardianFailure(t, "health", false, params) - - // assert data for guardians - token := testutil.GrootHttpLogin(adminEndpoint) - - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - resp.RequireNoGraphQLErrors(t) - var guardianResp struct { - Health []struct { - Instance string - Address string - LastEcho int64 - Status string - Version string - UpTime int64 - Group string - } - } - err := json.Unmarshal(resp.Data, &guardianResp) - require.NoError(t, err, "health request failed") - - // we have 9 instances of alphas/zeros in teamcity environment - require.Len(t, guardianResp.Health, 9) - for _, v := range guardianResp.Health { - t.Logf("Got health: %+v\n", v) - require.Contains(t, []string{"alpha", "zero"}, v.Instance) - require.NotEmpty(t, v.Address) - require.NotEmpty(t, v.LastEcho) - require.Equal(t, "healthy", v.Status) - require.NotEmpty(t, v.Version) - require.NotEmpty(t, v.UpTime) - require.NotEmpty(t, v.Group) - } + }`, string(gqlResp)) } -func assertNonGuardianFailure(t *testing.T, queryName string, respIsNull bool, - params testutil.GraphQLParams) { - resetUser(t) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: userid, - Passwd: userpassword, - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - - require.Len(t, resp.Errors, 1) - require.Contains(t, resp.Errors[0].Message, "rpc error: code = PermissionDenied") - require.Contains(t, resp.Errors[0].Message, fmt.Sprintf( +func assertNonGuardianFailure(t *testing.T, queryName string, respIsNull bool, gqlResp []byte, err error) { + require.Contains(t, err.Error(), "rpc error: code = PermissionDenied") + require.Contains(t, err.Error(), fmt.Sprintf( "Only guardians are allowed access. User '%s' is not a member of guardians group.", userid)) - if len(resp.Data) != 0 { + if len(gqlResp) != 0 { queryVal := "null" if !respIsNull { queryVal = "[]" } - require.JSONEq(t, fmt.Sprintf(`{"%s": %s}`, queryName, queryVal), string(resp.Data)) + require.JSONEq(t, fmt.Sprintf(`{"%s": %s}`, queryName, queryVal), string(gqlResp)) } } @@ -2836,259 +2309,14 @@ type graphQLAdminEndpointTestCase struct { guardianData string } -func TestGuardianOnlyAccessForAdminEndpoints(t *testing.T) { - tcases := []graphQLAdminEndpointTestCase{ - { - name: "backup has guardian auth", - query: ` - mutation { - backup(input: {destination: ""}) { - response { - code - message - } - } - }`, - queryName: "backup", - testGuardianAccess: true, - guardianErr: "you must specify a 'destination' value", - guardianData: `{"backup": null}`, - }, - { - name: "listBackups has guardian auth", - query: ` - query { - listBackups(input: {location: ""}) { - backupId - } - }`, - queryName: "listBackups", - respIsArray: true, - testGuardianAccess: true, - guardianErr: `The uri path: "" doesn't exist`, - guardianData: `{"listBackups": []}`, - }, - { - name: "config update has guardian auth", - query: ` - mutation { - config(input: {cacheMb: -1}) { - response { - code - message - } - } - }`, - queryName: "config", - testGuardianAccess: true, - guardianErr: "cache_mb must be non-negative", - guardianData: `{"config": null}`, - }, - { - name: "config get has guardian auth", - query: ` - query { - config { - cacheMb - } - }`, - queryName: "config", - testGuardianAccess: true, - guardianErr: "", - guardianData: "", - }, - { - name: "draining has guardian auth", - query: ` - mutation { - draining(enable: false) { - response { - code - message - } - } - }`, - queryName: "draining", - testGuardianAccess: true, - guardianErr: "", - guardianData: `{ - "draining": { - "response": { - "code": "Success", - "message": "draining mode has been set to false" - } - } - }`, - }, - { - name: "export has guardian auth", - query: ` - mutation { - export(input: {format: "invalid"}) { - response { - code - message - } - } - }`, - queryName: "export", - testGuardianAccess: true, - guardianErr: "invalid export format: invalid", - guardianData: `{"export": null}`, - }, - { - name: "restore has guardian auth", - query: ` - mutation { - restore(input: {location: "", backupId: "", encryptionKeyFile: ""}) { - code - } - }`, - queryName: "restore", - testGuardianAccess: true, - guardianErr: `The uri path: "" doesn't exist`, - guardianData: `{"restore": {"code": "Failure"}}`, - }, - { - name: "removeNode has guardian auth", - query: ` - mutation { - removeNode(input: {nodeId: 1, groupId: 2147483640}) { - response { - code - } - } - }`, - queryName: "removeNode", - testGuardianAccess: true, - guardianErr: "No group with groupId 2147483640 found", - guardianData: `{"removeNode": null}`, - }, - { - name: "moveTablet has guardian auth", - query: ` - mutation { - moveTablet(input: {tablet: "non_existent_pred", groupId: 2147483640}) { - response { - code - message - } - } - }`, - queryName: "moveTablet", - testGuardianAccess: true, - guardianErr: "group: [2147483640] is not a known group", - guardianData: `{"moveTablet": null}`, - }, - { - name: "assign has guardian auth", - query: ` - mutation { - assign(input: {what: UID, num: 0}) { - response { - startId - endId - readOnly - } - } - }`, - queryName: "assign", - testGuardianAccess: true, - guardianErr: "Nothing to be leased", - guardianData: `{"assign": null}`, - }, - { - name: "enterpriseLicense has guardian auth", - query: ` - mutation { - enterpriseLicense(input: {license: ""}) { - response { - code - } - } - }`, - queryName: "enterpriseLicense", - testGuardianAccess: true, - guardianErr: "while extracting enterprise details from the license: while decoding" + - " license file: EOF", - guardianData: `{"enterpriseLicense": null}`, - }, - { - name: "getGQLSchema has guardian auth", - query: ` - query { - getGQLSchema { - id - } - }`, - queryName: "getGQLSchema", - testGuardianAccess: true, - guardianErr: "", - guardianData: "", - }, - { - name: "updateGQLSchema has guardian auth", - query: ` - mutation { - updateGQLSchema(input: {set: {schema: ""}}) { - gqlSchema { - id - } - } - }`, - queryName: "updateGQLSchema", - testGuardianAccess: false, - guardianErr: "", - guardianData: "", - }, - { - name: "shutdown has guardian auth", - query: ` - mutation { - shutdown { - response { - code - message - } - } - }`, - queryName: "shutdown", - testGuardianAccess: false, - guardianErr: "", - guardianData: "", - }, - } - - for _, tcase := range tcases { - t.Run(tcase.name, func(t *testing.T) { - params := testutil.GraphQLParams{Query: tcase.query} - - // assert ACL error for non-guardians - assertNonGuardianFailure(t, tcase.queryName, !tcase.respIsArray, params) - - // for guardians, assert non-ACL error or success - if tcase.testGuardianAccess { - token := testutil.GrootHttpLogin(adminEndpoint) - resp := makeRequestAndRefreshTokenIfNecessary(t, token, params) - - if tcase.guardianErr == "" { - resp.RequireNoGraphQLErrors(t) - } else { - require.Len(t, resp.Errors, 1) - require.Contains(t, resp.Errors[0].Message, tcase.guardianErr) - } - - if tcase.guardianData != "" { - require.JSONEq(t, tcase.guardianData, string(resp.Data)) - } - } - }) - } -} - -func TestAddUpdateGroupWithDuplicateRules(t *testing.T) { +func (asuite *AclTestSuite) TestAddUpdateGroupWithDuplicateRules() { + t := asuite.T() + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) groupName := "testGroup" - addedRules := []rule{ + addedRules := []dgraphtest.AclRule{ { Predicate: "test", Permission: 1, @@ -3102,15 +2330,19 @@ func TestAddUpdateGroupWithDuplicateRules(t *testing.T) { Permission: 3, }, } - token := testutil.GrootHttpLogin(adminEndpoint) - addedGroup := createGroupWithRules(t, token, groupName, addedRules) + addedGroup, err := hc.CreateGroupWithRules(groupName, addedRules) + require.NoError(t, err) require.Equal(t, groupName, addedGroup.Name) require.Len(t, addedGroup.Rules, 2) require.ElementsMatch(t, addedRules[1:], addedGroup.Rules) - - updatedRules := []rule{ + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + updatedRules := []dgraphtest.AclRule{ { Predicate: "test", Permission: 3, @@ -3124,63 +2356,64 @@ func TestAddUpdateGroupWithDuplicateRules(t *testing.T) { Permission: 2, }, } - updatedGroup := updateGroup(t, token, groupName, updatedRules, nil) - + updatedGroup, err := hc.UpdateGroup(groupName, updatedRules, nil) + require.NoError(t, err) require.Equal(t, groupName, updatedGroup.Name) require.Len(t, updatedGroup.Rules, 3) - require.ElementsMatch(t, []rule{updatedRules[0], addedRules[2], updatedRules[2]}, + require.ElementsMatch(t, []dgraphtest.AclRule{updatedRules[0], addedRules[2], updatedRules[2]}, updatedGroup.Rules) - updatedGroup1 := updateGroup(t, token, groupName, nil, + updatedGroup1, err := hc.UpdateGroup(groupName, nil, []string{"test1", "test1", "test3"}) + require.NoError(t, err) require.Equal(t, groupName, updatedGroup1.Name) require.Len(t, updatedGroup1.Rules, 2) - require.ElementsMatch(t, []rule{updatedRules[0], updatedRules[2]}, updatedGroup1.Rules) + require.ElementsMatch(t, []dgraphtest.AclRule{updatedRules[0], updatedRules[2]}, updatedGroup1.Rules) // cleanup - _ = deleteGroup(t, token, groupName, true) + require.NoError(t, hc.DeleteGroup(groupName)) } -func TestAllowUIDAccess(t *testing.T) { +func (asuite *AclTestSuite) TestAllowUIDAccess() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - - testutil.DropAll(t, dg) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.DropAll()) op := api.Operation{Schema: ` name : string @index(exact) . `} - require.NoError(t, dg.Alter(ctx, &op)) - - resetUser(t) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - createGroup(t, token, devGroup) - addToGroup(t, token, userid, devGroup) + require.NoError(t, gc.Alter(ctx, &op)) - require.NoError(t, testutil.AssignUids(101)) - mutation := &api.Mutation{ - SetNquads: []byte(` - <100> "100th User" . - `), - CommitNow: true, - } - _, err = dg.NewTxn().Mutate(ctx, mutation) + resetUser(t, hc) + + createdGroup, err := hc.CreateGroup(devGroup) require.NoError(t, err) + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) - // give read access of to alice - addRulesToGroup(t, token, devGroup, []rule{{"name", Read.Code}}) + require.NoError(t, asuite.dc.AssignUids(gc.Dgraph, 101)) + mu := &api.Mutation{SetNquads: []byte(`<100> "100th User" .`), CommitNow: true} + _, err = gc.Mutate(mu) + require.NoError(t, err) - userClient, err := testutil.DgraphClient(testutil.SockAddr) + // give read access of to alice + require.NoError(t, hc.AddRulesToGroup(devGroup, + []dgraphtest.AclRule{{Predicate: "name", Permission: Read.Code}}, true)) + asuite.Upgrade() + userClient, cancel, err := asuite.dc.Client() require.NoError(t, err) + defer cancel() time.Sleep(defaultTimeToSleep) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) @@ -3194,22 +2427,35 @@ func TestAllowUIDAccess(t *testing.T) { } ` - resp, err := userClient.NewReadOnlyTxn().Query(ctx, uidQuery) + resp, err := userClient.Query(uidQuery) require.NoError(t, err) testutil.CompareJSON(t, `{"me":[{"name":"100th User", "uid": "0x64"}]}`, string(resp.GetJson())) } -func TestAddNewPredicate(t *testing.T) { +func (asuite *AclTestSuite) TestAddNewPredicate() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) - resetUser(t) - - userClient, err := testutil.DgraphClient(testutil.SockAddr) + require.NoError(t, gc.DropAll()) + resetUser(t, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + userClient, cancel, err := asuite.dc.Client() + defer cleanup() require.NoError(t, err) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) @@ -3219,14 +2465,7 @@ func TestAddNewPredicate(t *testing.T) { }) require.Error(t, err, "User can't create new predicate. Alter should have returned error.") - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - addToGroup(t, token, userid, "guardians") + require.NoError(t, hc.AddUserToGroup(userid, "guardians")) time.Sleep(expireJwtSleep) // Alice is a guardian now, it can create new predicate. @@ -3236,49 +2475,52 @@ func TestAddNewPredicate(t *testing.T) { require.NoError(t, err, "User is a guardian. Alter should have succeeded.") } -func TestCrossGroupPermission(t *testing.T) { +func (asuite *AclTestSuite) TestCrossGroupPermission() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - testutil.DropAll(t, dg) + require.NoError(t, gc.DropAll()) - err = dg.Alter(ctx, &api.Operation{ + err = gc.Alter(ctx, &api.Operation{ Schema: `newpred: string .`, }) require.NoError(t, err) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - }) - require.NoError(t, err) - // create groups - createGroup(t, token, "reader") - createGroup(t, token, "writer") - createGroup(t, token, "alterer") + createdGroup, err := hc.CreateGroup("reader") + require.NoError(t, err) + require.Equal(t, "reader", createdGroup) + createdGroup, err = hc.CreateGroup("writer") + require.NoError(t, err) + require.Equal(t, "writer", createdGroup) + createdGroup, err = hc.CreateGroup("alterer") + require.NoError(t, err) + require.Equal(t, "alterer", createdGroup) // add rules to groups - addRulesToGroup(t, token, "reader", []rule{{Predicate: "newpred", Permission: 4}}) - addRulesToGroup(t, token, "writer", []rule{{Predicate: "newpred", Permission: 2}}) - addRulesToGroup(t, token, "alterer", []rule{{Predicate: "newpred", Permission: 1}}) + require.NoError(t, hc.AddRulesToGroup("reader", []dgraphtest.AclRule{{Predicate: "newpred", Permission: 4}}, true)) + require.NoError(t, hc.AddRulesToGroup("writer", []dgraphtest.AclRule{{Predicate: "newpred", Permission: 2}}, true)) + require.NoError(t, hc.AddRulesToGroup("alterer", []dgraphtest.AclRule{{Predicate: "newpred", Permission: 1}}, true)) // Wait for acl cache to be refreshed time.Sleep(defaultTimeToSleep) - token, err = testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - }) - require.NoError(t, err) - + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // create 8 users. for i := 0; i < 8; i++ { userIdx := strconv.Itoa(i) - createUser(t, token, "user"+userIdx, "password"+userIdx) + _, err := hc.CreateUser("user"+userIdx, "password"+userIdx) + require.NoError(t, err) } // add users to groups. we create all possible combination @@ -3286,20 +2528,20 @@ func TestCrossGroupPermission(t *testing.T) { for i := 0; i < 8; i++ { userIdx := strconv.Itoa(i) if i&1 > 0 { - addToGroup(t, token, "user"+userIdx, "alterer") + require.NoError(t, hc.AddUserToGroup("user"+userIdx, "alterer")) } if i&2 > 0 { - addToGroup(t, token, "user"+userIdx, "writer") + require.NoError(t, hc.AddUserToGroup("user"+userIdx, "writer")) } if i&4 > 0 { - addToGroup(t, token, "user"+userIdx, "reader") + require.NoError(t, hc.AddUserToGroup("user"+userIdx, "reader")) } } time.Sleep(defaultTimeToSleep) // operations - dgQuery := func(client *dgo.Dgraph, shouldFail bool, user string) { - _, err := client.NewTxn().Query(ctx, ` + dgQuery := func(client *dgraphtest.GrpcClient, shouldFail bool, user string) { + _, err := client.Query(` { me(func: has(newpred)) { newpred @@ -3309,8 +2551,8 @@ func TestCrossGroupPermission(t *testing.T) { require.True(t, (err != nil) == shouldFail, "Query test Failed for: "+user+", shouldFail: "+strconv.FormatBool(shouldFail)) } - dgMutation := func(client *dgo.Dgraph, shouldFail bool, user string) { - _, err := client.NewTxn().Mutate(ctx, &api.Mutation{ + dgMutation := func(client *dgraphtest.GrpcClient, shouldFail bool, user string) { + _, err := client.Mutate(&api.Mutation{ Set: []*api.NQuad{ { Subject: "_:a", @@ -3323,7 +2565,7 @@ func TestCrossGroupPermission(t *testing.T) { require.True(t, (err != nil) == shouldFail, "Mutation test failed for: "+user+", shouldFail: "+strconv.FormatBool(shouldFail)) } - dgAlter := func(client *dgo.Dgraph, shouldFail bool, user string) { + dgAlter := func(client *dgraphtest.GrpcClient, shouldFail bool, user string) { err := client.Alter(ctx, &api.Operation{Schema: `newpred: string @index(exact) .`}) require.True(t, (err != nil) == shouldFail, "Alter test failed for: "+user+", shouldFail: "+strconv.FormatBool(shouldFail)) @@ -3333,15 +2575,15 @@ func TestCrossGroupPermission(t *testing.T) { require.True(t, (err != nil) == shouldFail, "Alter test failed for: "+user+", shouldFail: "+strconv.FormatBool(shouldFail)) } - + asuite.Upgrade() // test user access. for i := 0; i < 8; i++ { userIdx := strconv.Itoa(i) - userClient, err := testutil.DgraphClient(testutil.SockAddr) + userClient, _, err := asuite.dc.Client() require.NoError(t, err, "Client creation error") - err = userClient.LoginIntoNamespace(ctx, "user"+userIdx, "password"+userIdx, x.GalaxyNamespace) - require.NoError(t, err, "Login error") + require.NoError(t, userClient.LoginIntoNamespace(ctx, "user"+userIdx, + "password"+userIdx, x.GalaxyNamespace), "Login error") dgQuery(userClient, false, "user"+userIdx) // Query won't fail, will return empty result instead. dgMutation(userClient, i&2 == 0, "user"+userIdx) @@ -3349,16 +2591,22 @@ func TestCrossGroupPermission(t *testing.T) { } } -func TestMutationWithValueVar(t *testing.T) { +func (asuite *AclTestSuite) TestMutationWithValueVar() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - - testutil.DropAll(t, dg) - - err = dg.Alter(ctx, &api.Operation{ + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + require.NoError(t, gc.DropAll()) + err = gc.Alter(ctx, &api.Operation{ Schema: ` name : string @index(exact) . nickname: string . @@ -3367,27 +2615,21 @@ func TestMutationWithValueVar(t *testing.T) { }) require.NoError(t, err) - data := &api.Mutation{ - SetNquads: []byte(` - _:u1 "RandomGuy" . - _:u1 "r1" . - `), - CommitNow: true, - } - _, err = dg.NewTxn().Mutate(ctx, data) + mu := &api.Mutation{SetNquads: []byte(` + _:u1 "RandomGuy" . + _:u1 "r1" . + `), CommitNow: true} + + _, err = gc.Mutate(mu) require.NoError(t, err) - resetUser(t) - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: "groot", - Passwd: "password", - }) + resetUser(t, hc) + // require.NoError(t, hc.CreateUser(userid, userpassword)) + createdGroup, err := hc.CreateGroup(devGroup) require.NoError(t, err) - createUser(t, token, userid, userpassword) - createGroup(t, token, devGroup) - addToGroup(t, token, userid, devGroup) - addRulesToGroup(t, token, devGroup, []rule{ + require.Equal(t, devGroup, createdGroup) + require.NoError(t, hc.AddUserToGroup(userid, devGroup)) + require.NoError(t, hc.AddRulesToGroup(devGroup, []dgraphtest.AclRule{ { Predicate: "name", Permission: Read.Code | Write.Code, @@ -3400,7 +2642,8 @@ func TestMutationWithValueVar(t *testing.T) { Predicate: "age", Permission: Write.Code, }, - }) + }, true)) + time.Sleep(defaultTimeToSleep) query := ` @@ -3419,8 +2662,8 @@ func TestMutationWithValueVar(t *testing.T) { `), CommitNow: true, } - - userClient, err := testutil.DgraphClient(testutil.SockAddr) + asuite.Upgrade() + userClient, _, err := asuite.dc.Client() require.NoError(t, err) require.NoError(t, userClient.LoginIntoNamespace(ctx, userid, userpassword, x.GalaxyNamespace)) @@ -3441,169 +2684,186 @@ func TestMutationWithValueVar(t *testing.T) { } ` - resp, err := userClient.NewReadOnlyTxn().Query(ctx, query) + resp, err := userClient.Query(query) require.NoError(t, err) testutil.CompareJSON(t, `{"me": [{"name":"r1","nickname":"r1"}]}`, string(resp.GetJson())) } -func TestFailedLogin(t *testing.T) { +func (asuite *AclTestSuite) TestDeleteGuardiansGroupShouldFail() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - - grootClient, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err) - op := api.Operation{DropAll: true} - if err := grootClient.Alter(ctx, &op); err != nil { - t.Fatalf("Unable to cleanup db:%v", err) - } + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - - client, err := testutil.DgraphClient(testutil.SockAddr) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - // User is not present - err = client.LoginIntoNamespace(ctx, userid, "simplepassword", x.GalaxyNamespace) - require.Error(t, err) - require.Contains(t, err.Error(), x.ErrorInvalidLogin.Error()) - - resetUser(t) - // User is present - err = client.LoginIntoNamespace(ctx, userid, "randomstring", x.GalaxyNamespace) - require.Error(t, err) - require.Contains(t, err.Error(), x.ErrorInvalidLogin.Error()) -} + addDataAndRules(ctx, t, gc, hc) -func TestDeleteGuardiansGroupShouldFail(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) - defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: x.GrootId, - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - resp := deleteGroup(t, token, "guardians", false) - require.Contains(t, resp.Errors.Error(), + err = hc.DeleteGroup("guardians") + require.Error(t, err) + require.Contains(t, err.Error(), "guardians group and groot user cannot be deleted.") } -func TestDeleteGrootUserShouldFail(t *testing.T) { +func (asuite *AclTestSuite) TestDeleteGrootUserShouldFail() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: x.GrootId, - Passwd: "password", - Namespace: x.GalaxyNamespace, - }) - require.NoError(t, err, "login failed") - - resp := deleteUser(t, token, "groot", false) - require.Contains(t, resp.Errors.Error(), + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + err = hc.DeleteUser("groot") + require.Error(t, err) + require.Contains(t, err.Error(), "guardians group and groot user cannot be deleted.") } -func TestDeleteGrootUserFromGuardiansGroupShouldFail(t *testing.T) { +func (asuite *AclTestSuite) TestDeleteGrootUserFromGuardiansGroupShouldFail() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - require.NoError(t, err, "login failed") - - gqlresp := removeUserFromGroup(t, "groot", "guardians") + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) - require.Contains(t, gqlresp.Errors.Error(), - "guardians group and groot user cannot be deleted.") + err = hc.RemoveUserFromGroup("groot", "guardians") + require.Error(t, err) + require.Contains(t, err.Error(), "guardians group and groot user cannot be deleted.") } -func TestDeleteGrootAndGuardiansUsingDelNQuadShouldFail(t *testing.T) { +func (asuite *AclTestSuite) TestDeleteGrootAndGuardiansUsingDelNQuadShouldFail() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) - require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - require.NoError(t, err, "login failed") - - grootUid, guardiansUid := getGrootAndGuardiansUid(t, dg) - + gc, cleanup, err := asuite.dc.Client() + require.NoError(t, err) + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + gc, cleanup, err = asuite.dc.Client() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + defer cleanup() + grootUid, guardiansUid := getGrootAndGuardiansUid(t, gc) + + mu := &api.Mutation{DelNquads: []byte(fmt.Sprintf("%s %s %s .", "<"+grootUid+">", "*", "*")), CommitNow: true} // Try deleting groot user - _, err = deleteUsingNQuad(dg, "<"+grootUid+">", "*", "*") + _, err = gc.Mutate(mu) require.Error(t, err, "Deleting groot user should have returned an error") require.Contains(t, err.Error(), "Properties of guardians group and groot user cannot be deleted") + mu = &api.Mutation{DelNquads: []byte(fmt.Sprintf("%s %s %s .", "<"+guardiansUid+">", "*", "*")), CommitNow: true} // Try deleting guardians group - _, err = deleteUsingNQuad(dg, "<"+guardiansUid+">", "*", "*") + _, err = gc.Mutate(mu) require.Error(t, err, "Deleting guardians group should have returned an error") require.Contains(t, err.Error(), "Properties of guardians group and groot user cannot be deleted") } -func deleteGuardiansGroupAndGrootUserShouldFail(t *testing.T) { - token, err := testutil.HttpLogin(&testutil.LoginParams{ - Endpoint: adminEndpoint, - UserID: x.GrootId, - Passwd: "password", - }) - require.NoError(t, err, "login failed") - +func deleteGuardiansGroupAndGrootUserShouldFail(t *testing.T, hc *dgraphtest.HTTPClient) { // Try deleting guardians group should fail - resp := deleteGroup(t, token, "guardians", false) - require.Contains(t, resp.Errors.Error(), + err := hc.DeleteGroup("guardians") + require.Error(t, err) + require.Contains(t, err.Error(), "guardians group and groot user cannot be deleted.") // Try deleting groot user should fail - resp = deleteUser(t, token, "groot", false) - require.Contains(t, resp.Errors.Error(), + err = hc.DeleteUser("groot") + require.Error(t, err) + require.Contains(t, err.Error(), "guardians group and groot user cannot be deleted.") } -func TestDropAllShouldResetGuardiansAndGroot(t *testing.T) { +func (asuite *AclTestSuite) TestDropAllShouldResetGuardiansAndGroot() { + t := asuite.T() ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second) defer cancel() - dg, err := testutil.DgraphClientWithGroot(testutil.SockAddr) + gc, cleanup, err := asuite.dc.Client() require.NoError(t, err) - addDataAndRules(ctx, t, dg) - - require.NoError(t, err, "login failed") + defer cleanup() + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + hc, err := asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + addDataAndRules(ctx, t, gc, hc) + asuite.Upgrade() + gc, cleanup, err = asuite.dc.Client() + defer cleanup() + require.NoError(t, err) + require.NoError(t, gc.LoginIntoNamespace(ctx, dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) // Try Drop All op := api.Operation{ DropAll: true, DropOp: api.Operation_ALL, } - if err := dg.Alter(ctx, &op); err != nil { + if err := gc.Alter(ctx, &op); err != nil { t.Fatalf("Unable to drop all. Error:%v", err) } time.Sleep(defaultTimeToSleep) - deleteGuardiansGroupAndGrootUserShouldFail(t) + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + deleteGuardiansGroupAndGrootUserShouldFail(t, hc) // Try Drop Data op = api.Operation{ DropOp: api.Operation_DATA, } - if err := dg.Alter(ctx, &op); err != nil { + if err := gc.Alter(ctx, &op); err != nil { t.Fatalf("Unable to drop data. Error:%v", err) } - time.Sleep(defaultTimeToSleep) - deleteGuardiansGroupAndGrootUserShouldFail(t) -} -func TestMain(m *testing.M) { - adminEndpoint = "http://" + testutil.SockAddrHttp + "/admin" - fmt.Printf("Using adminEndpoint for acl package: %s\n", adminEndpoint) - m.Run() + hc, err = asuite.dc.HTTPClient() + require.NoError(t, err) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) + deleteGuardiansGroupAndGrootUserShouldFail(t, hc) } diff --git a/ee/acl/integration_test.go b/ee/acl/integration_test.go new file mode 100644 index 00000000000..76b518b7fae --- /dev/null +++ b/ee/acl/integration_test.go @@ -0,0 +1,38 @@ +//go:build integration + +/* + * Copyright 2023 Dgraph Labs, Inc. and Contributors + * + * Licensed under the Dgraph Community License (the "License"); you + * may not use this file except in compliance with the License. You + * may obtain a copy of the License at + * + * https://github.com/dgraph-io/dgraph/blob/main/licenses/DCL.txt + */ + +package acl + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dgraph-io/dgraph/dgraphtest" +) + +type AclTestSuite struct { + suite.Suite + dc dgraphtest.Cluster +} + +func (suite *AclTestSuite) SetupTest() { + suite.dc = dgraphtest.NewComposeCluster() +} + +func (suite *AclTestSuite) Upgrade() { + // not implemented for integration tests +} + +func TestACLSuite(t *testing.T) { + suite.Run(t, new(AclTestSuite)) +} diff --git a/ee/acl/upgrade_test.go b/ee/acl/upgrade_test.go new file mode 100644 index 00000000000..e20cf671d30 --- /dev/null +++ b/ee/acl/upgrade_test.go @@ -0,0 +1,66 @@ +//go:build upgrade + +/* + * Copyright 2023 Dgraph Labs, Inc. and Contributors + * + * Licensed under the Dgraph Community License (the "License"); you + * may not use this file except in compliance with the License. You + * may obtain a copy of the License at + * + * https://github.com/dgraph-io/dgraph/blob/main/licenses/DCL.txt + */ + +package acl + +import ( + "log" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + "github.com/dgraph-io/dgraph/dgraphtest" + "github.com/dgraph-io/dgraph/x" +) + +type AclTestSuite struct { + suite.Suite + lc *dgraphtest.LocalCluster + dc dgraphtest.Cluster + uc dgraphtest.UpgradeCombo +} + +func (asuite *AclTestSuite) SetupTest() { + conf := dgraphtest.NewClusterConfig().WithNumAlphas(1).WithNumZeros(1). + WithReplicas(1).WithACL(20 * time.Second).WithEncryption().WithVersion(asuite.uc.Before) + c, err := dgraphtest.NewLocalCluster(conf) + x.Panic(err) + if err := c.Start(); err != nil { + c.Cleanup(true) + asuite.T().Fatal(err) + } + asuite.lc = c + asuite.dc = c +} + +func (asuite *AclTestSuite) TearDownTest() { + asuite.lc.Cleanup(asuite.T().Failed()) +} + +func (asuite *AclTestSuite) Upgrade() { + if err := asuite.lc.Upgrade(asuite.uc.After, asuite.uc.Strategy); err != nil { + asuite.lc.Cleanup(true) + asuite.T().Fatal(err) + } +} + +func TestACLSuite(t *testing.T) { + for _, uc := range dgraphtest.AllUpgradeCombos { + log.Printf("running upgrade tests for confg: %+v", uc) + aclSuite := AclTestSuite{uc: uc} + suite.Run(t, &aclSuite) + if t.Failed() { + panic("TestACLSuite tests failed") + } + } +} diff --git a/systest/incremental-restore/incremental_restore_test.go b/systest/incremental-restore/incremental_restore_test.go index 7d8c41a5c06..a2dfd14a4f0 100644 --- a/systest/incremental-restore/incremental_restore_test.go +++ b/systest/incremental-restore/incremental_restore_test.go @@ -27,7 +27,9 @@ import ( "github.com/stretchr/testify/require" + "github.com/dgraph-io/dgo/v230/protos/api" "github.com/dgraph-io/dgraph/dgraphtest" + "github.com/dgraph-io/dgraph/x" ) func TestIncrementalRestore(t *testing.T) { @@ -45,7 +47,8 @@ func TestIncrementalRestore(t *testing.T) { hc, err := c.HTTPClient() require.NoError(t, err) - require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, dgraphtest.DefaultPassword, 0)) + require.NoError(t, hc.LoginIntoNamespace(dgraphtest.DefaultUser, + dgraphtest.DefaultPassword, x.GalaxyNamespace)) uids := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} c.AssignUids(gc.Dgraph, uint64(len(uids))) @@ -53,8 +56,8 @@ func TestIncrementalRestore(t *testing.T) { for i := 1; i <= len(uids); i++ { for j := 1; j <= i; j++ { - rdfs := fmt.Sprintf(`<%v> "%v" .`, j, i) - _, err := gc.Mutate(rdfs) + mu := &api.Mutation{SetNquads: []byte(fmt.Sprintf(`<%v> "%v" .`, j, i)), CommitNow: true} + _, err := gc.Mutate(mu) require.NoError(t, err) } t.Logf("taking backup #%v\n", i) diff --git a/systest/multi-tenancy/basic_test.go b/systest/multi-tenancy/basic_test.go index 16847d084dc..d3d15189bd7 100644 --- a/systest/multi-tenancy/basic_test.go +++ b/systest/multi-tenancy/basic_test.go @@ -561,7 +561,8 @@ func (msuite *MultitenancyTestSuite) AddData(gcli *dgraphtest.GrpcClient) { _:b "guy2" . _:b "RG2" . ` - _, err := gcli.Mutate(rdfs) + mu := &api.Mutation{SetNquads: []byte(rdfs), CommitNow: true} + _, err := gcli.Mutate(mu) require.NoError(msuite.T(), err) } @@ -570,7 +571,8 @@ func AddNumberOfTriples(gcli *dgraphtest.GrpcClient, start, end int) (*api.Respo for i := start; i <= end; i++ { triples.WriteString(fmt.Sprintf("_:person%[1]v \"person%[1]v\" .\n", i)) } - return gcli.Mutate(triples.String()) + mu := &api.Mutation{SetNquads: []byte(triples.String()), CommitNow: true} + return gcli.Mutate(mu) } func (msuite *MultitenancyTestSuite) createGroupAndSetPermissions(namespace uint64, group, user, predicate string) { diff --git a/systest/multi-tenancy/upgrade_test.go b/systest/multi-tenancy/upgrade_test.go index 93ec19eeb04..68b650bb86f 100644 --- a/systest/multi-tenancy/upgrade_test.go +++ b/systest/multi-tenancy/upgrade_test.go @@ -58,15 +58,19 @@ func (msuite *MultitenancyTestSuite) Upgrade() { t := msuite.T() if err := msuite.lc.Upgrade(msuite.uc.After, msuite.uc.Strategy); err != nil { + msuite.lc.Cleanup(true) t.Fatal(err) } } -func TestMultitenancyTestSuite(t *testing.T) { +func TestMultitenancySuite(t *testing.T) { for _, uc := range dgraphtest.AllUpgradeCombos { - log.Printf("running: backup in [%v], restore in [%v]", uc.Before, uc.After) + log.Printf("running upgrade tests for confg: %+v", uc) var msuite MultitenancyTestSuite msuite.uc = uc suite.Run(t, &msuite) + if t.Failed() { + panic("TestMultitenancySuite tests failed") + } } }