diff --git a/app/interactors/authorize_request.rb b/app/interactors/authorize_request.rb index 925eae3..c9a77af 100644 --- a/app/interactors/authorize_request.rb +++ b/app/interactors/authorize_request.rb @@ -3,20 +3,12 @@ class AuthorizeRequest include FailOnError def call - authorize!(context.identity, context.request) - end - - private - - def authorize!(identity, cert_req) - cert_req.fqdns.each do |fqdn| + context.request.fqdns.each do |fqdn| domain = Domain.where(fqdn: fqdn).first - raise AuthError unless domain.present? && - (domain.owner == identity.subject || - (domain.group_delegation && - (domain.groups & identity.groups).any?)) + raise AuthError unless domain.present? + raise AuthError unless (domain.users_array & [ context.identity.subject ]).any? || + (domain.group_delegation && (domain.groups_array & context.identity.groups).any?) end nil end - end diff --git a/app/interactors/fail_on_error.rb b/app/interactors/fail_on_error.rb index 89249f9..d164847 100644 --- a/app/interactors/fail_on_error.rb +++ b/app/interactors/fail_on_error.rb @@ -4,6 +4,8 @@ module FailOnError included do around do |interactor| interactor.call + rescue Interactor::Failure => e + raise e rescue => e Rails.logger.error("Error in #{self.class.name}: #{e.class.name} - #{e.message}") context.fail!(error: e) diff --git a/app/interactors/refresh_domain.rb b/app/interactors/refresh_domain.rb index b0011d5..58ef8d1 100644 --- a/app/interactors/refresh_domain.rb +++ b/app/interactors/refresh_domain.rb @@ -3,9 +3,16 @@ class RefreshDomain def call domain_info = Services::DomainOwnershipService.new.get_domain_info(context.request.fqdn) - Domain.first_or_create(fqdn: context.request.fqdn).update!( - group_delegation: domain_info["ownerDelegatedRequestsToTeam"] - groups: domain_info["autoApprovedGroups"] + domain_record = Domain.first_or_create(fqdn: context.request.fqdn) + + if !domain_info || domain_info["isDeleted"] + domain_record.delete + return + end + + domain_record.update!( + group_delegation: domain_info["ownerDelegatedRequestsToTeam"], + groups: domain_info["autoApprovedGroups"], users: domain_info["autoApprovedServiceAccounts"] ) rescue => e diff --git a/app/lib/services/domain_ownership_service.rb b/app/lib/services/domain_ownership_service.rb index 3b94ea1..8dea08d 100644 --- a/app/lib/services/domain_ownership_service.rb +++ b/app/lib/services/domain_ownership_service.rb @@ -1,11 +1,9 @@ module Services class DomainOwnershipService - def initialize end def get_domain_info end - end end diff --git a/app/models/domain.rb b/app/models/domain.rb index cbeb688..72003bb 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -1,11 +1,11 @@ class Domain < ApplicationRecord - serialize :groups, :users, coder: YAML, type: Array - before_save :clean_users_groups - validates :fqdn, presence: true - def clean_users_groups - self.groups = groups.sort.uniq - self.users = users.sort.uniq + def groups_array + (groups || "").split(",").sort.uniq + end + + def users_array + (users || "").split(",").sort.uniq end end diff --git a/db/schema.rb b/db/schema.rb index 4e5f14d..e14708e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -13,7 +13,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_09_04_175652) do create_table "domains", force: :cascade do |t| t.string "fqdn", null: false - t.string "owner", null: false + t.text "users" t.text "groups" t.boolean "group_delegation", default: false t.datetime "created_at", null: false diff --git a/db/seeds.rb b/db/seeds.rb index 11fded0..4c0f4e6 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -10,5 +10,5 @@ # this seed is for development only if Rails.env.development? - Domain.first_or_create!(fqdn: "example.com", owner: "john.doe@example.com") + Domain.first_or_create!(fqdn: "example.com", users: "john.doe@example.com") end diff --git a/test/fixtures/domains.yml b/test/fixtures/domains.yml index a0ecfad..59e3aa0 100644 --- a/test/fixtures/domains.yml +++ b/test/fixtures/domains.yml @@ -1,18 +1,16 @@ owner_match: fqdn: example.com - owner: john.doe@example.com + users: john.doe@example.com group_delegation: false group_match: fqdn: example2.com - owner: some.other@example2.com + users: some.other@example2.com group_delegation: true - groups: - - "group1" + groups: group1 no_match: fqdn: example3.com - owner: some.other@example2.com + users: some.other@example2.com group_delegation: true - groups: - - "group3" + groups: group3 diff --git a/test/interactors/authorize_request_test.rb b/test/interactors/authorize_request_test.rb index 30cd3e6..0af0d3f 100644 --- a/test/interactors/authorize_request_test.rb +++ b/test/interactors/authorize_request_test.rb @@ -3,31 +3,35 @@ class AuthorizeRequestTest < ActiveSupport::TestCase def setup @domain = domains(:group_match) - @identity = Identity.new(subject: @domain.owner) + @identity = Identity.new(subject: @domain.users_array.first) @cr = CertIssueRequest.new(common_name: @domain.fqdn) @interactor = AuthorizeRequest end - test "successful call" do - request = CertIssueRequest.new(common_name: @domain.fqdn) - srv = Minitest::Mock.new - srv.expect :authorize!, nil, [ @identity, @cr ] - Services::DomainOwnershipService.stub :new, srv do - context = @interactor.call(identity: @identity, request: @cr) - assert context.success? - end + test ".call with matching owner" do + rslt = @interactor.call(identity: @identity, request: @cr) + assert rslt.success? end - test "unsuccessful call" do - request = CertIssueRequest.new(common_name: @domain.fqdn) - srv = Services::DomainOwnershipService.new - Services::DomainOwnershipService.stub :new, srv do - err = ->(_, _) { raise AuthError.new "no can do" } - srv.stub :authorize!, err do - context = @interactor.call(identity: @identity, request: @cr) - assert_not context.success? - assert_kind_of AuthError, context.error - end - end + test ".call with non-matching owner" do + @identity.subject = "different_owner@example.com" + rslt = @interactor.call(identity: @identity, request: @cr) + assert_not rslt.success? + assert_kind_of AuthError, rslt.error + end + + test ".call with matching group" do + @domain.update(users: "different_owner@example.com") + @identity.groups = @domain.groups_array + rslt = @interactor.call(identity: @identity, request: @cr) + assert rslt.success? + end + + test ".call with non-matching group" do + @domain.update(users: "different_owner@example.com") + @identity.groups = [ "different_group" ] + rslt = @interactor.call(identity: @identity, request: @cr) + assert_not rslt.success? + assert_kind_of AuthError, rslt.error end end diff --git a/test/lib/services/domain_ownership_service_test.rb b/test/lib/services/domain_ownership_service_test.rb index 4f5815a..a3c856f 100644 --- a/test/lib/services/domain_ownership_service_test.rb +++ b/test/lib/services/domain_ownership_service_test.rb @@ -1,35 +1,4 @@ require "test_helper" class DomainOwnershipServiceTest < ActiveSupport::TestCase - def setup - @domain = domains(:group_match) - @identity = Identity.new(subject: @domain.owner) - @cr = CertIssueRequest.new(common_name: @domain.fqdn) - @ds = Services::DomainOwnershipService.new - end - - test "#authorize! with matching owner" do - assert_nil(@ds.authorize!(@identity, @cr)) - end - - test "#authorize! with non-matching owner" do - @identity.subject = "different_owner@example.com" - assert_raises(AuthError) do - @ds.authorize!(@identity, @cr) - end - end - - test "#authorize! with matching group" do - @domain.update(owner: "different_owner@example.com") - @identity.groups = @domain.groups - assert_nil(@ds.authorize!(@identity, @cr)) - end - - test "#authorize! with non-matching group" do - @domain.update(owner: "different_owner@example.com") - @identity.groups = [ "different_group" ] - assert_raises(AuthError) do - @ds.authorize!(@identity, @cr) - end - end end diff --git a/test/models/domain_test.rb b/test/models/domain_test.rb index 059b142..7f4f215 100644 --- a/test/models/domain_test.rb +++ b/test/models/domain_test.rb @@ -5,7 +5,7 @@ class DomainTest < ActiveSupport::TestCase def setup @attributes = { fqdn: "example4.com", - owner: "john.doe@example.com" + users: "john.doe@example.com" } @domain = Domain.new(@attributes) end @@ -26,15 +26,13 @@ def setup assert_includes @domain.errors[:fqdn], "can't be blank" end - test "#valid? should require an owner" do - @domain.owner = nil - assert_not @domain.valid? - assert_includes @domain.errors[:owner], "can't be blank" + test "#groups_array should sort dedupe groups" do + @domain.groups = "two,two,one" + assert_equal [ "one", "two" ], @domain.groups_array end - test "before_save should sort and dedupe groups" do - @domain.groups = [ "two", "two", "one" ] - @domain.save - assert_equal [ "one", "two" ], @domain.groups + test "#users_array should sort dedupe users" do + @domain.users = "two,two,one" + assert_equal [ "one", "two" ], @domain.users_array end end