From 6d287723a204bdfba70ce904de9cb9acca96778c Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Sun, 19 Feb 2017 12:16:39 -0600 Subject: [PATCH] Add tests for influx OSS user permissions --- influx/users_test.go | 732 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 732 insertions(+) diff --git a/influx/users_test.go b/influx/users_test.go index 97d0533a9..111a42680 100644 --- a/influx/users_test.go +++ b/influx/users_test.go @@ -6,6 +6,7 @@ import ( "net/http/httptest" "net/url" "reflect" + "strings" "testing" "github.com/influxdata/chronograf" @@ -90,6 +91,7 @@ func TestClient_userPermissions(t *testing.T) { } func TestClient_Add(t *testing.T) { + t.Parallel() type args struct { ctx context.Context u *chronograf.User @@ -161,3 +163,733 @@ func TestClient_Add(t *testing.T) { } } } + +func TestClient_Delete(t *testing.T) { + type args struct { + ctx context.Context + u *chronograf.User + } + tests := []struct { + name string + status int + dropUser []byte + args args + wantErr bool + }{ + { + name: "Drop User", + dropUser: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + status: http.StatusOK, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + }, + { + name: "No such user", + dropUser: []byte(`{"results":[{"error":"user not found"}]}`), + status: http.StatusOK, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + wantErr: true, + }, + { + name: "Bad InfluxQL", + dropUser: []byte(`{"error":"error parsing query: found doody, expected ; at line 1, char 17"}`), + status: http.StatusBadRequest, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + wantErr: true, + }, + { + name: "Bad JSON", + dropUser: []byte(`{"results":[{"error":breakhere}]}`), + status: http.StatusOK, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + rw.WriteHeader(tt.status) + rw.Write(tt.dropUser) + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + + if err := c.Delete(tt.args.ctx, tt.args.u); (err != nil) != tt.wantErr { + t.Errorf("%q. Client.Delete() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + } +} + +func TestClient_Get(t *testing.T) { + type args struct { + ctx context.Context + name string + } + tests := []struct { + name string + args args + statusUsers int + showUsers []byte + statusGrants int + showGrants []byte + want *chronograf.User + wantErr bool + }{ + { + name: "Get User", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + args: args{ + ctx: context.Background(), + name: "docbrown", + }, + want: &chronograf.User{ + Name: "docbrown", + Permissions: chronograf.Permissions{ + chronograf.Permission{ + Scope: "all", + Allowed: []string{"WRITE", "READ"}, + }, + chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + { + name: "Fail show users", + statusUsers: http.StatusBadRequest, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + args: args{ + ctx: context.Background(), + name: "docbrown", + }, + wantErr: true, + }, + { + name: "Fail show grants", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusBadRequest, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + args: args{ + ctx: context.Background(), + name: "docbrown", + }, + wantErr: true, + }, + { + name: "Fail no such user", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true]]}]}]}`), + args: args{ + ctx: context.Background(), + name: "docbrown", + }, + wantErr: true, + }, + } + for _, tt := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + query := r.URL.Query().Get("q") + if strings.Contains(query, "GRANTS") { + rw.WriteHeader(tt.statusGrants) + rw.Write(tt.showGrants) + } else if strings.Contains(query, "USERS") { + rw.WriteHeader(tt.statusUsers) + rw.Write(tt.showUsers) + } + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + got, err := c.Get(tt.args.ctx, tt.args.name) + if (err != nil) != tt.wantErr { + t.Errorf("%q. Client.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr) + continue + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("%q. Client.Get() = %v, want %v", tt.name, got, tt.want) + } + } +} + +func TestClient_grantPermission(t *testing.T) { + type args struct { + ctx context.Context + username string + perm chronograf.Permission + } + tests := []struct { + name string + args args + status int + results []byte + wantQuery string + wantErr bool + }{ + { + name: "simple grants", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + wantQuery: `GRANT ALL ON "mydb" TO "docbrown"`, + }, + { + name: "bad grants", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"howdy"}, + }, + }, + wantQuery: ``, + }, + { + name: "no grants", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{}, + }, + }, + wantQuery: ``, + }, + } + for _, tt := range tests { + query := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + query = r.URL.Query().Get("q") + rw.WriteHeader(tt.status) + rw.Write(tt.results) + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + if err := c.grantPermission(tt.args.ctx, tt.args.username, tt.args.perm); (err != nil) != tt.wantErr { + t.Errorf("%q. Client.grantPermission() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + if query != tt.wantQuery { + t.Errorf("%q. Client.grantPermission() = %v, want %v", tt.name, query, tt.wantQuery) + } + } +} + +func TestClient_revokePermission(t *testing.T) { + type args struct { + ctx context.Context + username string + perm chronograf.Permission + } + tests := []struct { + name string + args args + status int + results []byte + wantQuery string + wantErr bool + }{ + { + name: "simple revoke", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + wantQuery: `REVOKE ALL ON "mydb" FROM "docbrown"`, + }, + { + name: "bad revoke", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"howdy"}, + }, + }, + wantQuery: ``, + }, + { + name: "no permissions", + status: http.StatusOK, + results: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + username: "docbrown", + perm: chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{}, + }, + }, + wantQuery: `REVOKE ALL PRIVILEGES ON "mydb" FROM "docbrown"`, + }, + } + for _, tt := range tests { + query := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + query = r.URL.Query().Get("q") + rw.WriteHeader(tt.status) + rw.Write(tt.results) + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + if err := c.revokePermission(tt.args.ctx, tt.args.username, tt.args.perm); (err != nil) != tt.wantErr { + t.Errorf("%q. Client.revokePermission() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + if query != tt.wantQuery { + t.Errorf("%q. Client.revokePermission() = %v, want %v", tt.name, query, tt.wantQuery) + } + } +} + +func TestClient_All(t *testing.T) { + type args struct { + ctx context.Context + } + tests := []struct { + name string + args args + statusUsers int + showUsers []byte + statusGrants int + showGrants []byte + want []chronograf.User + wantErr bool + }{ + { + name: "All Users", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + args: args{ + ctx: context.Background(), + }, + want: []chronograf.User{ + { + Name: "admin", + Permissions: chronograf.Permissions{ + chronograf.Permission{ + Scope: "all", + Allowed: []string{"WRITE", "READ"}, + }, + chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + { + Name: "docbrown", + Permissions: chronograf.Permissions{ + chronograf.Permission{ + Scope: "all", + Allowed: []string{"WRITE", "READ"}, + }, + chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + { + Name: "reader", + Permissions: chronograf.Permissions{ + chronograf.Permission{ + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + }, + { + name: "Unauthorized", + statusUsers: http.StatusUnauthorized, + showUsers: []byte(`{}`), + args: args{ + ctx: context.Background(), + }, + wantErr: true, + }, + { + name: "Permission error", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusBadRequest, + showGrants: []byte(`{}`), + args: args{ + ctx: context.Background(), + }, + wantErr: true, + }, + } + for _, tt := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + query := r.URL.Query().Get("q") + if strings.Contains(query, "GRANTS") { + rw.WriteHeader(tt.statusGrants) + rw.Write(tt.showGrants) + } else if strings.Contains(query, "USERS") { + rw.WriteHeader(tt.statusUsers) + rw.Write(tt.showUsers) + } + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + got, err := c.All(tt.args.ctx) + if (err != nil) != tt.wantErr { + t.Errorf("%q. Client.All() error = %v, wantErr %v", tt.name, err, tt.wantErr) + continue + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("%q. Client.All() = %v, want %v", tt.name, got, tt.want) + } + } +} + +func TestClient_Update(t *testing.T) { + type args struct { + ctx context.Context + u *chronograf.User + } + tests := []struct { + name string + statusUsers int + showUsers []byte + statusGrants int + showGrants []byte + statusRevoke int + revoke []byte + statusGrant int + grant []byte + args args + want []string + wantErr bool + }{ + { + name: "Revoke all permissions", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusOK, + revoke: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + want: []string{ + `SHOW USERS`, + `SHOW GRANTS FOR "docbrown"`, + `REVOKE ALL PRIVILEGES FROM "docbrown"`, + `REVOKE ALL ON "mydb" FROM "docbrown"`, + }, + }, + { + name: "Grant all permissions", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusOK, + revoke: []byte(`{"results":[]}`), + statusGrant: http.StatusOK, + grant: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + Permissions: chronograf.Permissions{ + { + Scope: "all", + Allowed: []string{"WRITE", "READ"}, + }, + { + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + want: []string{ + `SHOW USERS`, + `SHOW GRANTS FOR "docbrown"`, + `GRANT ALL PRIVILEGES TO "docbrown"`, + `GRANT ALL ON "mydb" TO "docbrown"`, + }, + }, + { + name: "Revoke some add some", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusOK, + revoke: []byte(`{"results":[]}`), + statusGrant: http.StatusOK, + grant: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + Permissions: chronograf.Permissions{ + { + Scope: "all", + Allowed: []string{}, + }, + { + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE"}, + }, + { + Scope: "database", + Name: "newdb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + want: []string{ + `SHOW USERS`, + `SHOW GRANTS FOR "docbrown"`, + `GRANT WRITE ON "mydb" TO "docbrown"`, + `GRANT ALL ON "newdb" TO "docbrown"`, + `REVOKE ALL PRIVILEGES FROM "docbrown"`, + }, + }, + { + name: "Fail users", + statusUsers: http.StatusBadRequest, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusOK, + revoke: []byte(`{"results":[]}`), + statusGrant: http.StatusOK, + grant: []byte(`{"results":[]}`), + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + }, + }, + wantErr: true, + want: []string{ + `SHOW USERS`, + }, + }, + { + name: "fail grants", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusOK, + revoke: []byte(`{"results":[]}`), + statusGrant: http.StatusBadRequest, + grant: []byte(`{"results":[]}`), + wantErr: true, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + Permissions: chronograf.Permissions{ + { + Scope: "all", + Allowed: []string{}, + }, + { + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE"}, + }, + { + Scope: "database", + Name: "newdb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + want: []string{ + `SHOW USERS`, + `SHOW GRANTS FOR "docbrown"`, + `GRANT WRITE ON "mydb" TO "docbrown"`, + }, + }, + { + name: "fail revoke", + statusUsers: http.StatusOK, + showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`), + statusGrants: http.StatusOK, + showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`), + statusRevoke: http.StatusBadRequest, + revoke: []byte(`{"results":[]}`), + statusGrant: http.StatusOK, + grant: []byte(`{"results":[]}`), + wantErr: true, + args: args{ + ctx: context.Background(), + u: &chronograf.User{ + Name: "docbrown", + Permissions: chronograf.Permissions{ + { + Scope: "all", + Allowed: []string{}, + }, + { + Scope: "database", + Name: "mydb", + Allowed: []string{"WRITE"}, + }, + { + Scope: "database", + Name: "newdb", + Allowed: []string{"WRITE", "READ"}, + }, + }, + }, + }, + want: []string{ + `SHOW USERS`, + `SHOW GRANTS FOR "docbrown"`, + `GRANT WRITE ON "mydb" TO "docbrown"`, + `GRANT ALL ON "newdb" TO "docbrown"`, + `REVOKE ALL PRIVILEGES FROM "docbrown"`, + }, + }, + } + for _, tt := range tests { + queries := []string{} + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path := r.URL.Path; path != "/query" { + t.Error("Expected the path to contain `/query` but was", path) + } + query := r.URL.Query().Get("q") + if strings.Contains(query, "GRANTS") { + rw.WriteHeader(tt.statusGrants) + rw.Write(tt.showGrants) + } else if strings.Contains(query, "USERS") { + rw.WriteHeader(tt.statusUsers) + rw.Write(tt.showUsers) + } else if strings.Contains(query, "REVOKE") { + rw.WriteHeader(tt.statusRevoke) + rw.Write(tt.revoke) + } else if strings.Contains(query, "GRANT") { + rw.WriteHeader(tt.statusGrant) + rw.Write(tt.grant) + } + queries = append(queries, query) + })) + u, _ := url.Parse(ts.URL) + c := &Client{ + URL: u, + Logger: log.New(log.DebugLevel), + } + defer ts.Close() + if err := c.Update(tt.args.ctx, tt.args.u); (err != nil) != tt.wantErr { + t.Errorf("%q. Client.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + if !reflect.DeepEqual(queries, tt.want) { + t.Errorf("%q. Client.Update() = %v, want %v", tt.name, queries, tt.want) + } + } +} + +/* + + + + */