From 76e069e27b002d186005c14b1f1b86472cc209f2 Mon Sep 17 00:00:00 2001 From: Erik Pinders <34981356+pinders@users.noreply.github.com> Date: Wed, 25 Apr 2018 19:14:24 +0200 Subject: [PATCH] Add custom metadata to policies (#115) Signed-off-by: Erik Pinders --- manager/sql/databases.go | 24 +++++++++++++++++-- manager/sql/manager_sql.go | 13 ++++++---- .../sql/manager_sql_migration_0_5_to_0_6.go | 11 ++++++--- policy.go | 20 ++++++++++++++++ policy_test.go | 22 +++++++++++++++++ 5 files changed, 81 insertions(+), 9 deletions(-) diff --git a/manager/sql/databases.go b/manager/sql/databases.go index c5d0250..fd80d5f 100644 --- a/manager/sql/databases.go +++ b/manager/sql/databases.go @@ -136,9 +136,18 @@ var Migrations = map[string]Statements{ "DROP INDEX ladon_resource_compiled_idx", }, }, + { + Id: "4", + Up: []string{ + "ALTER TABLE ladon_policy ADD COLUMN meta json", + }, + Down: []string{ + "ALTER TABLE ladon_policy DROP COLUMN IF EXISTS meta", + }, + }, }, }, - QueryInsertPolicy: `INSERT INTO ladon_policy(id, description, effect, conditions) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_policy WHERE id = $1)`, + QueryInsertPolicy: `INSERT INTO ladon_policy(id, description, effect, conditions, meta) SELECT $1::varchar, $2, $3, $4, $5 WHERE NOT EXISTS (SELECT 1 FROM ladon_policy WHERE id = $1)`, QueryInsertPolicyActions: `INSERT INTO ladon_action (id, template, compiled, has_regex) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_action WHERE id = $1)`, QueryInsertPolicyActionsRel: `INSERT INTO ladon_policy_action_rel (policy, action) SELECT $1::varchar, $2::varchar WHERE NOT EXISTS (SELECT 1 FROM ladon_policy_action_rel WHERE policy = $1 AND action = $2)`, QueryInsertPolicyResources: `INSERT INTO ladon_resource (id, template, compiled, has_regex) SELECT $1::varchar, $2, $3, $4 WHERE NOT EXISTS (SELECT 1 FROM ladon_resource WHERE id = $1)`, @@ -151,6 +160,7 @@ var Migrations = map[string]Statements{ p.effect, p.conditions, p.description, + p.meta, subject.template AS subject, resource.template AS resource, action.template AS action @@ -187,9 +197,18 @@ var Migrations = map[string]Statements{ "DROP INDEX ladon_resource_compiled_idx", }, }, + { + Id: "4", + Up: []string{ + "ALTER TABLE ladon_policy ADD COLUMN meta text", + }, + Down: []string{ + "ALTER TABLE ladon_policy DROP COLUMN meta", + }, + }, }, }, - QueryInsertPolicy: `INSERT IGNORE INTO ladon_policy (id, description, effect, conditions) VALUES(?,?,?,?)`, + QueryInsertPolicy: `INSERT IGNORE INTO ladon_policy (id, description, effect, conditions, meta) VALUES(?,?,?,?,?)`, QueryInsertPolicyActions: `INSERT IGNORE INTO ladon_action (id, template, compiled, has_regex) VALUES(?,?,?,?)`, QueryInsertPolicyActionsRel: `INSERT IGNORE INTO ladon_policy_action_rel (policy, action) VALUES(?,?)`, QueryInsertPolicyResources: `INSERT IGNORE INTO ladon_resource (id, template, compiled, has_regex) VALUES(?,?,?,?)`, @@ -202,6 +221,7 @@ var Migrations = map[string]Statements{ p.effect, p.conditions, p.description, + p.meta, subject.template AS subject, resource.template AS resource, action.template AS action diff --git a/manager/sql/manager_sql.go b/manager/sql/manager_sql.go index 98453e5..22c91f6 100644 --- a/manager/sql/manager_sql.go +++ b/manager/sql/manager_sql.go @@ -136,11 +136,16 @@ func (s *SQLManager) create(policy Policy, tx *sqlx.Tx) (err error) { } } + meta := []byte("{}") + if policy.GetMeta() != nil { + meta = policy.GetMeta() + } + if _, ok := Migrations[s.database]; !ok { return errors.Errorf("Database %s is not supported", s.database) } - if _, err = tx.Exec(s.db.Rebind(Migrations[s.database].QueryInsertPolicy), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions); err != nil { + if _, err = tx.Exec(s.db.Rebind(Migrations[s.database].QueryInsertPolicy), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions, meta); err != nil { return errors.WithStack(err) } @@ -217,7 +222,7 @@ func scanRows(rows *sql.Rows) (Policies, error) { p.Subjects = []string{} p.Resources = []string{} - if err := rows.Scan(&p.ID, &p.Effect, &conditions, &p.Description, &subject, &resource, &action); err == sql.ErrNoRows { + if err := rows.Scan(&p.ID, &p.Effect, &conditions, &p.Description, &p.Meta, &subject, &resource, &action); err == sql.ErrNoRows { return nil, NewErrResourceNotFound(err) } else if err != nil { return nil, errors.WithStack(err) @@ -271,7 +276,7 @@ func scanRows(rows *sql.Rows) (Policies, error) { } var getQuery = `SELECT - p.id, p.effect, p.conditions, p.description, + p.id, p.effect, p.conditions, p.description, p.meta, subject.template as subject, resource.template as resource, action.template as action FROM ladon_policy as p @@ -287,7 +292,7 @@ LEFT JOIN ladon_resource as resource ON rr.resource = resource.id WHERE p.id=?` var getAllQuery = `SELECT - p.id, p.effect, p.conditions, p.description, + p.id, p.effect, p.conditions, p.description, p.meta, subject.template as subject, resource.template as resource, action.template as action FROM (SELECT * from ladon_policy ORDER BY id LIMIT ? OFFSET ?) as p diff --git a/manager/sql/manager_sql_migration_0_5_to_0_6.go b/manager/sql/manager_sql_migration_0_5_to_0_6.go index 5324fcc..d489e5b 100644 --- a/manager/sql/manager_sql_migration_0_5_to_0_6.go +++ b/manager/sql/manager_sql_migration_0_5_to_0_6.go @@ -44,7 +44,7 @@ func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) GetManager() ladon.Man // Get retrieves a policy. func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) Migrate() error { - rows, err := s.DB.Query(s.DB.Rebind("SELECT id, description, effect, conditions FROM ladon_policy")) + rows, err := s.DB.Query(s.DB.Rebind("SELECT id, description, effect, conditions, meta FROM ladon_policy")) if err != nil { return errors.WithStack(err) } @@ -55,7 +55,7 @@ func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) Migrate() error { var p DefaultPolicy var conditions []byte - if err := rows.Scan(&p.ID, &p.Description, &p.Effect, &conditions); err != nil { + if err := rows.Scan(&p.ID, &p.Description, &p.Effect, &conditions, &p.Meta); err != nil { return errors.WithStack(err) } @@ -131,9 +131,14 @@ func (s *SQLManagerMigrateFromMajor0Minor6ToMajor0Minor7) Create(policy Policy) } } + meta := []byte("{}") + if policy.GetMeta() != nil { + meta = policy.GetMeta() + } + if tx, err := s.DB.Begin(); err != nil { return errors.WithStack(err) - } else if _, err = tx.Exec(s.DB.Rebind("INSERT INTO ladon_policy (id, description, effect, conditions) VALUES (?, ?, ?, ?)"), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions); err != nil { + } else if _, err = tx.Exec(s.DB.Rebind("INSERT INTO ladon_policy (id, description, effect, conditions, meta) VALUES (?, ?, ?, ?, ?)"), policy.GetID(), policy.GetDescription(), policy.GetEffect(), conditions, meta); err != nil { if err := tx.Rollback(); err != nil { return errors.WithStack(err) } diff --git a/policy.go b/policy.go index c61add4..3adf8ad 100644 --- a/policy.go +++ b/policy.go @@ -55,6 +55,9 @@ type Policy interface { // GetConditions returns the policies conditions. GetConditions() Conditions + // GetMeta returns the policies arbitrary metadata set by the user. + GetMeta() []byte + // GetStartDelimiter returns the delimiter which identifies the beginning of a regular expression. GetStartDelimiter() byte @@ -71,6 +74,7 @@ type DefaultPolicy struct { Resources []string `json:"resources" gorethink:"resources"` Actions []string `json:"actions" gorethink:"actions"` Conditions Conditions `json:"conditions" gorethink:"conditions"` + Meta []byte `json:"meta" gorethink:"meta"` } // UnmarshalJSON overwrite own policy with values of the given in policy in JSON format @@ -83,6 +87,7 @@ func (p *DefaultPolicy) UnmarshalJSON(data []byte) error { Resources []string `json:"resources" gorethink:"resources"` Actions []string `json:"actions" gorethink:"actions"` Conditions Conditions `json:"conditions" gorethink:"conditions"` + Meta []byte `json:"meta" gorethink:"meta"` }{ Conditions: Conditions{}, } @@ -99,10 +104,20 @@ func (p *DefaultPolicy) UnmarshalJSON(data []byte) error { Resources: pol.Resources, Actions: pol.Actions, Conditions: pol.Conditions, + Meta: pol.Meta, } return nil } +// UnmarshalMeta parses the policies []byte encoded metadata and stores the result in the value pointed to by v. +func (p *DefaultPolicy) UnmarshalMeta(v interface{}) error { + if err := json.Unmarshal(p.Meta, &v); err != nil { + return errors.WithStack(err) + } + + return nil +} + // GetID returns the policies id. func (p *DefaultPolicy) GetID() string { return p.ID @@ -143,6 +158,11 @@ func (p *DefaultPolicy) GetConditions() Conditions { return p.Conditions } +// GetMeta returns the policies arbitrary metadata set by the user. +func (p *DefaultPolicy) GetMeta() []byte { + return p.Meta +} + // GetEndDelimiter returns the delimiter which identifies the end of a regular expression. func (p *DefaultPolicy) GetEndDelimiter() byte { return '>' diff --git a/policy_test.go b/policy_test.go index f949e02..51df875 100644 --- a/policy_test.go +++ b/policy_test.go @@ -50,6 +50,10 @@ var policyCases = []*DefaultPolicy{ }, } +type TestMeta struct { + Key string `json:"key"` +} + func TestHasAccess(t *testing.T) { assert.True(t, policyCases[0].AllowAccess()) assert.False(t, policyCases[1].AllowAccess()) @@ -71,6 +75,24 @@ func TestMarshalling(t *testing.T) { } } +func TestMetaUnmarshalling(t *testing.T) { + var m = TestMeta{ + Key: "test", + } + var mm TestMeta + var p = DefaultPolicy{} + + data, err := json.Marshal(&m) + RequireError(t, false, err) + + p.Meta = data + + err = p.UnmarshalMeta(&mm) + RequireError(t, false, err) + + assert.Equal(t, &m, &mm) +} + func TestGetters(t *testing.T) { for _, c := range policyCases { assert.Equal(t, c.ID, c.GetID())