From 61adc8a4028e18588079abeae1e27a7343a661e2 Mon Sep 17 00:00:00 2001 From: Greg Kostin Date: Mon, 11 Dec 2023 16:00:16 -0500 Subject: [PATCH] We add group and group_membership so we could authorize an individual by group. Oh, btw, group factory buggy because no auto-increment on primary key, factory returns nil but doesn't complain, nasty bug hunt, so we do it manually! Ask Noah about ROM and Sequal stuff. --- lauth/app/repositories/grant_repo.rb | 5 +++ lauth/lib/lauth/group.rb | 4 +++ .../lib/lauth/persistence/relations/grants.rb | 1 + .../relations/group_memberships.rb | 21 +++++++++++ .../lib/lauth/persistence/relations/groups.rb | 22 ++++++++++++ .../persistence/relations/institutions.rb | 1 - lauth/spec/repositories/grant_repo_spec.rb | 36 +++++++++++++++++++ lauth/spec/requests/authorized_spec.rb | 18 ++++++++++ lauth/spec/support/factories/grant.rb | 4 +++ lauth/spec/support/factories/group.rb | 8 +++++ .../support/factories/group_membership.rb | 7 ++++ lauth/spec/support/requests.rb | 6 ++++ 12 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 lauth/lib/lauth/group.rb create mode 100644 lauth/lib/lauth/persistence/relations/group_memberships.rb create mode 100644 lauth/lib/lauth/persistence/relations/groups.rb create mode 100644 lauth/spec/support/factories/group.rb create mode 100644 lauth/spec/support/factories/group_membership.rb diff --git a/lauth/app/repositories/grant_repo.rb b/lauth/app/repositories/grant_repo.rb index f35031f0..7068d352 100644 --- a/lauth/app/repositories/grant_repo.rb +++ b/lauth/app/repositories/grant_repo.rb @@ -15,6 +15,7 @@ def for_user_and_uri(username, uri) .join(locations.name.dataset, coll: :uniqueIdentifier) .left_join(users.name.dataset, userid: grants[:userid]) .left_join(institution_memberships.name.dataset, inst: grants[:inst]) + .left_join(group_memberships.name.dataset, user_grp: grants[:user_grp]) .where(Sequel.ilike(uri, locations[:dlpsPath])) .where( Sequel.|( @@ -25,6 +26,10 @@ def for_user_and_uri(username, uri) Sequel.&( Sequel.~(institution_memberships[:userid] => nil), {institution_memberships[:userid] => username} + ), + Sequel.&( + Sequel.~(group_memberships[:userid] => nil), + {group_memberships[:userid] => username} ) ) ) diff --git a/lauth/lib/lauth/group.rb b/lauth/lib/lauth/group.rb new file mode 100644 index 00000000..058f8edf --- /dev/null +++ b/lauth/lib/lauth/group.rb @@ -0,0 +1,4 @@ +module Lauth + class Group < ROM::Struct + end +end diff --git a/lauth/lib/lauth/persistence/relations/grants.rb b/lauth/lib/lauth/persistence/relations/grants.rb index 58749e06..6fa4e05a 100644 --- a/lauth/lib/lauth/persistence/relations/grants.rb +++ b/lauth/lib/lauth/persistence/relations/grants.rb @@ -17,6 +17,7 @@ class Grants < ROM::Relation[:sql] belongs_to :user, foreign_key: :userid belongs_to :collection, foreign_key: :coll belongs_to :institution, foreign_key: :inst + belongs_to :group, foreign_key: :user_grp end end diff --git a/lauth/lib/lauth/persistence/relations/group_memberships.rb b/lauth/lib/lauth/persistence/relations/group_memberships.rb new file mode 100644 index 00000000..a0c27ecf --- /dev/null +++ b/lauth/lib/lauth/persistence/relations/group_memberships.rb @@ -0,0 +1,21 @@ +module Lauth + module Persistence + module Relations + class GroupMemberships < ROM::Relation[:sql] + schema(:aa_is_member_of_grp, infer: true, as: :group_memberships) do + # attribute :lastModifiedTime, Types::Time.default { Time.now } + attribute :lastModifiedBy, Types::String.default("root".freeze) + attribute :dlpsDeleted, Types::String.default("f".freeze) + + associations do + belongs_to :user, foreign_key: :userid + belongs_to :group, foreign_key: :user_grp + end + end + + struct_namespace Lauth + auto_struct true + end + end + end +end diff --git a/lauth/lib/lauth/persistence/relations/groups.rb b/lauth/lib/lauth/persistence/relations/groups.rb new file mode 100644 index 00000000..209ba8c1 --- /dev/null +++ b/lauth/lib/lauth/persistence/relations/groups.rb @@ -0,0 +1,22 @@ +module Lauth + module Persistence + module Relations + class Groups < ROM::Relation[:sql] + schema(:aa_user_grp, infer: true, as: :groups) do + # attribute :lastModifiedTime, Types::Time.default { Time.now } + # attribute :manager, Types::Integer.default(0) + attribute :lastModifiedBy, Types::String.default("root".freeze) + attribute :dlpsDeleted, Types::String.default("f".freeze) + + associations do + has_many :grants, foreign_key: :user_grp + has_many :group_memberships, foreign_key: :user_grp + end + end + + struct_namespace Lauth + auto_struct true + end + end + end +end diff --git a/lauth/lib/lauth/persistence/relations/institutions.rb b/lauth/lib/lauth/persistence/relations/institutions.rb index 7b5aa283..97ff1e69 100644 --- a/lauth/lib/lauth/persistence/relations/institutions.rb +++ b/lauth/lib/lauth/persistence/relations/institutions.rb @@ -5,7 +5,6 @@ class Institutions < ROM::Relation[:sql] schema(:aa_inst, infer: true, as: :institutions) do # attribute :lastModifiedTime, Types::Time.default { Time.now } attribute :lastModifiedBy, Types::String.default("root".freeze) - # attribute :dlpsExpiryTime, Types::Time.default { Time.now } attribute :dlpsDeleted, Types::String.default("f".freeze) associations do diff --git a/lauth/spec/repositories/grant_repo_spec.rb b/lauth/spec/repositories/grant_repo_spec.rb index b1935bdc..57a918e5 100644 --- a/lauth/spec/repositories/grant_repo_spec.rb +++ b/lauth/spec/repositories/grant_repo_spec.rb @@ -70,5 +70,41 @@ expect(grants).to be_empty end end + + context "with a member of an authorized group" do + let!(:collection) { Factory[:collection, :restricted_by_username] } + let!(:user) { Factory[:user, userid: "lauth-group-member"] } + let!(:group) { + Factory[:group] + relations.groups.last + } + let!(:membership) { Factory[:group_membership, user: user, group: group] } + let!(:grant) { Factory[:grant, :for_group, group: group, collection: collection] } + + it "finds that member's grant" do + grant_ids = repo.for_user_and_uri("lauth-group-member", "/restricted-by-username/") + .map(&:uniqueIdentifier) + + expect(grant_ids).to contain_exactly(grant.uniqueIdentifier) + end + + it "finds nothing for a nonmember" do + grants = repo.for_user_and_uri("lauth-denied", "/restricted-by-username/") + + expect(grants).to be_empty + end + + it "finds nothing for an empty user" do + grants = repo.for_user_and_uri("", "/restricted-by-username/") + + expect(grants).to be_empty + end + + it "finds nothing for a nil user" do + grants = repo.for_user_and_uri(nil, "/restricted-by-username/") + + expect(grants).to be_empty + end + end end end diff --git a/lauth/spec/requests/authorized_spec.rb b/lauth/spec/requests/authorized_spec.rb index 09f97b2e..a3632ba5 100644 --- a/lauth/spec/requests/authorized_spec.rb +++ b/lauth/spec/requests/authorized_spec.rb @@ -21,4 +21,22 @@ expect(body).to eq({determination: "allowed"}) end end + + context "with an authorized group" do + let!(:user) { Factory[:user, userid: "lauth-group-member"] } + let!(:collection) { Factory[:collection, :restricted_by_username] } + let!(:group) { + Factory[:group] + relations.groups.last + } + let!(:group_membership) { Factory[:group_membership, group: group, user: user] } + let!(:grant) { Factory[:grant, :for_group, group: group, collection: collection] } + + it do + get "/authorized", {user: "lauth-group-member", uri: "/restricted-by-username/"} + + body = JSON.parse(last_response.body, symbolize_names: true) + expect(body).to eq({determination: "allowed"}) + end + end end diff --git a/lauth/spec/support/factories/grant.rb b/lauth/spec/support/factories/grant.rb index ea4c51c5..cbf940b7 100644 --- a/lauth/spec/support/factories/grant.rb +++ b/lauth/spec/support/factories/grant.rb @@ -13,4 +13,8 @@ f.trait(:for_institution) do |t| t.association(:institution) end + + f.trait(:for_group) do |t| + t.association(:group) + end end diff --git a/lauth/spec/support/factories/group.rb b/lauth/spec/support/factories/group.rb new file mode 100644 index 00000000..d5c1ee75 --- /dev/null +++ b/lauth/spec/support/factories/group.rb @@ -0,0 +1,8 @@ +Factory.define(:group, struct_namespace: Lauth) do |f| + f.sequence(:uniqueIdentifier) { |n| n } + f.sequence(:commonName) { |n| "Group #{n}" } + f.manager 0 + f.lastModifiedTime Time.now + f.lastModifiedBy "root" + f.dlpsDeleted "f" +end diff --git a/lauth/spec/support/factories/group_membership.rb b/lauth/spec/support/factories/group_membership.rb new file mode 100644 index 00000000..e363ae27 --- /dev/null +++ b/lauth/spec/support/factories/group_membership.rb @@ -0,0 +1,7 @@ +Factory.define(:group_membership, struct_namespace: Lauth) do |f| + f.association(:user) + f.association(:group) + f.lastModifiedTime Time.now + f.lastModifiedBy "root" + f.dlpsDeleted "f" +end diff --git a/lauth/spec/support/requests.rb b/lauth/spec/support/requests.rb index 1b50989c..978c5d5c 100644 --- a/lauth/spec/support/requests.rb +++ b/lauth/spec/support/requests.rb @@ -6,7 +6,13 @@ let(:app) { Hanami.app } end +RSpec.shared_context "Lauth::Persistence" do + let(:rom) { Hanami.app["persistence.rom"] } + let(:relations) { rom.relations } +end + RSpec.configure do |config| config.include Rack::Test::Methods, type: :request config.include_context "Rack::Test", type: :request + config.include_context "Lauth::Persistence", type: :database end