WIP: Add BoltDB implementation of RolesStore

Signed-off-by: Jared Scheib <jared.scheib@gmail.com>
pull/10616/head
Michael Desa 2017-10-11 17:50:34 -04:00 committed by Jared Scheib
parent b7c78f4c56
commit 4be172d988
5 changed files with 255 additions and 0 deletions

View File

@ -20,6 +20,7 @@ type Client struct {
ServersStore *ServersStore
LayoutStore *LayoutStore
UsersStore *UsersStore
RolesStore *RolesStore
DashboardsStore *DashboardsStore
}
@ -29,6 +30,7 @@ func NewClient() *Client {
c.SourcesStore = &SourcesStore{client: c}
c.ServersStore = &ServersStore{client: c}
c.UsersStore = &UsersStore{client: c}
c.RolesStore = &RolesStore{client: c}
c.LayoutStore = &LayoutStore{
client: c,
IDs: &uuid.V4{},
@ -70,6 +72,10 @@ func (c *Client) Open(ctx context.Context) error {
if _, err := tx.CreateBucketIfNotExists(UsersBucket); err != nil {
return err
}
// Always create Roles bucket.
if _, err := tx.CreateBucketIfNotExists(RolesBucket); err != nil {
return err
}
return nil
}); err != nil {
return err

View File

@ -439,3 +439,62 @@ func UnmarshalUserPB(data []byte, u *User) error {
}
return nil
}
// MarshalRole encodes a role to binary protobuf format.
func MarshalRole(r *chronograf.Role) ([]byte, error) {
pbUsers := make([]uint64, len(r.Users))
for i, u := range r.Users {
pbUsers[i] = u.ID
}
pbPermissions := make([]string, len(r.Permissions))
for i, m := range r.Permissions {
pbPermissions[i] = m.Name
}
return MarshalRolePB(&Role{
Name: r.Name,
Permissions: pbPermissions,
Users: pbUsers,
})
}
// MarshalRolePB encodes a role to binary protobuf format.
func MarshalRolePB(r *Role) ([]byte, error) {
return proto.Marshal(r)
}
// UnmarshalRole decodes a role from binary protobuf data.
func UnmarshalRole(data []byte, r *chronograf.Role) error {
var pb Role
if err := UnmarshalRolePB(data, &pb); err != nil {
return err
}
perms := make(chronograf.Permissions, len(pb.Permissions))
for i, m := range pb.Permissions {
perms[i] = chronograf.Permission{
Name: m,
}
}
users := make([]chronograf.User, len(pb.Users))
for i, u := range pb.Users {
users[i] = chronograf.User{
ID: u,
}
}
r.Name = pb.Name
r.Permissions = perms
r.Users = users
return nil
}
// UnmarshalRolePB decodes a role from binary protobuf data.
func UnmarshalRolePB(data []byte, r *Role) error {
if err := proto.Unmarshal(data, r); err != nil {
return err
}
return nil
}

138
bolt/roles.go Normal file
View File

@ -0,0 +1,138 @@
package bolt
import (
"context"
"github.com/boltdb/bolt"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/bolt/internal"
)
// Ensure RolesStore implements chronograf.RolesStore.
var _ chronograf.RolesStore = &RolesStore{}
// RolesBucket is used to store roles local to chronograf
var RolesBucket = []byte("RolesV1")
// RolesStore uses bolt to store and retrieve roles
type RolesStore struct {
client *Client
}
// get searches the RolesStore for role with name and returns the chronograf representation
func (s *RolesStore) get(ctx context.Context, name string) (*chronograf.Role, error) {
found := false
var role *chronograf.Role
err := s.client.db.View(func(tx *bolt.Tx) error {
err := tx.Bucket(RolesBucket).ForEach(func(k, v []byte) error {
var r chronograf.Role
if err := internal.UnmarshalRole(v, &r); err != nil {
return err
} else if role.Name != name {
return nil
}
found = true
role = &r
return nil
})
if err != nil {
return err
}
if found == false {
return chronograf.ErrRoleNotFound
}
return nil
})
if err != nil {
return nil, err
}
return role, nil
}
// Get searches the RolesStore for role with name
func (s *RolesStore) Get(ctx context.Context, name string) (*chronograf.Role, error) {
r, err := s.get(ctx, name)
if err != nil {
return nil, err
}
return r, nil
}
// Add a new Roles in the RolesStore.
func (s *RolesStore) Add(ctx context.Context, r *chronograf.Role) (*chronograf.Role, error) {
if err := s.client.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket(RolesBucket)
if v, err := internal.MarshalRole(r); err != nil {
return err
} else if err := b.Put([]byte(r.Name), v); err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return r, nil
}
// Delete the roles from the RolesStore
func (s *RolesStore) Delete(ctx context.Context, role *chronograf.Role) error {
r, err := s.get(ctx, role.Name)
if err != nil {
return err
}
if err := s.client.db.Update(func(tx *bolt.Tx) error {
if err := tx.Bucket(RolesBucket).Delete([]byte(r.Name)); err != nil {
return err
}
return nil
}); err != nil {
return err
}
return nil
}
// Update a role
func (s *RolesStore) Update(ctx context.Context, role *chronograf.Role) error {
r, err := s.get(ctx, role.Name)
if err != nil {
return err
}
if err := s.client.db.Update(func(tx *bolt.Tx) error {
r.Name = role.Name
if v, err := internal.MarshalRole(r); err != nil {
return err
} else if err := tx.Bucket(RolesBucket).Put([]byte(r.Name), v); err != nil {
return err
}
return nil
}); err != nil {
return err
}
return nil
}
// All returns all roles
func (s *RolesStore) All(ctx context.Context) ([]chronograf.Role, error) {
var roles []chronograf.Role
if err := s.client.db.View(func(tx *bolt.Tx) error {
if err := tx.Bucket(RolesBucket).ForEach(func(k, v []byte) error {
var role chronograf.Role
if err := internal.UnmarshalRole(v, &role); err != nil {
return err
}
roles = append(roles, role)
return nil
}); err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return roles, nil
}

51
bolt/roles_test.go Normal file
View File

@ -0,0 +1,51 @@
package bolt_test
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/chronograf"
)
func TestRolesStore_Get(t *testing.T) {
type args struct {
ctx context.Context
name string
}
tests := []struct {
name string
args args
want *chronograf.Role
wantErr bool
}{
{
name: "Role not found",
args: args{
ctx: context.Background(),
name: "unknown",
},
wantErr: true,
},
}
for _, tt := range tests {
client, err := NewTestClient()
if err != nil {
t.Fatal(err)
}
if err := client.Open(context.TODO()); err != nil {
t.Fatal(err)
}
defer client.Close()
s := client.RolesStore
got, err := s.Get(tt.args.ctx, tt.args.name)
if (err != nil) != tt.wantErr {
t.Errorf("%q. RolesStore.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if !cmp.Equal(got, tt.want) {
t.Errorf("%q. RolesStore.Get() = %v, want %v", tt.name, got, tt.want)
}
}
}

View File

@ -26,6 +26,7 @@ const (
ErrLayoutNotFound = Error("layout not found")
ErrDashboardNotFound = Error("dashboard not found")
ErrUserNotFound = Error("user not found")
ErrRoleNotFound = Error("role not found")
ErrLayoutInvalid = Error("layout is invalid")
ErrAlertNotFound = Error("alert not found")
ErrAuthentication = Error("user not authenticated")