diff --git a/auth/auth.go b/authz/authz.go similarity index 62% rename from auth/auth.go rename to authz/authz.go index 7376e2551..f48943c04 100644 --- a/auth/auth.go +++ b/authz/authz.go @@ -1,11 +1,23 @@ -// Copyright 2021 Molecula Corp. All rights reserved. -package auth +// Copyright 2017 Pilosa Corp. +// +// 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 authz import ( "fmt" + "io" "io/ioutil" - "log" - "path/filepath" "gopkg.in/yaml.v2" ) @@ -46,48 +58,39 @@ type Permissions struct { Permission string `yaml:"permission"` } -func ReadPermissionsFile(filePath string) (yamlData []byte) { - filePathAbs, _ := filepath.Abs(filePath) - yamlData, err := ioutil.ReadFile(filePathAbs) +type Group struct { + ID string `json:"id"` + Name string `json:"displayName"` +} + +func (p *GroupPermissions) ReadPermissionsFile(permsFile io.Reader) (err error) { + permsData, err := ioutil.ReadAll(permsFile) if err != nil { - panic(err) + return fmt.Errorf("reading permissions failed with error: %s", err) } - return yamlData -} -func (p *GroupPermissions) CreatePermissionsStruct(data []byte) { - err := yaml.Unmarshal([]byte(data), p) + err = yaml.Unmarshal(permsData, p) if err != nil { - log.Fatalf("error %s", err) + return fmt.Errorf("unmarshalling permissions failed with error: %s", err) } + + return nil } -func GetPermissions(Auth *Auth, groups []map[string]string, index []string) (permission string, err error) { - // read yaml permissions file - yamlData := ReadPermissionsFile(Auth.PermissionsFile) - - // get group permissions - var p GroupPermissions - p.CreatePermissionsStruct(yamlData) - - // check permissions for all groups and index, and return most permissive - return p.ResolvePermissions(groups, index) -} - -func (p *GroupPermissions) ResolvePermissions(groups []map[string]string, index []string) (permission string, err error) { +func (p *GroupPermissions) GetPermissions(groups []Group, index []string) (permission string, err error) { // get union of groups the user is part of obtained from identity provider and groups in permissions file var groupMatch []Permissions for _, group := range groups { for i := range p.Permissions { - if group["id"] == p.Permissions[i].GroupId { + if group.ID == p.Permissions[i].GroupId { groupMatch = append(groupMatch, p.Permissions[i]) } } } if len(groupMatch) == 0 { - return "", fmt.Errorf("user is NOT allowed access to FeatureBase") + return "", fmt.Errorf("the user's groups %s are NOT allowed access to FeatureBase", groups) } // check that user's groups have access to the index user want to access @@ -110,7 +113,7 @@ func (p *GroupPermissions) ResolvePermissions(groups []map[string]string, index indexNotFound = append(indexNotFound, idx) } } - return "", fmt.Errorf("user is not allowed access to index: %s", indexNotFound) + return "", fmt.Errorf("user is NOT allowed access to index: %s", indexNotFound) } // check permissions for index user has access to diff --git a/auth/auth_test.go b/authz/authz_test.go similarity index 61% rename from auth/auth_test.go rename to authz/authz_test.go index e27463c59..eaec5df13 100644 --- a/auth/auth_test.go +++ b/authz/authz_test.go @@ -11,7 +11,7 @@ // 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 auth_test +package authz_test import ( "fmt" @@ -19,23 +19,23 @@ import ( "strings" "testing" - "github.com/molecula/featurebase/v2/auth" + "github.com/molecula/featurebase/v2/authz" ) -func TestAuth_CreatePermissionsStruct(t *testing.T) { - var singleInput = []byte(`group_permissions: +func TestAuth_ReadPermissionsFile(t *testing.T) { + var singleInput = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee55906b" index: "test" - permission: "read"`) + permission: "read"` - var emptyInput = []byte(`group_permissions: + var emptyInput = `group_permissions: - group: groupId: "" index: "" - permission: ""`) + permission: ""` - var multiInput = []byte(`group_permissions: + var multiInput = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee55906b" index: "test" @@ -43,28 +43,28 @@ func TestAuth_CreatePermissionsStruct(t *testing.T) { - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" - permission: "admin"`) + permission: "admin"` - singleStruct := auth.GroupPermissions{ - Permissions: []auth.Permissions{ + singleStruct := authz.GroupPermissions{ + Permissions: []authz.Permissions{ {"dca35310-ecda-4f23-86cd-876aee55906b", "test", "read"}, }, } - emptyStruct := auth.GroupPermissions{ - Permissions: []auth.Permissions{ + emptyStruct := authz.GroupPermissions{ + Permissions: []authz.Permissions{ {"", "", ""}, }, } - multiStruct := auth.GroupPermissions{ - Permissions: []auth.Permissions{ + multiStruct := authz.GroupPermissions{ + Permissions: []authz.Permissions{ {"dca35310-ecda-4f23-86cd-876aee55906b", "test", "read"}, {"dca35310-ecda-4f23-86cd-876aee559900", "test", "admin"}, }, } tests := []struct { - input []byte - output auth.GroupPermissions + input string + output authz.GroupPermissions }{ {singleInput, singleStruct}, {emptyInput, emptyStruct}, @@ -73,8 +73,11 @@ func TestAuth_CreatePermissionsStruct(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - var p auth.GroupPermissions - p.CreatePermissionsStruct(test.input) + permFile := strings.NewReader(test.input) + var p authz.GroupPermissions + if err := p.ReadPermissionsFile(permFile); err != nil { + t.Fatalf("readPermissionsFile error: %s", err) + } if !reflect.DeepEqual(p, test.output) { t.Fatalf("expected output %s, but got %s", test.output, p) @@ -84,26 +87,15 @@ func TestAuth_CreatePermissionsStruct(t *testing.T) { } } -func createGroupMaps(groups []string) []map[string]string { - - var group1 []map[string]string - for _, i := range groups { - map1 := map[string]string{} - map1["id"] = i - group1 = append(group1, map1) - } - return group1 -} - func TestAuth_ResolvePermissions(t *testing.T) { // initializes different example of permissions file in yaml - var permissions1 = []byte(`group_permissions: + var permissions1 = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee55906b" index: "test" - permission: "read"`) + permission: "read"` - var permissions2 = []byte(`group_permissions: + var permissions2 = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" @@ -111,9 +103,9 @@ func TestAuth_ResolvePermissions(t *testing.T) { - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" - permission: "write"`) + permission: "write"` - var permissions3 = []byte(`group_permissions: + var permissions3 = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" @@ -121,9 +113,9 @@ func TestAuth_ResolvePermissions(t *testing.T) { - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" - permission: "admin"`) + permission: "admin"` - var permissions4 = []byte(`group_permissions: + var permissions4 = `group_permissions: - group: groupId: "dca35310-ecda-4f23-86cd-876aee55906b" index: "test" @@ -131,65 +123,68 @@ func TestAuth_ResolvePermissions(t *testing.T) { - group: groupId: "dca35310-ecda-4f23-86cd-876aee559900" index: "test" - permission: "admin"`) + permission: "admin"` // initializes groups that are returned from identity provider - groupsList1 := []string{} - groupsList2 := []string{"dca35310-ecda-4f23-86cd-876aee55906b"} - groupsList3 := []string{"dca35310-ecda-4f23-86cd-876aee55906b", "dca35310-ecda-4f23-86cd-876aee559900"} + groupsList1 := []authz.Group{} + groupsList2 := []authz.Group{{"dca35310-ecda-4f23-86cd-876aee55906b", "name"}} + groupsList3 := []authz.Group{ + {"dca35310-ecda-4f23-86cd-876aee55906b", "name"}, + {"dca35310-ecda-4f23-86cd-876aee559900", "name"}, + } tests := []struct { - permissions []byte - groups []map[string]string - index []string - userAccess string - err string + yamlData string + groups []authz.Group + index []string + userAccess string + err string }{ { permissions1, - createGroupMaps(groupsList1), + groupsList1, []string{"test"}, "", - "user is NOT allowed access to FeatureBase", + "NOT allowed access to FeatureBase", }, { permissions1, - createGroupMaps(groupsList2), + groupsList2, []string{"test1"}, "", - "user is not allowed access to index", + "NOT allowed access to index", }, { permissions1, - createGroupMaps(groupsList2), + groupsList2, []string{"test"}, "read", "", }, { permissions2, - createGroupMaps(groupsList3), + groupsList3, []string{"test"}, "write", "", }, { permissions3, - createGroupMaps(groupsList3), + groupsList3, []string{"test"}, "admin", "", }, { permissions2, - createGroupMaps(groupsList3), + groupsList3, []string{"test"}, "write", "", }, { permissions4, - createGroupMaps(groupsList2), + groupsList2, []string{"test"}, "", "no permissions found", @@ -199,10 +194,11 @@ func TestAuth_ResolvePermissions(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - var p auth.GroupPermissions - p.CreatePermissionsStruct(test.permissions) + permFile := strings.NewReader(test.yamlData) + var p authz.GroupPermissions + p.ReadPermissionsFile(permFile) - p1, err := p.ResolvePermissions(test.groups, test.index) + p1, err := p.GetPermissions(test.groups, test.index) if p1 != test.userAccess { t.Errorf("expected permission to be %s, but got %s", test.userAccess, p1) diff --git a/server/config.go b/server/config.go index 595697067..1f3b62b4a 100644 --- a/server/config.go +++ b/server/config.go @@ -7,13 +7,14 @@ import ( "log" "net" "net/url" + "os" "path/filepath" "runtime" "strconv" "strings" "time" - "github.com/molecula/featurebase/v2/auth" + "github.com/molecula/featurebase/v2/authz" petcd "github.com/molecula/featurebase/v2/etcd" rbfcfg "github.com/molecula/featurebase/v2/rbf/cfg" "github.com/molecula/featurebase/v2/storage" @@ -232,7 +233,7 @@ type Config struct { SchemaDetailsOn bool `toml:"schema-details-on"` // Enable AuthZ/AuthN - Auth auth.Auth `toml:"auth"` + Auth authz.Auth `toml:"auth"` } // Namespace returns the namespace to use based on the Future flag. @@ -642,13 +643,22 @@ func (c *Config) ValidateAuth() ([]error, error) { } func (c *Config) ValidatePermissions() (err error) { + permsFile, err := os.Open(c.Auth.PermissionsFile) + if err != nil { + return err + } + + var p *authz.GroupPermissions + if err := p.ReadPermissionsFile(permsFile); err != nil { + return err + } - yamlData := auth.ReadPermissionsFile(c.Auth.PermissionsFile) - var p auth.GroupPermissions - p.CreatePermissionsStruct(yamlData) if len(p.Permissions) == 0 { return fmt.Errorf("no group permissions found in permissions file: %s", c.Auth.PermissionsFile) } + + defer permsFile.Close() + return nil } diff --git a/server/config_internal_test.go b/server/config_internal_test.go index 7f387172e..4d6dcc71c 100644 --- a/server/config_internal_test.go +++ b/server/config_internal_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/molecula/featurebase/v2/auth" + "github.com/molecula/featurebase/v2/authz" ) type addrs struct{ bind, advertise string } @@ -294,7 +294,7 @@ func TestConfig_validateAuth(t *testing.T) { tests := []struct { expErrs []string - input auth.Auth + input authz.Auth }{ { @@ -308,7 +308,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: emptyString, ClientSecret: emptyString, @@ -329,7 +329,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: emptyString, @@ -350,7 +350,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: emptyString, ClientSecret: validClientSecret, @@ -370,7 +370,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -389,7 +389,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -407,7 +407,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgEmpty, errorMesgEmpty, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -423,7 +423,7 @@ func TestConfig_validateAuth(t *testing.T) { []string{ errorMesgURL, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -440,7 +440,7 @@ func TestConfig_validateAuth(t *testing.T) { errorMesgURL, errorMesgURL, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -456,7 +456,7 @@ func TestConfig_validateAuth(t *testing.T) { []string{ errorMesgPermissions, }, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -470,7 +470,7 @@ func TestConfig_validateAuth(t *testing.T) { { // Auth enabled, all configs are set properly []string{}, - auth.Auth{ + authz.Auth{ Enable: enable, ClientId: validClientID, ClientSecret: validClientSecret, @@ -484,7 +484,7 @@ func TestConfig_validateAuth(t *testing.T) { { // Auth disabled, all configs are set to empty string []string{}, - auth.Auth{ + authz.Auth{ Enable: disable, ClientId: emptyString, ClientSecret: emptyString, diff --git a/server/server.go b/server/server.go index a6d0049ae..b876f3eb6 100644 --- a/server/server.go +++ b/server/server.go @@ -29,6 +29,7 @@ import ( "golang.org/x/sync/errgroup" pilosa "github.com/molecula/featurebase/v2" + "github.com/molecula/featurebase/v2/authz" "github.com/molecula/featurebase/v2/boltdb" "github.com/molecula/featurebase/v2/encoding/proto" petcd "github.com/molecula/featurebase/v2/etcd" @@ -224,6 +225,19 @@ func (m *Command) Start() (err error) { if m.Config.Auth.Enable { m.Config.MustValidateAuth() + + // Read permissions file + permsFile, err := os.Open(m.Config.Auth.PermissionsFile) + if err != nil { + return err + } + + var p authz.GroupPermissions + if err := p.ReadPermissionsFile(permsFile); err != nil { + return err + } + + defer permsFile.Close() } // Initialize server.