implement schema, store and repository for IAM scopes of organization and project
This commit is contained in:
27 files changed
+2007
-38
No files matched your search
@@ -9,7 +9,7 @@ import (
|
||||
func TestPublicIdDomain(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table test_table (
|
||||
create table if not exists test_table (
|
||||
id bigint generated always as identity primary key,
|
||||
public_id wt_public_id
|
||||
);
|
||||
@@ -33,7 +33,7 @@ returning id;
|
||||
failTests := []string{
|
||||
" ",
|
||||
"bar",
|
||||
"0000000001000000000200000000031",
|
||||
"00000009",
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
value := tt
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/vault/sdk/helper/base62"
|
||||
)
|
||||
|
||||
// NewPublicId creates a new public id with the prefix
|
||||
func NewPublicId(prefix string) (string, error) {
|
||||
if prefix == "" {
|
||||
return "", errors.New("error no prefix for new public id")
|
||||
}
|
||||
publicId, err := base62.Random(10)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to generate public id: %w", err)
|
||||
}
|
||||
return fmt.Sprintf("%s_%s", prefix, publicId), nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewPublicId(t *testing.T) {
|
||||
type args struct {
|
||||
prefix string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
wantLen int
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
prefix: "id",
|
||||
},
|
||||
wantErr: false,
|
||||
wantLen: 10 + len("id_"),
|
||||
},
|
||||
{
|
||||
name: "bad-prefix",
|
||||
args: args{
|
||||
prefix: "",
|
||||
},
|
||||
wantErr: true,
|
||||
wantLen: 0,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewPublicId(tt.args.prefix)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewPublicId() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !tt.wantErr && !strings.HasPrefix(got, tt.args.prefix+"_") {
|
||||
t.Errorf("NewPublicId() = %v, wanted it to start with %v", got, tt.args.prefix)
|
||||
}
|
||||
if len(got) != tt.wantLen {
|
||||
t.Errorf("NewPublicId() = %v, with len of %d and wanted len of %v", got, len(got), tt.wantLen)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ begin;
|
||||
|
||||
create domain wt_public_id as text
|
||||
check(
|
||||
length(trim(value)) > 31
|
||||
length(trim(value)) > 10
|
||||
);
|
||||
comment on domain wt_public_id is
|
||||
'Random ID generated with github.com/hashicorp/vault/sdk/helper/base62';
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
BEGIN;
|
||||
|
||||
drop table if exists iam_scope CASCADE;
|
||||
drop trigger if exists iam_scope_insert;
|
||||
drop function if exists iam_sub_scopes_func;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,86 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE if not exists iam_scope_type_enm (
|
||||
string text NOT NULL primary key CHECK(string IN ('unknown', 'organization', 'project'))
|
||||
);
|
||||
INSERT INTO iam_scope_type_enm (string)
|
||||
values
|
||||
('unknown'),
|
||||
('organization'),
|
||||
('project');
|
||||
|
||||
|
||||
CREATE TABLE if not exists iam_scope (
|
||||
public_id wt_public_id primary key,
|
||||
create_time timestamp with time zone default current_timestamp,
|
||||
update_time timestamp with time zone default current_timestamp,
|
||||
name text,
|
||||
type text NOT NULL REFERENCES iam_scope_type_enm(string) CHECK(
|
||||
(
|
||||
type = 'organization'
|
||||
and parent_id = NULL
|
||||
)
|
||||
or (
|
||||
type = 'project'
|
||||
and parent_id IS NOT NULL
|
||||
)
|
||||
),
|
||||
description text,
|
||||
parent_id text REFERENCES iam_scope(public_id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
create table if not exists iam_scope_organization (
|
||||
scope_id wt_public_id NOT NULL UNIQUE REFERENCES iam_scope(public_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
name text UNIQUE,
|
||||
primary key(scope_id)
|
||||
);
|
||||
create table if not exists iam_scope_project (
|
||||
scope_id wt_public_id NOT NULL REFERENCES iam_scope(public_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
parent_id wt_public_id NOT NULL REFERENCES iam_scope_organization(scope_id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
name text,
|
||||
unique(parent_id, name),
|
||||
primary key(scope_id, parent_id)
|
||||
);
|
||||
|
||||
|
||||
CREATE
|
||||
OR REPLACE FUNCTION iam_sub_scopes_func() RETURNS TRIGGER
|
||||
SET SCHEMA
|
||||
'public' LANGUAGE plpgsql AS $$ DECLARE parent_type INT;
|
||||
BEGIN IF new.type = 'organization' THEN
|
||||
insert into iam_scope_organization (scope_id, name)
|
||||
values
|
||||
(new.public_id, new.name);
|
||||
return NEW;
|
||||
END IF;
|
||||
IF new.type = 'project' THEN
|
||||
insert into iam_scope_project (scope_id, parent_id, name)
|
||||
values
|
||||
(new.public_id, new.parent_id, new.name);
|
||||
return NEW;
|
||||
END IF;
|
||||
RAISE EXCEPTION 'unknown scope type';
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
CREATE TRIGGER iam_scope_insert
|
||||
AFTER
|
||||
insert ON iam_scope FOR EACH ROW EXECUTE PROCEDURE iam_sub_scopes_func();
|
||||
|
||||
|
||||
CREATE
|
||||
OR REPLACE FUNCTION iam_immutable_scope_type_func() RETURNS TRIGGER
|
||||
SET SCHEMA
|
||||
'public' LANGUAGE plpgsql AS $$ DECLARE parent_type INT;
|
||||
BEGIN IF new.type != old.type THEN
|
||||
RAISE EXCEPTION 'scope type cannot be updated';
|
||||
END IF;
|
||||
return NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER iam_scope_update
|
||||
BEFORE
|
||||
update ON iam_scope FOR EACH ROW EXECUTE PROCEDURE iam_immutable_scope_type_func();
|
||||
|
||||
COMMIT;
|
||||
+22
-12
@@ -5,8 +5,8 @@ import (
|
||||
"github.com/hashicorp/watchtower/internal/oplog"
|
||||
)
|
||||
|
||||
// getOpts - iterate the inbound Options and return a struct
|
||||
func getOpts(opt ...Option) options {
|
||||
// GetOpts - iterate the inbound Options and return a struct
|
||||
func GetOpts(opt ...Option) Options {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
@@ -15,14 +15,16 @@ func getOpts(opt ...Option) options {
|
||||
}
|
||||
|
||||
// Option - how Options are passed as arguments
|
||||
type Option func(*options)
|
||||
type Option func(*Options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
// Options = how Options are represented
|
||||
type Options struct {
|
||||
withOplog bool
|
||||
oplogOpts oplogOpts
|
||||
withDebug bool
|
||||
withLookup bool
|
||||
// WithFieldMaskPaths must be accessible from other packages
|
||||
WithFieldMaskPaths []string
|
||||
}
|
||||
|
||||
type oplogOpts struct {
|
||||
@@ -30,35 +32,36 @@ type oplogOpts struct {
|
||||
metadata oplog.Metadata
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
func getDefaultOptions() Options {
|
||||
return Options{
|
||||
withOplog: false,
|
||||
oplogOpts: oplogOpts{
|
||||
wrapper: nil,
|
||||
metadata: oplog.Metadata{},
|
||||
},
|
||||
withDebug: false,
|
||||
withLookup: false,
|
||||
withDebug: false,
|
||||
withLookup: false,
|
||||
WithFieldMaskPaths: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// WithLookup enables a lookup
|
||||
func WithLookup(enable bool) Option {
|
||||
return func(o *options) {
|
||||
return func(o *Options) {
|
||||
o.withLookup = enable
|
||||
}
|
||||
}
|
||||
|
||||
// WithDebug enables debug
|
||||
func WithDebug(enable bool) Option {
|
||||
return func(o *options) {
|
||||
return func(o *Options) {
|
||||
o.withDebug = enable
|
||||
}
|
||||
}
|
||||
|
||||
// WithOplog provides an option to write an oplog entry
|
||||
func WithOplog(wrapper wrapping.Wrapper, md oplog.Metadata) Option {
|
||||
return func(o *options) {
|
||||
return func(o *Options) {
|
||||
o.withOplog = true
|
||||
o.oplogOpts = oplogOpts{
|
||||
wrapper: wrapper,
|
||||
@@ -66,3 +69,10 @@ func WithOplog(wrapper wrapping.Wrapper, md oplog.Metadata) Option {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithOplog provides an option to provide field mask paths
|
||||
func WithFieldMaskPaths(paths []string) Option {
|
||||
return func(o *Options) {
|
||||
o.WithFieldMaskPaths = paths
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ func Test_getOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("WithOplog", func(t *testing.T) {
|
||||
// test default of false
|
||||
opts := getOpts()
|
||||
opts := GetOpts()
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withOplog = false
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
@@ -26,7 +26,7 @@ func Test_getOpts(t *testing.T) {
|
||||
"project": []string{"central-info-systems", "local-info-systems"},
|
||||
}
|
||||
// try setting to true
|
||||
opts = getOpts(WithOplog(wrapper, md))
|
||||
opts = GetOpts(WithOplog(wrapper, md))
|
||||
testOpts = getDefaultOptions()
|
||||
testOpts.withOplog = true
|
||||
testOpts.oplogOpts = oplogOpts{
|
||||
@@ -37,13 +37,13 @@ func Test_getOpts(t *testing.T) {
|
||||
})
|
||||
t.Run("WithLookup", func(t *testing.T) {
|
||||
// test default of true
|
||||
opts := getOpts()
|
||||
opts := GetOpts()
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withLookup = false
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
|
||||
// try setting to false
|
||||
opts = getOpts(WithLookup(true))
|
||||
opts = GetOpts(WithLookup(true))
|
||||
testOpts = getDefaultOptions()
|
||||
testOpts.withLookup = true
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
@@ -51,13 +51,13 @@ func Test_getOpts(t *testing.T) {
|
||||
})
|
||||
t.Run("WithDebug", func(t *testing.T) {
|
||||
// test default of false
|
||||
opts := getOpts()
|
||||
opts := GetOpts()
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withDebug = false
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
|
||||
// try setting to true
|
||||
opts = getOpts(WithDebug(true))
|
||||
opts = GetOpts(WithDebug(true))
|
||||
testOpts = getDefaultOptions()
|
||||
testOpts.withDebug = true
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
|
||||
+18
-16
@@ -63,6 +63,10 @@ type Writer interface {
|
||||
DB() (*sql.DB, error)
|
||||
}
|
||||
|
||||
const (
|
||||
StdRetryCnt = 20
|
||||
)
|
||||
|
||||
// RetryInfo provides information on the retries of a transaction
|
||||
type RetryInfo struct {
|
||||
Retries int
|
||||
@@ -93,7 +97,7 @@ const (
|
||||
|
||||
// VetForWriter provides an interface that Create and Update can use to vet the resource before sending it to the db
|
||||
type VetForWriter interface {
|
||||
VetForWrite(ctx context.Context, r Reader, opType OpType) error
|
||||
VetForWrite(ctx context.Context, r Reader, opType OpType, opt ...Option) error
|
||||
}
|
||||
|
||||
// GormReadWriter uses a gorm DB connection for read/write
|
||||
@@ -118,15 +122,10 @@ func (rw *GormReadWriter) ScanRows(rows *sql.Rows, result interface{}) error {
|
||||
return rw.Tx.ScanRows(rows, result)
|
||||
}
|
||||
|
||||
// CreateConstraint will create a db constraint if it doesn't already exist
|
||||
func (w *GormReadWriter) CreateConstraint(tableName string, constraintName string, constraint string) error {
|
||||
return w.Tx.Exec("create_constraint_if_not_exists(?, ?, ?)", tableName, constraintName, constraint).Error
|
||||
}
|
||||
|
||||
var ErrNotResourceWithId = errors.New("not a resource with an id")
|
||||
|
||||
func (rw *GormReadWriter) lookupAfterWrite(ctx context.Context, i interface{}, opt ...Option) error {
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withLookup := opts.withLookup
|
||||
|
||||
if !withLookup {
|
||||
@@ -146,7 +145,7 @@ func (rw *GormReadWriter) Create(ctx context.Context, i interface{}, opt ...Opti
|
||||
if rw.Tx == nil {
|
||||
return errors.New("create tx is nil")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withOplog := opts.withOplog
|
||||
withDebug := opts.withDebug
|
||||
if withDebug {
|
||||
@@ -158,7 +157,7 @@ func (rw *GormReadWriter) Create(ctx context.Context, i interface{}, opt ...Opti
|
||||
}
|
||||
if vetter, ok := i.(VetForWriter); ok {
|
||||
if err := vetter.VetForWrite(ctx, rw, CreateOp); err != nil {
|
||||
return fmt.Errorf("error on Create %w", err)
|
||||
return fmt.Errorf("error on create %w", err)
|
||||
}
|
||||
}
|
||||
if err := rw.Tx.Create(i).Error; err != nil {
|
||||
@@ -181,7 +180,7 @@ func (rw *GormReadWriter) Update(ctx context.Context, i interface{}, fieldMaskPa
|
||||
if rw.Tx == nil {
|
||||
return errors.New("update tx is nil")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withDebug := opts.withDebug
|
||||
withOplog := opts.withOplog
|
||||
if withDebug {
|
||||
@@ -192,8 +191,8 @@ func (rw *GormReadWriter) Update(ctx context.Context, i interface{}, fieldMaskPa
|
||||
return errors.New("update interface is nil")
|
||||
}
|
||||
if vetter, ok := i.(VetForWriter); ok {
|
||||
if err := vetter.VetForWrite(ctx, rw, UpdateOp); err != nil {
|
||||
return fmt.Errorf("error on Create %w", err)
|
||||
if err := vetter.VetForWrite(ctx, rw, UpdateOp, WithFieldMaskPaths(fieldMaskPaths)); err != nil {
|
||||
return fmt.Errorf("error on update %w", err)
|
||||
}
|
||||
}
|
||||
if len(fieldMaskPaths) == 0 {
|
||||
@@ -246,6 +245,9 @@ func (rw *GormReadWriter) Update(ctx context.Context, i interface{}, fieldMaskPa
|
||||
return err
|
||||
}
|
||||
}
|
||||
// we need to force a lookupAfterWrite so the resource returned is correctly initialized
|
||||
// from the db
|
||||
opt = append(opt, WithLookup(true))
|
||||
if err := rw.lookupAfterWrite(ctx, i, opt...); err != nil {
|
||||
return fmt.Errorf("lookup error after update: %w", err)
|
||||
}
|
||||
@@ -260,7 +262,7 @@ func (rw *GormReadWriter) Delete(ctx context.Context, i interface{}, opt ...Opti
|
||||
if i == nil {
|
||||
return errors.New("delete interface is nil")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withDebug := opts.withDebug
|
||||
withOplog := opts.withOplog
|
||||
if withDebug {
|
||||
@@ -278,7 +280,7 @@ func (rw *GormReadWriter) Delete(ctx context.Context, i interface{}, opt ...Opti
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rw *GormReadWriter) addOplog(ctx context.Context, opType OpType, opts options, i interface{}) error {
|
||||
func (rw *GormReadWriter) addOplog(ctx context.Context, opType OpType, opts Options, i interface{}) error {
|
||||
oplogArgs := opts.oplogOpts
|
||||
if oplogArgs.wrapper == nil {
|
||||
return errors.New("error wrapper is nil for WithWrapper")
|
||||
@@ -386,7 +388,7 @@ func (rw *GormReadWriter) LookupByName(ctx context.Context, resource ResourceNam
|
||||
if rw.Tx == nil {
|
||||
return errors.New("error tx nil for lookup by name")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withDebug := opts.withDebug
|
||||
if withDebug {
|
||||
rw.Tx.LogMode(true)
|
||||
@@ -406,7 +408,7 @@ func (rw *GormReadWriter) LookupByPublicId(ctx context.Context, resource Resourc
|
||||
if rw.Tx == nil {
|
||||
return errors.New("error tx nil for lookup by public id")
|
||||
}
|
||||
opts := getOpts(opt...)
|
||||
opts := GetOpts(opt...)
|
||||
withDebug := opts.withDebug
|
||||
if withDebug {
|
||||
rw.Tx.LogMode(true)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package iam
|
||||
|
||||
// Action defines a type for the Actions of Resources
|
||||
// actions are also stored as a lookup db table named iam_action
|
||||
type Action int
|
||||
|
||||
// not using iota intentionally, since the values are stored in the db as well.
|
||||
const (
|
||||
ActionUnknown Action = 0
|
||||
ActionList Action = 1
|
||||
ActionCreate Action = 2
|
||||
ActionUpdate Action = 3
|
||||
ActionRead Action = 4
|
||||
ActionDelete Action = 5
|
||||
ActionAuthen Action = 6
|
||||
ActionAll Action = 7
|
||||
ActionConnect Action = 8
|
||||
)
|
||||
|
||||
func (a Action) String() string {
|
||||
return [...]string{
|
||||
"unknown",
|
||||
"list",
|
||||
"create",
|
||||
"update",
|
||||
"read",
|
||||
"delete",
|
||||
"authen",
|
||||
"all",
|
||||
"connect",
|
||||
}[a]
|
||||
}
|
||||
|
||||
// CrudActions returns a standard set of actions for resources that support a CRUD API
|
||||
func CrudActions() map[string]Action {
|
||||
return map[string]Action{
|
||||
ActionCreate.String(): ActionCreate,
|
||||
ActionUpdate.String(): ActionUpdate,
|
||||
ActionRead.String(): ActionRead,
|
||||
ActionDelete.String(): ActionDelete,
|
||||
}
|
||||
}
|
||||
|
||||
// CrudlActions adds list to the standard set of actions for resources that support a CRUD API
|
||||
func CrudlActions() map[string]Action {
|
||||
return map[string]Action{
|
||||
ActionCreate.String(): ActionCreate,
|
||||
ActionUpdate.String(): ActionUpdate,
|
||||
ActionRead.String(): ActionRead,
|
||||
ActionDelete.String(): ActionDelete,
|
||||
ActionList.String(): ActionList,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test_CrudActions provides unit tests for CrudActions()
|
||||
func Test_CrudActions(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := CrudlActions()
|
||||
assert.Equal(a[ActionCreate.String()], ActionCreate)
|
||||
assert.Equal(a[ActionUpdate.String()], ActionUpdate)
|
||||
assert.Equal(a[ActionRead.String()], ActionRead)
|
||||
assert.Equal(a[ActionDelete.String()], ActionDelete)
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
a := CrudlActions()
|
||||
action, ok := a["invalid"]
|
||||
assert.Equal(ok, false)
|
||||
assert.Equal(action, ActionUnknown)
|
||||
})
|
||||
}
|
||||
|
||||
// Test_CrudlActions provides unit tests for CrudlActions()
|
||||
func Test_CrudlActions(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
a := CrudlActions()
|
||||
assert.Equal(a[ActionList.String()], ActionList)
|
||||
assert.Equal(a[ActionCreate.String()], ActionCreate)
|
||||
assert.Equal(a[ActionUpdate.String()], ActionUpdate)
|
||||
assert.Equal(a[ActionRead.String()], ActionRead)
|
||||
assert.Equal(a[ActionDelete.String()], ActionDelete)
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
a := CrudlActions()
|
||||
action, ok := a["invalid"]
|
||||
assert.Equal(ok, false)
|
||||
assert.Equal(action, ActionUnknown)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test_GetOpts provides unit tests for GetOpts and all the options
|
||||
func Test_GetOpts(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
t.Run("WithName", func(t *testing.T) {
|
||||
opts := getOpts(WithName("test"))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withName = "test"
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
})
|
||||
t.Run("withScope", func(t *testing.T) {
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
|
||||
opts := getOpts(withScope(s))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withScope = s
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
})
|
||||
t.Run("WithDescription", func(t *testing.T) {
|
||||
opts := getOpts(WithDescription("test desc"))
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withDescription = "test desc"
|
||||
assert.True(reflect.DeepEqual(opts, testOpts))
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package iam
|
||||
|
||||
// getOpts - iterate the inbound Options and return a struct
|
||||
func getOpts(opt ...Option) options {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Option - how Options are passed as arguments
|
||||
type Option func(*options)
|
||||
|
||||
// options = how options are represented
|
||||
type options struct {
|
||||
withPublicId string
|
||||
withName string
|
||||
withScope *Scope
|
||||
withDescription string
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
withPublicId: "",
|
||||
withScope: nil,
|
||||
withDescription: "",
|
||||
withName: "",
|
||||
}
|
||||
}
|
||||
|
||||
// WithPublicId provides an optional public id
|
||||
func WithPublicId(id string) Option {
|
||||
return func(o *options) {
|
||||
o.withPublicId = id
|
||||
}
|
||||
}
|
||||
|
||||
// WithDescription provides an optional description
|
||||
func WithDescription(desc string) Option {
|
||||
return func(o *options) {
|
||||
o.withDescription = desc
|
||||
}
|
||||
}
|
||||
|
||||
// withScope provides an optional scope and used within the package
|
||||
func withScope(s *Scope) Option {
|
||||
return func(o *options) {
|
||||
o.withScope = s
|
||||
}
|
||||
}
|
||||
|
||||
// WithName provides an option to search by a friendly name
|
||||
func WithName(name string) Option {
|
||||
return func(o *options) {
|
||||
o.withName = name
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
"github.com/hashicorp/watchtower/internal/oplog"
|
||||
)
|
||||
|
||||
// Repository is the iam database repository
|
||||
type Repository struct {
|
||||
reader db.Reader
|
||||
writer db.Writer
|
||||
wrapper wrapping.Wrapper
|
||||
}
|
||||
|
||||
// NewRepository creates a new iam Repository
|
||||
func NewRepository(r db.Reader, w db.Writer, wrapper wrapping.Wrapper) (*Repository, error) {
|
||||
if r == nil {
|
||||
return nil, errors.New("error creating db repository with nil reader")
|
||||
}
|
||||
if w == nil {
|
||||
return nil, errors.New("error creating db repository with nil writer")
|
||||
}
|
||||
if wrapper == nil {
|
||||
return nil, errors.New("error creating db repository with nil wrapper")
|
||||
}
|
||||
return &Repository{
|
||||
reader: r,
|
||||
writer: w,
|
||||
wrapper: wrapper,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create will create a new iam resource in the db repository with an oplog entry
|
||||
func (r *Repository) create(ctx context.Context, resource Resource, opt ...Option) (Resource, error) {
|
||||
if resource == nil {
|
||||
return nil, errors.New("error creating resource that is nil")
|
||||
}
|
||||
resourceCloner, ok := resource.(ClonableResource)
|
||||
if !ok {
|
||||
return nil, errors.New("error resource is not clonable for create")
|
||||
}
|
||||
metadata, err := r.stdMetadata(ctx, resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting metadata for create: %w", err)
|
||||
}
|
||||
metadata["op-type"] = []string{strconv.Itoa(int(oplog.OpType_OP_TYPE_CREATE))}
|
||||
|
||||
var returnedResource Resource
|
||||
_, err = r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(w db.Writer) error {
|
||||
returnedResource = resourceCloner.Clone()
|
||||
return w.Create(
|
||||
ctx,
|
||||
returnedResource,
|
||||
db.WithOplog(r.wrapper, metadata),
|
||||
)
|
||||
},
|
||||
)
|
||||
return returnedResource, err
|
||||
}
|
||||
|
||||
// Update will update an iam resource in the db repository with an oplog entry
|
||||
func (r *Repository) update(ctx context.Context, resource Resource, fieldMaskPaths []string, opt ...Option) (Resource, error) {
|
||||
if resource == nil {
|
||||
return nil, errors.New("error updating resource that is nil")
|
||||
}
|
||||
resourceCloner, ok := resource.(ClonableResource)
|
||||
if !ok {
|
||||
return nil, errors.New("error resource is not clonable for update")
|
||||
}
|
||||
metadata, err := r.stdMetadata(ctx, resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting metadata for update: %w", err)
|
||||
}
|
||||
metadata["op-type"] = []string{strconv.Itoa(int(oplog.OpType_OP_TYPE_UPDATE))}
|
||||
|
||||
var returnedResource Resource
|
||||
_, err = r.writer.DoTx(
|
||||
ctx,
|
||||
db.StdRetryCnt,
|
||||
db.ExpBackoff{},
|
||||
func(w db.Writer) error {
|
||||
returnedResource = resourceCloner.Clone()
|
||||
return w.Update(
|
||||
ctx,
|
||||
returnedResource,
|
||||
fieldMaskPaths,
|
||||
db.WithOplog(r.wrapper, metadata),
|
||||
)
|
||||
},
|
||||
)
|
||||
return returnedResource, err
|
||||
}
|
||||
|
||||
func (r *Repository) stdMetadata(ctx context.Context, resource Resource) (oplog.Metadata, error) {
|
||||
if s, ok := resource.(*Scope); ok {
|
||||
if s.Type == OrganizationScope.String() {
|
||||
return oplog.Metadata{
|
||||
"resource-public-id": []string{resource.GetPublicId()},
|
||||
"scope-id": []string{s.PublicId},
|
||||
"scope-type": []string{s.Type},
|
||||
"resource-type": []string{resource.ResourceType().String()},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
scope, err := resource.GetScope(ctx, r.reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get scope for standard metadata: %w", err)
|
||||
}
|
||||
if scope == nil {
|
||||
return nil, errors.New("scope was nil for standard metadata")
|
||||
}
|
||||
return oplog.Metadata{
|
||||
"resource-public-id": []string{resource.GetPublicId()},
|
||||
"scope-id": []string{scope.PublicId},
|
||||
"scope-type": []string{scope.Type},
|
||||
"resource-type": []string{resource.ResourceType().String()},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CreateScope will create a scope in the repository and return the written scope
|
||||
func (r *Repository) CreateScope(ctx context.Context, scope *Scope, opt ...Option) (*Scope, error) {
|
||||
if scope == nil {
|
||||
return nil, errors.New("error scope is nil for create")
|
||||
}
|
||||
resource, err := r.create(ctx, scope)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create scope: %w", err)
|
||||
}
|
||||
return resource.(*Scope), err
|
||||
}
|
||||
|
||||
// UpdateScope will update a scope in the repository and return the written scope
|
||||
func (r *Repository) UpdateScope(ctx context.Context, scope *Scope, fieldMaskPaths []string, opt ...Option) (*Scope, error) {
|
||||
if scope == nil {
|
||||
return nil, errors.New("error scope is nil for update")
|
||||
}
|
||||
resource, err := r.update(ctx, scope, fieldMaskPaths)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update scope: %w", err)
|
||||
}
|
||||
return resource.(*Scope), err
|
||||
}
|
||||
|
||||
// LookupScope will look up a scope in the respository
|
||||
func (r *Repository) LookupScope(ctx context.Context, opt ...Option) (*Scope, error) {
|
||||
opts := getOpts(opt...)
|
||||
withPublicId := opts.withPublicId
|
||||
withName := opts.withName
|
||||
|
||||
if withPublicId != "" {
|
||||
scope := allocScope()
|
||||
scope.PublicId = withPublicId
|
||||
if err := r.reader.LookupByPublicId(ctx, &scope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &scope, nil
|
||||
}
|
||||
if withName != "" {
|
||||
scope := allocScope()
|
||||
scope.Name = withName
|
||||
if err := r.reader.LookupByName(ctx, &scope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &scope, nil
|
||||
}
|
||||
return nil, errors.New("you must look up scopes by id or name")
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
"github.com/hashicorp/watchtower/internal/oplog"
|
||||
"github.com/hashicorp/watchtower/internal/oplog/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Repository_CreateScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid-scope", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
s, err := NewOrganization(WithName("fname-" + id))
|
||||
s, err = repo.CreateScope(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s != nil)
|
||||
assert.True(s.GetPublicId() != "")
|
||||
assert.Equal(s.GetName(), "fname-"+id)
|
||||
|
||||
foundScope, err := repo.LookupScope(context.Background(), WithPublicId(s.PublicId))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), s.GetPublicId())
|
||||
|
||||
foundScope, err = repo.LookupScope(context.Background(), WithName("fname-"+id))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), s.GetPublicId())
|
||||
|
||||
var metadata store.Metadata
|
||||
err = conn.Where("key = ? and value = ?", "resource-public-id", s.PublicId).First(&metadata).Error
|
||||
assert.Nil(err)
|
||||
|
||||
var foundEntry oplog.Entry
|
||||
err = conn.Where("id = ?", metadata.EntryId).First(&foundEntry).Error
|
||||
assert.Nil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Repository_UpdateScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid-scope", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
s, err := NewOrganization(WithName("fname-" + id))
|
||||
s, err = repo.CreateScope(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s != nil)
|
||||
assert.True(s.GetPublicId() != "")
|
||||
assert.Equal(s.GetName(), "fname-"+id)
|
||||
|
||||
foundScope, err := repo.LookupScope(context.Background(), WithPublicId(s.PublicId))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), s.GetPublicId())
|
||||
|
||||
foundScope, err = repo.LookupScope(context.Background(), WithName("fname-"+id))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), s.GetPublicId())
|
||||
|
||||
var metadata store.Metadata
|
||||
err = conn.Where("key = ? and value = ?", "resource-public-id", s.PublicId).First(&metadata).Error
|
||||
assert.Nil(err)
|
||||
|
||||
var foundEntry oplog.Entry
|
||||
err = conn.Where("id = ?", metadata.EntryId).First(&foundEntry).Error
|
||||
assert.Nil(err)
|
||||
|
||||
s.Name = "fname-" + id
|
||||
s.Description = "desc-id" // not in the field mask paths
|
||||
s, err = repo.UpdateScope(context.Background(), s, []string{"Name"})
|
||||
assert.Nil(err)
|
||||
assert.True(s != nil)
|
||||
assert.Equal(s.GetName(), "fname-"+id)
|
||||
assert.Equal(foundScope.GetDescription(), "") // should be "" after update in db
|
||||
|
||||
foundScope, err = repo.LookupScope(context.Background(), WithName("fname-"+id))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), s.GetPublicId())
|
||||
assert.Equal(foundScope.GetDescription(), "")
|
||||
|
||||
err = conn.Where("key = ? and value = ?", "resource-public-id", s.PublicId).First(&metadata).Error
|
||||
assert.Nil(err)
|
||||
|
||||
err = conn.Where("id = ?", metadata.EntryId).First(&foundEntry).Error
|
||||
assert.Nil(err)
|
||||
})
|
||||
t.Run("bad-parent-scope", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
s, err := NewOrganization(WithName("fname-" + id))
|
||||
assert.Nil(err)
|
||||
s, err = repo.CreateScope(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s != nil)
|
||||
assert.True(s.GetPublicId() != "")
|
||||
assert.Equal(s.GetName(), "fname-"+id)
|
||||
|
||||
project, err := NewProject(s.PublicId)
|
||||
assert.Nil(err)
|
||||
project, err = repo.CreateScope(context.Background(), project)
|
||||
assert.Nil(err)
|
||||
assert.True(project != nil)
|
||||
|
||||
project.ParentId = project.PublicId
|
||||
project, err = repo.UpdateScope(context.Background(), project, []string{"ParentId"})
|
||||
assert.True(err != nil)
|
||||
assert.Equal("failed to update scope: error on update you cannot change a scope's parent", err.Error())
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
"github.com/hashicorp/watchtower/internal/oplog"
|
||||
"github.com/hashicorp/watchtower/internal/oplog/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewRepository(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
type args struct {
|
||||
r db.Reader
|
||||
w db.Writer
|
||||
wrapper wrapping.Wrapper
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *Repository
|
||||
wantErr bool
|
||||
wantErrString string
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
wrapper: wrapper,
|
||||
},
|
||||
want: &Repository{
|
||||
reader: rw,
|
||||
writer: rw,
|
||||
wrapper: wrapper,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil-wrapper",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: rw,
|
||||
wrapper: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
wantErrString: "error creating db repository with nil wrapper",
|
||||
},
|
||||
{
|
||||
name: "nil-writer",
|
||||
args: args{
|
||||
r: rw,
|
||||
w: nil,
|
||||
wrapper: wrapper,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
wantErrString: "error creating db repository with nil writer",
|
||||
},
|
||||
{
|
||||
name: "nil-reader",
|
||||
args: args{
|
||||
r: nil,
|
||||
w: rw,
|
||||
wrapper: wrapper,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
wantErrString: "error creating db repository with nil reader",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewRepository(tt.args.r, tt.args.w, tt.args.wrapper)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewRepository() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewRepository() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if err != nil {
|
||||
assert.Equal(err.Error(), tt.wantErrString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func Test_Repository_create(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid-scope", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
s, err := NewOrganization(WithName("fname-" + id))
|
||||
retScope, err := repo.create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(retScope != nil)
|
||||
assert.True(retScope.GetPublicId() != "")
|
||||
assert.Equal(retScope.GetName(), "fname-"+id)
|
||||
|
||||
foundScope, err := repo.LookupScope(context.Background(), WithPublicId(s.PublicId))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), retScope.GetPublicId())
|
||||
|
||||
foundScope.Name = "fname-" + id
|
||||
foundScope, err = repo.LookupScope(context.Background(), WithName("fname-"+id))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), retScope.GetPublicId())
|
||||
|
||||
var metadata store.Metadata
|
||||
err = conn.Where("key = ? and value = ?", "resource-public-id", s.PublicId).First(&metadata).Error
|
||||
assert.Nil(err)
|
||||
|
||||
var foundEntry oplog.Entry
|
||||
err = conn.Where("id = ?", metadata.EntryId).First(&foundEntry).Error
|
||||
assert.Nil(err)
|
||||
})
|
||||
t.Run("nil-resource", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
resource, err := repo.create(context.Background(), nil)
|
||||
assert.True(err != nil)
|
||||
assert.True(resource == nil)
|
||||
assert.Equal(err.Error(), "error creating resource that is nil")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_dbRepository_update(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid-scope", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
s, err := NewOrganization()
|
||||
retScope, err := repo.create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(retScope != nil)
|
||||
assert.True(retScope.GetPublicId() != "")
|
||||
assert.Equal(retScope.GetName(), "")
|
||||
|
||||
retScope.(*Scope).Name = "fname-" + id
|
||||
retScope, err = repo.update(context.Background(), retScope, []string{"Name"})
|
||||
assert.Nil(err)
|
||||
assert.True(retScope != nil)
|
||||
assert.Equal(retScope.GetName(), "fname-"+id)
|
||||
|
||||
foundScope, err := repo.LookupScope(context.Background(), WithName("fname-"+id))
|
||||
assert.Nil(err)
|
||||
assert.Equal(foundScope.GetPublicId(), retScope.GetPublicId())
|
||||
|
||||
var metadata store.Metadata
|
||||
err = conn.Where("key = ? and value = ?", "resource-public-id", s.PublicId).First(&metadata).Error
|
||||
assert.Nil(err)
|
||||
|
||||
var foundEntry oplog.Entry
|
||||
err = conn.Where("id = ?", metadata.EntryId).First(&foundEntry).Error
|
||||
assert.Nil(err)
|
||||
})
|
||||
t.Run("nil-resource", func(t *testing.T) {
|
||||
rw := &db.GormReadWriter{Tx: conn}
|
||||
wrapper := db.TestWrapper(t)
|
||||
repo, err := NewRepository(rw, rw, wrapper)
|
||||
resource, err := repo.update(context.Background(), nil, nil)
|
||||
assert.True(err != nil)
|
||||
assert.True(resource == nil)
|
||||
assert.Equal(err.Error(), "error updating resource that is nil")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
)
|
||||
|
||||
// Resource declares the shared behavior of IAM Resources
|
||||
type Resource interface {
|
||||
// GetPublicId is the resource ID used to access the resource via an API
|
||||
GetPublicId() string
|
||||
|
||||
// GetName is the optional friendly name used to
|
||||
// access the resource via an API
|
||||
GetName() string
|
||||
|
||||
// GetDescription is the optional description of the resource
|
||||
GetDescription() string
|
||||
|
||||
// GetScope is the Scope that owns the Resource
|
||||
GetScope(ctx context.Context, r db.Reader) (*Scope, error)
|
||||
|
||||
// Type of Resource (Target, Policy, User, Group, etc)
|
||||
ResourceType() ResourceType
|
||||
|
||||
// Actions that can be assigned permissions for
|
||||
// the Resource in Policies. Action String() is key for
|
||||
// the map of Actions returned.
|
||||
Actions() map[string]Action
|
||||
}
|
||||
|
||||
// ResourceType defines the types of resources in the system
|
||||
type ResourceType int
|
||||
|
||||
const (
|
||||
ResourceTypeUnknown ResourceType = 0
|
||||
ResourceTypeScope ResourceType = 1
|
||||
ResourceTypeUser ResourceType = 2
|
||||
ResourceTypeGroup ResourceType = 3
|
||||
ResourceTypeRole ResourceType = 4
|
||||
ResourceTypeOrganization ResourceType = 5
|
||||
ResourceTypeGroupMember ResourceType = 6
|
||||
ResourceTypeGroupUserMember ResourceType = 7
|
||||
ResourceTypeAssignedRole ResourceType = 8
|
||||
ResourceTypeAssignedUserRole ResourceType = 9
|
||||
ResourceTypeAssignedGroupRole ResourceType = 10
|
||||
ResourceTypeRoleGrant ResourceType = 11
|
||||
ResourceTypeAuthMethod ResourceType = 12
|
||||
ResourceTypeProject ResourceType = 13
|
||||
)
|
||||
|
||||
func (r ResourceType) String() string {
|
||||
return [...]string{
|
||||
"unknown",
|
||||
"scope",
|
||||
"user",
|
||||
"group",
|
||||
"role",
|
||||
"organization",
|
||||
"group member",
|
||||
"group user member",
|
||||
"assigned role",
|
||||
"assigned user role",
|
||||
"assigned group role",
|
||||
"role grant",
|
||||
"auth method",
|
||||
"project",
|
||||
}[r]
|
||||
}
|
||||
|
||||
type ClonableResource interface {
|
||||
Clone() Resource
|
||||
}
|
||||
|
||||
// ResourceWithScope defines an interface for Resources that have a scope
|
||||
type ResourceWithScope interface {
|
||||
GetPublicId() string
|
||||
GetScopeId() string
|
||||
}
|
||||
|
||||
// LookupScope looks up the resource's scope
|
||||
func LookupScope(ctx context.Context, reader db.Reader, resource ResourceWithScope) (*Scope, error) {
|
||||
if reader == nil {
|
||||
return nil, errors.New("error reader is nil for LookupScope")
|
||||
}
|
||||
if resource == nil {
|
||||
return nil, errors.New("error resource is nil for LookupScope")
|
||||
}
|
||||
if resource.GetScopeId() == "" {
|
||||
return nil, errors.New("error scope is unset for LookupScope")
|
||||
}
|
||||
var p Scope
|
||||
if err := reader.LookupWhere(ctx, &p, "public_id = ?", resource.GetScopeId()); err != nil {
|
||||
return nil, fmt.Errorf("error getting scope for LookupScope: %w", err)
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
"github.com/hashicorp/watchtower/internal/iam/store"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// ScopeType defines the possible types for Scopes
|
||||
type ScopeType uint32
|
||||
|
||||
const (
|
||||
UnknownScope ScopeType = 0
|
||||
OrganizationScope ScopeType = 1
|
||||
ProjectScope ScopeType = 2
|
||||
)
|
||||
|
||||
func (s ScopeType) String() string {
|
||||
return [...]string{
|
||||
"unknown",
|
||||
"organization",
|
||||
"project",
|
||||
}[s]
|
||||
}
|
||||
|
||||
// Scope is used to create a hierarchy of "containers" that encompass the scope of
|
||||
// an IAM resource. Scopes are Organizations and Projects.
|
||||
type Scope struct {
|
||||
*store.Scope
|
||||
|
||||
// tableName which is used to support overriding the table name in the db
|
||||
// and making the Scope a ReplayableMessage
|
||||
tableName string `gorm:"-"`
|
||||
}
|
||||
|
||||
// ensure that Scope implements the interfaces of: Resource, ClonableResource, and db.VetForWriter
|
||||
var _ Resource = (*Scope)(nil)
|
||||
var _ db.VetForWriter = (*Scope)(nil)
|
||||
var _ ClonableResource = (*Scope)(nil)
|
||||
|
||||
func NewOrganization(opt ...Option) (*Scope, error) {
|
||||
return newScope(OrganizationScope, opt...)
|
||||
}
|
||||
|
||||
func NewProject(organizationPublicId string, opt ...Option) (*Scope, error) {
|
||||
org := allocScope()
|
||||
org.PublicId = organizationPublicId
|
||||
opt = append(opt, withScope(&org))
|
||||
p, err := newScope(ProjectScope, opt...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating new project: %w", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// newScope creates a new Scope of the specified ScopeType with options:
|
||||
// WithName specifies the Scope's friendly name.
|
||||
// WithScope specifies the Scope's parent
|
||||
func newScope(scopeType ScopeType, opt ...Option) (*Scope, error) {
|
||||
opts := getOpts(opt...)
|
||||
withName := opts.withName
|
||||
withScope := opts.withScope
|
||||
withDescription := opts.withDescription
|
||||
|
||||
if scopeType == UnknownScope {
|
||||
return nil, errors.New("error unknown scope type for new scope")
|
||||
}
|
||||
if scopeType == ProjectScope {
|
||||
if withScope == nil {
|
||||
return nil, errors.New("error project scope must be with a scope")
|
||||
}
|
||||
if withScope.PublicId == "" {
|
||||
return nil, errors.New("error project scope parent id is unset")
|
||||
}
|
||||
}
|
||||
prefix := "p"
|
||||
if scopeType == OrganizationScope {
|
||||
prefix = "o"
|
||||
}
|
||||
|
||||
publicId, err := db.NewPublicId(prefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating public id %w for new scope", err)
|
||||
}
|
||||
s := &Scope{
|
||||
Scope: &store.Scope{
|
||||
PublicId: publicId,
|
||||
Type: scopeType.String(),
|
||||
},
|
||||
}
|
||||
if withScope != nil {
|
||||
if withScope.PublicId == "" {
|
||||
return nil, errors.New("error assigning scope parent id to a scope with unset id")
|
||||
}
|
||||
s.ParentId = withScope.PublicId
|
||||
}
|
||||
if withName != "" {
|
||||
s.Name = withName
|
||||
}
|
||||
if withDescription != "" {
|
||||
s.Description = withDescription
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func allocScope() Scope {
|
||||
return Scope{
|
||||
Scope: &store.Scope{},
|
||||
}
|
||||
}
|
||||
|
||||
// Clone creates a clone of the Scope
|
||||
func (s *Scope) Clone() Resource {
|
||||
cp := proto.Clone(s.Scope)
|
||||
return &Scope{
|
||||
Scope: cp.(*store.Scope),
|
||||
}
|
||||
}
|
||||
|
||||
// VetForWrite implements db.VetForWrite() interface for scopes
|
||||
// this function is intended to be callled by a db.Writer (Create and Update) to validate
|
||||
// the scope before writing it to the db.
|
||||
func (s *Scope) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||
if s.Type == UnknownScope.String() {
|
||||
return errors.New("unknown scope type for scope write")
|
||||
}
|
||||
if s.PublicId == "" {
|
||||
return errors.New("public id is empty string for scope write")
|
||||
}
|
||||
if opType == db.UpdateOp {
|
||||
dbOptions := db.GetOpts(opt...)
|
||||
for _, path := range dbOptions.WithFieldMaskPaths {
|
||||
switch path {
|
||||
case "ParentId":
|
||||
return errors.New("you cannot change a scope's parent")
|
||||
case "Type":
|
||||
return errors.New("you cannot change a scope's type")
|
||||
}
|
||||
}
|
||||
}
|
||||
if opType == db.CreateOp {
|
||||
if s.ParentId == "" && s.Type == ProjectScope.String() {
|
||||
return errors.New("project has no organization")
|
||||
}
|
||||
if s.Type == ProjectScope.String() {
|
||||
parentScope := allocScope()
|
||||
parentScope.PublicId = s.ParentId
|
||||
if err := r.LookupByPublicId(ctx, &parentScope, opt...); err != nil {
|
||||
return fmt.Errorf("unable to verify project's organization scope: %w", err)
|
||||
}
|
||||
if parentScope.Type != OrganizationScope.String() {
|
||||
return errors.New("project parent scope is not an organization")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResourceType returns the type of scope
|
||||
func (s *Scope) ResourceType() ResourceType {
|
||||
switch s.Type {
|
||||
case OrganizationScope.String():
|
||||
return ResourceTypeOrganization
|
||||
case ProjectScope.String():
|
||||
return ResourceTypeProject
|
||||
default:
|
||||
return ResourceTypeScope
|
||||
}
|
||||
}
|
||||
|
||||
// Actions returns the available actions for Scopes
|
||||
func (*Scope) Actions() map[string]Action {
|
||||
return CrudActions()
|
||||
}
|
||||
|
||||
// GetScope returns the scope for the "scope" if there is one defined
|
||||
func (s *Scope) GetScope(ctx context.Context, r db.Reader) (*Scope, error) {
|
||||
if r == nil {
|
||||
return nil, errors.New("error db is nil for get scope")
|
||||
}
|
||||
if s.ParentId == "" {
|
||||
return nil, nil
|
||||
}
|
||||
var p Scope
|
||||
if err := r.LookupWhere(ctx, &p, "public_id = ?", s.ParentId); err != nil {
|
||||
return nil, fmt.Errorf("error getting scope %w", err)
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// TableName returns the tablename to override the default gorm table name
|
||||
func (s *Scope) TableName() string {
|
||||
if s.tableName != "" {
|
||||
return s.tableName
|
||||
}
|
||||
return "iam_scope"
|
||||
}
|
||||
|
||||
// SetTableName sets the tablename and satisfies the ReplayableMessage interface
|
||||
func (s *Scope) SetTableName(n string) {
|
||||
if n != "" {
|
||||
s.tableName = n
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/watchtower/internal/db"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func Test_NewScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid-org-with-project", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
projScope, err := NewProject(s.PublicId, WithDescription(id))
|
||||
assert.Nil(err)
|
||||
assert.True(projScope.Scope != nil)
|
||||
assert.Equal(projScope.GetParentId(), s.PublicId)
|
||||
assert.Equal(projScope.GetDescription(), id)
|
||||
})
|
||||
t.Run("unknown-scope", func(t *testing.T) {
|
||||
s, err := newScope(UnknownScope)
|
||||
assert.True(err != nil)
|
||||
assert.True(s == nil)
|
||||
assert.Equal(err.Error(), "error unknown scope type for new scope")
|
||||
})
|
||||
t.Run("proj-scope-with-no-org", func(t *testing.T) {
|
||||
s, err := NewProject("")
|
||||
assert.True(err != nil)
|
||||
assert.True(s == nil)
|
||||
assert.Equal(err.Error(), "error creating new project: error project scope parent id is unset")
|
||||
})
|
||||
}
|
||||
func Test_ScopeCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
})
|
||||
t.Run("valid-with-parent", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
|
||||
project, err := NewProject(s.PublicId, WithDescription(id))
|
||||
assert.Nil(err)
|
||||
assert.True(project.Scope != nil)
|
||||
assert.Equal(project.Scope.ParentId, s.PublicId)
|
||||
assert.Equal(project.GetDescription(), id)
|
||||
|
||||
err = w.Create(context.Background(), project)
|
||||
assert.Nil(err)
|
||||
assert.Equal(project.ParentId, s.PublicId)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ScopeUpdate(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.Nil(err)
|
||||
s.Name = id
|
||||
err = w.Update(context.Background(), s, []string{"Name"})
|
||||
assert.Nil(err)
|
||||
})
|
||||
t.Run("type-update-not-allowed", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
s.Type = ProjectScope.String()
|
||||
err = w.Update(context.Background(), s, []string{"Type"})
|
||||
assert.True(err != nil)
|
||||
})
|
||||
}
|
||||
func Test_ScopeGetScope(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
t.Run("valid-scope", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
project, err := NewProject(s.PublicId)
|
||||
assert.Nil(err)
|
||||
assert.True(project.Scope != nil)
|
||||
assert.Equal(project.ParentId, s.PublicId)
|
||||
err = w.Create(context.Background(), project)
|
||||
assert.Nil(err)
|
||||
|
||||
projectOrg, err := project.GetScope(context.Background(), &w)
|
||||
assert.Nil(err)
|
||||
assert.True(projectOrg != nil)
|
||||
assert.Equal(projectOrg.PublicId, project.ParentId)
|
||||
})
|
||||
}
|
||||
|
||||
func TestScope_Actions(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := &Scope{}
|
||||
a := s.Actions()
|
||||
assert.Equal(a[ActionCreate.String()], ActionCreate)
|
||||
assert.Equal(a[ActionUpdate.String()], ActionUpdate)
|
||||
assert.Equal(a[ActionRead.String()], ActionRead)
|
||||
assert.Equal(a[ActionDelete.String()], ActionDelete)
|
||||
|
||||
if _, ok := a[ActionList.String()]; ok {
|
||||
t.Errorf("scopes should not include %s as an action", ActionList.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestScope_ResourceType(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
o, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
ty := o.ResourceType()
|
||||
assert.Equal(ty, ResourceTypeOrganization)
|
||||
}
|
||||
|
||||
func TestScope_Clone(t *testing.T) {
|
||||
t.Parallel()
|
||||
cleanup, conn := db.TestSetup(t, "../db/migrations/postgres")
|
||||
defer cleanup()
|
||||
assert := assert.New(t)
|
||||
defer conn.Close()
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
cp := s.Clone()
|
||||
assert.True(proto.Equal(cp.(*Scope).Scope, s.Scope))
|
||||
})
|
||||
t.Run("not-equal", func(t *testing.T) {
|
||||
w := db.GormReadWriter{Tx: conn}
|
||||
s, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s.Scope != nil)
|
||||
err = w.Create(context.Background(), s)
|
||||
assert.Nil(err)
|
||||
assert.True(s.PublicId != "")
|
||||
|
||||
s2, err := NewOrganization()
|
||||
assert.Nil(err)
|
||||
assert.True(s2.Scope != nil)
|
||||
err = w.Create(context.Background(), s2)
|
||||
assert.Nil(err)
|
||||
assert.True(s2.PublicId != "")
|
||||
|
||||
cp := s.Clone()
|
||||
assert.True(!proto.Equal(cp.(*Scope).Scope, s2.Scope))
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_TimestampValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
ts := Timestamp{Timestamp: ×tamp.Timestamp{Seconds: 0, Nanos: 0}}
|
||||
v, err := ts.Value()
|
||||
assert.Nil(err)
|
||||
assert.Equal(v, utcDate(1970, 1, 1))
|
||||
})
|
||||
t.Run("valid nil ts", func(t *testing.T) {
|
||||
var ts *Timestamp
|
||||
v, err := ts.Value()
|
||||
assert.Nil(err)
|
||||
assert.Equal(v, nil)
|
||||
})
|
||||
t.Run("invalid ts", func(t *testing.T) {
|
||||
ts := Timestamp{Timestamp: ×tamp.Timestamp{Seconds: maxValidSeconds, Nanos: 0}}
|
||||
v, err := ts.Value()
|
||||
assert.True(err != nil)
|
||||
assert.Equal(err.Error(), "timestamp: seconds:253402300800 after 10000-01-01")
|
||||
assert.Equal(v, utcDate(10000, 1, 1))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TimestampScan(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := assert.New(t)
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
v := time.Unix(0, 0)
|
||||
ts := Timestamp{}
|
||||
err := ts.Scan(v)
|
||||
assert.Nil(err)
|
||||
assert.True(reflect.DeepEqual(ts.Timestamp, ×tamp.Timestamp{Seconds: 0, Nanos: 0}))
|
||||
})
|
||||
t.Run("valid default time", func(t *testing.T) {
|
||||
var v time.Time
|
||||
ts := Timestamp{}
|
||||
err := ts.Scan(v)
|
||||
assert.Nil(err)
|
||||
assert.True(reflect.DeepEqual(ts.Timestamp, ×tamp.Timestamp{Seconds: -62135596800, Nanos: 0}))
|
||||
})
|
||||
t.Run("invalid type", func(t *testing.T) {
|
||||
v := 1
|
||||
ts := Timestamp{}
|
||||
err := ts.Scan(v)
|
||||
assert.True(err != nil)
|
||||
assert.Equal(err.Error(), "Not a protobuf Timestamp")
|
||||
})
|
||||
t.Run("invalid time", func(t *testing.T) {
|
||||
v := time.Unix(maxValidSeconds, 0)
|
||||
ts := Timestamp{}
|
||||
err := ts.Scan(v)
|
||||
assert.True(err != nil)
|
||||
assert.Equal(err.Error(), "error converting the timestamp: timestamp: seconds:253402300800 after 10000-01-01")
|
||||
})
|
||||
}
|
||||
|
||||
const maxValidSeconds = 253402300800
|
||||
|
||||
func utcDate(year, month, day int) time.Time {
|
||||
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
)
|
||||
|
||||
// Scan supports Timestamps for oplogs
|
||||
func (ts *Timestamp) Scan(value interface{}) error {
|
||||
switch t := value.(type) {
|
||||
case time.Time:
|
||||
var err error
|
||||
ts.Timestamp, err = ptypes.TimestampProto(t) // google proto version
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting the timestamp: %w", err)
|
||||
}
|
||||
default:
|
||||
return errors.New("Not a protobuf Timestamp")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value supports Timestamps for oplogs
|
||||
func (ts *Timestamp) Value() (driver.Value, error) {
|
||||
if ts == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return ptypes.Timestamp(ts.Timestamp)
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.21.0
|
||||
// protoc v3.11.4
|
||||
// source: controller/storage/iam/store/v1/scope.proto
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Scope struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// public_id is the used to access the Scope via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
PublicId string `protobuf:"bytes,1,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"`
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
CreateTime *Timestamp `protobuf:"bytes,2,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// update_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
UpdateTime *Timestamp `protobuf:"bytes,3,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"`
|
||||
// name is the optional friendly name used to
|
||||
// access the Scope via an API
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// Type of scope
|
||||
Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"`
|
||||
// parent_id is the id (primary key in the Scope aggregate) of scope's parent
|
||||
// @inject_tag: `gorm:"default:0"`
|
||||
ParentId string `protobuf:"bytes,6,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty" gorm:"default:0"`
|
||||
// description of scope
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *Scope) Reset() {
|
||||
*x = Scope{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_iam_store_v1_scope_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Scope) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Scope) ProtoMessage() {}
|
||||
|
||||
func (x *Scope) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_iam_store_v1_scope_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Scope.ProtoReflect.Descriptor instead.
|
||||
func (*Scope) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_iam_store_v1_scope_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Scope) GetPublicId() string {
|
||||
if x != nil {
|
||||
return x.PublicId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Scope) GetCreateTime() *Timestamp {
|
||||
if x != nil {
|
||||
return x.CreateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Scope) GetUpdateTime() *Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdateTime
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Scope) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Scope) GetType() string {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Scope) GetParentId() string {
|
||||
if x != nil {
|
||||
return x.ParentId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Scope) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_controller_storage_iam_store_v1_scope_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_iam_store_v1_scope_proto_rawDesc = []byte{
|
||||
0x0a, 0x2b, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x2e, 0x69, 0x61, 0x6d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
|
||||
0xa5, 0x02, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62,
|
||||
0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75,
|
||||
0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
|
||||
0x2e, 0x69, 0x61, 0x6d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
||||
0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x69, 0x61,
|
||||
0x6d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
|
||||
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65,
|
||||
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72,
|
||||
0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
|
||||
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
|
||||
0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_storage_iam_store_v1_scope_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_iam_store_v1_scope_proto_rawDescData = file_controller_storage_iam_store_v1_scope_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_iam_store_v1_scope_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_iam_store_v1_scope_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_iam_store_v1_scope_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_iam_store_v1_scope_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_iam_store_v1_scope_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_iam_store_v1_scope_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_controller_storage_iam_store_v1_scope_proto_goTypes = []interface{}{
|
||||
(*Scope)(nil), // 0: controller.storage.iam.store.v1.Scope
|
||||
(*Timestamp)(nil), // 1: controller.storage.iam.store.v1.Timestamp
|
||||
}
|
||||
var file_controller_storage_iam_store_v1_scope_proto_depIdxs = []int32{
|
||||
1, // 0: controller.storage.iam.store.v1.Scope.create_time:type_name -> controller.storage.iam.store.v1.Timestamp
|
||||
1, // 1: controller.storage.iam.store.v1.Scope.update_time:type_name -> controller.storage.iam.store.v1.Timestamp
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_iam_store_v1_scope_proto_init() }
|
||||
func file_controller_storage_iam_store_v1_scope_proto_init() {
|
||||
if File_controller_storage_iam_store_v1_scope_proto != nil {
|
||||
return
|
||||
}
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_init()
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_iam_store_v1_scope_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Scope); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_iam_store_v1_scope_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_iam_store_v1_scope_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_iam_store_v1_scope_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_iam_store_v1_scope_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_iam_store_v1_scope_proto = out.File
|
||||
file_controller_storage_iam_store_v1_scope_proto_rawDesc = nil
|
||||
file_controller_storage_iam_store_v1_scope_proto_goTypes = nil
|
||||
file_controller_storage_iam_store_v1_scope_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.21.0
|
||||
// protoc v3.11.4
|
||||
// source: controller/storage/iam/store/v1/timestamp.proto
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
// Timestamp for iam messages. We've defined a new local type wrapper
|
||||
// of google.protobuf.Timestamp so we can implement sql.Scanner and sql.Valuer
|
||||
// interfaces. See:
|
||||
// https://golang.org/pkg/database/sql/#Scanner
|
||||
// https://golang.org/pkg/database/sql/driver/#Valuer
|
||||
type Timestamp struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Timestamp *timestamp.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Timestamp) Reset() {
|
||||
*x = Timestamp{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_controller_storage_iam_store_v1_timestamp_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Timestamp) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Timestamp) ProtoMessage() {}
|
||||
|
||||
func (x *Timestamp) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_controller_storage_iam_store_v1_timestamp_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Timestamp.ProtoReflect.Descriptor instead.
|
||||
func (*Timestamp) Descriptor() ([]byte, []int) {
|
||||
return file_controller_storage_iam_store_v1_timestamp_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Timestamp) GetTimestamp() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.Timestamp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_controller_storage_iam_store_v1_timestamp_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_controller_storage_iam_store_v1_timestamp_proto_rawDesc = []byte{
|
||||
0x0a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76,
|
||||
0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x12, 0x1f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x69, 0x61, 0x6d, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0x45, 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||
0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69,
|
||||
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
|
||||
0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65,
|
||||
0x3b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_rawDescOnce sync.Once
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_rawDescData = file_controller_storage_iam_store_v1_timestamp_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_controller_storage_iam_store_v1_timestamp_proto_rawDescGZIP() []byte {
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_rawDescOnce.Do(func() {
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_iam_store_v1_timestamp_proto_rawDescData)
|
||||
})
|
||||
return file_controller_storage_iam_store_v1_timestamp_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_controller_storage_iam_store_v1_timestamp_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_controller_storage_iam_store_v1_timestamp_proto_goTypes = []interface{}{
|
||||
(*Timestamp)(nil), // 0: controller.storage.iam.store.v1.Timestamp
|
||||
(*timestamp.Timestamp)(nil), // 1: google.protobuf.Timestamp
|
||||
}
|
||||
var file_controller_storage_iam_store_v1_timestamp_proto_depIdxs = []int32{
|
||||
1, // 0: controller.storage.iam.store.v1.Timestamp.timestamp:type_name -> google.protobuf.Timestamp
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_controller_storage_iam_store_v1_timestamp_proto_init() }
|
||||
func file_controller_storage_iam_store_v1_timestamp_proto_init() {
|
||||
if File_controller_storage_iam_store_v1_timestamp_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Timestamp); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_controller_storage_iam_store_v1_timestamp_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_controller_storage_iam_store_v1_timestamp_proto_goTypes,
|
||||
DependencyIndexes: file_controller_storage_iam_store_v1_timestamp_proto_depIdxs,
|
||||
MessageInfos: file_controller_storage_iam_store_v1_timestamp_proto_msgTypes,
|
||||
}.Build()
|
||||
File_controller_storage_iam_store_v1_timestamp_proto = out.File
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_rawDesc = nil
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_goTypes = nil
|
||||
file_controller_storage_iam_store_v1_timestamp_proto_depIdxs = nil
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package controller.storage.iam.store.v1;
|
||||
option go_package = "github.com/hashicorp/watchtower/internal/iam/store;store";
|
||||
|
||||
import "controller/storage/iam/store/v1/timestamp.proto";
|
||||
|
||||
message Scope {
|
||||
// public_id is the used to access the Scope via an API
|
||||
// @inject_tag: gorm:"primary_key"
|
||||
string public_id = 1;
|
||||
|
||||
// create_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
Timestamp create_time = 2;
|
||||
|
||||
// update_time from the RDBMS
|
||||
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||
Timestamp update_time = 3;
|
||||
|
||||
// name is the optional friendly name used to
|
||||
// access the Scope via an API
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 4;
|
||||
|
||||
// Type of scope
|
||||
string type = 5;
|
||||
|
||||
// parent_id is the id (primary key in the Scope aggregate) of scope's parent
|
||||
// @inject_tag: `gorm:"default:0"`
|
||||
string parent_id = 6;
|
||||
|
||||
// description of scope
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string description = 7;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package controller.storage.iam.store.v1;
|
||||
option go_package = "github.com/hashicorp/watchtower/internal/iam/store;store";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// Timestamp for iam messages. We've defined a new local type wrapper
|
||||
// of google.protobuf.Timestamp so we can implement sql.Scanner and sql.Valuer
|
||||
// interfaces. See:
|
||||
// https://golang.org/pkg/database/sql/#Scanner
|
||||
// https://golang.org/pkg/database/sql/driver/#Valuer
|
||||
message Timestamp {
|
||||
google.protobuf.Timestamp timestamp = 1;
|
||||
}
|
||||
Reference in new issue
Block a user