From b05b627e57964210d8893ccd75c007d6b47b0f4e Mon Sep 17 00:00:00 2001 From: Ahsan Barkati Date: Sun, 20 Feb 2022 12:54:08 +0530 Subject: [PATCH] Remove acl, multi-tenancy tests --- .../poorman_auth_with_acl/admin_auth_test.go | 153 ------ .../poorman_auth_with_acl/docker-compose.yml | 40 -- graphql/e2e/common/common.go | 20 +- graphql/e2e/multi_tenancy/docker-compose.yml | 87 ---- .../e2e/multi_tenancy/multi_tenancy_test.go | 483 ------------------ systest/backup/multi-tenancy/acl-secret | 1 - systest/backup/multi-tenancy/backup_test.go | 223 -------- systest/backup/multi-tenancy/data/.gitkeep | 0 .../multi-tenancy/data/backups/.gitkeep | 0 .../backup/multi-tenancy/docker-compose.yml | 99 ---- systest/multi-tenancy/basic_test.go | 450 ---------------- tlstest/acl/acl_over_tls_test.go | 36 -- tlstest/acl/docker-compose.yml | 47 -- 13 files changed, 1 insertion(+), 1638 deletions(-) delete mode 100644 graphql/e2e/admin_auth/poorman_auth_with_acl/admin_auth_test.go delete mode 100644 graphql/e2e/admin_auth/poorman_auth_with_acl/docker-compose.yml delete mode 100644 graphql/e2e/multi_tenancy/docker-compose.yml delete mode 100644 graphql/e2e/multi_tenancy/multi_tenancy_test.go delete mode 100644 systest/backup/multi-tenancy/acl-secret delete mode 100644 systest/backup/multi-tenancy/backup_test.go delete mode 100644 systest/backup/multi-tenancy/data/.gitkeep delete mode 100644 systest/backup/multi-tenancy/data/backups/.gitkeep delete mode 100644 systest/backup/multi-tenancy/docker-compose.yml delete mode 100644 systest/multi-tenancy/basic_test.go delete mode 100644 tlstest/acl/acl_over_tls_test.go delete mode 100644 tlstest/acl/docker-compose.yml diff --git a/graphql/e2e/admin_auth/poorman_auth_with_acl/admin_auth_test.go b/graphql/e2e/admin_auth/poorman_auth_with_acl/admin_auth_test.go deleted file mode 100644 index 5eaa9c298..000000000 --- a/graphql/e2e/admin_auth/poorman_auth_with_acl/admin_auth_test.go +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2020 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 admin_auth - -import ( - "encoding/json" - "net/http" - "testing" - "time" - - "github.com/outcaste-io/outserv/x" - - "github.com/stretchr/testify/require" - - "github.com/outcaste-io/outserv/graphql/e2e/common" -) - -const ( - authTokenHeader = "X-Dgraph-AuthToken" - authToken = "itIsSecret" - wrongAuthToken = "wrongToken" - - accessJwtHeader = "X-Dgraph-AccessToken" -) - -func TestLoginWithPoorManAuth(t *testing.T) { - // without X-Dgraph-AuthToken should give error - params := getGrootLoginParams() - assertAuthTokenError(t, params.ExecuteAsPost(t, common.GraphqlAdminURL)) - - // setting a wrong value for the token should still give error - params.Headers.Set(authTokenHeader, wrongAuthToken) - assertAuthTokenError(t, params.ExecuteAsPost(t, common.GraphqlAdminURL)) - - // setting correct value for the token should not give any GraphQL error - params.Headers.Set(authTokenHeader, authToken) - var resp *common.GraphQLResponse - for i := 0; i < 10; i++ { - resp = params.ExecuteAsPost(t, common.GraphqlAdminURL) - if len(resp.Errors) == 0 { - break - } - time.Sleep(time.Second) - } - common.RequireNoGQLErrors(t, resp) -} - -func TestAdminPoorManWithAcl(t *testing.T) { - schema := `type Person { - id: ID! - name: String! - }` - // without auth token and access JWT headers, should give auth token related error - headers := http.Header{} - assertAuthTokenError(t, common.RetryUpdateGQLSchema(t, common.Alpha1HTTP, schema, headers)) - - // setting a wrong value for the auth token should still give auth token related error - headers.Set(authTokenHeader, wrongAuthToken) - assertAuthTokenError(t, common.RetryUpdateGQLSchema(t, common.Alpha1HTTP, schema, headers)) - - // setting correct value for the auth token should now give ACL related GraphQL error - headers.Set(authTokenHeader, authToken) - assertMissingAclError(t, common.RetryUpdateGQLSchema(t, common.Alpha1HTTP, schema, headers)) - - // setting wrong value for the access JWT should still give ACL related GraphQL error - headers.Set(accessJwtHeader, wrongAuthToken) - assertBadAclError(t, common.RetryUpdateGQLSchema(t, common.Alpha1HTTP, schema, headers)) - - // setting correct value for both tokens should not give errors - accessJwt, _ := grootLogin(t) - headers.Set(accessJwtHeader, accessJwt) - common.AssertUpdateGQLSchemaSuccess(t, common.Alpha1HTTP, schema, headers) -} - -func assertAuthTokenError(t *testing.T, resp *common.GraphQLResponse) { - require.Equal(t, x.GqlErrorList{{ - Message: "Invalid X-Dgraph-AuthToken", - Extensions: map[string]interface{}{"code": "ErrorUnauthorized"}, - }}, resp.Errors) - require.Nil(t, resp.Data) -} - -func assertMissingAclError(t *testing.T, resp *common.GraphQLResponse) { - require.Equal(t, x.GqlErrorList{{ - Message: "resolving updateGQLSchema failed because rpc error: code = PermissionDenied desc = no accessJwt available", - Locations: []x.Location{{ - Line: 2, - Column: 4, - }}, - }}, resp.Errors) -} - -func assertBadAclError(t *testing.T, resp *common.GraphQLResponse) { - require.Equal(t, x.GqlErrorList{{ - Message: "resolving updateGQLSchema failed because rpc error: code = Unauthenticated desc = unable to parse jwt token: token contains an invalid number of segments", - Locations: []x.Location{{ - Line: 2, - Column: 4, - }}, - }}, resp.Errors) -} - -func grootLogin(t *testing.T) (string, string) { - loginParams := getGrootLoginParams() - loginParams.Headers.Set(authTokenHeader, authToken) - resp := loginParams.ExecuteAsPost(t, common.GraphqlAdminURL) - common.RequireNoGQLErrors(t, resp) - - var loginResp struct { - Login struct { - Response struct { - AccessJWT string - RefreshJWT string - } - } - } - require.NoError(t, json.Unmarshal(resp.Data, &loginResp)) - - return loginResp.Login.Response.AccessJWT, loginResp.Login.Response.RefreshJWT -} - -func getGrootLoginParams() *common.GraphQLParams { - return &common.GraphQLParams{ - Query: `mutation login($userId: String, $password: String, $refreshToken: String) { - login(userId: $userId, password: $password, refreshToken: $refreshToken) { - response { - accessJWT - refreshJWT - } - } - }`, - Variables: map[string]interface{}{ - "userId": x.GrootId, - "password": "password", - "refreshToken": "", - }, - Headers: http.Header{}, - } -} diff --git a/graphql/e2e/admin_auth/poorman_auth_with_acl/docker-compose.yml b/graphql/e2e/admin_auth/poorman_auth_with_acl/docker-compose.yml deleted file mode 100644 index 9e68fb781..000000000 --- a/graphql/e2e/admin_auth/poorman_auth_with_acl/docker-compose.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: "3.5" -services: - zero1: - image: dgraph/dgraph:latest - working_dir: /data/zero1 - ports: - - 5080 - - 6080 - labels: - cluster: test - service: zero1 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - command: /gobin/outserv zero --my=zero1:5080 --logtostderr -v=2 --bindall --expose_trace --profile_mode block --block_rate 10 - - alpha1: - image: dgraph/dgraph:latest - working_dir: /data/alpha1 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../../../../ee/acl/hmac-secret - target: /dgraph-acl/hmac-secret - read_only: true - ports: - - 8080 - - 9080 - labels: - cluster: test - service: alpha1 - command: /gobin/outserv alpha --my=alpha1:7080 --zero=zero1:5080 --expose_trace --profile_mode block --block_rate 10 --logtostderr -v=2 - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16; token=itIsSecret;" - --acl "secret-file=/dgraph-acl/hmac-secret; access-ttl=3s;" - --trace "ratio=1.0;" diff --git a/graphql/e2e/common/common.go b/graphql/e2e/common/common.go index 7d1fc7be4..f238e6cf4 100644 --- a/graphql/e2e/common/common.go +++ b/graphql/e2e/common/common.go @@ -523,34 +523,16 @@ func SafelyUpdateGQLSchemaOnAlpha1(t *testing.T, schema string) *GqlSchema { return SafelyUpdateGQLSchema(t, Alpha1HTTP, schema, nil) } -// SafelyDropAllWithGroot can be used in tests for doing DROP_ALL when ACL is enabled. -// This should be used after at least one schema update operation has succeeded. -// Once the control returns from it, one can be sure that the DROP_ALL has reached -// the GraphQL layer and the existing schema has been updated to an empty schema. -func SafelyDropAllWithGroot(t *testing.T) { - safelyDropAll(t, true) -} - // SafelyDropAll can be used in tests for doing DROP_ALL when ACL is disabled. // This should be used after at least one schema update operation has succeeded. // Once the control returns from it, one can be sure that the DROP_ALL has reached // the GraphQL layer and the existing schema has been updated to an empty schema. func SafelyDropAll(t *testing.T) { - safelyDropAll(t, false) -} - -func safelyDropAll(t *testing.T, withGroot bool) { // first, make an initial probe to get the schema update counter oldCounter := RetryProbeGraphQL(t, Alpha1HTTP, nil).SchemaUpdateCounter // do DROP_ALL - var dg *dgo.Dgraph - var err error - if withGroot { - dg, err = testutil.DgraphClientWithGroot(Alpha1gRPC) - } else { - dg, err = testutil.DgraphClient(Alpha1gRPC) - } + dg, err := testutil.DgraphClient(Alpha1gRPC) require.NoError(t, err) testutil.DropAll(t, dg) diff --git a/graphql/e2e/multi_tenancy/docker-compose.yml b/graphql/e2e/multi_tenancy/docker-compose.yml deleted file mode 100644 index 851af909c..000000000 --- a/graphql/e2e/multi_tenancy/docker-compose.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Auto-generated with: [./compose -a 3 -z 1 -w] -# -version: "3.5" -services: - alpha1: - image: dgraph/dgraph:latest - working_dir: /data/alpha1 - labels: - cluster: test - ports: - - 8080 - - 9080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../../../ee/acl/hmac-secret - target: /dgraph-acl/hmac-secret - read_only: true - command: /gobin/outserv alpha --my=alpha1:7080 --zero=zero1:5080 - --logtostderr -v=2 --raft "idx=1;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/dgraph-acl/hmac-secret; access-ttl=3000s;" - alpha2: - image: dgraph/dgraph:latest - working_dir: /data/alpha2 - depends_on: - - alpha1 - labels: - cluster: test - ports: - - 8080 - - 9080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../../../ee/acl/hmac-secret - target: /dgraph-acl/hmac-secret - read_only: true - command: /gobin/outserv alpha --my=alpha2:7080 --zero=zero1:5080 - --logtostderr -v=2 --raft "idx=2;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/dgraph-acl/hmac-secret; access-ttl=3000s;" - alpha3: - image: dgraph/dgraph:latest - working_dir: /data/alpha3 - depends_on: - - alpha2 - labels: - cluster: test - ports: - - 8080 - - 9080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../../../ee/acl/hmac-secret - target: /dgraph-acl/hmac-secret - read_only: true - command: /gobin/outserv alpha --my=alpha3:7080 --zero=zero1:5080 - --logtostderr -v=2 --raft "idx=3;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/dgraph-acl/hmac-secret; access-ttl=3000s;" - zero1: - image: dgraph/dgraph:latest - working_dir: /data/zero1 - labels: - cluster: test - ports: - - 5080 - - 6080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - command: /gobin/outserv zero --raft "idx=1;" --my=zero1:5080 --replicas=1 --logtostderr - -v=2 --bindall -volumes: {} diff --git a/graphql/e2e/multi_tenancy/multi_tenancy_test.go b/graphql/e2e/multi_tenancy/multi_tenancy_test.go deleted file mode 100644 index e8c42a67c..000000000 --- a/graphql/e2e/multi_tenancy/multi_tenancy_test.go +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright 2020 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 multi_tenancy - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "strconv" - "strings" - "testing" - "time" - - "github.com/outcaste-io/outserv/x" - - "github.com/outcaste-io/outserv/graphql/e2e/common" - "github.com/outcaste-io/outserv/testutil" - "github.com/stretchr/testify/require" -) - -const ( - accessJwtHeader = "X-Dgraph-AccessToken" -) - -var ( - groupOneHTTP = testutil.ContainerAddr("alpha1", 8080) - groupTwoHTTP = testutil.ContainerAddr("alpha2", 8080) - groupThreeHTTP = testutil.ContainerAddr("alpha3", 8080) - - groupOneGraphQLServer = "http://" + groupOneHTTP + "/graphql" - groupTwoGraphQLServer = "http://" + groupTwoHTTP + "/graphql" - groupThreeGraphQLServer = "http://" + groupThreeHTTP + "/graphql" - - groupOneAdminServer = "http://" + groupOneHTTP + "/admin" -) - -// This test is supposed to test the graphql schema subscribe feature for multiple namespaces. -// Whenever schema is updated in a dgraph alpha for one group for any namespace, -// that update should also be propagated to alpha nodes in other groups. -func TestSchemaSubscribe(t *testing.T) { - header := http.Header{} - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - schema := ` - type Author { - id: ID! - name: String! - }` - grp1NS0PreUpdateCounter := common.RetryProbeGraphQL(t, groupOneHTTP, header).SchemaUpdateCounter - common.SafelyUpdateGQLSchema(t, groupOneHTTP, schema, header) - // since the schema has been updated on group one, the schemaUpdateCounter on all the servers - // should have got incremented and must be the same, indicating that the schema update has - // reached all the servers. - common.AssertSchemaUpdateCounterIncrement(t, groupOneHTTP, grp1NS0PreUpdateCounter, header) - common.AssertSchemaUpdateCounterIncrement(t, groupTwoHTTP, grp1NS0PreUpdateCounter, header) - common.AssertSchemaUpdateCounterIncrement(t, groupThreeHTTP, grp1NS0PreUpdateCounter, header) - - introspectionQuery := ` - query { - __type(name: "Author") { - name - fields { - name - } - } - }` - introspect := &common.GraphQLParams{ - Query: introspectionQuery, - Headers: header, - } - - expectedResult := - `{ - "__type": { - "name":"Author", - "fields": [ - { - "name": "id" - }, - { - "name": "name" - } - ] - } - }` - - // Also, the introspection query on all the servers should - // give the same result as they have the same schema. - introspectionResult := introspect.ExecuteAsPost(t, groupOneGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - introspectionResult = introspect.ExecuteAsPost(t, groupTwoGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - introspectionResult = introspect.ExecuteAsPost(t, groupThreeGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - // Now update schema on an alpha node for group 3 for new namespace and see if nodes in group 1 - // and 2 also get it. - ns := common.CreateNamespace(t, header) - header.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, ns).AccessJwt) - schema = ` - type Author { - id: ID! - name: String! - posts: [Post] - } - - interface Post { - id: ID! - }` - grp3NS1PreUpdateCounter := uint64(0) // this has to be 0 as namespace was just created - common.SafelyUpdateGQLSchema(t, groupThreeHTTP, schema, header) - - common.AssertSchemaUpdateCounterIncrement(t, groupOneHTTP, grp3NS1PreUpdateCounter, header) - common.AssertSchemaUpdateCounterIncrement(t, groupTwoHTTP, grp3NS1PreUpdateCounter, header) - common.AssertSchemaUpdateCounterIncrement(t, groupThreeHTTP, grp3NS1PreUpdateCounter, header) - - expectedResult = - `{ - "__type": { - "name": "Author", - "fields": [ - { - "name": "id" - }, - { - "name": "name" - }, - { - "name": "posts" - }, - { - "name": "postsAggregate" - } - ] - } - }` - introspectionResult = introspect.ExecuteAsPost(t, groupOneGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - introspectionResult = introspect.ExecuteAsPost(t, groupTwoGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - introspectionResult = introspect.ExecuteAsPost(t, groupThreeGraphQLServer) - common.RequireNoGQLErrors(t, introspectionResult) - testutil.CompareJSON(t, expectedResult, string(introspectionResult.Data)) - - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - common.DeleteNamespace(t, ns, header) -} - -// This test ensures that even though different namespaces have the same GraphQL schema, if their -// data is different the same should be reflected in the GraphQL responses. -// In a way, it also tests lazy-loading of GraphQL schema. -func TestGraphQLResponse(t *testing.T) { - common.SafelyDropAllWithGroot(t) - - header := http.Header{} - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - - ns := common.CreateNamespace(t, header) - header1 := http.Header{} - header1.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, - ns).AccessJwt) - - // initially, when no schema is set, we should get error: `there is no GraphQL schema in Dgraph` - // for both the namespaces - query := ` - query { - queryAuthor { - name - } - }` - resp0 := (&common.GraphQLParams{Query: query, Headers: header}).ExecuteAsPost(t, - groupOneGraphQLServer) - resp1 := (&common.GraphQLParams{Query: query, Headers: header1}).ExecuteAsPost(t, - groupOneGraphQLServer) - expectedErrs := x.GqlErrorList{{Message: "Not resolving queryAuthor. " + - "There's no GraphQL schema in Dgraph. Use the /admin API to add a GraphQL schema"}} - require.Equal(t, expectedErrs, resp0.Errors) - require.Equal(t, expectedErrs, resp1.Errors) - require.Nil(t, resp0.Data) - require.Nil(t, resp1.Data) - - schema := ` - type Author { - id: ID! - name: String! - }` - common.SafelyUpdateGQLSchema(t, common.Alpha1HTTP, schema, header) - common.SafelyUpdateGQLSchema(t, common.Alpha1HTTP, schema, header1) - - require.Equal(t, schema, common.AssertGetGQLSchema(t, common.Alpha1HTTP, header).Schema) - require.Equal(t, schema, common.AssertGetGQLSchema(t, common.Alpha1HTTP, header1).Schema) - - queryHelper(t, groupOneGraphQLServer, ` - mutation { - addAuthor(input:{name: "Alice"}) { - author{ - name - } - } - }`, header, - `{ - "addAuthor": { - "author":[{ - "name":"Alice" - }] - } - }`) - - queryHelper(t, groupOneGraphQLServer, query, header, - `{ - "queryAuthor": [ - { - "name":"Alice" - } - ] - }`) - - queryHelper(t, groupOneGraphQLServer, query, header1, - `{ - "queryAuthor": [] - }`) - - common.DeleteNamespace(t, ns, header) -} - -func TestAuth(t *testing.T) { - common.SafelyDropAllWithGroot(t) - - header := http.Header{} - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - schema := ` - type User @auth( - query: { rule: """ - query($USER: String!) { - queryUser(filter: { username: { eq: $USER } }) { - __typename - } - } - """} - ) { - id: ID! - username: String! @id - isPublic: Boolean @search - } - # Dgraph.Authorization {"VerificationKey":"secret","Header":"Authorization","Namespace":"https://dgraph.io/jwt/claims","Algo":"HS256"}` - common.SafelyUpdateGQLSchema(t, common.Alpha1HTTP, schema, header) - - ns := common.CreateNamespace(t, header) - header1 := http.Header{} - header1.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, - ns).AccessJwt) - schema1 := ` - type User @auth( - query: { rule: """ - query { - queryUser(filter: { isPublic: true }) { - __typename - } - } - """} - ) { - id: ID! - username: String! @id - isPublic: Boolean @search - } - # Dgraph.Authorization {"VerificationKey":"secret1","Header":"Authorization1","Namespace":"https://dgraph.io/jwt/claims1","Algo":"HS256"}` - common.SafelyUpdateGQLSchema(t, common.Alpha1HTTP, schema1, header1) - - require.Equal(t, schema, common.AssertGetGQLSchema(t, common.Alpha1HTTP, header).Schema) - require.Equal(t, schema1, common.AssertGetGQLSchema(t, common.Alpha1HTTP, header1).Schema) - - addUserMutation := `mutation { - addUser(input:[ - {username: "Alice", isPublic: false}, - {username: "Bob", isPublic: true} - ]) { - user { - username - } - } - }` - - // for namespace 0, after adding multiple users, we should only get back the user "Alice" - header = common.GetJWT(t, "Alice", nil, &testutil.AuthMeta{ - PublicKey: "secret", - Namespace: "https://dgraph.io/jwt/claims", - Algo: "HS256", - Header: "Authorization", - }) - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - queryHelper(t, groupOneGraphQLServer, addUserMutation, header, `{ - "addUser": { - "user":[{ - "username":"Alice" - }] - } - }`) - - // for namespace 1, after adding multiple users, we should only get back the public users - header1 = common.GetJWT(t, "Alice", nil, &testutil.AuthMeta{ - PublicKey: "secret1", - Namespace: "https://dgraph.io/jwt/claims1", - Algo: "HS256", - Header: "Authorization1", - }) - header1.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, - ns).AccessJwt) - queryHelper(t, groupOneGraphQLServer, addUserMutation, header1, `{ - "addUser": { - "user":[{ - "username":"Bob" - }] - } - }`) - - common.DeleteNamespace(t, ns, header) -} - -// TestCORS checks that all the CORS headers are correctly set in the response for each namespace. -func TestCORS(t *testing.T) { - header := http.Header{} - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - common.SafelyUpdateGQLSchema(t, groupOneHTTP, ` - type TestCORS { - id: ID! - name: String - cf: String @custom(http:{ - url: "https://play.dgraph.io", - method: GET, - forwardHeaders: ["Test-CORS"] - }) - } - # Dgraph.Allow-Origin "https://play.dgraph.io" - # Dgraph.Authorization {"VerificationKey":"secret","Header":"X-Test-Dgraph","Namespace":"https://dgraph.io/jwt/claims","Algo":"HS256"} - `, header) - - ns := common.CreateNamespace(t, header) - header1 := http.Header{} - header1.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, - ns).AccessJwt) - common.SafelyUpdateGQLSchema(t, groupOneHTTP, ` - type TestCORS { - id: ID! - name: String - cf: String @custom(http:{ - url: "https://play.dgraph.io", - method: GET, - forwardHeaders: ["Test-CORS1"] - }) - } - # Dgraph.Allow-Origin "https://play1.dgraph.io" - # Dgraph.Authorization {"VerificationKey":"secret","Header":"X-Test-Dgraph1","Namespace":"https://dgraph.io/jwt/claims","Algo":"HS256"} - `, header1) - - // testCORS for namespace 0 - testCORS(t, 0, "https://play.dgraph.io", "https://play.dgraph.io", - strings.Join([]string{x.AccessControlAllowedHeaders, "Test-CORS", "X-Test-Dgraph"}, ",")) - - // testCORS for the new namespace - testCORS(t, ns, "https://play1.dgraph.io", "https://play1.dgraph.io", - strings.Join([]string{x.AccessControlAllowedHeaders, "Test-CORS1", "X-Test-Dgraph1"}, ",")) - - // cleanup - common.DeleteNamespace(t, ns, header) -} - -func queryHelper(t *testing.T, server, query string, headers http.Header, - expectedResult string) { - params := &common.GraphQLParams{ - Query: query, - Headers: headers, - } - queryResult := params.ExecuteAsPost(t, server) - common.RequireNoGQLErrors(t, queryResult) - testutil.CompareJSON(t, expectedResult, string(queryResult.Data)) -} - -func testCORS(t *testing.T, namespace uint64, reqOrigin, expectedAllowedOrigin, - expectedAllowedHeaders string) { - params := &common.GraphQLParams{ - Query: `query { queryTestCORS { name } }`, - } - req, err := params.CreateGQLPost(groupOneGraphQLServer) - require.NoError(t, err) - - if reqOrigin != "" { - req.Header.Set("Origin", reqOrigin) - } - req.Header.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, namespace).AccessJwt) - - client := &http.Client{Timeout: 5 * time.Second} - resp, err := client.Do(req) - require.NoError(t, err) - - // GraphQL server should always return OK and JSON content, even when there are errors - require.Equal(t, resp.StatusCode, http.StatusOK) - require.Equal(t, strings.ToLower(resp.Header.Get("Content-Type")), "application/json") - // assert that the CORS headers are there as expected - require.Equal(t, resp.Header.Get("Access-Control-Allow-Origin"), expectedAllowedOrigin) - require.Equal(t, resp.Header.Get("Access-Control-Allow-Methods"), "POST, OPTIONS") - require.Equal(t, resp.Header.Get("Access-Control-Allow-Headers"), expectedAllowedHeaders) - require.Equal(t, resp.Header.Get("Access-Control-Allow-Credentials"), "true") - - gqlRes := &common.GraphQLResponse{} - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(body, gqlRes)) - common.RequireNoGQLErrors(t, gqlRes) - testutil.CompareJSON(t, `{"queryTestCORS":[]}`, string(gqlRes.Data)) -} - -// TestNamespacesQueryField checks that namespaces field in state query of /admin endpoint is -// properly working. -func TestNamespacesQueryField(t *testing.T) { - header := http.Header{} - header.Set(accessJwtHeader, testutil.GrootHttpLogin(groupOneAdminServer).AccessJwt) - - namespaceQuery := - `query { - state { - namespaces - } - }` - - // Test namespaces query shows 0 as the only namespace. - queryHelper(t, groupOneAdminServer, namespaceQuery, header, - `{ - "state": { - "namespaces":[0] - } - }`) - - ns1 := common.CreateNamespace(t, header) - ns2 := common.CreateNamespace(t, header) - header1 := http.Header{} - header1.Set(accessJwtHeader, testutil.GrootHttpLoginNamespace(groupOneAdminServer, - ns1).AccessJwt) - - // Test namespaces query shows no namespace in case user is not guardian of galaxy. - queryHelper(t, groupOneAdminServer, namespaceQuery, header1, - `{ - "state": { - "namespaces":[] - } - }`) - - // Test namespaces query shows all 3 namespaces, 0,ns1,ns2 in case user is guardian of galaxy. - queryHelper(t, groupOneAdminServer, namespaceQuery, header, - `{ - "state": { - "namespaces":[0,`+ - strconv.FormatUint(ns1, 10)+`,`+ - strconv.FormatUint(ns2, 10)+`] - } - }`) - - // cleanup - common.DeleteNamespace(t, ns1, header) - common.DeleteNamespace(t, ns2, header) -} diff --git a/systest/backup/multi-tenancy/acl-secret b/systest/backup/multi-tenancy/acl-secret deleted file mode 100644 index f02b5b996..000000000 --- a/systest/backup/multi-tenancy/acl-secret +++ /dev/null @@ -1 +0,0 @@ -12345678901234567890123456789012 \ No newline at end of file diff --git a/systest/backup/multi-tenancy/backup_test.go b/systest/backup/multi-tenancy/backup_test.go deleted file mode 100644 index b3caa4501..000000000 --- a/systest/backup/multi-tenancy/backup_test.go +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2018 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 main - -import ( - "bytes" - "context" - "encoding/json" - "strings" - "testing" - - "github.com/outcaste-io/dgo/v210" - "github.com/outcaste-io/dgo/v210/protos/api" - "github.com/stretchr/testify/require" - - "github.com/outcaste-io/outserv/systest/backup/common" - "github.com/outcaste-io/outserv/testutil" - "github.com/outcaste-io/outserv/x" -) - -var ( - copyBackupDir = "./data/backups_copy" - restoreDir = "./data/restore" - testDirs = []string{restoreDir} - alphaBackupDir = "/data/backups" - oldBackupDir = "/data/to_restore" - alphaContainers = []string{ - "alpha1", - "alpha2", - "alpha3", - } -) - -func TestBackupMultiTenancy(t *testing.T) { - ctx := context.Background() - - dg := testutil.DgClientWithLogin(t, "groot", "password", x.GalaxyNamespace) - testutil.DropAll(t, dg) - - galaxyCreds := &testutil.LoginParams{ - UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace} - galaxyToken := testutil.Login(t, galaxyCreds) - - // Create a new namespace - ns1, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - ns2, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - dg1 := testutil.DgClientWithLogin(t, "groot", "password", ns1) - dg2 := testutil.DgClientWithLogin(t, "groot", "password", ns2) - - addSchema := func(dg *dgo.Dgraph) { - // Add schema and types. - require.NoError(t, dg.Alter(ctx, &api.Operation{Schema: `movie: string . - name: string @index(hash) . - type Node { - movie - }`})) - } - - addSchema(dg) - addSchema(dg1) - addSchema(dg2) - - addData := func(dg *dgo.Dgraph, name string) *api.Response { - var buf bytes.Buffer - // Add initial data. - _, err = dg.NewTxn().Mutate(ctx, &api.Mutation{ - CommitNow: true, - SetNquads: buf.Bytes(), - }) - - require.NoError(t, err) - original, err := dg.NewTxn().Mutate(ctx, &api.Mutation{ - CommitNow: true, - SetNquads: []byte(` - <_:x1> "a" . - <_:x2> "b" . - <_:x3> "c" . - <_:x4> "d" . - <_:x5> "e" . - `), - }) - require.NoError(t, err) - t.Logf("--- Original uid mapping: %+v\n", original.Uids) - return original - } - - original := make(map[uint64]*api.Response) - original[x.GalaxyNamespace] = addData(dg, "galaxy") - original[ns1] = addData(dg1, "ns1") - original[ns2] = addData(dg2, "ns2") - - // Setup test directories. - common.DirSetup(t) - - // Send backup request. - _ = runBackup(t, galaxyToken, 3, 1) - testutil.DropAll(t, dg) - sendRestoreRequest(t, alphaBackupDir, galaxyToken.AccessJwt) - testutil.WaitForRestore(t, dg) - - query := `{ q(func: has(movie)) { count(uid) } }` - expectedResponse := `{ "q": [{ "count": 5 }]}` - testutil.VerifyQueryResponse(t, dg, query, expectedResponse) - testutil.VerifyQueryResponse(t, dg1, query, expectedResponse) - testutil.VerifyQueryResponse(t, dg2, query, expectedResponse) - - // Call drop data from namespace ns2. - require.NoError(t, dg2.Alter(ctx, &api.Operation{DropOp: api.Operation_DATA})) - // Send backup request. - _ = runBackup(t, galaxyToken, 6, 2) - testutil.DropAll(t, dg) - sendRestoreRequest(t, alphaBackupDir, galaxyToken.AccessJwt) - testutil.WaitForRestore(t, dg) - testutil.VerifyQueryResponse(t, dg, query, expectedResponse) - testutil.VerifyQueryResponse(t, dg1, query, expectedResponse) - testutil.VerifyQueryResponse(t, dg2, query, `{ "q": [{ "count": 0 }]}`) - - // After deleting a namespace in incremental backup, we should not be able to get the data from - // banned namespace. - require.NoError(t, testutil.DeleteNamespace(t, galaxyToken, ns1)) - _ = runBackup(t, galaxyToken, 9, 3) - testutil.DropAll(t, dg) - sendRestoreRequest(t, alphaBackupDir, galaxyToken.AccessJwt) - testutil.WaitForRestore(t, dg) - query = `{ q(func: has(movie)) { count(uid) } }` - expectedResponse = `{ "q": [{ "count": 5 }]}` - testutil.VerifyQueryResponse(t, dg, query, expectedResponse) - expectedResponse = `{ "q": [{ "count": 0 }]}` - testutil.VerifyQueryResponse(t, dg1, query, expectedResponse) - - common.DirCleanup(t) -} - -func runBackup(t *testing.T, token *testutil.HttpToken, numExpectedFiles, numExpectedDirs int) []string { - return runBackupInternal(t, token, false, numExpectedFiles, numExpectedDirs) -} - -func runBackupInternal(t *testing.T, token *testutil.HttpToken, forceFull bool, numExpectedFiles, - numExpectedDirs int) []string { - backupRequest := `mutation backup($dst: String!, $ff: Boolean!) { - backup(input: {destination: $dst, forceFull: $ff}) { - response { - code - } - taskId - } - }` - - params := testutil.GraphQLParams{ - Query: backupRequest, - Variables: map[string]interface{}{ - "dst": alphaBackupDir, - "ff": forceFull, - }, - } - - resp := testutil.MakeRequest(t, token, params) - var data interface{} - require.NoError(t, json.Unmarshal(resp.Data, &data)) - require.Equal(t, "Success", testutil.JsonGet(data, "backup", "response", "code").(string)) - taskId := testutil.JsonGet(data, "backup", "taskId").(string) - testutil.WaitForTask(t, taskId, false) - - // Verify that the right amount of files and directories were created. - common.CopyToLocalFs(t) - - files := x.WalkPathFunc(copyBackupDir, func(path string, isdir bool) bool { - return !isdir && strings.HasSuffix(path, ".backup") && strings.HasPrefix(path, "data/backups_copy/dgraph.") - }) - require.Equal(t, numExpectedFiles, len(files)) - - dirs := x.WalkPathFunc(copyBackupDir, func(path string, isdir bool) bool { - return isdir && strings.HasPrefix(path, "data/backups_copy/dgraph.") - }) - require.Equal(t, numExpectedDirs, len(dirs)) - - return dirs -} - -func sendRestoreRequest(t *testing.T, location string, token string) { - if location == "" { - location = "/data/backup" - } - params := testutil.GraphQLParams{ - Query: `mutation restore($location: String!) { - restore(input: {location: $location}) { - code - message - } - }`, - Variables: map[string]interface{}{ - "location": location, - }, - } - resp := testutil.MakeGQLRequestWithAccessJwt(t, ¶ms, token) - resp.RequireNoGraphQLErrors(t) - - var restoreResp struct { - Restore struct { - Code string - Message string - } - } - - require.NoError(t, json.Unmarshal(resp.Data, &restoreResp)) - require.Equal(t, restoreResp.Restore.Code, "Success") - return -} diff --git a/systest/backup/multi-tenancy/data/.gitkeep b/systest/backup/multi-tenancy/data/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/systest/backup/multi-tenancy/data/backups/.gitkeep b/systest/backup/multi-tenancy/data/backups/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/systest/backup/multi-tenancy/docker-compose.yml b/systest/backup/multi-tenancy/docker-compose.yml deleted file mode 100644 index 3819cacc4..000000000 --- a/systest/backup/multi-tenancy/docker-compose.yml +++ /dev/null @@ -1,99 +0,0 @@ -# Auto-generated with: [./compose -a 3 -z 1 -r 1 -w --port_offset=0 --expose_ports=false --alpha_volume=./data/backups:/data/backups/ --zero_volume=./data/backups:/data/backups/ --mem= --names=false -O ../systest/backup/multi-tenancy/docker-compose.yml --acl] -# -version: "3.5" -services: - alpha1: - image: dgraph/dgraph:latest - working_dir: /data/alpha1 - labels: - cluster: test - ports: - - "8080" - - "9080" - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ./acl-secret - target: /secret/hmac - read_only: true - - type: bind - source: ./data/backups - target: /data/backups/ - read_only: false - command: /gobin/outserv alpha --my=alpha1:7080 --zero=zero1:5080 --logtostderr - -v=2 --raft "idx=1; group=1;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/secret/hmac;" - alpha2: - image: dgraph/dgraph:latest - working_dir: /data/alpha2 - labels: - cluster: test - ports: - - "8080" - - "9080" - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ./acl-secret - target: /secret/hmac - read_only: true - - type: bind - source: ./data/backups - target: /data/backups/ - read_only: false - command: /gobin/outserv alpha --my=alpha2:7080 --zero=zero1:5080 --logtostderr - -v=2 --raft "idx=2; group=2;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/secret/hmac;" - alpha3: - image: dgraph/dgraph:latest - working_dir: /data/alpha3 - labels: - cluster: test - ports: - - "8080" - - "9080" - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ./acl-secret - target: /secret/hmac - read_only: true - - type: bind - source: ./data/backups - target: /data/backups/ - read_only: false - command: /gobin/outserv alpha --my=alpha3:7080 --zero=zero1:5080 --logtostderr - -v=2 --raft "idx=3; group=3;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --acl "secret-file=/secret/hmac;" - zero1: - image: dgraph/dgraph:latest - working_dir: /data/zero1 - labels: - cluster: test - ports: - - "5080" - - "6080" - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ./data/backups - target: /data/backups/ - read_only: false - command: /gobin/outserv zero --raft "idx=1;" --my=zero1:5080 --replicas=1 --logtostderr - -v=2 --bindall -volumes: {} diff --git a/systest/multi-tenancy/basic_test.go b/systest/multi-tenancy/basic_test.go deleted file mode 100644 index 7a2eb810b..000000000 --- a/systest/multi-tenancy/basic_test.go +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2021 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 main - -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "testing" - "time" - - "github.com/outcaste-io/dgo/v210" - "github.com/outcaste-io/dgo/v210/protos/api" - "github.com/outcaste-io/outserv/ee/acl" - "github.com/outcaste-io/outserv/graphql/e2e/common" - "github.com/outcaste-io/outserv/graphql/schema" - "github.com/outcaste-io/outserv/testutil" - "github.com/outcaste-io/outserv/x" - "github.com/stretchr/testify/require" -) - -func prepare(t *testing.T) { - dc := testutil.DgClientWithLogin(t, "groot", "password", x.GalaxyNamespace) - require.NoError(t, dc.Alter(context.Background(), &api.Operation{DropAll: true})) -} - -// TODO(Ahsan): This is just a basic test, for the purpose of development. The functions used in -// this file can me made common to the other acl tests as well. Needs some refactoring as well. -func TestAclBasic(t *testing.T) { - prepare(t) - galaxyToken := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace}) - - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - require.Greater(t, int(ns), 0) - - // Add some data to namespace 1 - dc := testutil.DgClientWithLogin(t, "groot", "password", ns) - testutil.AddData(t, dc) - - query := ` - { - me(func: has(name)) { - nickname - name - } - } - ` - resp := testutil.QueryData(t, dc, query) - testutil.CompareJSON(t, - `{"me": [{"name":"guy1","nickname":"RG"}, - {"name": "guy2", "nickname":"RG2"}]}`, - string(resp)) - - // groot of namespace 0 should not see the data of namespace-1 - dc = testutil.DgClientWithLogin(t, "groot", "password", 0) - resp = testutil.QueryData(t, dc, query) - testutil.CompareJSON(t, `{"me": []}`, string(resp)) - - // Login to namespace 1 via groot and create new user alice. - token := testutil.Login(t, &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}) - testutil.CreateUser(t, token, "alice", "newpassword") - - // Alice should not be able to see data added by groot in namespace 1 - dc = testutil.DgClientWithLogin(t, "alice", "newpassword", ns) - resp = testutil.QueryData(t, dc, query) - testutil.CompareJSON(t, `{}`, string(resp)) - - // Create a new group, add alice to that group and give read access of to dev group. - testutil.CreateGroup(t, token, "dev") - testutil.AddToGroup(t, token, "alice", "dev") - testutil.AddRulesToGroup(t, token, "dev", - []testutil.Rule{{Predicate: "name", Permission: acl.Read.Code}}) - - // Wait for acl cache to get updated - time.Sleep(5 * time.Second) - - // Now alice should see the name predicate but not nickname. - dc = testutil.DgClientWithLogin(t, "alice", "newpassword", ns) - resp = testutil.QueryData(t, dc, query) - testutil.CompareJSON(t, `{"me": [{"name":"guy1"},{"name": "guy2"}]}`, string(resp)) - -} - -func TestCreateNamespace(t *testing.T) { - prepare(t) - galaxyToken := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace}) - - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - - token := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}) - - // Create a new namespace using guardian of other namespace. - _, err = testutil.CreateNamespaceWithRetry(t, token) - require.Error(t, err) - require.Contains(t, err.Error(), "Only guardian of galaxy is allowed to do this operation") -} - -func TestDeleteNamespace(t *testing.T) { - prepare(t) - galaxyToken := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace}) - - dg := make(map[uint64]*dgo.Dgraph) - dg[x.GalaxyNamespace] = testutil.DgClientWithLogin(t, "groot", "password", x.GalaxyNamespace) - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - dg[ns] = testutil.DgClientWithLogin(t, "groot", "password", ns) - - addData := func(ns uint64) error { - mutation := &api.Mutation{ - SetNquads: []byte(fmt.Sprintf(` - _:a "%d" . - `, ns)), - CommitNow: true, - } - _, err := dg[ns].NewTxn().Mutate(context.Background(), mutation) - return err - } - check := func(ns uint64, expected string) { - query := ` - { - me(func: has(name)) { - name - } - } - ` - resp := testutil.QueryData(t, dg[ns], query) - testutil.CompareJSON(t, expected, string(resp)) - } - - err = addData(x.GalaxyNamespace) - require.NoError(t, err) - check(x.GalaxyNamespace, `{"me": [{"name":"0"}]}`) - err = addData(ns) - require.NoError(t, err) - check(ns, fmt.Sprintf(`{"me": [{"name":"%d"}]}`, ns)) - - require.NoError(t, testutil.DeleteNamespace(t, galaxyToken, ns)) - - err = addData(x.GalaxyNamespace) - require.NoError(t, err) - check(x.GalaxyNamespace, `{"me": [{"name":"0"}, {"name":"0"}]}`) - err = addData(ns) - require.Contains(t, err.Error(), "Key is using the banned prefix") - check(ns, `{"me": []}`) - - // No one should be able to delete the default namespace. Not even guardian of galaxy. - err = testutil.DeleteNamespace(t, galaxyToken, x.GalaxyNamespace) - require.Error(t, err) - require.Contains(t, err.Error(), "Cannot delete default namespace") -} - -type liveOpts struct { - rdfs string - schema string - gqlSchema string - creds *testutil.LoginParams - forceNs int64 -} - -func liveLoadData(t *testing.T, opts *liveOpts) error { - // Prepare directories. - dir, err := ioutil.TempDir("", "multi") - require.NoError(t, err) - defer func() { - os.RemoveAll(dir) - }() - rdfFile := filepath.Join(dir, "rdfs.rdf") - require.NoError(t, ioutil.WriteFile(rdfFile, []byte(opts.rdfs), 0644)) - schemaFile := filepath.Join(dir, "schema.txt") - require.NoError(t, ioutil.WriteFile(schemaFile, []byte(opts.schema), 0644)) - gqlSchemaFile := filepath.Join(dir, "gql_schema.txt") - require.NoError(t, ioutil.WriteFile(gqlSchemaFile, []byte(opts.gqlSchema), 0644)) - // Load the data. - return testutil.LiveLoad(testutil.LiveOpts{ - Zero: testutil.ContainerAddr("zero1", 5080), - Alpha: testutil.ContainerAddr("alpha1", 9080), - RdfFile: rdfFile, - SchemaFile: schemaFile, - Creds: opts.creds, - ForceNs: opts.forceNs, - }) -} - -func TestLiveLoadMulti(t *testing.T) { - prepare(t) - dc0 := testutil.DgClientWithLogin(t, "groot", "password", x.GalaxyNamespace) - galaxyCreds := &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace} - galaxyToken := testutil.Login(t, galaxyCreds) - - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - dc1 := testutil.DgClientWithLogin(t, "groot", "password", ns) - - // Load data. - require.NoError(t, liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(` - _:a "galaxy alice" . - _:b "galaxy bob" . - _:a "ns alice" <%#x> . - _:b "ns bob" <%#x> . -`, ns, ns), - schema: fmt.Sprintf(` - name: string @index(term) . - [%#x] name: string . -`, ns), - creds: galaxyCreds, - forceNs: -1, - })) - - query1 := ` - { - me(func: has(name)) { - name - } - } - ` - query2 := ` - { - me(func: anyofterms(name, "galaxy")) { - name - } - } - ` - query3 := ` - { - me(func: anyofterms(name, "ns")) { - name - } - } - ` - - resp := testutil.QueryData(t, dc0, query1) - testutil.CompareJSON(t, - `{"me": [{"name":"galaxy alice"}, {"name": "galaxy bob"}]}`, string(resp)) - resp = testutil.QueryData(t, dc1, query1) - testutil.CompareJSON(t, - `{"me": [{"name":"ns alice"}, {"name": "ns bob"}]}`, string(resp)) - - resp = testutil.QueryData(t, dc0, query2) - testutil.CompareJSON(t, - `{"me": [{"name":"galaxy alice"}, {"name": "galaxy bob"}]}`, string(resp)) - - _, err = dc1.NewReadOnlyTxn().Query(context.Background(), query3) - require.Error(t, err) - require.Contains(t, err.Error(), "Attribute name is not indexed") - - // live load data into namespace ns using the guardian of galaxy. - require.NoError(t, liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(` - _:a "ns chew" . - _:b "ns dan" <%#x> . - _:c "ns eon" <%#x> . -`, ns, 0x100), - schema: ` - name: string @index(term) . -`, - creds: galaxyCreds, - forceNs: int64(ns), - })) - - resp = testutil.QueryData(t, dc1, query3) - testutil.CompareJSON(t, - `{"me": [{"name":"ns alice"}, {"name": "ns bob"},{"name":"ns chew"}, - {"name": "ns dan"},{"name":"ns eon"}]}`, string(resp)) - - // Try loading data into a namespace that does not exist. Expect a failure. - err = liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(`_:c "ns eon" <%#x> .`, ns), - schema: `name: string @index(term) .`, - creds: &testutil.LoginParams{UserID: "groot", Passwd: "password", - Namespace: x.GalaxyNamespace}, - forceNs: int64(0x123456), // Assuming this namespace does not exist. - }) - require.Error(t, err) - require.Contains(t, err.Error(), "Cannot load into namespace 0x123456") - - // Try loading into a multiple namespaces. - err = liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(`_:c "ns eon" <%#x> .`, ns), - schema: `[0x123456] name: string @index(term) .`, - creds: galaxyCreds, - forceNs: -1, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "Namespace 0x123456 doesn't exist for pred") - - err = liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(`_:c "ns eon" <0x123456> .`), - schema: `name: string @index(term) .`, - creds: galaxyCreds, - forceNs: -1, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "Cannot load nquad") - - // Load data by non-galaxy user. - err = liveLoadData(t, &liveOpts{ - rdfs: `_:c "ns hola" .`, - schema: ` - name: string @index(term) . -`, - creds: &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}, - forceNs: -1, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot force namespace") - - err = liveLoadData(t, &liveOpts{ - rdfs: `_:c "ns hola" .`, - schema: ` - name: string @index(term) . -`, - creds: &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}, - forceNs: 10, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot force namespace") - - require.NoError(t, liveLoadData(t, &liveOpts{ - rdfs: fmt.Sprintf(` - _:a "ns free" . - _:b "ns gary" <%#x> . - _:c "ns hola" <%#x> . -`, ns, 0x100), - schema: ` - name: string @index(term) . -`, - creds: &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}, - })) - - resp = testutil.QueryData(t, dc1, query3) - testutil.CompareJSON(t, `{"me": [{"name":"ns alice"}, {"name": "ns bob"},{"name":"ns chew"}, - {"name": "ns dan"},{"name":"ns eon"}, {"name": "ns free"},{"name":"ns gary"}, - {"name": "ns hola"}]}`, string(resp)) -} - -func postGqlSchema(t *testing.T, schema string, accessJwt string) { - groupOneHTTP := testutil.ContainerAddr("alpha1", 8080) - header := http.Header{} - header.Set("X-Dgraph-AccessToken", accessJwt) - common.SafelyUpdateGQLSchema(t, groupOneHTTP, schema, header) -} - -func postPersistentQuery(t *testing.T, query, sha, accessJwt string) *common.GraphQLResponse { - header := http.Header{} - header.Set("X-Dgraph-AccessToken", accessJwt) - queryCountryParams := &common.GraphQLParams{ - Query: query, - Extensions: &schema.RequestExtensions{PersistedQuery: schema.PersistedQuery{ - Sha256Hash: sha, - }}, - Headers: header, - } - url := "http://" + testutil.ContainerAddr("alpha1", 8080) + "/graphql" - return queryCountryParams.ExecuteAsPost(t, url) -} - -func TestPersistentQuery(t *testing.T) { - prepare(t) - galaxyToken := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace}) - - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - - token := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}) - - sch := `type Product { - productID: ID! - name: String @search(by: [term]) - }` - postGqlSchema(t, sch, galaxyToken.AccessJwt) - postGqlSchema(t, sch, token.AccessJwt) - - p1 := "query {queryProduct{productID}}" - sha1 := "7a8ff7a69169371c1eb52a8921387079ca281bb2d55feb4b535cbf0ab3896be5" - resp := postPersistentQuery(t, p1, sha1, galaxyToken.AccessJwt) - common.RequireNoGQLErrors(t, resp) - - p2 := "query {queryProduct{name}}" - sha2 := "0efcdde144167b1046360b73c7f6bec325d9f555099a2ae9b820a13328d270e4" - resp = postPersistentQuery(t, p2, sha2, token.AccessJwt) - common.RequireNoGQLErrors(t, resp) - - // User cannnot see persistent query from other namespace. - resp = postPersistentQuery(t, "", sha2, galaxyToken.AccessJwt) - require.Equal(t, 1, len(resp.Errors)) - require.Contains(t, resp.Errors[0].Message, "PersistedQueryNotFound") - - resp = postPersistentQuery(t, "", sha1, token.AccessJwt) - require.Equal(t, 1, len(resp.Errors)) - require.Contains(t, resp.Errors[0].Message, "PersistedQueryNotFound") - - resp = postPersistentQuery(t, "", sha1, "") - require.Equal(t, 1, len(resp.Errors)) - require.Contains(t, resp.Errors[0].Message, "no accessJwt available") -} - -func TestTokenExpired(t *testing.T) { - prepare(t) - galaxyToken := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: x.GalaxyNamespace}) - - // Create a new namespace - ns, err := testutil.CreateNamespaceWithRetry(t, galaxyToken) - require.NoError(t, err) - token := testutil.Login(t, - &testutil.LoginParams{UserID: "groot", Passwd: "password", Namespace: ns}) - - // Relogin using refresh JWT. - token = testutil.Login(t, - &testutil.LoginParams{RefreshJwt: token.RefreshToken}) - _, err = testutil.CreateNamespaceWithRetry(t, token) - require.Error(t, err) - require.Contains(t, err.Error(), "Only guardian of galaxy is allowed to do this operation") -} - -func TestMain(m *testing.M) { - fmt.Printf("Using adminEndpoint : %s for multi-tenancy test.\n", testutil.AdminUrl()) - os.Exit(m.Run()) -} diff --git a/tlstest/acl/acl_over_tls_test.go b/tlstest/acl/acl_over_tls_test.go deleted file mode 100644 index fa31d6740..000000000 --- a/tlstest/acl/acl_over_tls_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package acl - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/outcaste-io/outserv/testutil" - "github.com/outcaste-io/outserv/x" - "github.com/spf13/viper" -) - -func TestLoginOverTLS(t *testing.T) { - conf := viper.New() - conf.Set("tls", fmt.Sprintf("ca-cert=%s; server-name=%s;", - // ca-cert - "../mtls_internal/tls/live/ca.crt", - // server-name - "alpha1")) - - dg, err := testutil.DgraphClientWithCerts(testutil.SockAddr, conf) - require.NoError(t, err) - for i := 0; i < 30; i++ { - err = dg.LoginIntoNamespace(context.Background(), "groot", "password", x.GalaxyNamespace) - if err == nil { - return - } - fmt.Printf("Login failed: %v. Retrying...\n", err) - time.Sleep(time.Second) - } - - t.Fatalf("Unable to login to %s\n", err) -} diff --git a/tlstest/acl/docker-compose.yml b/tlstest/acl/docker-compose.yml deleted file mode 100644 index d52211f90..000000000 --- a/tlstest/acl/docker-compose.yml +++ /dev/null @@ -1,47 +0,0 @@ -version: "3.5" -services: - alpha1: - image: dgraph/dgraph:latest - working_dir: /data/alpha1 - labels: - cluster: test - ports: - - 8080 - - 9080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../../ee/acl/hmac-secret - target: /dgraph-acl/hmac-secret - read_only: true - - type: bind - source: ../mtls_internal/tls/alpha1 - target: /dgraph-tls - read_only: true - command: /gobin/outserv alpha --my=alpha1:7080 --zero=zero1:5080 --logtostderr -v=2 - --acl "secret-file=/dgraph-acl/hmac-secret;" - --security "whitelist=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16;" - --tls "ca-cert=/dgraph-tls/ca.crt; server-cert=/dgraph-tls/node.crt; server-key=/dgraph-tls/node.key; internal-port=true; client-cert=/dgraph-tls/client.alpha1.crt; client-key=/dgraph-tls/client.alpha1.key; client-auth-type=VERIFYIFGIVEN;" - zero1: - image: dgraph/dgraph:latest - working_dir: /data/zero1 - labels: - cluster: test - ports: - - 5080 - - 6080 - volumes: - - type: bind - source: $GOPATH/bin - target: /gobin - read_only: true - - type: bind - source: ../mtls_internal/tls/zero1 - target: /dgraph-tls - read_only: true - command: /gobin/outserv zero --raft "idx=1;" --my=zero1:5080 --logtostderr -v=2 --bindall - --tls "ca-cert=/dgraph-tls/ca.crt; server-cert=/dgraph-tls/node.crt; server-key=/dgraph-tls/node.key; internal-port=true; client-cert=/dgraph-tls/client.zero1.crt; client-key=/dgraph-tls/client.zero1.key;" -volumes: {}