// 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" "github.com/molecula/featurebase/v3/authn" "gopkg.in/yaml.v2" ) type GroupPermissions struct { Permissions map[string]map[string]Permission `yaml:"user-groups"` Admin string `yaml:"admin"` } type Permission string const ( None Permission = "" Read Permission = "read" Write Permission = "write" Admin Permission = "admin" ) // Satisfies returns whether `p` satisfies the permissions required by `b` func (p Permission) Satisfies(b Permission) bool { switch p { case "": return b == "" case "read": return b == "" || b == "read" case "write": return b == "" || b == "read" || b == "write" case "admin": return b == "" || b == "read" || b == "write" || b == "admin" } return false } func (p *GroupPermissions) ReadPermissionsFile(permsFile io.Reader) (err error) { permsData, err := io.ReadAll(permsFile) if err != nil { return fmt.Errorf("reading permissions failed with error: %s", err) } err = yaml.UnmarshalStrict(permsData, &p) if err != nil { return fmt.Errorf("unmarshalling permissions failed with error: %s", err) } return } func (p *GroupPermissions) GetPermissions(user *authn.UserInfo, index string) (permission Permission, errors error) { groups := user.Groups if admin := p.IsAdmin(groups); admin { return Admin, nil } allPermissions := map[Permission]bool{ Write: false, Read: false, } if len(groups) == 0 { return None, fmt.Errorf("user is not part of any groups in identity provider") } var groupsDenied []string for _, group := range groups { if _, ok := p.Permissions[group.GroupID]; ok { if perm, ok := p.Permissions[group.GroupID][index]; ok { allPermissions[perm] = true } else { return None, fmt.Errorf("user %s does not have permission to index %s", user.UserID, index) } } else { groupsDenied = append(groupsDenied, group.GroupID) } } if len(groupsDenied) == len(groups) { return None, fmt.Errorf("group(s) %s does not have permission to FeatureBase", groupsDenied) } if allPermissions[Write] { return Write, nil } else if allPermissions[Read] { return Read, nil } else { return None, fmt.Errorf("no permissions found") } } func (p *GroupPermissions) IsAdmin(groups []authn.Group) bool { for _, group := range groups { if p.Admin == group.GroupID { return true } } return false } func (p *GroupPermissions) GetAuthorizedIndexList(groups []authn.Group, desiredPermission Permission) (indexList []string) { // if user is admin, find all indexes in permissions file and return them if p.IsAdmin(groups) { for groupId := range p.Permissions { for index := range p.Permissions[groupId] { indexList = append(indexList, index) } } return indexList } for _, group := range groups { if _, ok := p.Permissions[group.GroupID]; ok { for index, permission := range p.Permissions[group.GroupID] { if permission.Satisfies(desiredPermission) { indexList = append(indexList, index) } } } } return indexList }