-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the policy store that adds policy management semantics on top KVStore. Policies added to the store are automatically versioned. Policy version is now recorded along the Policy ID in the AttestationResult. Signed-off-by: Sergei Trofimov <sergei.trofimov@arm.com>
- Loading branch information
Showing
8 changed files
with
233 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2022 Contributors to the Veraison project. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package policy | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/spf13/viper" | ||
"github.com/veraison/services/kvstore" | ||
) | ||
|
||
var ErrNoPolicy = errors.New("no policy found") | ||
|
||
// NewStore returns a new policy store. Config options are the same as those | ||
// used for kvstore.New(). | ||
func NewStore(v *viper.Viper) (*Store, error) { | ||
kvStore, err := kvstore.New(v) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Store{KVStore: kvStore}, nil | ||
} | ||
|
||
type Store struct { | ||
KVStore kvstore.IKVStore | ||
} | ||
|
||
// Setup the underyling kvstore. This is a one-time setup that only needs to be | ||
// performed once for a deployment. | ||
func (o *Store) Setup() error { | ||
return o.KVStore.Setup() | ||
} | ||
|
||
// Add a policy with the specified ID and rules. If a policy with that ID | ||
// already exists, an error is returned. | ||
func (o *Store) Add(id, rules string) error { | ||
if _, err := o.Get(id); err == nil { | ||
return fmt.Errorf("policy with id %q already exists", id) | ||
} | ||
|
||
return o.Update(id, rules) | ||
} | ||
|
||
// Update sets the provided rules as the latest version of the policy with the | ||
// specified ID. If a policy with that ID does not exist, it is created. | ||
func (o *Store) Update(id, rules string) error { | ||
var oldVersion int32 | ||
|
||
oldPolicy, err := o.GetLatest(id) | ||
|
||
if err == nil { | ||
oldVersion = oldPolicy.Version | ||
} else if errors.Is(err, ErrNoPolicy) { | ||
oldVersion = 0 | ||
} else { | ||
return err | ||
} | ||
|
||
newPolicy := Policy{ID: id, Rules: rules, Version: oldVersion + 1} | ||
|
||
newPolicyBytes, err := json.Marshal(newPolicy) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return o.KVStore.Add(id, string(newPolicyBytes)) | ||
} | ||
|
||
// Get returns the slice of all Policies associated with he specfied ID. Each | ||
// Policy represents a different version of the same logical policy. | ||
func (o *Store) Get(id string) ([]Policy, error) { | ||
vals, err := o.KVStore.Get(id) | ||
if err != nil { | ||
if errors.Is(err, kvstore.ErrKeyNotFound) { | ||
return nil, fmt.Errorf("%w: %q", ErrNoPolicy, id) | ||
} | ||
return nil, err | ||
} | ||
|
||
var policies []Policy | ||
|
||
for _, v := range vals { | ||
var p Policy | ||
if err = json.Unmarshal([]byte(v), &p); err != nil { | ||
return nil, err | ||
} | ||
|
||
policies = append(policies, p) | ||
} | ||
|
||
return policies, nil | ||
} | ||
|
||
// GetLatest returns the latest version of the policy with the specified ID. If | ||
// no such policy exists, a wrapped ErrNoPolicy is returned. | ||
func (o *Store) GetLatest(id string) (Policy, error) { | ||
policies, err := o.Get(id) | ||
if err != nil { | ||
return Policy{}, err | ||
} | ||
|
||
return policies[len(policies)-1], nil | ||
} | ||
|
||
// Del removes all policy versisions associated with the specfied id. | ||
func (o *Store) Del(id string) error { | ||
return o.KVStore.Del(id) | ||
} | ||
|
||
// Close the connection to the underlying kvstore. | ||
func (o *Store) Close() error { | ||
return o.KVStore.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2022 Contributors to the Veraison project. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package policy | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/spf13/viper" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_Store_CRUD(t *testing.T) { | ||
v := viper.New() | ||
v.Set("backend", "memory") | ||
|
||
store, err := NewStore(v) | ||
require.NoError(t, err) | ||
defer store.Close() | ||
|
||
err = store.Add("p1", "1. the chief's always right; 2. if the chief's wrong, see 1.") | ||
require.NoError(t, err) | ||
|
||
policy, err := store.GetLatest("p1") | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, "p1", policy.ID) | ||
assert.Equal(t, int32(1), policy.Version) | ||
|
||
err = store.Add("p1", "On second thought, chief's not always right.") | ||
assert.ErrorContains(t, err, "already exists") | ||
|
||
err = store.Update("p1", "On second thought, chief's not always right.") | ||
require.NoError(t, err) | ||
|
||
policy, err = store.GetLatest("p1") | ||
require.NoError(t, err) | ||
assert.Equal(t, int32(2), policy.Version) | ||
assert.Equal(t, "On second thought, chief's not always right.", policy.Rules) | ||
|
||
versions, err := store.Get("p1") | ||
require.NoError(t, err) | ||
assert.Len(t, versions, 2) | ||
assert.Equal(t, int32(2), versions[1].Version) | ||
|
||
err = store.Del("p1") | ||
require.NoError(t, err) | ||
|
||
_, err = store.GetLatest("p1") | ||
assert.ErrorIs(t, err, ErrNoPolicy) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.