Skip to content

Commit

Permalink
update config import for user role mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
BruceMacD committed Jul 29, 2021
1 parent 16b2e72 commit fb15f5e
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 256 deletions.
24 changes: 24 additions & 0 deletions internal/registry/_testdata/infra.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
sources:
- type: okta
oktaDomain: acme.okta.com
oktaClientId: 0oapn0qwiQPiMIyR35d6
oktaClientSecret: jfpn0qwiQPiMIfs408fjs048fjpn0qwiQPiMajsdf08j10j2
oktaApiToken: 001XJv9xhv899sdfns938haos3h8oahsdaohd2o8hdao82hd
users:
admin@example.com:
roles:
admin:
kind: cluster-role
clusters:
- cluster-AAA
- cluster-BBB
bob@example.com:
roles:
writer:
kind: cluster-role
clusters:
- cluster-AAA
reader:
kind: cluster-role
clusters:
- cluster-BBB
86 changes: 53 additions & 33 deletions internal/registry/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package registry
import (
"errors"

"github.com/infrahq/infra/internal/logging"
"gopkg.in/yaml.v2"
"gorm.io/gorm"
)
Expand All @@ -15,15 +16,25 @@ type ConfigSource struct {
OktaApiToken string `yaml:"oktaApiToken"`
}

type ConfigPermission struct {
Role string `yaml:"role"`
UserEmail string `yaml:"user"`
DestinationName string `yaml:"destination"`
type ConfigRoleKubernetes struct {
Kind string `yaml:"kind"`
Clusters []string `yaml:"clusters"`
}

type ConfigUserMapping struct {
Roles map[string]ConfigRoleKubernetes
// TODO (brucemacd): Add groups here
}

type Config struct {
Sources []ConfigSource `yaml:"sources"`
Permissions []ConfigPermission `yaml:"permissions"`
Sources []ConfigSource `yaml:"sources"`
Users map[string]ConfigUserMapping `yaml:"users"`
}

func NewConfig() Config {
var config Config
config.Users = make(map[string]ConfigUserMapping)
return config
}

var initialConfig Config
Expand Down Expand Up @@ -60,55 +71,64 @@ func ImportSources(db *gorm.DB, sources []ConfigSource) error {
return nil
}

func ApplyPermissions(db *gorm.DB, permissions []ConfigPermission) ([]string, error) {
func ApplyUserMapping(db *gorm.DB, users map[string]ConfigUserMapping) ([]string, error) {
var ids []string
for _, p := range permissions {
for email, userMapping := range users {
var user User
err := db.Where(&User{Email: p.UserEmail}).First(&user).Error
err := db.Where(&User{Email: email}).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// skip this user, if they're created these permissions will be added later
continue
}
return nil, err
}

var destination Destination
err = db.Where(&Destination{Name: p.DestinationName}).First(&destination).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
continue
for roleName, role := range userMapping.Roles {
switch role.Kind {
case PERMISSION_KIND_ROLE:
// TODO (brucemacd): Handle config imports of role permissions when we support RoleBindings
logging.L.Info("Skipping permission " + roleName + ", Role permissions are not supported yet")
case PERMISSION_KIND_CLUSTER_ROLE:
for _, dest := range role.Clusters {
var destination Destination
err := db.Where(&Destination{Name: dest}).First(&destination).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// when a destination is added then the config import will be retried
continue
}
return nil, err
}

var permission Permission
err = db.FirstOrCreate(&permission, &Permission{Role: roleName, Kind: role.Kind, UserId: user.Id, DestinationId: destination.Id, FromConfig: true}).Error
if err != nil {
return nil, err
}

ids = append(ids, permission.Id)
}
default:
logging.L.Info("Unrecognized permission kind " + role.Kind + " in infra.yaml, permission skipped.")
}
return nil, err
}

var permission Permission
err = db.FirstOrCreate(&permission, &Permission{UserId: user.Id, DestinationId: destination.Id, Role: p.Role, FromDefault: false}).Error
if err != nil {
return nil, err
}

permission.FromConfig = true

err = db.Save(&permission).Error
if err != nil {
return nil, err
}

ids = append(ids, permission.Id)
// TODO: add user to groups here
}
return ids, nil
}

