Skip to content

Commit

Permalink
Multiple Policy Files (#231)
Browse files Browse the repository at this point in the history
Update policy app config & config loader to support multiple policy files by passing in a policy directory. Files are merged and the merged policy document is validated after all files are loaded. Also added a check to disallow multiple bindings of the same action to the same type.

---------

Signed-off-by: Jacob See <[email protected]>
  • Loading branch information
jacobsee authored Apr 5, 2024
1 parent d026d24 commit 23c9d73
Show file tree
Hide file tree
Showing 15 changed files with 458 additions and 45 deletions.
12 changes: 6 additions & 6 deletions chart/permissions-api/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
secretName: {{ . }}
{{- end }}
{{- with .Values.config.spicedb.policyConfigMapName }}
- name: policy-file
- name: policy-files
configMap:
name: {{ . }}
{{- end }}
Expand All @@ -46,8 +46,8 @@
mountPath: {{ .Values.config.crdb.caMountPath }}
{{- end }}
{{- if .Values.config.spicedb.policyConfigMapName }}
- name: policy-file
mountPath: /policy
- name: policy-files
mountPath: /policies
{{- end }}
{{- end }}

Expand All @@ -71,7 +71,7 @@
secretName: {{ . }}
{{- end }}
{{- with .Values.config.spicedb.policyConfigMapName }}
- name: policy-file
- name: policy-files
configMap:
name: {{ . }}
{{- end }}
Expand All @@ -93,7 +93,7 @@
mountPath: /nats
{{- end }}
{{- if .Values.config.spicedb.policyConfigMapName }}
- name: policy-file
mountPath: /policy
- name: policy-files
mountPath: /policies
{{- end }}
{{- end }}
4 changes: 2 additions & 2 deletions chart/permissions-api/templates/deployment-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ spec:
key: uri
{{- end }}
{{- if .Values.config.spicedb.policyConfigMapName }}
- name: PERMISSIONSAPI_SPICEDB_POLICYFILE
value: /policy/policy.yaml
- name: PERMISSIONSAPI_SPICEDB_POLICYDIR
value: /policies
{{- end }}
{{- if .Values.config.spicedb.caSecretName }}
- name: SSL_CERT_DIR
Expand Down
4 changes: 2 additions & 2 deletions chart/permissions-api/templates/deployment-worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ spec:
key: token
{{- end }}
{{- if .Values.config.spicedb.policyConfigMapName }}
- name: PERMISSIONSAPI_SPICEDB_POLICYFILE
value: /policy/policy.yaml
- name: PERMISSIONSAPI_SPICEDB_POLICYDIR
value: /policies
{{- end }}
{{- if .Values.config.spicedb.caSecretName }}
- name: SSL_CERT_DIR
Expand Down
1 change: 0 additions & 1 deletion chart/permissions-api/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ config:
pskSecretName: ""
# policyConfigMapName is the name of the Config Map containing the policy file configuration
policyConfigMapName: ""

crdb:
# migrateHook sets when to run database migrations. one of: pre-sync, init, manual
# - pre-sync: hook runs as a job before any other changes are synced.
Expand Down
8 changes: 4 additions & 4 deletions cmd/createrole.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ func createRole(ctx context.Context, cfg *config.AppConfig) {

var policy iapl.Policy

if cfg.SpiceDB.PolicyFile != "" {
policy, err = iapl.NewPolicyFromFile(cfg.SpiceDB.PolicyFile)
if cfg.SpiceDB.PolicyDir != "" {
policy, err = iapl.NewPolicyFromDirectory(cfg.SpiceDB.PolicyDir)
if err != nil {
logger.Fatalw("unable to load new policy from schema file", "policy_file", cfg.SpiceDB.PolicyFile, "error", err)
logger.Fatalw("unable to load new policy from schema directory", "policy_dir", cfg.SpiceDB.PolicyDir, "error", err)
}
} else {
logger.Warn("no spicedb policy file defined, using default policy")
logger.Warn("no spicedb policy defined, using default policy")

policy = iapl.DefaultPolicy()
}
Expand Down
28 changes: 23 additions & 5 deletions cmd/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"

v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -49,13 +52,13 @@ func writeSchema(_ context.Context, dryRun bool, cfg *config.AppConfig) {
policy iapl.Policy
)

if cfg.SpiceDB.PolicyFile != "" {
policy, err = iapl.NewPolicyFromFile(cfg.SpiceDB.PolicyFile)
if cfg.SpiceDB.PolicyDir != "" {
policy, err = iapl.NewPolicyFromDirectory(cfg.SpiceDB.PolicyDir)
if err != nil {
logger.Fatalw("unable to load new policy from schema file", "policy_file", cfg.SpiceDB.PolicyFile, "error", err)
logger.Fatalw("unable to load new policy from schema directory", "policy_dir", cfg.SpiceDB.PolicyDir, "error", err)
}
} else {
logger.Warn("no spicedb policy file defined, using default policy")
logger.Warn("no spicedb policy defined, using default policy")

policy = iapl.DefaultPolicy()
}
Expand All @@ -70,7 +73,22 @@ func writeSchema(_ context.Context, dryRun bool, cfg *config.AppConfig) {
}

if viper.GetBool("mermaid") || viper.GetBool("mermaid-markdown") {
outputPolicyMermaid(cfg.SpiceDB.PolicyFile, viper.GetBool("mermaid-markdown"))
if cfg.SpiceDB.PolicyDir != "" {
files, err := os.ReadDir(cfg.SpiceDB.PolicyDir)
if err != nil {
logger.Fatalw("failed to read policy files from directory", "error", err)
}

filePaths := make([]string, 0, len(files))

for _, file := range files {
if !file.IsDir() && (strings.EqualFold(filepath.Ext(file.Name()), ".yml") || strings.EqualFold(filepath.Ext(file.Name()), ".yaml")) {
filePaths = append(filePaths, cfg.SpiceDB.PolicyDir+"/"+file.Name())
}
}

outputPolicyMermaid(filePaths, viper.GetBool("mermaid-markdown"))
}

return
}
Expand Down
25 changes: 15 additions & 10 deletions cmd/schema_mermaid.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,24 @@ type mermaidContext struct {
RelatedActions map[string]map[string][]string
}

func outputPolicyMermaid(filePath string, markdown bool) {
var policy iapl.PolicyDocument
func outputPolicyMermaid(filePaths []string, markdown bool) {
policy := iapl.PolicyDocument{}

if len(filePaths) > 0 {
for _, filePath := range filePaths {
file, err := os.Open(filePath)
if err != nil {
logger.Fatalw("failed to open policy document file", "error", err)
}
defer file.Close()

if filePath != "" {
file, err := os.Open(filePath)
if err != nil {
logger.Fatalw("failed to open policy document file", "error", err)
}
var filePolicy iapl.PolicyDocument

defer file.Close()
if err := yaml.NewDecoder(file).Decode(&filePolicy); err != nil {
logger.Fatalw("failed to open policy document file", "error", err)
}

if err := yaml.NewDecoder(file).Decode(&policy); err != nil {
logger.Fatalw("failed to load policy document file", "error", err)
policy = policy.MergeWithPolicyDocument(filePolicy)
}
} else {
policy = iapl.DefaultPolicyDocument()
Expand Down
8 changes: 4 additions & 4 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ func serve(_ context.Context, cfg *config.AppConfig) {

var policy iapl.Policy

if cfg.SpiceDB.PolicyFile != "" {
policy, err = iapl.NewPolicyFromFile(cfg.SpiceDB.PolicyFile)
if cfg.SpiceDB.PolicyDir != "" {
policy, err = iapl.NewPolicyFromDirectory(cfg.SpiceDB.PolicyDir)
if err != nil {
logger.Fatalw("unable to load new policy from schema file", "policy_file", cfg.SpiceDB.PolicyFile, "error", err)
logger.Fatalw("unable to load new policy from schema directory", "policy_dir", cfg.SpiceDB.PolicyDir, "error", err)
}
} else {
logger.Warn("no spicedb policy file defined, using default policy")
logger.Warn("no spicedb policy defined, using default policy")

policy = iapl.DefaultPolicy()
}
Expand Down
8 changes: 4 additions & 4 deletions cmd/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ func worker(ctx context.Context, cfg *config.AppConfig) {

var policy iapl.Policy

if cfg.SpiceDB.PolicyFile != "" {
policy, err = iapl.NewPolicyFromFile(cfg.SpiceDB.PolicyFile)
if cfg.SpiceDB.PolicyDir != "" {
policy, err = iapl.NewPolicyFromDirectory(cfg.SpiceDB.PolicyDir)
if err != nil {
logger.Fatalw("unable to load new policy from schema file", "policy_file", cfg.SpiceDB.PolicyFile, "error", err)
logger.Fatalw("unable to load new policy from schema directory", "policy_dir", cfg.SpiceDB.PolicyDir, "error", err)
}
} else {
logger.Warn("no spicedb policy file defined, using default policy")
logger.Warn("no spicedb policy defined, using default policy")

policy = iapl.DefaultPolicy()
}
Expand Down
Loading

0 comments on commit 23c9d73

Please sign in to comment.