add principal roles (users and groups) to iam (#163)
This commit is contained in:
23 files changed
+3248
-53
No files matched your search
@@ -67,6 +67,9 @@ protobuild:
|
|||||||
@protoc --proto_path=internal/proto/local --proto_path=internal/proto/third_party --swagger_out=logtostderr=true,disable_default_errors=true,include_package_in_tags=true,fqn_for_swagger_name=true,allow_merge,merge_file_name=controller:internal/gen/. internal/proto/local/controller/api/services/v1/*.proto
|
@protoc --proto_path=internal/proto/local --proto_path=internal/proto/third_party --swagger_out=logtostderr=true,disable_default_errors=true,include_package_in_tags=true,fqn_for_swagger_name=true,allow_merge,merge_file_name=controller:internal/gen/. internal/proto/local/controller/api/services/v1/*.proto
|
||||||
@protoc-go-inject-tag -input=./internal/oplog/store/oplog.pb.go
|
@protoc-go-inject-tag -input=./internal/oplog/store/oplog.pb.go
|
||||||
@protoc-go-inject-tag -input=./internal/oplog/oplog_test/oplog_test.pb.go
|
@protoc-go-inject-tag -input=./internal/oplog/oplog_test/oplog_test.pb.go
|
||||||
|
@protoc-go-inject-tag -input=./internal/iam/store/group.pb.go
|
||||||
|
@protoc-go-inject-tag -input=./internal/iam/store/role.pb.go
|
||||||
|
@protoc-go-inject-tag -input=./internal/iam/store/principal_role.pb.go
|
||||||
@protoc-go-inject-tag -input=./internal/iam/store/role.pb.go
|
@protoc-go-inject-tag -input=./internal/iam/store/role.pb.go
|
||||||
@protoc-go-inject-tag -input=./internal/iam/store/user.pb.go
|
@protoc-go-inject-tag -input=./internal/iam/store/user.pb.go
|
||||||
@protoc-go-inject-tag -input=./internal/iam/store/scope.pb.go
|
@protoc-go-inject-tag -input=./internal/iam/store/scope.pb.go
|
||||||
|
|||||||
@@ -245,14 +245,14 @@ values
|
|||||||
('iam_group_member_user', 1),
|
('iam_group_member_user', 1),
|
||||||
('iam_role', 1),
|
('iam_role', 1),
|
||||||
('iam_role_grant', 1),
|
('iam_role_grant', 1),
|
||||||
('iam_role_group', 1),
|
('iam_group_role', 1),
|
||||||
('iam_role_user', 1),
|
('iam_user_role', 1),
|
||||||
('db_test_user', 1),
|
('db_test_user', 1),
|
||||||
('db_test_car', 1),
|
('db_test_car', 1),
|
||||||
('db_test_rental', 1),
|
('db_test_rental', 1),
|
||||||
('db_test_scooter', 1),
|
('db_test_scooter', 1),
|
||||||
('auth_account', 1);
|
('auth_account', 1),
|
||||||
;
|
('iam_principal_role', 1);
|
||||||
|
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
@@ -418,11 +418,17 @@ drop table iam_scope_organization cascade;
|
|||||||
drop table iam_scope cascade;
|
drop table iam_scope cascade;
|
||||||
drop table iam_scope_type_enm cascade;
|
drop table iam_scope_type_enm cascade;
|
||||||
drop table iam_role cascade;
|
drop table iam_role cascade;
|
||||||
|
drop view iam_principal_role cascade;
|
||||||
|
drop table iam_group_role cascade;
|
||||||
|
drop table iam_user_role cascade;
|
||||||
|
|
||||||
drop function iam_sub_names cascade;
|
drop function iam_sub_names cascade;
|
||||||
drop function iam_immutable_scope_type_func cascade;
|
drop function iam_immutable_scope_type_func cascade;
|
||||||
drop function iam_sub_scopes_func cascade;
|
drop function iam_sub_scopes_func cascade;
|
||||||
|
drop function iam_immutable_role cascade;
|
||||||
|
drop function iam_user_role_scope_check cascade;
|
||||||
|
drop function iam_group_role_scope_check cascade;
|
||||||
|
drop function immutable_scope_id_func cascade;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
@@ -443,7 +449,24 @@ values
|
|||||||
('organization'),
|
('organization'),
|
||||||
('project');
|
('project');
|
||||||
|
|
||||||
|
|
||||||
|
create or replace function
|
||||||
|
iam_immutable_scope_id_func()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
if new.scope_id is distinct from old.scope_id then
|
||||||
|
raise exception 'scope_id cannot be set to %', new.scope_id;
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
comment on function
|
||||||
|
iam_immutable_scope_id_func()
|
||||||
|
is
|
||||||
|
'function used in before update triggers to make scope_id column is immutable';
|
||||||
|
|
||||||
create table iam_scope (
|
create table iam_scope (
|
||||||
public_id wt_public_id primary key,
|
public_id wt_public_id primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -573,7 +596,6 @@ before
|
|||||||
update on iam_scope
|
update on iam_scope
|
||||||
for each row execute procedure iam_sub_names();
|
for each row execute procedure iam_sub_names();
|
||||||
|
|
||||||
|
|
||||||
create table iam_user (
|
create table iam_user (
|
||||||
public_id wt_public_id not null primary key,
|
public_id wt_public_id not null primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -607,6 +629,11 @@ before
|
|||||||
insert on iam_user
|
insert on iam_user
|
||||||
for each row execute procedure default_create_time();
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_scope_id_user
|
||||||
|
before
|
||||||
|
update on iam_user
|
||||||
|
for each row execute procedure iam_immutable_scope_id_func();
|
||||||
|
|
||||||
create table iam_role (
|
create table iam_role (
|
||||||
public_id wt_public_id not null primary key,
|
public_id wt_public_id not null primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -615,9 +642,23 @@ create table iam_role (
|
|||||||
description text,
|
description text,
|
||||||
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
||||||
unique(name, scope_id),
|
unique(name, scope_id),
|
||||||
disabled boolean not null default false
|
disabled boolean not null default false,
|
||||||
|
-- version allows optimistic locking of the role when modifying the role
|
||||||
|
-- itself and when modifying dependent items like principal roles.
|
||||||
|
-- TODO (jlambert 6/2020) add before update trigger to automatically
|
||||||
|
-- increment the version when needed. This trigger can be addded when PR
|
||||||
|
-- #126 is merged and update_version_column() is available.
|
||||||
|
version bigint not null default 1,
|
||||||
|
|
||||||
|
-- add unique index so a composite fk can be declared.
|
||||||
|
unique(scope_id, public_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
update_version_column
|
||||||
|
after update on iam_role
|
||||||
|
for each row execute procedure update_version_column();
|
||||||
|
|
||||||
create trigger
|
create trigger
|
||||||
update_time_column
|
update_time_column
|
||||||
before update on iam_role
|
before update on iam_role
|
||||||
@@ -643,7 +684,9 @@ create table iam_group (
|
|||||||
description text,
|
description text,
|
||||||
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
||||||
unique(name, scope_id),
|
unique(name, scope_id),
|
||||||
disabled boolean not null default false
|
disabled boolean not null default false,
|
||||||
|
-- add unique index so a composite fk can be declared.
|
||||||
|
unique(scope_id, public_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create trigger
|
create trigger
|
||||||
@@ -662,7 +705,147 @@ create trigger
|
|||||||
before
|
before
|
||||||
insert on iam_group
|
insert on iam_group
|
||||||
for each row execute procedure default_create_time();
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_scope_id_group
|
||||||
|
before
|
||||||
|
update on iam_group
|
||||||
|
for each row execute procedure iam_immutable_scope_id_func();
|
||||||
|
|
||||||
|
-- iam_user_role contains roles that have been assigned to users. Users can only
|
||||||
|
-- be assigned roles which are within its organization, or the role is within a project within its
|
||||||
|
-- organization. There's no way to declare this constraint, so it will be
|
||||||
|
-- maintained with a before insert trigger using iam_user_role_scope_check().
|
||||||
|
-- The rows in this table must be immutable after insert, which will be ensured
|
||||||
|
-- with a before update trigger using iam_immutable_role().
|
||||||
|
create table iam_user_role (
|
||||||
|
create_time wt_timestamp,
|
||||||
|
scope_id wt_public_id not null,
|
||||||
|
role_id wt_public_id not null,
|
||||||
|
principal_id wt_public_id not null references iam_user(public_id) on delete cascade on update cascade,
|
||||||
|
primary key (role_id, principal_id),
|
||||||
|
foreign key (scope_id, role_id)
|
||||||
|
references iam_role(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
-- iam_group_role contains roles that have been assigned to groups. Groups can
|
||||||
|
-- only be assigned roles which are within its scope (organization or project)
|
||||||
|
-- and that integrity can be maintained with a foreign key. The rows in this
|
||||||
|
-- table must be immutable after insert, which will be ensured with a before
|
||||||
|
-- update trigger using iam_immutable_role().
|
||||||
|
create table iam_group_role (
|
||||||
|
create_time wt_timestamp,
|
||||||
|
scope_id wt_public_id not null,
|
||||||
|
role_id wt_public_id not null,
|
||||||
|
principal_id wt_public_id not null,
|
||||||
|
primary key (role_id, principal_id),
|
||||||
|
foreign key (scope_id, role_id)
|
||||||
|
references iam_role(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade,
|
||||||
|
foreign key (scope_id, principal_id)
|
||||||
|
references iam_group(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
-- iam_principle_role provides a consolidated view all principal roles assigned
|
||||||
|
-- (user and group roles).
|
||||||
|
create view iam_principal_role as
|
||||||
|
select
|
||||||
|
-- intentionally using * to specify the view which requires that the concrete role assignment tables match
|
||||||
|
*, 'user' as type
|
||||||
|
from iam_user_role
|
||||||
|
union
|
||||||
|
select
|
||||||
|
-- intentionally using * to specify the view which requires that the concrete role assignment tables match
|
||||||
|
*, 'group' as type
|
||||||
|
from iam_group_role;
|
||||||
|
|
||||||
|
-- iam_user_role_scope_check() ensures that the user is only assigned roles
|
||||||
|
-- which are within its organization, or the role is within a project within its
|
||||||
|
-- organization.
|
||||||
|
create or replace function
|
||||||
|
iam_user_role_scope_check()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
declare cnt int;
|
||||||
|
begin
|
||||||
|
select count(*) into cnt
|
||||||
|
from iam_user
|
||||||
|
where
|
||||||
|
public_id = new.principal_id and
|
||||||
|
scope_id in(
|
||||||
|
-- check to see if they have the same org scope
|
||||||
|
select s.public_id
|
||||||
|
from iam_scope s, iam_role r
|
||||||
|
where s.public_id = r.scope_id and r.public_id = new.role_id and r.scope_id = new.scope_id
|
||||||
|
union
|
||||||
|
-- check to see if the role has a parent that's the same org
|
||||||
|
select s.parent_id as public_id
|
||||||
|
from iam_scope s, iam_role r
|
||||||
|
where s.public_id = r.scope_id and r.public_id = new.role_id and r.scope_id = new.scope_id
|
||||||
|
);
|
||||||
|
if cnt = 0 then
|
||||||
|
raise exception 'user and role do not belong to the same organization';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
-- iam_immutable_role() ensures that roles assigned to principals are immutable.
|
||||||
|
create or replace function
|
||||||
|
iam_immutable_role()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
if row(new.*) is distinct from row(old.*) then
|
||||||
|
raise exception 'roles are immutable';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
create trigger iam_user_role_scope_check
|
||||||
|
before
|
||||||
|
insert on iam_user_role
|
||||||
|
for each row execute procedure iam_user_role_scope_check();
|
||||||
|
|
||||||
|
create trigger immutable_role
|
||||||
|
before
|
||||||
|
update on iam_user_role
|
||||||
|
for each row execute procedure iam_immutable_role();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
immutable_create_time
|
||||||
|
before
|
||||||
|
update on iam_user_role
|
||||||
|
for each row execute procedure immutable_create_time_func();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
default_create_time_column
|
||||||
|
before
|
||||||
|
insert on iam_user_role
|
||||||
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_role
|
||||||
|
before
|
||||||
|
update on iam_group_role
|
||||||
|
for each row execute procedure iam_immutable_role();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
immutable_create_time
|
||||||
|
before
|
||||||
|
update on iam_group_role
|
||||||
|
for each row execute procedure immutable_create_time_func();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
default_create_time_column
|
||||||
|
before
|
||||||
|
insert on iam_group_role
|
||||||
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|
||||||
`),
|
`),
|
||||||
|
|||||||
@@ -94,14 +94,14 @@ values
|
|||||||
('iam_group_member_user', 1),
|
('iam_group_member_user', 1),
|
||||||
('iam_role', 1),
|
('iam_role', 1),
|
||||||
('iam_role_grant', 1),
|
('iam_role_grant', 1),
|
||||||
('iam_role_group', 1),
|
('iam_group_role', 1),
|
||||||
('iam_role_user', 1),
|
('iam_user_role', 1),
|
||||||
('db_test_user', 1),
|
('db_test_user', 1),
|
||||||
('db_test_car', 1),
|
('db_test_car', 1),
|
||||||
('db_test_rental', 1),
|
('db_test_rental', 1),
|
||||||
('db_test_scooter', 1),
|
('db_test_scooter', 1),
|
||||||
('auth_account', 1);
|
('auth_account', 1),
|
||||||
;
|
('iam_principal_role', 1);
|
||||||
|
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
|
|||||||
@@ -7,10 +7,16 @@ drop table iam_scope_organization cascade;
|
|||||||
drop table iam_scope cascade;
|
drop table iam_scope cascade;
|
||||||
drop table iam_scope_type_enm cascade;
|
drop table iam_scope_type_enm cascade;
|
||||||
drop table iam_role cascade;
|
drop table iam_role cascade;
|
||||||
|
drop view iam_principal_role cascade;
|
||||||
|
drop table iam_group_role cascade;
|
||||||
|
drop table iam_user_role cascade;
|
||||||
|
|
||||||
drop function iam_sub_names cascade;
|
drop function iam_sub_names cascade;
|
||||||
drop function iam_immutable_scope_type_func cascade;
|
drop function iam_immutable_scope_type_func cascade;
|
||||||
drop function iam_sub_scopes_func cascade;
|
drop function iam_sub_scopes_func cascade;
|
||||||
|
drop function iam_immutable_role cascade;
|
||||||
|
drop function iam_user_role_scope_check cascade;
|
||||||
|
drop function iam_group_role_scope_check cascade;
|
||||||
|
drop function immutable_scope_id_func cascade;
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
@@ -10,7 +10,24 @@ values
|
|||||||
('organization'),
|
('organization'),
|
||||||
('project');
|
('project');
|
||||||
|
|
||||||
|
|
||||||
|
create or replace function
|
||||||
|
iam_immutable_scope_id_func()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
if new.scope_id is distinct from old.scope_id then
|
||||||
|
raise exception 'scope_id cannot be set to %', new.scope_id;
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
comment on function
|
||||||
|
iam_immutable_scope_id_func()
|
||||||
|
is
|
||||||
|
'function used in before update triggers to make scope_id column is immutable';
|
||||||
|
|
||||||
create table iam_scope (
|
create table iam_scope (
|
||||||
public_id wt_public_id primary key,
|
public_id wt_public_id primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -140,7 +157,6 @@ before
|
|||||||
update on iam_scope
|
update on iam_scope
|
||||||
for each row execute procedure iam_sub_names();
|
for each row execute procedure iam_sub_names();
|
||||||
|
|
||||||
|
|
||||||
create table iam_user (
|
create table iam_user (
|
||||||
public_id wt_public_id not null primary key,
|
public_id wt_public_id not null primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -174,6 +190,11 @@ before
|
|||||||
insert on iam_user
|
insert on iam_user
|
||||||
for each row execute procedure default_create_time();
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_scope_id_user
|
||||||
|
before
|
||||||
|
update on iam_user
|
||||||
|
for each row execute procedure iam_immutable_scope_id_func();
|
||||||
|
|
||||||
create table iam_role (
|
create table iam_role (
|
||||||
public_id wt_public_id not null primary key,
|
public_id wt_public_id not null primary key,
|
||||||
create_time wt_timestamp,
|
create_time wt_timestamp,
|
||||||
@@ -182,9 +203,23 @@ create table iam_role (
|
|||||||
description text,
|
description text,
|
||||||
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
||||||
unique(name, scope_id),
|
unique(name, scope_id),
|
||||||
disabled boolean not null default false
|
disabled boolean not null default false,
|
||||||
|
-- version allows optimistic locking of the role when modifying the role
|
||||||
|
-- itself and when modifying dependent items like principal roles.
|
||||||
|
-- TODO (jlambert 6/2020) add before update trigger to automatically
|
||||||
|
-- increment the version when needed. This trigger can be addded when PR
|
||||||
|
-- #126 is merged and update_version_column() is available.
|
||||||
|
version bigint not null default 1,
|
||||||
|
|
||||||
|
-- add unique index so a composite fk can be declared.
|
||||||
|
unique(scope_id, public_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
update_version_column
|
||||||
|
after update on iam_role
|
||||||
|
for each row execute procedure update_version_column();
|
||||||
|
|
||||||
create trigger
|
create trigger
|
||||||
update_time_column
|
update_time_column
|
||||||
before update on iam_role
|
before update on iam_role
|
||||||
@@ -210,7 +245,9 @@ create table iam_group (
|
|||||||
description text,
|
description text,
|
||||||
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
scope_id wt_public_id not null references iam_scope(public_id) on delete cascade on update cascade,
|
||||||
unique(name, scope_id),
|
unique(name, scope_id),
|
||||||
disabled boolean not null default false
|
disabled boolean not null default false,
|
||||||
|
-- add unique index so a composite fk can be declared.
|
||||||
|
unique(scope_id, public_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create trigger
|
create trigger
|
||||||
@@ -229,5 +266,145 @@ create trigger
|
|||||||
before
|
before
|
||||||
insert on iam_group
|
insert on iam_group
|
||||||
for each row execute procedure default_create_time();
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_scope_id_group
|
||||||
|
before
|
||||||
|
update on iam_group
|
||||||
|
for each row execute procedure iam_immutable_scope_id_func();
|
||||||
|
|
||||||
|
-- iam_user_role contains roles that have been assigned to users. Users can only
|
||||||
|
-- be assigned roles which are within its organization, or the role is within a project within its
|
||||||
|
-- organization. There's no way to declare this constraint, so it will be
|
||||||
|
-- maintained with a before insert trigger using iam_user_role_scope_check().
|
||||||
|
-- The rows in this table must be immutable after insert, which will be ensured
|
||||||
|
-- with a before update trigger using iam_immutable_role().
|
||||||
|
create table iam_user_role (
|
||||||
|
create_time wt_timestamp,
|
||||||
|
scope_id wt_public_id not null,
|
||||||
|
role_id wt_public_id not null,
|
||||||
|
principal_id wt_public_id not null references iam_user(public_id) on delete cascade on update cascade,
|
||||||
|
primary key (role_id, principal_id),
|
||||||
|
foreign key (scope_id, role_id)
|
||||||
|
references iam_role(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
-- iam_group_role contains roles that have been assigned to groups. Groups can
|
||||||
|
-- only be assigned roles which are within its scope (organization or project)
|
||||||
|
-- and that integrity can be maintained with a foreign key. The rows in this
|
||||||
|
-- table must be immutable after insert, which will be ensured with a before
|
||||||
|
-- update trigger using iam_immutable_role().
|
||||||
|
create table iam_group_role (
|
||||||
|
create_time wt_timestamp,
|
||||||
|
scope_id wt_public_id not null,
|
||||||
|
role_id wt_public_id not null,
|
||||||
|
principal_id wt_public_id not null,
|
||||||
|
primary key (role_id, principal_id),
|
||||||
|
foreign key (scope_id, role_id)
|
||||||
|
references iam_role(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade,
|
||||||
|
foreign key (scope_id, principal_id)
|
||||||
|
references iam_group(scope_id, public_id)
|
||||||
|
on delete cascade
|
||||||
|
on update cascade
|
||||||
|
);
|
||||||
|
|
||||||
|
-- iam_principle_role provides a consolidated view all principal roles assigned
|
||||||
|
-- (user and group roles).
|
||||||
|
create view iam_principal_role as
|
||||||
|
select
|
||||||
|
-- intentionally using * to specify the view which requires that the concrete role assignment tables match
|
||||||
|
*, 'user' as type
|
||||||
|
from iam_user_role
|
||||||
|
union
|
||||||
|
select
|
||||||
|
-- intentionally using * to specify the view which requires that the concrete role assignment tables match
|
||||||
|
*, 'group' as type
|
||||||
|
from iam_group_role;
|
||||||
|
|
||||||
|
-- iam_user_role_scope_check() ensures that the user is only assigned roles
|
||||||
|
-- which are within its organization, or the role is within a project within its
|
||||||
|
-- organization.
|
||||||
|
create or replace function
|
||||||
|
iam_user_role_scope_check()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
declare cnt int;
|
||||||
|
begin
|
||||||
|
select count(*) into cnt
|
||||||
|
from iam_user
|
||||||
|
where
|
||||||
|
public_id = new.principal_id and
|
||||||
|
scope_id in(
|
||||||
|
-- check to see if they have the same org scope
|
||||||
|
select s.public_id
|
||||||
|
from iam_scope s, iam_role r
|
||||||
|
where s.public_id = r.scope_id and r.public_id = new.role_id and r.scope_id = new.scope_id
|
||||||
|
union
|
||||||
|
-- check to see if the role has a parent that's the same org
|
||||||
|
select s.parent_id as public_id
|
||||||
|
from iam_scope s, iam_role r
|
||||||
|
where s.public_id = r.scope_id and r.public_id = new.role_id and r.scope_id = new.scope_id
|
||||||
|
);
|
||||||
|
if cnt = 0 then
|
||||||
|
raise exception 'user and role do not belong to the same organization';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
-- iam_immutable_role() ensures that roles assigned to principals are immutable.
|
||||||
|
create or replace function
|
||||||
|
iam_immutable_role()
|
||||||
|
returns trigger
|
||||||
|
as $$
|
||||||
|
begin
|
||||||
|
if row(new.*) is distinct from row(old.*) then
|
||||||
|
raise exception 'roles are immutable';
|
||||||
|
end if;
|
||||||
|
return new;
|
||||||
|
end;
|
||||||
|
$$ language plpgsql;
|
||||||
|
|
||||||
|
create trigger iam_user_role_scope_check
|
||||||
|
before
|
||||||
|
insert on iam_user_role
|
||||||
|
for each row execute procedure iam_user_role_scope_check();
|
||||||
|
|
||||||
|
create trigger immutable_role
|
||||||
|
before
|
||||||
|
update on iam_user_role
|
||||||
|
for each row execute procedure iam_immutable_role();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
immutable_create_time
|
||||||
|
before
|
||||||
|
update on iam_user_role
|
||||||
|
for each row execute procedure immutable_create_time_func();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
default_create_time_column
|
||||||
|
before
|
||||||
|
insert on iam_user_role
|
||||||
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
|
create trigger immutable_role
|
||||||
|
before
|
||||||
|
update on iam_group_role
|
||||||
|
for each row execute procedure iam_immutable_role();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
immutable_create_time
|
||||||
|
before
|
||||||
|
update on iam_group_role
|
||||||
|
for each row execute procedure immutable_create_time_func();
|
||||||
|
|
||||||
|
create trigger
|
||||||
|
default_create_time_column
|
||||||
|
before
|
||||||
|
insert on iam_group_role
|
||||||
|
for each row execute procedure default_create_time();
|
||||||
|
|
||||||
commit;
|
commit;
|
||||||
@@ -34,6 +34,8 @@ type Options struct {
|
|||||||
|
|
||||||
// WithVersion must be accessible from other packages
|
// WithVersion must be accessible from other packages
|
||||||
WithVersion int
|
WithVersion int
|
||||||
|
|
||||||
|
withSkipVetForWrite bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type oplogOpts struct {
|
type oplogOpts struct {
|
||||||
@@ -124,3 +126,11 @@ func WithVersion(version int) Option {
|
|||||||
o.WithVersion = version
|
o.WithVersion = version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSkipVetForWrite provides an option to allow skipping vet checks to allow
|
||||||
|
// testing lower-level SQL triggers and constraints
|
||||||
|
func WithSkipVetForWrite(enable bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.withSkipVetForWrite = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -132,4 +132,18 @@ func Test_getOpts(t *testing.T) {
|
|||||||
testOpts.WithVersion = 2
|
testOpts.WithVersion = 2
|
||||||
assert.Equal(opts, testOpts)
|
assert.Equal(opts, testOpts)
|
||||||
})
|
})
|
||||||
|
t.Run("WithSkipVetForWrite", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
// test default of false
|
||||||
|
opts := GetOpts()
|
||||||
|
testOpts := getDefaultOptions()
|
||||||
|
testOpts.withSkipVetForWrite = false
|
||||||
|
assert.Equal(opts, testOpts)
|
||||||
|
|
||||||
|
// try setting to false
|
||||||
|
opts = GetOpts(WithSkipVetForWrite(true))
|
||||||
|
testOpts = getDefaultOptions()
|
||||||
|
testOpts.withSkipVetForWrite = true
|
||||||
|
assert.Equal(opts, testOpts)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
@@ -241,9 +241,11 @@ func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error {
|
|||||||
// db to manage them
|
// db to manage them
|
||||||
setFieldsToNil(i, []string{"CreateTime", "UpdateTime"})
|
setFieldsToNil(i, []string{"CreateTime", "UpdateTime"})
|
||||||
|
|
||||||
if vetter, ok := i.(VetForWriter); ok {
|
if !opts.withSkipVetForWrite {
|
||||||
if err := vetter.VetForWrite(ctx, rw, CreateOp); err != nil {
|
if vetter, ok := i.(VetForWriter); ok {
|
||||||
return fmt.Errorf("create: vet for write failed %w", err)
|
if err := vetter.VetForWrite(ctx, rw, CreateOp); err != nil {
|
||||||
|
return fmt.Errorf("create: vet for write failed %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ticket *store.Ticket
|
var ticket *store.Ticket
|
||||||
@@ -407,9 +409,11 @@ func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string
|
|||||||
return NoRowsAffected, fmt.Errorf("update: oplog validation failed %w", err)
|
return NoRowsAffected, fmt.Errorf("update: oplog validation failed %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if vetter, ok := i.(VetForWriter); ok {
|
if !opts.withSkipVetForWrite {
|
||||||
if err := vetter.VetForWrite(ctx, rw, UpdateOp, WithFieldMaskPaths(fieldMaskPaths), WithNullPaths(setToNullPaths)); err != nil {
|
if vetter, ok := i.(VetForWriter); ok {
|
||||||
return NoRowsAffected, fmt.Errorf("update: vet for write failed %w", err)
|
if err := vetter.VetForWrite(ctx, rw, UpdateOp, WithFieldMaskPaths(fieldMaskPaths), WithNullPaths(setToNullPaths)); err != nil {
|
||||||
|
return NoRowsAffected, fmt.Errorf("update: vet for write failed %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ticket *store.Ticket
|
var ticket *store.Ticket
|
||||||
|
|||||||
+24
-14
@@ -14,24 +14,26 @@ type Option func(*options)
|
|||||||
|
|
||||||
// options = how options are represented
|
// options = how options are represented
|
||||||
type options struct {
|
type options struct {
|
||||||
withPublicId string
|
withPublicId string
|
||||||
withName string
|
withName string
|
||||||
withScope *Scope
|
withScope *Scope
|
||||||
withDescription string
|
withDescription string
|
||||||
withParentId *string
|
withParentId *string
|
||||||
withLimit int
|
withLimit int
|
||||||
withAutoVivify bool
|
withAutoVivify bool
|
||||||
|
withSkipVetForWrite bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultOptions() options {
|
func getDefaultOptions() options {
|
||||||
return options{
|
return options{
|
||||||
withPublicId: "",
|
withPublicId: "",
|
||||||
withScope: nil,
|
withScope: nil,
|
||||||
withDescription: "",
|
withDescription: "",
|
||||||
withName: "",
|
withName: "",
|
||||||
withParentId: nil,
|
withParentId: nil,
|
||||||
withLimit: 0,
|
withLimit: 0,
|
||||||
withAutoVivify: false,
|
withAutoVivify: false,
|
||||||
|
withSkipVetForWrite: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,3 +81,11 @@ func WithAutoVivify(enable bool) Option {
|
|||||||
o.withAutoVivify = enable
|
o.withAutoVivify = enable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSkipVetForWrite provides an option to allow skipping vet checks to allow
|
||||||
|
// testing lower-level SQL triggers and constraints
|
||||||
|
func WithSkipVetForWrite(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.withSkipVetForWrite = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
package iam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/watchtower/internal/db"
|
||||||
|
"github.com/hashicorp/watchtower/internal/iam/store"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoleType defines the possible types for roles.
|
||||||
|
type RoleType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnknownRoleType RoleType = 0
|
||||||
|
UserRoleType RoleType = 1
|
||||||
|
GroupRoleType RoleType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a string representation of the role type.
|
||||||
|
func (r RoleType) String() string {
|
||||||
|
return [...]string{
|
||||||
|
"unknown",
|
||||||
|
"user",
|
||||||
|
"group",
|
||||||
|
}[r]
|
||||||
|
}
|
||||||
|
|
||||||
|
const principalRoleViewDefaultTable = "iam_principal_role"
|
||||||
|
|
||||||
|
// PrincipalRole declares a common interface for all roles assigned to resources (Users and Groups).
|
||||||
|
type PrincipalRole interface {
|
||||||
|
GetRoleId() string
|
||||||
|
GetPrincipalId() string
|
||||||
|
GetType() string
|
||||||
|
GetScopeId() string
|
||||||
|
Clone() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// principalRoleView provides a common way to return roles regardless of their underlying type.
|
||||||
|
type principalRoleView struct {
|
||||||
|
*store.PrincipalRoleView
|
||||||
|
tableName string `gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName provides an overridden gorm table name for principal roles.
|
||||||
|
func (v *principalRoleView) TableName() string {
|
||||||
|
if v.tableName != "" {
|
||||||
|
return v.tableName
|
||||||
|
}
|
||||||
|
return principalRoleViewDefaultTable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTableName sets the table name for the resource. If the caller attempts to
|
||||||
|
// set the name to "" the name will be reset to the default name.
|
||||||
|
func (v *principalRoleView) SetTableName(n string) {
|
||||||
|
switch n {
|
||||||
|
case "":
|
||||||
|
v.tableName = principalRoleViewDefaultTable
|
||||||
|
default:
|
||||||
|
v.tableName = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v principalRoleView) Clone() interface{} {
|
||||||
|
cp := proto.Clone(v.PrincipalRoleView)
|
||||||
|
return &principalRoleView{
|
||||||
|
PrincipalRoleView: cp.(*store.PrincipalRoleView),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRole is a role assigned to a user
|
||||||
|
type UserRole struct {
|
||||||
|
*store.UserRole
|
||||||
|
tableName string `gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that UserRole implements the interfaces of: Clonable, AssignedRole and db.VetForWriter
|
||||||
|
var _ Clonable = (*UserRole)(nil)
|
||||||
|
var _ PrincipalRole = (*UserRole)(nil)
|
||||||
|
var _ db.VetForWriter = (*UserRole)(nil)
|
||||||
|
|
||||||
|
// NewUserRole creates a new user role in memory. Users can be assigned roles
|
||||||
|
// which are within its organization, or the role is within a project within its
|
||||||
|
// organization. This relationship will not be enforced until the user role is
|
||||||
|
// written to the database. No options are supported currently.
|
||||||
|
func NewUserRole(scopeId, roleId, userId string, opt ...Option) (PrincipalRole, error) {
|
||||||
|
if roleId == "" {
|
||||||
|
return nil, fmt.Errorf("new user role: missing role id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if userId == "" {
|
||||||
|
return nil, fmt.Errorf("new user role: missing user id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
return &UserRole{
|
||||||
|
UserRole: &store.UserRole{
|
||||||
|
PrincipalId: userId,
|
||||||
|
RoleId: roleId,
|
||||||
|
ScopeId: scopeId,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetType returns the user role type.
|
||||||
|
func (r *UserRole) GetType() string {
|
||||||
|
return UserRoleType.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func allocUserRole() UserRole {
|
||||||
|
return UserRole{
|
||||||
|
UserRole: &store.UserRole{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a clone of the UserRole.
|
||||||
|
func (r *UserRole) Clone() interface{} {
|
||||||
|
cp := proto.Clone(r.UserRole)
|
||||||
|
return &UserRole{
|
||||||
|
UserRole: cp.(*store.UserRole),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VetForWrite implements db.VetForWrite() interface for user roles. The
|
||||||
|
// constraint between user and role scopes will be enforced by the database via
|
||||||
|
// constraints and triggers.
|
||||||
|
func (role *UserRole) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||||
|
if role.RoleId == "" {
|
||||||
|
return fmt.Errorf("new user role: missing role id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if role.PrincipalId == "" {
|
||||||
|
return fmt.Errorf("new user role: missing user id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the tablename to override the default gorm table name for
|
||||||
|
// user roles.
|
||||||
|
func (r *UserRole) TableName() string {
|
||||||
|
if r.tableName != "" {
|
||||||
|
return r.tableName
|
||||||
|
}
|
||||||
|
return "iam_user_role"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTableName sets the tablename and satisfies the ReplayableMessage interface
|
||||||
|
func (r *UserRole) SetTableName(n string) {
|
||||||
|
if r.tableName != "" {
|
||||||
|
r.tableName = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupRole is a role assigned to a group
|
||||||
|
type GroupRole struct {
|
||||||
|
*store.GroupRole
|
||||||
|
tableName string `gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that GroupRole implements the interfaces of: Clonable, AssignedRole and db.VetForWriter
|
||||||
|
var _ Clonable = (*GroupRole)(nil)
|
||||||
|
var _ PrincipalRole = (*GroupRole)(nil)
|
||||||
|
var _ db.VetForWriter = (*GroupRole)(nil)
|
||||||
|
|
||||||
|
// NewGroupRole creates a new group role in memory. Groups can only be
|
||||||
|
// assigned roles within its scope (org or project). This relationship will not
|
||||||
|
// be enforced until the group role is written to the database. No options are
|
||||||
|
// supported currently.
|
||||||
|
func NewGroupRole(scopeId, roleId, groupId string, opt ...Option) (PrincipalRole, error) {
|
||||||
|
if roleId == "" {
|
||||||
|
return nil, fmt.Errorf("new group role: missing role id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if groupId == "" {
|
||||||
|
return nil, fmt.Errorf("new group role: missing group id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
return &GroupRole{
|
||||||
|
GroupRole: &store.GroupRole{
|
||||||
|
PrincipalId: groupId,
|
||||||
|
RoleId: roleId,
|
||||||
|
ScopeId: scopeId,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetType returns the group role type.
|
||||||
|
func (r *GroupRole) GetType() string {
|
||||||
|
return GroupRoleType.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func allocGroupRole() GroupRole {
|
||||||
|
return GroupRole{
|
||||||
|
GroupRole: &store.GroupRole{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a clone of the GroupRole.
|
||||||
|
func (r *GroupRole) Clone() interface{} {
|
||||||
|
cp := proto.Clone(r.GroupRole)
|
||||||
|
return &GroupRole{
|
||||||
|
GroupRole: cp.(*store.GroupRole),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VetForWrite implements db.VetForWrite() interface for group roles. The
|
||||||
|
// constraint between groups and role scopes will be enforced by the database via
|
||||||
|
// constraints and triggers.
|
||||||
|
func (role *GroupRole) VetForWrite(ctx context.Context, r db.Reader, opType db.OpType, opt ...db.Option) error {
|
||||||
|
if role.RoleId == "" {
|
||||||
|
return fmt.Errorf("new group role: missing role id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if role.PrincipalId == "" {
|
||||||
|
return fmt.Errorf("new group role: missing user id %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName returns the tablename to override the default gorm table name for
|
||||||
|
// group roles.
|
||||||
|
func (r *GroupRole) TableName() string {
|
||||||
|
if r.tableName != "" {
|
||||||
|
return r.tableName
|
||||||
|
}
|
||||||
|
return "iam_group_role"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTableName sets the tablename and satisfies the ReplayableMessage interface
|
||||||
|
// for group roles.
|
||||||
|
func (r *GroupRole) SetTableName(n string) {
|
||||||
|
if r.tableName != "" {
|
||||||
|
r.tableName = n
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,747 @@
|
|||||||
|
package iam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/watchtower/internal/db"
|
||||||
|
"github.com/hashicorp/watchtower/internal/iam/store"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewUserRole(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
orgRole := TestRole(t, conn, org.PublicId)
|
||||||
|
projRole := TestRole(t, conn, proj.PublicId)
|
||||||
|
user := TestUser(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
roleId string
|
||||||
|
userId string
|
||||||
|
scopeId string
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want PrincipalRole
|
||||||
|
wantErr bool
|
||||||
|
wantIsErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid-org",
|
||||||
|
args: args{
|
||||||
|
roleId: orgRole.PublicId,
|
||||||
|
userId: user.PublicId,
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: func() PrincipalRole {
|
||||||
|
r := allocUserRole()
|
||||||
|
r.RoleId = orgRole.PublicId
|
||||||
|
r.PrincipalId = user.PublicId
|
||||||
|
r.ScopeId = org.PublicId
|
||||||
|
return &r
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-proj",
|
||||||
|
args: args{
|
||||||
|
roleId: projRole.PublicId,
|
||||||
|
userId: user.PublicId,
|
||||||
|
scopeId: proj.PublicId,
|
||||||
|
},
|
||||||
|
want: func() PrincipalRole {
|
||||||
|
r := allocUserRole()
|
||||||
|
r.RoleId = projRole.PublicId
|
||||||
|
r.PrincipalId = user.PublicId
|
||||||
|
r.ScopeId = proj.PublicId
|
||||||
|
return &r
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-role-id",
|
||||||
|
args: args{
|
||||||
|
roleId: "",
|
||||||
|
userId: user.PublicId,
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-user-id",
|
||||||
|
args: args{
|
||||||
|
roleId: orgRole.PublicId,
|
||||||
|
userId: "",
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
got, err := NewUserRole(tt.args.scopeId, tt.args.roleId, tt.args.userId, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
assert.True(errors.Is(err, tt.wantIsErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserRole_Create(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
org2, proj2 := TestScopes(t, conn)
|
||||||
|
type args struct {
|
||||||
|
role *UserRole
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantDup bool
|
||||||
|
wantErr bool
|
||||||
|
wantErrMsg string
|
||||||
|
wantIsErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid-with-org",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(org.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-with-proj",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(proj.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid-org",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, org2.PublicId)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(org.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid-proj",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, proj2.PublicId)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(proj.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-role-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
id := testId(t)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(org.PublicId, id, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: failed pq: user and role do not belong to the same organization",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-user-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
id := testId(t)
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
principalRole, err := NewUserRole(proj.PublicId, role.PublicId, id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: failed pq: user and role do not belong to the same organization",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-role-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
return &UserRole{
|
||||||
|
UserRole: &store.UserRole{
|
||||||
|
RoleId: "",
|
||||||
|
PrincipalId: principal.PublicId,
|
||||||
|
ScopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: vet for write failed new user role: missing role id invalid parameter",
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-user-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
return &UserRole{
|
||||||
|
UserRole: &store.UserRole{
|
||||||
|
RoleId: role.PublicId,
|
||||||
|
PrincipalId: "",
|
||||||
|
ScopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: vet for write failed new user role: missing user id invalid parameter",
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dup-at-org",
|
||||||
|
args: args{
|
||||||
|
role: func() *UserRole {
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
principal := TestUser(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewUserRole(org.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*UserRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantDup: true,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `create: failed pq: duplicate key value violates unique constraint "iam_user_role_pkey"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
w := db.New(conn)
|
||||||
|
if tt.wantDup {
|
||||||
|
r := tt.args.role.Clone().(*UserRole)
|
||||||
|
err := w.Create(context.Background(), r)
|
||||||
|
require.NoError(err)
|
||||||
|
}
|
||||||
|
r := tt.args.role.Clone().(*UserRole)
|
||||||
|
err := w.Create(context.Background(), r)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
assert.Contains(err.Error(), tt.wantErrMsg)
|
||||||
|
if tt.wantIsErr != nil {
|
||||||
|
assert.True(errors.Is(err, tt.wantIsErr))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
found := allocUserRole()
|
||||||
|
err = w.LookupWhere(context.Background(), &found, "role_id = ? and principal_id = ?", r.RoleId, r.PrincipalId)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(r, &found)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserRole_Update(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, _ := TestScopes(t, conn)
|
||||||
|
rw := db.New(conn)
|
||||||
|
|
||||||
|
t.Run("updates not allowed", func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
r := TestRole(t, conn, org.PublicId)
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
u2 := TestUser(t, conn, org.PublicId)
|
||||||
|
userRole := TestUserRole(t, conn, org.PublicId, r.PublicId, u.PublicId)
|
||||||
|
updateRole := userRole.Clone().(*UserRole)
|
||||||
|
updateRole.PrincipalId = u2.PublicId
|
||||||
|
updatedRows, err := rw.Update(context.Background(), updateRole, []string{"PrincipalId"}, nil)
|
||||||
|
require.Error(err)
|
||||||
|
assert.Equal(0, updatedRows)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserRole_Delete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
id := testId(t)
|
||||||
|
org, _ := TestScopes(t, conn)
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
r := TestRole(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
role *UserRole
|
||||||
|
wantRowsDeleted int
|
||||||
|
wantErr bool
|
||||||
|
wantErrMsg string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
role: TestUserRole(t, conn, org.PublicId, r.PublicId, u.PublicId),
|
||||||
|
wantErr: false,
|
||||||
|
wantRowsDeleted: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-id",
|
||||||
|
role: func() *UserRole { r := allocUserRole(); r.PrincipalId = id; r.RoleId = id; return &r }(),
|
||||||
|
wantErr: false,
|
||||||
|
wantRowsDeleted: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
deleteRole := allocUserRole()
|
||||||
|
deleteRole.RoleId = tt.role.GetRoleId()
|
||||||
|
deleteRole.PrincipalId = tt.role.GetPrincipalId()
|
||||||
|
deletedRows, err := rw.Delete(context.Background(), &deleteRole)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
if tt.wantRowsDeleted == 0 {
|
||||||
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||||
|
found := allocUserRole()
|
||||||
|
err = rw.LookupWhere(context.Background(), &found, "role_id = ? and principal_id = ?", tt.role.GetRoleId(), tt.role.GetPrincipalId())
|
||||||
|
require.Error(err)
|
||||||
|
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserRole_Clone(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
user := TestUser(t, conn, org.PublicId)
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
userRole := TestUserRole(t, conn, org.PublicId, role.PublicId, user.PublicId)
|
||||||
|
cp := userRole.Clone()
|
||||||
|
assert.True(proto.Equal(cp.(*UserRole).UserRole, userRole.UserRole))
|
||||||
|
})
|
||||||
|
t.Run("not-equal", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
role2 := TestRole(t, conn, proj.PublicId)
|
||||||
|
userRole := TestUserRole(t, conn, org.PublicId, role.PublicId, user.PublicId)
|
||||||
|
userRole2 := TestUserRole(t, conn, proj.PublicId, role2.PublicId, user.PublicId)
|
||||||
|
cp := userRole.Clone()
|
||||||
|
assert.True(!proto.Equal(cp.(*UserRole).UserRole, userRole2.UserRole))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserRole_GetType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
r := &UserRole{}
|
||||||
|
ty := r.GetType()
|
||||||
|
assert.Equal("user", ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewGroupRole(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
orgRole := TestRole(t, conn, org.PublicId)
|
||||||
|
projRole := TestRole(t, conn, proj.PublicId)
|
||||||
|
group := TestGroup(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
roleId string
|
||||||
|
groupId string
|
||||||
|
scopeId string
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want PrincipalRole
|
||||||
|
wantErr bool
|
||||||
|
wantIsErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid-org",
|
||||||
|
args: args{
|
||||||
|
roleId: orgRole.PublicId,
|
||||||
|
groupId: group.PublicId,
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: func() PrincipalRole {
|
||||||
|
r := allocGroupRole()
|
||||||
|
r.RoleId = orgRole.PublicId
|
||||||
|
r.PrincipalId = group.PublicId
|
||||||
|
r.ScopeId = org.PublicId
|
||||||
|
return &r
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-proj",
|
||||||
|
args: args{
|
||||||
|
roleId: projRole.PublicId,
|
||||||
|
groupId: group.PublicId,
|
||||||
|
scopeId: proj.PublicId,
|
||||||
|
},
|
||||||
|
want: func() PrincipalRole {
|
||||||
|
r := allocGroupRole()
|
||||||
|
r.RoleId = projRole.PublicId
|
||||||
|
r.PrincipalId = group.PublicId
|
||||||
|
r.ScopeId = proj.PublicId
|
||||||
|
return &r
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-role-id",
|
||||||
|
args: args{
|
||||||
|
roleId: "",
|
||||||
|
groupId: group.PublicId,
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-group-id",
|
||||||
|
args: args{
|
||||||
|
roleId: orgRole.PublicId,
|
||||||
|
groupId: "",
|
||||||
|
scopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
got, err := NewGroupRole(tt.args.scopeId, tt.args.roleId, tt.args.groupId, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
assert.True(errors.Is(err, tt.wantIsErr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole_Create(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
type args struct {
|
||||||
|
role *GroupRole
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantDup bool
|
||||||
|
wantErr bool
|
||||||
|
wantErrMsg string
|
||||||
|
wantIsErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid-with-org",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
principal := TestGroup(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(org.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-with-proj",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
principal := TestGroup(t, conn, proj.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(proj.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-role-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
id := testId(t)
|
||||||
|
principal := TestGroup(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(org.PublicId, id, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `create: failed pq: insert or update on table "iam_group_role" violates foreign key constraint "iam_group_role_scope_id_role_id_fkey"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-user-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
id := testId(t)
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(org.PublicId, role.PublicId, id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `create: failed pq: insert or update on table "iam_group_role" violates foreign key constraint "iam_group_role_scope_id_role_id_fkey"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-role-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
principal := TestGroup(t, conn, org.PublicId)
|
||||||
|
return &GroupRole{
|
||||||
|
GroupRole: &store.GroupRole{
|
||||||
|
RoleId: "",
|
||||||
|
PrincipalId: principal.PublicId,
|
||||||
|
ScopeId: org.PublicId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: vet for write failed new group role: missing role id invalid parameter",
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-user-id",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
return &GroupRole{
|
||||||
|
GroupRole: &store.GroupRole{
|
||||||
|
RoleId: role.PublicId,
|
||||||
|
PrincipalId: "",
|
||||||
|
ScopeId: proj.PublicId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: "create: vet for write failed new group role: missing user id invalid parameter",
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dup-at-org",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
principal := TestGroup(t, conn, org.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(org.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantDup: true,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `create: failed pq: duplicate key value violates unique constraint`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dup-at-proj",
|
||||||
|
args: args{
|
||||||
|
role: func() *GroupRole {
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
principal := TestGroup(t, conn, proj.PublicId)
|
||||||
|
principalRole, err := NewGroupRole(proj.PublicId, role.PublicId, principal.PublicId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return principalRole.(*GroupRole)
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
wantDup: true,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `create: failed pq: duplicate key value violates unique constraint`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
w := db.New(conn)
|
||||||
|
if tt.wantDup {
|
||||||
|
r := tt.args.role.Clone().(*GroupRole)
|
||||||
|
err := w.Create(context.Background(), r)
|
||||||
|
require.NoError(err)
|
||||||
|
}
|
||||||
|
r := tt.args.role.Clone().(*GroupRole)
|
||||||
|
err := w.Create(context.Background(), r)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
assert.Contains(err.Error(), tt.wantErrMsg)
|
||||||
|
if tt.wantIsErr != nil {
|
||||||
|
assert.True(errors.Is(err, tt.wantIsErr))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
found := allocGroupRole()
|
||||||
|
err = w.LookupWhere(context.Background(), &found, "role_id = ? and principal_id = ?", r.RoleId, r.PrincipalId)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(r, &found)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole_Update(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, _ := TestScopes(t, conn)
|
||||||
|
rw := db.New(conn)
|
||||||
|
|
||||||
|
t.Run("updates not allowed", func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
r := TestRole(t, conn, org.PublicId)
|
||||||
|
g := TestGroup(t, conn, org.PublicId)
|
||||||
|
g2 := TestGroup(t, conn, org.PublicId)
|
||||||
|
userRole := TestGroupRole(t, conn, org.PublicId, r.PublicId, g.PublicId)
|
||||||
|
updateRole := userRole.Clone().(*GroupRole)
|
||||||
|
updateRole.PrincipalId = g2.PublicId
|
||||||
|
updatedRows, err := rw.Update(context.Background(), updateRole, []string{"PrincipalId"}, nil)
|
||||||
|
require.Error(err)
|
||||||
|
assert.Equal(0, updatedRows)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole_Delete(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
id := testId(t)
|
||||||
|
org, _ := TestScopes(t, conn)
|
||||||
|
g := TestGroup(t, conn, org.PublicId)
|
||||||
|
r := TestRole(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
role *GroupRole
|
||||||
|
wantRowsDeleted int
|
||||||
|
wantErr bool
|
||||||
|
wantErrMsg string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
role: TestGroupRole(t, conn, org.PublicId, r.PublicId, g.PublicId),
|
||||||
|
wantErr: false,
|
||||||
|
wantRowsDeleted: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-id",
|
||||||
|
role: func() *GroupRole { r := allocGroupRole(); r.PrincipalId = id; r.RoleId = id; r.ScopeId = id; return &r }(),
|
||||||
|
wantErr: false,
|
||||||
|
wantRowsDeleted: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
deleteRole := allocGroupRole()
|
||||||
|
deleteRole.RoleId = tt.role.GetRoleId()
|
||||||
|
deleteRole.PrincipalId = tt.role.GetPrincipalId()
|
||||||
|
deletedRows, err := rw.Delete(context.Background(), &deleteRole)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
if tt.wantRowsDeleted == 0 {
|
||||||
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||||
|
found := allocGroupRole()
|
||||||
|
err = rw.LookupWhere(context.Background(), &found, "role_id = ? and principal_id = ?", tt.role.GetRoleId(), tt.role.GetPrincipalId())
|
||||||
|
require.Error(err)
|
||||||
|
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole_Clone(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
grp := TestGroup(t, conn, org.PublicId)
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
grpRole := TestGroupRole(t, conn, org.PublicId, role.PublicId, grp.PublicId)
|
||||||
|
cp := grpRole.Clone()
|
||||||
|
assert.True(proto.Equal(cp.(*GroupRole).GroupRole, grpRole.GroupRole))
|
||||||
|
})
|
||||||
|
t.Run("not-equal", func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
grp := TestGroup(t, conn, org.PublicId)
|
||||||
|
grp2 := TestGroup(t, conn, proj.PublicId)
|
||||||
|
role := TestRole(t, conn, org.PublicId)
|
||||||
|
role2 := TestRole(t, conn, proj.PublicId)
|
||||||
|
grpRole := TestGroupRole(t, conn, org.PublicId, role.PublicId, grp.PublicId)
|
||||||
|
grpRole2 := TestGroupRole(t, conn, proj.PublicId, role2.PublicId, grp2.PublicId)
|
||||||
|
cp := grpRole.Clone()
|
||||||
|
assert.True(!proto.Equal(cp.(*GroupRole).GroupRole, grpRole2.GroupRole))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole_GetType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
assert := assert.New(t)
|
||||||
|
r := &GroupRole{}
|
||||||
|
ty := r.GetType()
|
||||||
|
assert.Equal("group", ty)
|
||||||
|
}
|
||||||
@@ -111,6 +111,12 @@ func (r *Repository) update(ctx context.Context, resource Resource, fieldMaskPat
|
|||||||
}
|
}
|
||||||
metadata["op-type"] = []string{oplog.OpType_OP_TYPE_UPDATE.String()}
|
metadata["op-type"] = []string{oplog.OpType_OP_TYPE_UPDATE.String()}
|
||||||
|
|
||||||
|
dbOpts := []db.Option{db.WithOplog(r.wrapper, metadata)}
|
||||||
|
opts := getOpts(opt...)
|
||||||
|
if opts.withSkipVetForWrite {
|
||||||
|
dbOpts = append(dbOpts, db.WithSkipVetForWrite(true))
|
||||||
|
}
|
||||||
|
|
||||||
var rowsUpdated int
|
var rowsUpdated int
|
||||||
var returnedResource interface{}
|
var returnedResource interface{}
|
||||||
_, err = r.writer.DoTx(
|
_, err = r.writer.DoTx(
|
||||||
@@ -125,7 +131,7 @@ func (r *Repository) update(ctx context.Context, resource Resource, fieldMaskPat
|
|||||||
returnedResource,
|
returnedResource,
|
||||||
fieldMaskPaths,
|
fieldMaskPaths,
|
||||||
setToNullPaths,
|
setToNullPaths,
|
||||||
db.WithOplog(r.wrapper, metadata),
|
dbOpts...,
|
||||||
)
|
)
|
||||||
if err == nil && rowsUpdated > 1 {
|
if err == nil && rowsUpdated > 1 {
|
||||||
// return err, which will result in a rollback of the update
|
// return err, which will result in a rollback of the update
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ func TestRepository_UpdateGroup(t *testing.T) {
|
|||||||
wantErrMsg string
|
wantErrMsg string
|
||||||
wantIsError error
|
wantIsError error
|
||||||
wantDup bool
|
wantDup bool
|
||||||
|
directUpdate bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
@@ -378,6 +379,19 @@ func TestRepository_UpdateGroup(t *testing.T) {
|
|||||||
wantErrMsg: " already exists in organization " + org.PublicId,
|
wantErrMsg: " already exists in organization " + org.PublicId,
|
||||||
wantIsError: db.ErrNotUnique,
|
wantIsError: db.ErrNotUnique,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "modified-scope",
|
||||||
|
args: args{
|
||||||
|
name: "modified-scope" + id,
|
||||||
|
fieldMaskPaths: []string{"ScopeId"},
|
||||||
|
ScopeId: proj.PublicId,
|
||||||
|
opt: []Option{WithSkipVetForWrite(true)},
|
||||||
|
},
|
||||||
|
newScopeId: org.PublicId,
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `update: failed pq: scope_id cannot be set`,
|
||||||
|
directUpdate: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -400,13 +414,25 @@ func TestRepository_UpdateGroup(t *testing.T) {
|
|||||||
updateGrp.Name = tt.args.name
|
updateGrp.Name = tt.args.name
|
||||||
updateGrp.Description = tt.args.description
|
updateGrp.Description = tt.args.description
|
||||||
|
|
||||||
userAfterUpdate, updatedRows, err := repo.UpdateGroup(context.Background(), &updateGrp, tt.args.fieldMaskPaths, tt.args.opt...)
|
var groupAfterUpdate *Group
|
||||||
|
var updatedRows int
|
||||||
|
var err error
|
||||||
|
if tt.directUpdate {
|
||||||
|
g := updateGrp.Clone()
|
||||||
|
var resource interface{}
|
||||||
|
resource, updatedRows, err = repo.update(context.Background(), g.(*Group), tt.args.fieldMaskPaths, nil, tt.args.opt...)
|
||||||
|
if err == nil {
|
||||||
|
groupAfterUpdate = resource.(*Group)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groupAfterUpdate, updatedRows, err = repo.UpdateGroup(context.Background(), &updateGrp, tt.args.fieldMaskPaths, tt.args.opt...)
|
||||||
|
}
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
if tt.wantIsError != nil {
|
if tt.wantIsError != nil {
|
||||||
assert.True(errors.Is(err, tt.wantIsError))
|
assert.True(errors.Is(err, tt.wantIsError))
|
||||||
}
|
}
|
||||||
assert.Nil(userAfterUpdate)
|
assert.Nil(groupAfterUpdate)
|
||||||
assert.Equal(0, updatedRows)
|
assert.Equal(0, updatedRows)
|
||||||
assert.Contains(err.Error(), tt.wantErrMsg)
|
assert.Contains(err.Error(), tt.wantErrMsg)
|
||||||
err = db.TestVerifyOplog(t, rw, u.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
err = db.TestVerifyOplog(t, rw, u.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
||||||
@@ -418,13 +444,13 @@ func TestRepository_UpdateGroup(t *testing.T) {
|
|||||||
assert.Equal(tt.wantRowsUpdate, updatedRows)
|
assert.Equal(tt.wantRowsUpdate, updatedRows)
|
||||||
switch tt.name {
|
switch tt.name {
|
||||||
case "valid-no-op":
|
case "valid-no-op":
|
||||||
assert.Equal(u.UpdateTime, userAfterUpdate.UpdateTime)
|
assert.Equal(u.UpdateTime, groupAfterUpdate.UpdateTime)
|
||||||
default:
|
default:
|
||||||
assert.NotEqual(u.UpdateTime, userAfterUpdate.UpdateTime)
|
assert.NotEqual(u.UpdateTime, groupAfterUpdate.UpdateTime)
|
||||||
}
|
}
|
||||||
foundGrp, err := repo.LookupGroup(context.Background(), u.PublicId)
|
foundGrp, err := repo.LookupGroup(context.Background(), u.PublicId)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.True(proto.Equal(userAfterUpdate, foundGrp))
|
assert.True(proto.Equal(groupAfterUpdate, foundGrp))
|
||||||
dbassert := dbassert.New(t, rw)
|
dbassert := dbassert.New(t, rw)
|
||||||
if tt.args.name == "" {
|
if tt.args.name == "" {
|
||||||
dbassert.IsNull(foundGrp, "name")
|
dbassert.IsNull(foundGrp, "name")
|
||||||
|
|||||||
@@ -0,0 +1,453 @@
|
|||||||
|
package iam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/watchtower/internal/db"
|
||||||
|
"github.com/hashicorp/watchtower/internal/oplog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddPrincipalRoles provides the ability to add principals (userIds and
|
||||||
|
// groupIds) to a role (roleId). The role's current db version must match the
|
||||||
|
// roleVersion or an error will be returned. The roles and groups must be in
|
||||||
|
// the same scope. User can only be added to roles which are within the user's
|
||||||
|
// organization, or the role is within a project within the user's organization.
|
||||||
|
func (r *Repository) AddPrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, error) {
|
||||||
|
// NOTE - we are intentionally not going to check that the scopes are
|
||||||
|
// correct for the userIds and groupIds, given the roleId. We are going to
|
||||||
|
// rely on the database constraints and triggers to maintain the integrity
|
||||||
|
// of these scope relationships. The users and role need to either be in
|
||||||
|
// the same organization or the role needs to be in a project of the user's
|
||||||
|
// org. The groups and role have to be in the same scope (org or project).
|
||||||
|
// There are constraints and triggers to enforce these relationships.
|
||||||
|
if roleId == "" {
|
||||||
|
return nil, fmt.Errorf("add principal roles: missing role id: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if len(userIds) == 0 && len(groupIds) == 0 {
|
||||||
|
return nil, fmt.Errorf("add principal roles: missing either user or groups to add: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
role := allocRole()
|
||||||
|
role.PublicId = roleId
|
||||||
|
scope, err := role.GetScope(ctx, r.reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("add principal roles: unable to get role %s scope: %w", roleId, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newUserRoles := make([]interface{}, 0, len(userIds))
|
||||||
|
for _, id := range userIds {
|
||||||
|
usrRole, err := NewUserRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("add principal roles: unable to create in memory user role: %w", err)
|
||||||
|
}
|
||||||
|
newUserRoles = append(newUserRoles, usrRole)
|
||||||
|
}
|
||||||
|
newGrpRoles := make([]interface{}, 0, len(groupIds))
|
||||||
|
for _, id := range groupIds {
|
||||||
|
grpRole, err := NewGroupRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("add principal roles: unable to create in memory group role: %w", err)
|
||||||
|
}
|
||||||
|
newGrpRoles = append(newGrpRoles, grpRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = r.writer.DoTx(
|
||||||
|
ctx,
|
||||||
|
db.StdRetryCnt,
|
||||||
|
db.ExpBackoff{},
|
||||||
|
func(reader db.Reader, w db.Writer) error {
|
||||||
|
msgs := make([]*oplog.Message, 0, 2)
|
||||||
|
roleTicket, err := w.GetTicket(&role)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("add principal roles: unable to get ticket: %w", err)
|
||||||
|
}
|
||||||
|
updatedRole := allocRole()
|
||||||
|
updatedRole.PublicId = roleId
|
||||||
|
updatedRole.Version = uint32(roleVersion) + 1
|
||||||
|
var roleOplogMsg oplog.Message
|
||||||
|
rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(roleVersion))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("add principal roles: unable to update role version: %w", err)
|
||||||
|
}
|
||||||
|
if rowsUpdated != 1 {
|
||||||
|
return fmt.Errorf("add principal roles: updated role and %d rows updated", rowsUpdated)
|
||||||
|
}
|
||||||
|
msgs = append(msgs, &roleOplogMsg)
|
||||||
|
if len(newUserRoles) > 0 {
|
||||||
|
userOplogMsgs := make([]*oplog.Message, 0, len(newUserRoles))
|
||||||
|
if err := w.CreateItems(ctx, newUserRoles, db.NewOplogMsgs(&userOplogMsgs)); err != nil {
|
||||||
|
return fmt.Errorf("add principal roles: unable to add users: %w", err)
|
||||||
|
}
|
||||||
|
msgs = append(msgs, userOplogMsgs...)
|
||||||
|
}
|
||||||
|
if len(newGrpRoles) > 0 {
|
||||||
|
grpOplogMsgs := make([]*oplog.Message, 0, len(newGrpRoles))
|
||||||
|
if err := w.CreateItems(ctx, newGrpRoles, db.NewOplogMsgs(&grpOplogMsgs)); err != nil {
|
||||||
|
return fmt.Errorf("add principal roles: unable to add groups: %w", err)
|
||||||
|
}
|
||||||
|
msgs = append(msgs, grpOplogMsgs...)
|
||||||
|
}
|
||||||
|
metadata := oplog.Metadata{
|
||||||
|
"op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()},
|
||||||
|
"scope-id": []string{scope.PublicId},
|
||||||
|
"scope-type": []string{scope.Type},
|
||||||
|
"resource-public-id": []string{roleId},
|
||||||
|
}
|
||||||
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, roleTicket, metadata, msgs); err != nil {
|
||||||
|
return fmt.Errorf("add principal roles: unable to write oplog: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("add principal roles: error creating roles: %w", err)
|
||||||
|
}
|
||||||
|
principalRoles := make([]PrincipalRole, 0, len(newUserRoles)+len(newUserRoles))
|
||||||
|
for _, role := range newUserRoles {
|
||||||
|
principalRoles = append(principalRoles, role.(*UserRole))
|
||||||
|
}
|
||||||
|
for _, role := range newGrpRoles {
|
||||||
|
principalRoles = append(principalRoles, role.(*GroupRole))
|
||||||
|
}
|
||||||
|
return principalRoles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPrincipalRoles will set the role's principals. Set add and/or delete
|
||||||
|
// principals as need to reconcile the existing principals with the principals
|
||||||
|
// requested. If both userIds and groupIds are empty, the principal roles will
|
||||||
|
// be cleared.
|
||||||
|
func (r *Repository) SetPrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) ([]PrincipalRole, int, error) {
|
||||||
|
// NOTE - we are intentionally not going to check that the scopes are
|
||||||
|
// correct for the userIds and groupIds, given the roleId. We are going to
|
||||||
|
// rely on the database constraints and triggers to maintain the integrity
|
||||||
|
// of these scope relationships. The users and role need to either be in
|
||||||
|
// the same organization or the role needs to be in a project of the user's
|
||||||
|
// org. The groups and role have to be in the same scope (org or project).
|
||||||
|
// There are constraints and triggers to enforce these relationships.
|
||||||
|
if roleId == "" {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: missing role id: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
role := allocRole()
|
||||||
|
role.PublicId = roleId
|
||||||
|
scope, err := role.GetScope(ctx, r.reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to get role %s scope: %w", roleId, err)
|
||||||
|
}
|
||||||
|
// it's "safe" to do this lookup outside the DoTx transaction because we
|
||||||
|
// have a roleVersion so the principals can’t change without the version
|
||||||
|
// changing.
|
||||||
|
toSet, err := r.principalsToSet(ctx, &role, userIds, groupIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to determine set: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle no change to existing principal roles
|
||||||
|
if len(toSet.addUserRoles) == 0 && len(toSet.addGroupRoles) == 0 && len(toSet.deleteUserRoles) == 0 && len(toSet.deleteGroupRoles) == 0 {
|
||||||
|
results := make([]PrincipalRole, 0, len(userIds)+len(groupIds))
|
||||||
|
for _, id := range userIds {
|
||||||
|
role, err := NewUserRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("add principal roles: unable to create in memory user role: %w", err)
|
||||||
|
}
|
||||||
|
results = append(results, role)
|
||||||
|
}
|
||||||
|
for _, id := range groupIds {
|
||||||
|
role, err := NewGroupRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("add principal roles: unable to create in memory group role: %w", err)
|
||||||
|
}
|
||||||
|
results = append(results, role)
|
||||||
|
}
|
||||||
|
return results, db.NoRowsAffected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentPrincipals []PrincipalRole
|
||||||
|
var totalRowsAffected int
|
||||||
|
_, err = r.writer.DoTx(
|
||||||
|
ctx,
|
||||||
|
db.StdRetryCnt,
|
||||||
|
db.ExpBackoff{},
|
||||||
|
func(reader db.Reader, w db.Writer) error {
|
||||||
|
|
||||||
|
// we need a roleTicket, which won't be redeemed until all the other
|
||||||
|
// writes are successful. We can't just use a single ticket because
|
||||||
|
// we need to write oplog entries for deletes and adds
|
||||||
|
roleTicket, err := w.GetTicket(&role)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to get ticket for role: %w", err)
|
||||||
|
}
|
||||||
|
updatedRole := allocRole()
|
||||||
|
updatedRole.PublicId = roleId
|
||||||
|
updatedRole.Version = uint32(roleVersion) + 1
|
||||||
|
var roleOplogMsg oplog.Message
|
||||||
|
rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(roleVersion))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to update role version: %w", err)
|
||||||
|
}
|
||||||
|
if rowsUpdated != 1 {
|
||||||
|
return fmt.Errorf("set principal roles: updated role and %d rows updated", rowsUpdated)
|
||||||
|
}
|
||||||
|
msgs := make([]*oplog.Message, 0, 5)
|
||||||
|
metadata := oplog.Metadata{
|
||||||
|
"op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()},
|
||||||
|
"scope-id": []string{scope.PublicId},
|
||||||
|
"scope-type": []string{scope.Type},
|
||||||
|
"resource-public-id": []string{roleId},
|
||||||
|
}
|
||||||
|
msgs = append(msgs, &roleOplogMsg)
|
||||||
|
|
||||||
|
if len(toSet.deleteUserRoles) > 0 || len(toSet.deleteGroupRoles) > 0 {
|
||||||
|
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String())
|
||||||
|
if len(toSet.deleteUserRoles) > 0 {
|
||||||
|
userOplogMsgs := make([]*oplog.Message, 0, len(toSet.deleteUserRoles))
|
||||||
|
rowsDeleted, err := w.DeleteItems(ctx, toSet.deleteUserRoles, db.NewOplogMsgs(&userOplogMsgs))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to delete user roles: %w", err)
|
||||||
|
}
|
||||||
|
if rowsDeleted != len(toSet.deleteUserRoles) {
|
||||||
|
return fmt.Errorf("set principal roles: user roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteUserRoles))
|
||||||
|
}
|
||||||
|
totalRowsAffected += rowsDeleted
|
||||||
|
msgs = append(msgs, userOplogMsgs...)
|
||||||
|
}
|
||||||
|
if len(toSet.deleteGroupRoles) > 0 {
|
||||||
|
grpOplogMsgs := make([]*oplog.Message, 0, len(toSet.deleteGroupRoles))
|
||||||
|
rowsDeleted, err := w.DeleteItems(ctx, toSet.deleteGroupRoles, db.NewOplogMsgs(&grpOplogMsgs))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to delete groups: %w", err)
|
||||||
|
}
|
||||||
|
if rowsDeleted != len(toSet.deleteGroupRoles) {
|
||||||
|
return fmt.Errorf("set principal roles: group roles deleted %d did not match request for %d", rowsDeleted, len(toSet.deleteGroupRoles))
|
||||||
|
}
|
||||||
|
totalRowsAffected += rowsDeleted
|
||||||
|
msgs = append(msgs, grpOplogMsgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(toSet.addUserRoles) > 0 || len(toSet.addGroupRoles) > 0 {
|
||||||
|
metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String())
|
||||||
|
if len(toSet.addUserRoles) > 0 {
|
||||||
|
userOplogMsgs := make([]*oplog.Message, 0, len(toSet.addUserRoles))
|
||||||
|
if err := w.CreateItems(ctx, toSet.addUserRoles, db.NewOplogMsgs(&userOplogMsgs)); err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to add users: %w", err)
|
||||||
|
}
|
||||||
|
totalRowsAffected += len(toSet.addUserRoles)
|
||||||
|
msgs = append(msgs, userOplogMsgs...)
|
||||||
|
}
|
||||||
|
if len(toSet.addGroupRoles) > 0 {
|
||||||
|
grpOplogMsgs := make([]*oplog.Message, 0, len(toSet.addGroupRoles))
|
||||||
|
if err := w.CreateItems(ctx, toSet.addGroupRoles, db.NewOplogMsgs(&grpOplogMsgs)); err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to add groups: %w", err)
|
||||||
|
}
|
||||||
|
totalRowsAffected += len(toSet.addGroupRoles)
|
||||||
|
msgs = append(msgs, grpOplogMsgs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, roleTicket, metadata, msgs); err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to write oplog for additions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPrincipals, err = r.ListPrincipalRoles(ctx, roleId)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("set principal roles: unable to retrieve current principal roles after sets: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, db.NoRowsAffected, fmt.Errorf("set principal roles: unable to set principals: %w", err)
|
||||||
|
}
|
||||||
|
return currentPrincipals, totalRowsAffected, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePrincipalRoles principals (userIds and/or groupIds) from a role
|
||||||
|
// (roleId). The role's current db version must match the roleVersion or an
|
||||||
|
// error will be returned.
|
||||||
|
func (r *Repository) DeletePrincipalRoles(ctx context.Context, roleId string, roleVersion int, userIds, groupIds []string, opt ...Option) (int, error) {
|
||||||
|
if roleId == "" {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing role id: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
if len(userIds) == 0 && len(groupIds) == 0 {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: missing either user or groups to delete: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
role := allocRole()
|
||||||
|
role.PublicId = roleId
|
||||||
|
scope, err := role.GetScope(ctx, r.reader)
|
||||||
|
if err != nil {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to get role %s scope to create metadata: %w", roleId, err)
|
||||||
|
}
|
||||||
|
deleteUserRoles := make([]interface{}, 0, len(userIds))
|
||||||
|
for _, id := range userIds {
|
||||||
|
usrRole, err := NewUserRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to create in memory user role: %w", err)
|
||||||
|
}
|
||||||
|
deleteUserRoles = append(deleteUserRoles, usrRole)
|
||||||
|
}
|
||||||
|
deleteGrpRoles := make([]interface{}, 0, len(groupIds))
|
||||||
|
for _, id := range groupIds {
|
||||||
|
grpRole, err := NewGroupRole(scope.PublicId, roleId, id)
|
||||||
|
if err != nil {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: unable to create in memory group role: %w", err)
|
||||||
|
}
|
||||||
|
deleteGrpRoles = append(deleteGrpRoles, grpRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalRowsDeleted int
|
||||||
|
_, err = r.writer.DoTx(
|
||||||
|
ctx,
|
||||||
|
db.StdRetryCnt,
|
||||||
|
db.ExpBackoff{},
|
||||||
|
func(reader db.Reader, w db.Writer) error {
|
||||||
|
msgs := make([]*oplog.Message, 0, 2)
|
||||||
|
roleTicket, err := w.GetTicket(&role)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("delete principal roles: unable to get ticket: %w", err)
|
||||||
|
}
|
||||||
|
updatedRole := allocRole()
|
||||||
|
updatedRole.PublicId = roleId
|
||||||
|
updatedRole.Version = uint32(roleVersion) + 1
|
||||||
|
var roleOplogMsg oplog.Message
|
||||||
|
rowsUpdated, err := w.Update(ctx, &updatedRole, []string{"Version"}, nil, db.NewOplogMsg(&roleOplogMsg), db.WithVersion(roleVersion))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("delete principal roles: unable to update role version: %w", err)
|
||||||
|
}
|
||||||
|
if rowsUpdated != 1 {
|
||||||
|
return fmt.Errorf("delete principal roles: updated role and %d rows updated", rowsUpdated)
|
||||||
|
}
|
||||||
|
msgs = append(msgs, &roleOplogMsg)
|
||||||
|
if len(deleteUserRoles) > 0 {
|
||||||
|
userOplogMsgs := make([]*oplog.Message, 0, len(deleteUserRoles))
|
||||||
|
rowsDeleted, err := w.DeleteItems(ctx, deleteUserRoles, db.NewOplogMsgs(&userOplogMsgs))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("delete principal roles: unable to delete user roles: %w", err)
|
||||||
|
}
|
||||||
|
if rowsDeleted != len(deleteUserRoles) {
|
||||||
|
return fmt.Errorf("delete principal roles: user roles deleted %d did not match request for %d", rowsDeleted, len(deleteUserRoles))
|
||||||
|
}
|
||||||
|
totalRowsDeleted += rowsDeleted
|
||||||
|
msgs = append(msgs, userOplogMsgs...)
|
||||||
|
}
|
||||||
|
if len(deleteGrpRoles) > 0 {
|
||||||
|
grpOplogMsgs := make([]*oplog.Message, 0, len(deleteGrpRoles))
|
||||||
|
rowsDeleted, err := w.DeleteItems(ctx, deleteGrpRoles, db.NewOplogMsgs(&grpOplogMsgs))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("delete principal roles: unable to delete groups: %w", err)
|
||||||
|
}
|
||||||
|
if rowsDeleted != len(deleteGrpRoles) {
|
||||||
|
return fmt.Errorf("delete principal roles: group roles deleted %d did not match request for %d", rowsDeleted, len(deleteGrpRoles))
|
||||||
|
}
|
||||||
|
totalRowsDeleted += rowsDeleted
|
||||||
|
msgs = append(msgs, grpOplogMsgs...)
|
||||||
|
}
|
||||||
|
metadata := oplog.Metadata{
|
||||||
|
"op-type": []string{oplog.OpType_OP_TYPE_DELETE.String()},
|
||||||
|
"scope-id": []string{scope.PublicId},
|
||||||
|
"scope-type": []string{scope.Type},
|
||||||
|
"resource-public-id": []string{roleId},
|
||||||
|
}
|
||||||
|
if err := w.WriteOplogEntryWith(ctx, r.wrapper, roleTicket, metadata, msgs); err != nil {
|
||||||
|
return fmt.Errorf("delete principal roles: unable to write oplog: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return db.NoRowsAffected, fmt.Errorf("delete principal roles: error deleting principal roles: %w", err)
|
||||||
|
}
|
||||||
|
return totalRowsDeleted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPrincipalRoles returns the principal roles for the roleId and supports the WithLimit option.
|
||||||
|
func (r *Repository) ListPrincipalRoles(ctx context.Context, roleId string, opt ...Option) ([]PrincipalRole, error) {
|
||||||
|
if roleId == "" {
|
||||||
|
return nil, fmt.Errorf("lookup principal roles: missing role id: %w", db.ErrInvalidParameter)
|
||||||
|
}
|
||||||
|
var roles []principalRoleView
|
||||||
|
if err := r.list(ctx, &roles, "role_id = ?", []interface{}{roleId}, opt...); err != nil {
|
||||||
|
return nil, fmt.Errorf("lookup principal role: unable to lookup roles: %w", err)
|
||||||
|
}
|
||||||
|
principals := make([]PrincipalRole, 0, len(roles))
|
||||||
|
for _, r := range roles {
|
||||||
|
principals = append(principals, r)
|
||||||
|
}
|
||||||
|
return principals, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type principalSet struct {
|
||||||
|
addUserRoles []interface{}
|
||||||
|
addGroupRoles []interface{}
|
||||||
|
deleteUserRoles []interface{}
|
||||||
|
deleteGroupRoles []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Repository) principalsToSet(ctx context.Context, role *Role, userIds, groupIds []string) (*principalSet, error) {
|
||||||
|
if role == nil {
|
||||||
|
return nil, fmt.Errorf("missing role: %w", db.ErrNilParameter)
|
||||||
|
}
|
||||||
|
existing, err := r.ListPrincipalRoles(ctx, role.PublicId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to list existing principal role %s: %w", role.PublicId, err)
|
||||||
|
}
|
||||||
|
existingUsers := map[string]PrincipalRole{}
|
||||||
|
existingGroups := map[string]PrincipalRole{}
|
||||||
|
for _, p := range existing {
|
||||||
|
switch p.GetType() {
|
||||||
|
case UserRoleType.String():
|
||||||
|
existingUsers[p.GetPrincipalId()] = p
|
||||||
|
case GroupRoleType.String():
|
||||||
|
existingGroups[p.GetPrincipalId()] = p
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%s is unknown principal type %s", p.GetPrincipalId(), p.GetType())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newUserRoles []interface{}
|
||||||
|
userIdsMap := map[string]struct{}{}
|
||||||
|
for _, id := range userIds {
|
||||||
|
userIdsMap[id] = struct{}{}
|
||||||
|
if _, ok := existingUsers[id]; !ok {
|
||||||
|
usrRole, err := NewUserRole(role.ScopeId, role.PublicId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create in memory user role for add: %w", err)
|
||||||
|
}
|
||||||
|
newUserRoles = append(newUserRoles, usrRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newGrpRoles []interface{}
|
||||||
|
groupIdsMap := map[string]struct{}{}
|
||||||
|
for _, id := range groupIds {
|
||||||
|
groupIdsMap[id] = struct{}{}
|
||||||
|
if _, ok := existingGroups[id]; !ok {
|
||||||
|
grpRole, err := NewGroupRole(role.ScopeId, role.PublicId, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create in memory group role for add: %w", err)
|
||||||
|
}
|
||||||
|
newGrpRoles = append(newGrpRoles, grpRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var deleteUserRoles []interface{}
|
||||||
|
for _, p := range existingUsers {
|
||||||
|
if _, ok := userIdsMap[p.GetPrincipalId()]; !ok {
|
||||||
|
usrRole, err := NewUserRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create in memory user role for delete: %w", err)
|
||||||
|
}
|
||||||
|
deleteUserRoles = append(deleteUserRoles, usrRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var deleteGrpRoles []interface{}
|
||||||
|
for _, p := range existingGroups {
|
||||||
|
if _, ok := groupIdsMap[p.GetPrincipalId()]; !ok {
|
||||||
|
grpRole, err := NewGroupRole(p.GetScopeId(), p.GetRoleId(), p.GetPrincipalId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create in memory group role for delete: %w", err)
|
||||||
|
}
|
||||||
|
deleteGrpRoles = append(deleteGrpRoles, grpRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &principalSet{
|
||||||
|
addUserRoles: newUserRoles,
|
||||||
|
addGroupRoles: newGrpRoles,
|
||||||
|
deleteUserRoles: deleteUserRoles,
|
||||||
|
deleteGroupRoles: deleteGrpRoles,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,730 @@
|
|||||||
|
package iam
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/watchtower/internal/db"
|
||||||
|
"github.com/hashicorp/watchtower/internal/oplog"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepository_AddPrincipalRoles(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
wrapper := db.TestWrapper(t)
|
||||||
|
repo, err := NewRepository(rw, rw, wrapper)
|
||||||
|
require.NoError(t, err)
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
createUsersFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
results = append(results, u.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
createGrpsFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
g := TestGroup(t, conn, proj.PublicId)
|
||||||
|
results = append(results, g.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
roleId string
|
||||||
|
roleVersion int
|
||||||
|
userIds []string
|
||||||
|
groupIds []string
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
wantErrIs error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid-both-users-and-groups",
|
||||||
|
args: args{
|
||||||
|
roleId: role.PublicId,
|
||||||
|
roleVersion: 1,
|
||||||
|
userIds: createUsersFn(),
|
||||||
|
groupIds: createGrpsFn(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-just-groups",
|
||||||
|
args: args{
|
||||||
|
roleId: role.PublicId,
|
||||||
|
roleVersion: 2,
|
||||||
|
userIds: nil,
|
||||||
|
groupIds: createGrpsFn(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-just-users",
|
||||||
|
args: args{
|
||||||
|
roleId: role.PublicId,
|
||||||
|
roleVersion: 3,
|
||||||
|
userIds: createUsersFn(),
|
||||||
|
groupIds: nil,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-version",
|
||||||
|
args: args{
|
||||||
|
roleId: role.PublicId,
|
||||||
|
roleVersion: 1000,
|
||||||
|
userIds: createUsersFn(),
|
||||||
|
groupIds: createGrpsFn(),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-principals",
|
||||||
|
args: args{
|
||||||
|
roleId: role.PublicId,
|
||||||
|
roleVersion: 1,
|
||||||
|
userIds: nil,
|
||||||
|
groupIds: nil,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
require.NoError(conn.Where("1=1").Delete(allocUserRole()).Error)
|
||||||
|
require.NoError(conn.Where("1=1").Delete(allocGroupRole()).Error)
|
||||||
|
got, err := repo.AddPrincipalRoles(context.Background(), tt.args.roleId, tt.args.roleVersion, tt.args.userIds, tt.args.groupIds, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
if tt.wantErrIs != nil {
|
||||||
|
assert.Truef(errors.Is(err, tt.wantErrIs), "unexpected error %s", err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
gotPrincipal := map[string]PrincipalRole{}
|
||||||
|
for _, r := range got {
|
||||||
|
gotPrincipal[r.GetPrincipalId()] = r
|
||||||
|
}
|
||||||
|
for _, id := range tt.args.userIds {
|
||||||
|
assert.NotEmpty(gotPrincipal[id])
|
||||||
|
u, err := repo.LookupUser(context.Background(), id)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(id, u.PublicId)
|
||||||
|
}
|
||||||
|
for _, id := range tt.args.groupIds {
|
||||||
|
assert.NotEmpty(gotPrincipal[id])
|
||||||
|
g, err := repo.LookupGroup(context.Background(), id)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(id, g.PublicId)
|
||||||
|
}
|
||||||
|
err = db.TestVerifyOplog(t, rw, role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second))
|
||||||
|
assert.NoError(err)
|
||||||
|
|
||||||
|
foundRoles, err := repo.ListPrincipalRoles(context.Background(), role.PublicId)
|
||||||
|
require.NoError(err)
|
||||||
|
for _, r := range foundRoles {
|
||||||
|
assert.NotEmpty(gotPrincipal[r.GetPrincipalId()])
|
||||||
|
assert.Equal(gotPrincipal[r.GetPrincipalId()].GetRoleId(), r.GetRoleId())
|
||||||
|
assert.Equal(gotPrincipal[r.GetPrincipalId()].GetScopeId(), r.GetScopeId())
|
||||||
|
assert.Equal(gotPrincipal[r.GetPrincipalId()].GetType(), r.GetType())
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepository_ListPrincipalRoles(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
const testLimit = 10
|
||||||
|
rw := db.New(conn)
|
||||||
|
wrapper := db.TestWrapper(t)
|
||||||
|
repo, err := NewRepository(rw, rw, wrapper, WithLimit(testLimit))
|
||||||
|
require.NoError(t, err)
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
withRoleId string
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
createCnt int
|
||||||
|
createScopeId string
|
||||||
|
args args
|
||||||
|
wantCnt int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no-limit",
|
||||||
|
createCnt: repo.defaultLimit + 2,
|
||||||
|
createScopeId: org.PublicId,
|
||||||
|
args: args{
|
||||||
|
opt: []Option{WithLimit(-1)},
|
||||||
|
},
|
||||||
|
wantCnt: repo.defaultLimit + 2,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-limit-proj-group",
|
||||||
|
createCnt: repo.defaultLimit + 2,
|
||||||
|
createScopeId: proj.PublicId,
|
||||||
|
args: args{
|
||||||
|
opt: []Option{WithLimit(-1)},
|
||||||
|
},
|
||||||
|
wantCnt: repo.defaultLimit + 2,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default-limit",
|
||||||
|
createCnt: repo.defaultLimit + 2,
|
||||||
|
createScopeId: org.PublicId,
|
||||||
|
wantCnt: repo.defaultLimit,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom-limit",
|
||||||
|
createCnt: repo.defaultLimit + 2,
|
||||||
|
createScopeId: org.PublicId,
|
||||||
|
args: args{
|
||||||
|
opt: []Option{WithLimit(3)},
|
||||||
|
},
|
||||||
|
wantCnt: 3,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-role-id",
|
||||||
|
createCnt: 2,
|
||||||
|
createScopeId: org.PublicId,
|
||||||
|
args: args{
|
||||||
|
withRoleId: "bad-id",
|
||||||
|
},
|
||||||
|
wantCnt: 0,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
require.NoError(conn.Where("1=1").Delete(allocRole()).Error)
|
||||||
|
role := TestRole(t, conn, tt.createScopeId)
|
||||||
|
userRoles := make([]string, 0, tt.createCnt)
|
||||||
|
groupRoles := make([]string, 0, tt.createCnt)
|
||||||
|
for i := 0; i < tt.createCnt/2; i++ {
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
userRoles = append(userRoles, u.PublicId)
|
||||||
|
g := TestGroup(t, conn, tt.createScopeId)
|
||||||
|
groupRoles = append(groupRoles, g.PublicId)
|
||||||
|
}
|
||||||
|
testRoles, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, int(role.Version), userRoles, groupRoles, tt.args.opt...)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.createCnt, len(testRoles))
|
||||||
|
|
||||||
|
var roleId string
|
||||||
|
switch {
|
||||||
|
case tt.args.withRoleId != "":
|
||||||
|
roleId = tt.args.withRoleId
|
||||||
|
default:
|
||||||
|
roleId = role.PublicId
|
||||||
|
}
|
||||||
|
got, err := repo.ListPrincipalRoles(context.Background(), roleId, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.wantCnt, len(got))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepository_DeletePrincipalRoles(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
wrapper := db.TestWrapper(t)
|
||||||
|
repo, err := NewRepository(rw, rw, wrapper)
|
||||||
|
require.NoError(t, err)
|
||||||
|
org, _ := TestScopes(t, conn)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
role *Role
|
||||||
|
roleIdOverride *string
|
||||||
|
createUserCnt int
|
||||||
|
createGroupCnt int
|
||||||
|
deleteUserCnt int
|
||||||
|
deleteGroupCnt int
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantRowsDeleted int
|
||||||
|
wantErr bool
|
||||||
|
wantIsErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
createUserCnt: 5,
|
||||||
|
createGroupCnt: 5,
|
||||||
|
deleteUserCnt: 5,
|
||||||
|
deleteGroupCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 10,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid-keeping-some",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
createUserCnt: 5,
|
||||||
|
createGroupCnt: 5,
|
||||||
|
deleteUserCnt: 2,
|
||||||
|
deleteGroupCnt: 2,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 4,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-deletes",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
createUserCnt: 5,
|
||||||
|
createGroupCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 0,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "just-user-roles",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
createUserCnt: 5,
|
||||||
|
deleteUserCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 5,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "just-group-roles",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
createGroupCnt: 5,
|
||||||
|
deleteGroupCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 5,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not-found",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
roleIdOverride: func() *string { id := testId(t); return &id }(),
|
||||||
|
createUserCnt: 5,
|
||||||
|
createGroupCnt: 5,
|
||||||
|
deleteUserCnt: 5,
|
||||||
|
deleteGroupCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 0,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-role-id",
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, org.PublicId),
|
||||||
|
roleIdOverride: func() *string { id := ""; return &id }(),
|
||||||
|
createUserCnt: 5,
|
||||||
|
createGroupCnt: 5,
|
||||||
|
deleteUserCnt: 5,
|
||||||
|
deleteGroupCnt: 5,
|
||||||
|
},
|
||||||
|
wantRowsDeleted: 0,
|
||||||
|
wantErr: true,
|
||||||
|
wantIsErr: db.ErrInvalidParameter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
userIds := make([]string, 0, tt.args.createUserCnt)
|
||||||
|
for i := 0; i < tt.args.createUserCnt; i++ {
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
userIds = append(userIds, u.PublicId)
|
||||||
|
}
|
||||||
|
groupIds := make([]string, 0, tt.args.createGroupCnt)
|
||||||
|
for i := 0; i < tt.args.createGroupCnt; i++ {
|
||||||
|
g := TestGroup(t, conn, tt.args.role.ScopeId)
|
||||||
|
groupIds = append(groupIds, g.PublicId)
|
||||||
|
}
|
||||||
|
principalRoles, err := repo.AddPrincipalRoles(context.Background(), tt.args.role.PublicId, 1, userIds, groupIds, tt.args.opt...)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.args.createUserCnt+tt.args.createGroupCnt, len(principalRoles))
|
||||||
|
|
||||||
|
deleteUserIds := make([]string, 0, tt.args.deleteUserCnt)
|
||||||
|
for i := 0; i < tt.args.deleteUserCnt; i++ {
|
||||||
|
deleteUserIds = append(deleteUserIds, userIds[i])
|
||||||
|
}
|
||||||
|
deleteGroupIds := make([]string, 0, tt.args.deleteGroupCnt)
|
||||||
|
for i := 0; i < tt.args.deleteGroupCnt; i++ {
|
||||||
|
deleteGroupIds = append(deleteGroupIds, groupIds[i])
|
||||||
|
}
|
||||||
|
var roleId string
|
||||||
|
switch {
|
||||||
|
case tt.args.roleIdOverride != nil:
|
||||||
|
roleId = *tt.args.roleIdOverride
|
||||||
|
default:
|
||||||
|
roleId = tt.args.role.PublicId
|
||||||
|
}
|
||||||
|
deletedRows, err := repo.DeletePrincipalRoles(context.Background(), roleId, 2, deleteUserIds, deleteGroupIds, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
assert.Equal(0, deletedRows)
|
||||||
|
if tt.wantIsErr != nil {
|
||||||
|
assert.Truef(errors.Is(err, tt.wantIsErr), "unexpected error %s", err.Error())
|
||||||
|
}
|
||||||
|
err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||||
|
assert.Error(err)
|
||||||
|
assert.True(errors.Is(db.ErrRecordNotFound, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
||||||
|
|
||||||
|
err = db.TestVerifyOplog(t, rw, tt.args.role.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second))
|
||||||
|
assert.NoError(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepository_SetPrincipalRoles(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
wrapper := db.TestWrapper(t)
|
||||||
|
|
||||||
|
repo, err := NewRepository(rw, rw, wrapper)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
testUser := TestUser(t, conn, org.PublicId)
|
||||||
|
testGrp := TestGroup(t, conn, proj.PublicId)
|
||||||
|
|
||||||
|
createUsersFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
results = append(results, u.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
createGrpsFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
g := TestGroup(t, conn, proj.PublicId)
|
||||||
|
results = append(results, g.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
setupFn := func(role *Role) ([]string, []string) {
|
||||||
|
users := createUsersFn()
|
||||||
|
grps := createGrpsFn()
|
||||||
|
_, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, users, grps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return users, grps
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
role *Role
|
||||||
|
roleVersion int
|
||||||
|
userIds []string
|
||||||
|
groupIds []string
|
||||||
|
addToOrigUsers bool
|
||||||
|
addToOrigGrps bool
|
||||||
|
opt []Option
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setup func(*Role) ([]string, []string)
|
||||||
|
args args
|
||||||
|
wantAffectedRows int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "clear",
|
||||||
|
setup: setupFn,
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, proj.PublicId),
|
||||||
|
roleVersion: 2, // yep, since setupFn will increment it to 2
|
||||||
|
userIds: []string{},
|
||||||
|
groupIds: []string{},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
wantAffectedRows: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no change",
|
||||||
|
setup: setupFn,
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, proj.PublicId),
|
||||||
|
roleVersion: 2, // yep, since setupFn will increment it to 2
|
||||||
|
userIds: []string{},
|
||||||
|
groupIds: []string{},
|
||||||
|
addToOrigUsers: true,
|
||||||
|
addToOrigGrps: true,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
wantAffectedRows: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add users and grps",
|
||||||
|
setup: setupFn,
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, proj.PublicId),
|
||||||
|
roleVersion: 2, // yep, since setupFn will increment it to 2
|
||||||
|
userIds: []string{testUser.PublicId},
|
||||||
|
groupIds: []string{testGrp.PublicId},
|
||||||
|
addToOrigUsers: true,
|
||||||
|
addToOrigGrps: true,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
wantAffectedRows: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove existing and add users and grps",
|
||||||
|
setup: setupFn,
|
||||||
|
args: args{
|
||||||
|
role: TestRole(t, conn, proj.PublicId),
|
||||||
|
roleVersion: 2, // yep, since setupFn will increment it to 2
|
||||||
|
userIds: []string{testUser.PublicId},
|
||||||
|
groupIds: []string{testGrp.PublicId},
|
||||||
|
addToOrigUsers: false,
|
||||||
|
addToOrigGrps: false,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
wantAffectedRows: 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
var origUsers, origGrps []string
|
||||||
|
if tt.setup != nil {
|
||||||
|
origUsers, origGrps = tt.setup(tt.args.role)
|
||||||
|
}
|
||||||
|
setUsers := tt.args.userIds
|
||||||
|
setGrps := tt.args.groupIds
|
||||||
|
if tt.args.addToOrigUsers {
|
||||||
|
setUsers = append(setUsers, origUsers...)
|
||||||
|
}
|
||||||
|
if tt.args.addToOrigGrps {
|
||||||
|
setGrps = append(setGrps, origGrps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, affectedRows, err := repo.SetPrincipalRoles(context.Background(), tt.args.role.PublicId, tt.args.roleVersion, setUsers, setGrps, tt.args.opt...)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Equal(tt.wantAffectedRows, affectedRows)
|
||||||
|
var gotIds []string
|
||||||
|
for _, r := range got {
|
||||||
|
gotIds = append(gotIds, r.GetPrincipalId())
|
||||||
|
}
|
||||||
|
var wantIds []string
|
||||||
|
wantIds = append(wantIds, tt.args.userIds...)
|
||||||
|
wantIds = append(wantIds, tt.args.groupIds...)
|
||||||
|
sort.Strings(wantIds)
|
||||||
|
sort.Strings(gotIds)
|
||||||
|
assert.Equal(wantIds, wantIds)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepository_principalsToSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
rw := db.New(conn)
|
||||||
|
wrapper := db.TestWrapper(t)
|
||||||
|
repo, err := NewRepository(rw, rw, wrapper)
|
||||||
|
require.NoError(t, err)
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
createUsersFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
u := TestUser(t, conn, org.PublicId)
|
||||||
|
results = append(results, u.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
createGrpsFn := func() []string {
|
||||||
|
results := []string{}
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
g := TestGroup(t, conn, proj.PublicId)
|
||||||
|
results = append(results, g.PublicId)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
setupFn := func() (*Role, []string, []string) {
|
||||||
|
users := createUsersFn()
|
||||||
|
grps := createGrpsFn()
|
||||||
|
role := TestRole(t, conn, proj.PublicId)
|
||||||
|
_, err := repo.AddPrincipalRoles(context.Background(), role.PublicId, 1, users, grps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return role, users, grps
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
userIds []string
|
||||||
|
groupIds []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all new",
|
||||||
|
args: args{
|
||||||
|
userIds: createUsersFn(),
|
||||||
|
groupIds: createGrpsFn(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clear all",
|
||||||
|
args: args{
|
||||||
|
userIds: nil,
|
||||||
|
groupIds: nil,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "just new users",
|
||||||
|
args: args{
|
||||||
|
userIds: createUsersFn(),
|
||||||
|
groupIds: nil,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "just new groups",
|
||||||
|
args: args{
|
||||||
|
userIds: nil,
|
||||||
|
groupIds: createGrpsFn(),
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
r, origUsers, origGrps := setupFn()
|
||||||
|
got, err := repo.principalsToSet(context.Background(), r, tt.args.userIds, tt.args.groupIds)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(err)
|
||||||
|
assertSetResults(t, got, tt.args.userIds, tt.args.groupIds, origUsers, origGrps)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
t.Run("nil role", func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
_, users, grps := setupFn()
|
||||||
|
got, err := repo.principalsToSet(context.Background(), nil, users, grps)
|
||||||
|
require.Error(err)
|
||||||
|
assert.Nil(got)
|
||||||
|
assert.Truef(errors.Is(err, db.ErrNilParameter), "unexpected error %s", err.Error())
|
||||||
|
})
|
||||||
|
t.Run("no change", func(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
r, users, grps := setupFn()
|
||||||
|
got, err := repo.principalsToSet(context.Background(), r, users, grps)
|
||||||
|
require.NoError(err)
|
||||||
|
assert.Empty(got.addUserRoles)
|
||||||
|
assert.Empty(got.addGroupRoles)
|
||||||
|
assert.Empty(got.deleteUserRoles)
|
||||||
|
assert.Empty(got.deleteGroupRoles)
|
||||||
|
})
|
||||||
|
t.Run("mixed", func(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
r, users, grps := setupFn()
|
||||||
|
var wantSetUsers, wantSetGrps, wantDeleteUsers, wantDeleteGrps []string
|
||||||
|
for i, id := range users {
|
||||||
|
if i < 2 {
|
||||||
|
wantSetUsers = append(wantSetUsers, id)
|
||||||
|
} else {
|
||||||
|
wantDeleteUsers = append(wantDeleteUsers, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, id := range grps {
|
||||||
|
if i < 2 {
|
||||||
|
wantSetGrps = append(wantSetGrps, id)
|
||||||
|
} else {
|
||||||
|
wantDeleteGrps = append(wantDeleteGrps, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newUser := TestUser(t, conn, org.PublicId)
|
||||||
|
newGrp := TestGroup(t, conn, proj.PublicId)
|
||||||
|
wantSetUsers = append(wantSetUsers, newUser.PublicId)
|
||||||
|
wantSetGrps = append(wantSetGrps, newGrp.PublicId)
|
||||||
|
|
||||||
|
got, err := repo.principalsToSet(context.Background(), r, wantSetUsers, wantSetGrps)
|
||||||
|
require.NoError(err)
|
||||||
|
assertSetResults(t, got, []string{newUser.PublicId}, []string{newGrp.PublicId}, wantDeleteUsers, wantDeleteGrps)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertSetResults(t *testing.T, got *principalSet, wantAddUsers, wantAddGroups, wantDeleteUsers, wantDeleteGroups []string) {
|
||||||
|
t.Helper()
|
||||||
|
assert := assert.New(t)
|
||||||
|
var gotAddUsers []string
|
||||||
|
for _, r := range got.addUserRoles {
|
||||||
|
gotAddUsers = append(gotAddUsers, r.(*UserRole).PrincipalId)
|
||||||
|
}
|
||||||
|
// sort.Strings(wantAddUsers)
|
||||||
|
// sort.Strings(gotAddUsers)
|
||||||
|
assert.Equal(wantAddUsers, gotAddUsers)
|
||||||
|
|
||||||
|
var gotAddGrps []string
|
||||||
|
for _, r := range got.addGroupRoles {
|
||||||
|
gotAddGrps = append(gotAddGrps, r.(*GroupRole).PrincipalId)
|
||||||
|
}
|
||||||
|
// sort.Strings(wantAddGroups)
|
||||||
|
// sort.Strings(gotAddGrps)
|
||||||
|
assert.Equal(wantAddGroups, gotAddGrps)
|
||||||
|
|
||||||
|
var gotDeleteUsers []string
|
||||||
|
for _, r := range got.deleteUserRoles {
|
||||||
|
gotDeleteUsers = append(gotDeleteUsers, r.(*UserRole).PrincipalId)
|
||||||
|
}
|
||||||
|
sort.Strings(wantDeleteUsers)
|
||||||
|
sort.Strings(gotDeleteUsers)
|
||||||
|
assert.Equal(wantDeleteUsers, gotDeleteUsers)
|
||||||
|
|
||||||
|
var gotDeleteGroups []string
|
||||||
|
for _, r := range got.deleteGroupRoles {
|
||||||
|
gotDeleteGroups = append(gotDeleteGroups, r.(*GroupRole).PrincipalId)
|
||||||
|
}
|
||||||
|
sort.Strings(wantDeleteGroups)
|
||||||
|
sort.Strings(gotDeleteGroups)
|
||||||
|
assert.Equal(wantDeleteGroups, gotDeleteGroups)
|
||||||
|
}
|
||||||
@@ -66,8 +66,9 @@ func (r *Repository) UpdateUser(ctx context.Context, user *User, fieldMaskPaths
|
|||||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||||
return nil, db.NoRowsAffected, fmt.Errorf("update user: %w", db.ErrEmptyFieldMask)
|
return nil, db.NoRowsAffected, fmt.Errorf("update user: %w", db.ErrEmptyFieldMask)
|
||||||
}
|
}
|
||||||
|
|
||||||
u := user.Clone()
|
u := user.Clone()
|
||||||
resource, rowsUpdated, err := r.update(ctx, u.(*User), dbMask, nullFields)
|
resource, rowsUpdated, err := r.update(ctx, u.(*User), dbMask, nullFields, opt...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if db.IsUniqueError(err) {
|
if db.IsUniqueError(err) {
|
||||||
return nil, db.NoRowsAffected, fmt.Errorf("update user: user %s already exists in organization %s", user.Name, user.ScopeId)
|
return nil, db.NoRowsAffected, fmt.Errorf("update user: user %s already exists in organization %s", user.Name, user.ScopeId)
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ func TestRepository_UpdateUser(t *testing.T) {
|
|||||||
wantErrMsg string
|
wantErrMsg string
|
||||||
wantIsErr error
|
wantIsErr error
|
||||||
wantDup bool
|
wantDup bool
|
||||||
|
directUpdate bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
@@ -280,6 +281,18 @@ func TestRepository_UpdateUser(t *testing.T) {
|
|||||||
wantDup: true,
|
wantDup: true,
|
||||||
wantErrMsg: `update user: user %s already exists in organization %s`,
|
wantErrMsg: `update user: user %s already exists in organization %s`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "modified-scope",
|
||||||
|
args: args{
|
||||||
|
name: "modified-scope" + id,
|
||||||
|
fieldMaskPaths: []string{"ScopeId"},
|
||||||
|
ScopeId: proj.PublicId,
|
||||||
|
opt: []Option{WithSkipVetForWrite(true)},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
wantErrMsg: `update: failed pq: scope_id cannot be set`,
|
||||||
|
directUpdate: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -302,7 +315,19 @@ func TestRepository_UpdateUser(t *testing.T) {
|
|||||||
updateUser.Name = tt.args.name
|
updateUser.Name = tt.args.name
|
||||||
updateUser.Description = tt.args.description
|
updateUser.Description = tt.args.description
|
||||||
|
|
||||||
userAfterUpdate, updatedRows, err := repo.UpdateUser(context.Background(), &updateUser, tt.args.fieldMaskPaths, tt.args.opt...)
|
var userAfterUpdate *User
|
||||||
|
var updatedRows int
|
||||||
|
var err error
|
||||||
|
if tt.directUpdate {
|
||||||
|
u := updateUser.Clone()
|
||||||
|
var resource interface{}
|
||||||
|
resource, updatedRows, err = repo.update(context.Background(), u.(*User), tt.args.fieldMaskPaths, nil, tt.args.opt...)
|
||||||
|
if err == nil {
|
||||||
|
userAfterUpdate = resource.(*User)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userAfterUpdate, updatedRows, err = repo.UpdateUser(context.Background(), &updateUser, tt.args.fieldMaskPaths, tt.args.opt...)
|
||||||
|
}
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
if tt.wantIsErr != nil {
|
if tt.wantIsErr != nil {
|
||||||
@@ -314,7 +339,7 @@ func TestRepository_UpdateUser(t *testing.T) {
|
|||||||
case "dup-name":
|
case "dup-name":
|
||||||
assert.Equal(fmt.Sprintf(tt.wantErrMsg, "dup-name"+id, org.PublicId), err.Error())
|
assert.Equal(fmt.Sprintf(tt.wantErrMsg, "dup-name"+id, org.PublicId), err.Error())
|
||||||
default:
|
default:
|
||||||
assert.Equal(tt.wantErrMsg, err.Error())
|
assert.True(strings.Contains(err.Error(), tt.wantErrMsg))
|
||||||
}
|
}
|
||||||
err = db.TestVerifyOplog(t, rw, u.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
err = db.TestVerifyOplog(t, rw, u.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second))
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|||||||
@@ -0,0 +1,422 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.24.0
|
||||||
|
// protoc v3.12.3
|
||||||
|
// source: controller/storage/iam/store/v1/principal_role.proto
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
timestamp "github.com/hashicorp/watchtower/internal/db/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
|
||||||
|
|
||||||
|
type UserRole struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
CreateTime *timestamp.Timestamp `protobuf:"bytes,1,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
RoleId string `protobuf:"bytes,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty" gorm:"primary_key"`
|
||||||
|
// principal_id is the public_id of the user (which is the principal)
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
PrincipalId string `protobuf:"bytes,3,opt,name=principal_id,json=principalId,proto3" json:"principal_id,omitempty" gorm:"primary_key"`
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
ScopeId string `protobuf:"bytes,4,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) Reset() {
|
||||||
|
*x = UserRole{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UserRole) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *UserRole) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_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 UserRole.ProtoReflect.Descriptor instead.
|
||||||
|
func (*UserRole) Descriptor() ([]byte, []int) {
|
||||||
|
return file_controller_storage_iam_store_v1_principal_role_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) GetCreateTime() *timestamp.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreateTime
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) GetRoleId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.RoleId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) GetPrincipalId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PrincipalId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *UserRole) GetScopeId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ScopeId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupRole struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
CreateTime *timestamp.Timestamp `protobuf:"bytes,1,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
RoleId string `protobuf:"bytes,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty" gorm:"primary_key"`
|
||||||
|
// principal_id is the public_id of the group (which is the principal)
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
PrincipalId string `protobuf:"bytes,3,opt,name=principal_id,json=principalId,proto3" json:"principal_id,omitempty" gorm:"primary_key"`
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
ScopeId string `protobuf:"bytes,4,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) Reset() {
|
||||||
|
*x = GroupRole{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GroupRole) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *GroupRole) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[1]
|
||||||
|
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 GroupRole.ProtoReflect.Descriptor instead.
|
||||||
|
func (*GroupRole) Descriptor() ([]byte, []int) {
|
||||||
|
return file_controller_storage_iam_store_v1_principal_role_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) GetCreateTime() *timestamp.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreateTime
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) GetRoleId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.RoleId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) GetPrincipalId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PrincipalId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *GroupRole) GetScopeId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ScopeId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrincipalRoleView struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
CreateTime *timestamp.Timestamp `protobuf:"bytes,1,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"`
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
RoleId string `protobuf:"bytes,2,opt,name=role_id,json=roleId,proto3" json:"role_id,omitempty" gorm:"primary_key"`
|
||||||
|
// Principal type (User or Group)
|
||||||
|
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
PrincipalId string `protobuf:"bytes,4,opt,name=principal_id,json=principalId,proto3" json:"principal_id,omitempty" gorm:"primary_key"`
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
ScopeId string `protobuf:"bytes,5,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) Reset() {
|
||||||
|
*x = PrincipalRoleView{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PrincipalRoleView) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[2]
|
||||||
|
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 PrincipalRoleView.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PrincipalRoleView) Descriptor() ([]byte, []int) {
|
||||||
|
return file_controller_storage_iam_store_v1_principal_role_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) GetCreateTime() *timestamp.Timestamp {
|
||||||
|
if x != nil {
|
||||||
|
return x.CreateTime
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) GetRoleId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.RoleId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) GetType() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Type
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) GetPrincipalId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PrincipalId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrincipalRoleView) GetScopeId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ScopeId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_controller_storage_iam_store_v1_principal_role_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_controller_storage_iam_store_v1_principal_role_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x34, 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, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x6c, 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, 0x74, 0x69, 0x6d, 0x65,
|
||||||
|
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||||
|
0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 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, 0x22, 0xae, 0x01, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x52, 0x6f,
|
||||||
|
0x6c, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
|
||||||
|
0x65, 0x18, 0x01, 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, 0x74, 0x69, 0x6d,
|
||||||
|
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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,
|
||||||
|
0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6e,
|
||||||
|
0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||||
|
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73,
|
||||||
|
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
|
||||||
|
0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x22, 0xaf, 0x01, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70,
|
||||||
|
0x52, 0x6f, 0x6c, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x18, 0x01, 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, 0x74,
|
||||||
|
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72,
|
||||||
|
0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x0b, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x19, 0x0a,
|
||||||
|
0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x11, 0x50, 0x72, 0x69,
|
||||||
|
0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x6f, 0x6c, 0x65, 0x56, 0x69, 0x65, 0x77, 0x12, 0x4b,
|
||||||
|
0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 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, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||||
|
0x6d, 0x70, 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, 0x17, 0x0a, 0x07, 0x72,
|
||||||
|
0x6f, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f,
|
||||||
|
0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x6e,
|
||||||
|
0x63, 0x69, 0x70, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||||
|
0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73,
|
||||||
|
0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
|
||||||
|
0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 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_principal_role_proto_rawDescOnce sync.Once
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_rawDescData = file_controller_storage_iam_store_v1_principal_role_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_controller_storage_iam_store_v1_principal_role_proto_rawDescGZIP() []byte {
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_rawDescOnce.Do(func() {
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_iam_store_v1_principal_role_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_controller_storage_iam_store_v1_principal_role_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_controller_storage_iam_store_v1_principal_role_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||||
|
var file_controller_storage_iam_store_v1_principal_role_proto_goTypes = []interface{}{
|
||||||
|
(*UserRole)(nil), // 0: controller.storage.iam.store.v1.UserRole
|
||||||
|
(*GroupRole)(nil), // 1: controller.storage.iam.store.v1.GroupRole
|
||||||
|
(*PrincipalRoleView)(nil), // 2: controller.storage.iam.store.v1.PrincipalRoleView
|
||||||
|
(*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp
|
||||||
|
}
|
||||||
|
var file_controller_storage_iam_store_v1_principal_role_proto_depIdxs = []int32{
|
||||||
|
3, // 0: controller.storage.iam.store.v1.UserRole.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||||
|
3, // 1: controller.storage.iam.store.v1.GroupRole.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||||
|
3, // 2: controller.storage.iam.store.v1.PrincipalRoleView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp
|
||||||
|
3, // [3:3] is the sub-list for method output_type
|
||||||
|
3, // [3:3] is the sub-list for method input_type
|
||||||
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_controller_storage_iam_store_v1_principal_role_proto_init() }
|
||||||
|
func file_controller_storage_iam_store_v1_principal_role_proto_init() {
|
||||||
|
if File_controller_storage_iam_store_v1_principal_role_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file_controller_storage_iam_store_v1_scope_proto_init()
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*UserRole); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GroupRole); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PrincipalRoleView); 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_principal_role_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 3,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_controller_storage_iam_store_v1_principal_role_proto_goTypes,
|
||||||
|
DependencyIndexes: file_controller_storage_iam_store_v1_principal_role_proto_depIdxs,
|
||||||
|
MessageInfos: file_controller_storage_iam_store_v1_principal_role_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_controller_storage_iam_store_v1_principal_role_proto = out.File
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_rawDesc = nil
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_goTypes = nil
|
||||||
|
file_controller_storage_iam_store_v1_principal_role_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -53,6 +53,10 @@ type Role struct {
|
|||||||
// disabled is by default false and allows a Role to be marked disabled.
|
// disabled is by default false and allows a Role to be marked disabled.
|
||||||
// @inject_tag: `gorm:"default:null"`
|
// @inject_tag: `gorm:"default:null"`
|
||||||
Disabled bool `protobuf:"varint,9,opt,name=disabled,proto3" json:"disabled,omitempty" gorm:"default:null"`
|
Disabled bool `protobuf:"varint,9,opt,name=disabled,proto3" json:"disabled,omitempty" gorm:"default:null"`
|
||||||
|
// version allows optimistic locking of the role when modifying the role
|
||||||
|
// itself and when modifying dependent items like principal roles.
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
Version uint32 `protobuf:"varint,10,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Role) Reset() {
|
func (x *Role) Reset() {
|
||||||
@@ -136,6 +140,13 @@ func (x *Role) GetDisabled() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Role) GetVersion() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Version
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
var File_controller_storage_iam_store_v1_role_proto protoreflect.FileDescriptor
|
var File_controller_storage_iam_store_v1_role_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
|
var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
|
||||||
@@ -149,7 +160,7 @@ var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
|
|||||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2b,
|
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2b,
|
||||||
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
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,
|
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, 0x22, 0xaa, 0x02, 0x0a, 0x04,
|
0x73, 0x63, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc4, 0x02, 0x0a, 0x04,
|
||||||
0x52, 0x6f, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69,
|
0x52, 0x6f, 0x6c, 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, 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,
|
0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||||
@@ -168,11 +179,13 @@ var file_controller_storage_iam_store_v1_role_proto_rawDesc = []byte{
|
|||||||
0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20,
|
0x6e, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08,
|
0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
||||||
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68,
|
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70,
|
0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
|
||||||
0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
0x6f, 0x6e, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x69, 0x61, 0x6d, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73,
|
0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68,
|
||||||
0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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 (
|
var (
|
||||||
|
|||||||
@@ -142,6 +142,30 @@ func TestGroup(t *testing.T, conn *gorm.DB, scopeId string, opt ...Option) *Grou
|
|||||||
return grp
|
return grp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUserRole(t *testing.T, conn *gorm.DB, scopeId, roleId, userId string, opt ...Option) *UserRole {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
rw := db.New(conn)
|
||||||
|
r, err := NewUserRole(scopeId, roleId, userId, opt...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
err = rw.Create(context.Background(), r)
|
||||||
|
require.NoError(err)
|
||||||
|
return r.(*UserRole)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupRole(t *testing.T, conn *gorm.DB, scopeId, roleId, grpId string, opt ...Option) *GroupRole {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
rw := db.New(conn)
|
||||||
|
r, err := NewGroupRole(scopeId, roleId, grpId, opt...)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
err = rw.Create(context.Background(), r)
|
||||||
|
require.NoError(err)
|
||||||
|
return r.(*GroupRole)
|
||||||
|
}
|
||||||
|
|
||||||
// testAuthAccount is a temporary test function. TODO - replace with an auth
|
// testAuthAccount is a temporary test function. TODO - replace with an auth
|
||||||
// subsystem testAuthAccount function. If userId is zero value, then an auth
|
// subsystem testAuthAccount function. If userId is zero value, then an auth
|
||||||
// account will be created with a null IamUserId
|
// account will be created with a null IamUserId
|
||||||
|
|||||||
@@ -85,3 +85,45 @@ func Test_TestRole(t *testing.T) {
|
|||||||
require.NotNil(projRole)
|
require.NotNil(projRole)
|
||||||
assert.NotEmpty(projRole.PublicId)
|
assert.NotEmpty(projRole.PublicId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_TestUserRole(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
orgRole := TestRole(t, conn, org.PublicId)
|
||||||
|
projRole := TestRole(t, conn, proj.PublicId)
|
||||||
|
user := TestUser(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
userRole := TestUserRole(t, conn, org.PublicId, orgRole.PublicId, user.PublicId)
|
||||||
|
require.NotNil(userRole)
|
||||||
|
require.Equal(orgRole.PublicId, userRole.RoleId)
|
||||||
|
require.Equal(user.PublicId, userRole.PrincipalId)
|
||||||
|
|
||||||
|
userRole = TestUserRole(t, conn, proj.PublicId, projRole.PublicId, user.PublicId)
|
||||||
|
require.NotNil(userRole)
|
||||||
|
require.Equal(projRole.PublicId, userRole.RoleId)
|
||||||
|
require.Equal(user.PublicId, userRole.PrincipalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TestGroupRole(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
require := require.New(t)
|
||||||
|
conn, _ := db.TestSetup(t, "postgres")
|
||||||
|
org, proj := TestScopes(t, conn)
|
||||||
|
orgRole := TestRole(t, conn, org.PublicId)
|
||||||
|
orgGroup := TestGroup(t, conn, org.PublicId)
|
||||||
|
|
||||||
|
projRole := TestRole(t, conn, proj.PublicId)
|
||||||
|
projGroup := TestGroup(t, conn, proj.PublicId)
|
||||||
|
|
||||||
|
groupRole := TestGroupRole(t, conn, org.PublicId, orgRole.PublicId, orgGroup.PublicId)
|
||||||
|
require.NotNil(groupRole)
|
||||||
|
require.Equal(orgRole.PublicId, groupRole.RoleId)
|
||||||
|
require.Equal(orgGroup.PublicId, groupRole.PrincipalId)
|
||||||
|
|
||||||
|
groupRole = TestGroupRole(t, conn, proj.PublicId, projRole.PublicId, projGroup.PublicId)
|
||||||
|
require.NotNil(groupRole)
|
||||||
|
require.Equal(projRole.PublicId, groupRole.RoleId)
|
||||||
|
require.Equal(projGroup.PublicId, groupRole.PrincipalId)
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package controller.storage.iam.store.v1;
|
||||||
|
option go_package = "github.com/hashicorp/watchtower/internal/iam/store;store";
|
||||||
|
|
||||||
|
import "controller/storage/timestamp/v1/timestamp.proto";
|
||||||
|
import "controller/storage/iam/store/v1/scope.proto";
|
||||||
|
|
||||||
|
message UserRole {
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
timestamp.v1.Timestamp create_time = 1;
|
||||||
|
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string role_id = 2;
|
||||||
|
|
||||||
|
// principal_id is the public_id of the user (which is the principal)
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string principal_id = 3;
|
||||||
|
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
string scope_id = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupRole {
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
timestamp.v1.Timestamp create_time = 1;
|
||||||
|
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string role_id = 2;
|
||||||
|
|
||||||
|
// principal_id is the public_id of the group (which is the principal)
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string principal_id = 3;
|
||||||
|
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
string scope_id = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrincipalRoleView {
|
||||||
|
// create_time from the RDBMS
|
||||||
|
// @inject_tag: `gorm:"default:current_timestamp"`
|
||||||
|
timestamp.v1.Timestamp create_time = 1;
|
||||||
|
|
||||||
|
// role_id is the role of this principal.
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string role_id = 2;
|
||||||
|
|
||||||
|
// Principal type (User or Group)
|
||||||
|
string type = 3;
|
||||||
|
|
||||||
|
// @inject_tag: gorm:"primary_key"
|
||||||
|
string principal_id = 4;
|
||||||
|
|
||||||
|
// scope_id of the principal role
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
string scope_id = 5;
|
||||||
|
}
|
||||||
@@ -35,4 +35,9 @@ message Role {
|
|||||||
// disabled is by default false and allows a Role to be marked disabled.
|
// disabled is by default false and allows a Role to be marked disabled.
|
||||||
// @inject_tag: `gorm:"default:null"`
|
// @inject_tag: `gorm:"default:null"`
|
||||||
bool disabled = 9;
|
bool disabled = 9;
|
||||||
|
|
||||||
|
// version allows optimistic locking of the role when modifying the role
|
||||||
|
// itself and when modifying dependent items like principal roles.
|
||||||
|
// @inject_tag: `gorm:"default:null"`
|
||||||
|
uint32 version = 10;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user