func ImportPermissions(db *gorm.DB, permissions []ConfigPermission) error {
idsToKeep, err := ApplyPermissions(db, permissions)
func ImportUserMappings(db *gorm.DB, users map[string]ConfigUserMapping) error {
idsToKeep, err := ApplyUserMapping(db, users)
if err != nil {
return err
}
return db.Where(&Permission{FromConfig: true}).Not(idsToKeep).Delete(Permission{}).Error
}

func ImportConfig(db *gorm.DB, bs []byte) error {
var config Config
config := NewConfig()
err := yaml.Unmarshal(bs, &config)
if err != nil {
return err
Expand All @@ -120,7 +140,7 @@ func ImportConfig(db *gorm.DB, bs []byte) error {
if err = ImportSources(tx, config.Sources); err != nil {
return err
}
if err = ImportPermissions(tx, config.Permissions); err != nil {
if err = ImportUserMappings(tx, config.Users); err != nil {
return err
}
return nil
Expand Down
50 changes: 50 additions & 0 deletions internal/registry/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package registry

import (
"io/ioutil"
"testing"

"github.com/stretchr/testify/assert"
)

func TestImportCurrentValidConfig(t *testing.T) {
conf, err := ioutil.ReadFile("_testdata/infra.yaml")
if err != nil {
t.Fatal(err)
}

db, err := NewDB("file::memory:")
if err != nil {
t.Fatal(err)
}

assert.NoError(t, ImportConfig(db, conf))
}

// func TestImportUsersThatDoNotExist(t *testing.T) {
// confFile, err := ioutil.ReadFile("_testdata/infra.yaml")
// if err != nil {
// t.Fatal(err)
// }
// config := NewConfig()
// err = yaml.Unmarshal(confFile, &config)
// if err != nil {
// t.Fatal(err)
// }

// db, err := NewDB("file::memory:")
// if err != nil {
// t.Fatal(err)
// }

// ImportUserMappings(db, config.Users)
// for user, userMapping := range config.Users {
// for roleName, role := range userMapping.Roles {
// var permission Permission
// err = db.Where(&permission, &Permission{Role: roleName, Kind: role.Kind, UserId: user.Id, DestinationId: destination.Id, FromConfig: true}).Error
// if err != nil {
// return nil, err
// }
// }
// }
// }
12 changes: 10 additions & 2 deletions internal/registry/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"log"
"os"
"path"
Expand Down Expand Up @@ -81,6 +82,7 @@ type Permission struct {
Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"`
Role string
Kind string
UserId string
DestinationId string
User User `gorm:"foreignKey:UserId;references:Id"`
Expand All @@ -90,6 +92,11 @@ type Permission struct {
FromDefault bool
}

var (
PERMISSION_KIND_ROLE = "role"
PERMISSION_KIND_CLUSTER_ROLE = "cluster-role"
)

type Settings struct {
Id string `gorm:"primaryKey"`
Created int64 `gorm:"autoCreateTime"`
Expand Down Expand Up @@ -135,7 +142,8 @@ func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
}

func (u *User) AfterCreate(tx *gorm.DB) error {
_, err := ApplyPermissions(tx, initialConfig.Permissions)
fmt.Println(initialConfig.Users)
_, err := ApplyUserMapping(tx, initialConfig.Users)
return err
}

Expand Down Expand Up @@ -191,7 +199,7 @@ func (r *Destination) BeforeCreate(tx *gorm.DB) (err error) {
}

func (d *Destination) AfterCreate(tx *gorm.DB) error {
_, err := ApplyPermissions(tx, initialConfig.Permissions)
_, err := ApplyUserMapping(tx, initialConfig.Users)
return err
}

Expand Down
9 changes: 8 additions & 1 deletion internal/registry/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,14 +470,21 @@ func (v *V1Server) CreateDestination(ctx context.Context, in *v1.CreateDestinati
}

func dbToProtoPermission(in *Permission) *v1.Permission {
return &v1.Permission{
permission := v1.Permission{
Id: in.Id,
Created: in.Created,
Updated: in.Updated,
Role: in.Role,
User: dbToProtoUser(&in.User),
Destination: dbToProtoDestination(&in.Destination),
}
switch in.Kind {
case PERMISSION_KIND_ROLE:
permission.Kind = v1.KubernetesRoleType_ROLE
case PERMISSION_KIND_CLUSTER_ROLE:
permission.Kind = v1.KubernetesRoleType_CLUSTER_ROLE
}
return &permission
}

func (v *V1Server) ListPermissions(ctx context.Context, in *v1.ListPermissionsRequest) (*v1.ListPermissionsResponse, error) {
Expand Down

0 comments on commit fb15f5e

Please sign in to comment.