diff --git a/app/models/scimitar/resources/base.rb b/app/models/scimitar/resources/base.rb index 6d3f375..fae0959 100644 --- a/app/models/scimitar/resources/base.rb +++ b/app/models/scimitar/resources/base.rb @@ -139,23 +139,13 @@ def constantize_complex_types(hash) def as_json(options = {}) self.meta = Meta.new unless self.meta && self.meta.is_a?(Meta) - self.meta.resourceType = self.class.resource_type_id - - non_returnable_attributes = self.class - .schemas - .flat_map(&:scim_attributes) - .filter_map { |attribute| attribute.name if attribute.returned == 'never' } - - non_returnable_attributes << 'errors' - - original_hash = super(options).except(*non_returnable_attributes) + meta.resourceType = self.class.resource_type_id + original_hash = super(options).except('errors') original_hash.merge!('schemas' => self.class.schemas.map(&:id)) - self.class.extended_schemas.each do |extension_schema| extension_attributes = extension_schema.scim_attributes.map(&:name) original_hash.merge!(extension_schema.id => original_hash.extract!(*extension_attributes)) end - original_hash end diff --git a/spec/apps/dummy/app/models/mock_group.rb b/spec/apps/dummy/app/models/mock_group.rb index 3a178dd..66a3d27 100644 --- a/spec/apps/dummy/app/models/mock_group.rb +++ b/spec/apps/dummy/app/models/mock_group.rb @@ -4,7 +4,7 @@ class MockGroup < ActiveRecord::Base # TEST ATTRIBUTES - see db/migrate/20210308020313_create_mock_groups.rb etc. # =========================================================================== - READWRITE_ATTRS = %w{ + WRITE_ATTRS = %w{ id scim_uid display_name diff --git a/spec/apps/dummy/app/models/mock_user.rb b/spec/apps/dummy/app/models/mock_user.rb index 478ed1e..c5c6d05 100644 --- a/spec/apps/dummy/app/models/mock_user.rb +++ b/spec/apps/dummy/app/models/mock_user.rb @@ -6,11 +6,10 @@ class MockUser < ActiveRecord::Base # TEST ATTRIBUTES - see db/migrate/20210304014602_create_mock_users.rb etc. # =========================================================================== - READWRITE_ATTRS = %w{ + WRITE_ATTRS = %w{ primary_key scim_uid username - password first_name last_name work_email_address @@ -19,6 +18,7 @@ class MockUser < ActiveRecord::Base organization department mock_groups + password } has_and_belongs_to_many :mock_groups @@ -47,11 +47,11 @@ def self.scim_attributes_map id: :primary_key, externalId: :scim_uid, userName: :username, - password: :password, name: { givenName: :first_name, familyName: :last_name }, + password: :password, emails: [ { match: 'type', diff --git a/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb b/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb index 80c129b..926e1a4 100644 --- a/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +++ b/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb @@ -7,12 +7,12 @@ def change # t.text :scim_uid t.text :username - t.text :password t.text :first_name t.text :last_name t.text :work_email_address t.text :home_email_address t.text :work_phone_number + t.text :password # Support the custom extension schema - see configuration in # "spec/apps/dummy/config/initializers/scimitar.rb". diff --git a/spec/apps/dummy/db/schema.rb b/spec/apps/dummy/db/schema.rb index b20a616..1ae6f56 100644 --- a/spec/apps/dummy/db/schema.rb +++ b/spec/apps/dummy/db/schema.rb @@ -33,7 +33,6 @@ t.datetime "updated_at", null: false t.text "scim_uid" t.text "username" - t.text "password" t.text "first_name" t.text "last_name" t.text "work_email_address" @@ -41,6 +40,7 @@ t.text "work_phone_number" t.text "organization" t.text "department" + t.text "password" end add_foreign_key "mock_groups_users", "mock_groups" diff --git a/spec/models/scimitar/resources/base_spec.rb b/spec/models/scimitar/resources/base_spec.rb index 1d627b3..bf95f59 100644 --- a/spec/models/scimitar/resources/base_spec.rb +++ b/spec/models/scimitar/resources/base_spec.rb @@ -14,10 +14,7 @@ def self.scim_attributes ), Scimitar::Schema::Attribute.new( name: 'names', multiValued: true, complexType: Scimitar::ComplexTypes::Name, required: false - ), - Scimitar::Schema::Attribute.new( - name: 'privateName', complexType: Scimitar::ComplexTypes::Name, required: false, returned: false - ), + ) ] end end @@ -28,11 +25,10 @@ def self.scim_attributes context '#initialize' do it 'accepts nil for non-required attributes' do - resource = CustomResourse.new(name: nil, names: nil, privateName: nil) + resource = CustomResourse.new(name: nil, names: nil) expect(resource.name).to be_nil expect(resource.names).to be_nil - expect(resource.privateName).to be_nil end shared_examples 'an initializer' do | force_upper_case: | @@ -41,10 +37,6 @@ def self.scim_attributes name: { givenName: 'John', familyName: 'Smith' - }, - privateName: { - givenName: 'Alt John', - familyName: 'Alt Smith' } } @@ -54,9 +46,6 @@ def self.scim_attributes expect(resource.name.is_a?(Scimitar::ComplexTypes::Name)).to be(true) expect(resource.name.givenName).to eql('John') expect(resource.name.familyName).to eql('Smith') - expect(resource.privateName.is_a?(Scimitar::ComplexTypes::Name)).to be(true) - expect(resource.privateName.givenName).to eql('Alt John') - expect(resource.privateName.familyName).to eql('Alt Smith') end it 'which builds an array of nested resources' do @@ -119,38 +108,14 @@ def self.scim_attributes context '#as_json' do it 'renders the json with the resourceType' do resource = CustomResourse.new(name: { - givenName: 'John', + givenName: 'John', familyName: 'Smith' }) result = resource.as_json - - expect(result['schemas'] ).to eql(['custom-id']) - expect(result['meta']['resourceType']).to eql('CustomResourse') - expect(result['errors'] ).to be_nil - end - - it 'excludes attributes that are flagged as do-not-return' do - resource = CustomResourse.new( - name: { - givenName: 'John', - familyName: 'Smith' - }, - privateName: { - givenName: 'Alt John', - familyName: 'Alt Smith' - } - ) - - result = resource.as_json - - expect(result['schemas'] ).to eql(['custom-id']) + expect(result['schemas']).to eql(['custom-id']) expect(result['meta']['resourceType']).to eql('CustomResourse') - expect(result['errors'] ).to be_nil - expect(result['name'] ).to be_present - expect(result['name']['givenName'] ).to eql('John') - expect(result['name']['familyName'] ).to eql('Smith') - expect(result['privateName'] ).to be_present + expect(result['errors']).to be_nil end end # "context '#as_json' do" diff --git a/spec/models/scimitar/resources/mixin_spec.rb b/spec/models/scimitar/resources/mixin_spec.rb index c8a1231..e96c3d2 100644 --- a/spec/models/scimitar/resources/mixin_spec.rb +++ b/spec/models/scimitar/resources/mixin_spec.rb @@ -138,19 +138,19 @@ def self.scim_queryable_attributes context '#scim_mutable_attributes' do it 'self-compiles mutable attributes and exposes them as an instance method' do - readwrite_attrs = MockUser::READWRITE_ATTRS.map(&:to_sym) - readwrite_attrs.delete(:id) # Should never be offered as writable in SCIM + write_attrs = MockUser::WRITE_ATTRS.map(&:to_sym) + write_attrs.delete(:id) # Should never be offered as writable in SCIM result = MockUser.new.scim_mutable_attributes() - expect(result).to match_array(readwrite_attrs) + expect(result).to match_array(write_attrs) end it 'includes read-write dynamic list attributes' do - readwrite_attrs = MockGroup::READWRITE_ATTRS.map(&:to_sym) - readwrite_attrs.delete(:id) # Should never be offered as writable in SCIM + write_attrs = MockGroup::WRITE_ATTRS.map(&:to_sym) + write_attrs.delete(:id) # Should never be offered as writable in SCIM result = MockGroup.new.scim_mutable_attributes() - expect(result).to match_array(readwrite_attrs) + expect(result).to match_array(write_attrs) end end # "context '#scim_mutable_attributes' do" @@ -160,20 +160,20 @@ def self.scim_queryable_attributes context '#to_scim' do context 'with a UUID, renamed primary key column' do - it 'compiles instance attribute values into a SCIM representation, but omits do-not-return fields' do + it 'compiles instance attribute values into a SCIM representation' do uuid = SecureRandom.uuid instance = MockUser.new instance.primary_key = uuid instance.scim_uid = 'AA02984' instance.username = 'foo' - instance.password = 'correcthorsebatterystaple' instance.first_name = 'Foo' instance.last_name = 'Bar' instance.work_email_address = 'foo.bar@test.com' instance.home_email_address = nil instance.work_phone_number = '+642201234567' instance.organization = 'SOMEORG' + instance.password = 'top-secret' g1 = MockGroup.create!(display_name: 'Group 1') g2 = MockGroup.create!(display_name: 'Group 2') @@ -194,6 +194,7 @@ def self.scim_queryable_attributes 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567'}], 'id' => uuid, 'externalId' => 'AA02984', + 'password' => 'top-secret', 'groups' => [{'display'=>g1.display_name, 'value'=>g1.id.to_s}, {'display'=>g3.display_name, 'value'=>g3.id.to_s}], 'meta' => {'location'=>"https://test.com/mock_users/#{uuid}", 'resourceType'=>'User'}, 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User', 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'], @@ -405,7 +406,6 @@ def self.scim_timestamps_map it 'ignoring read-only lists' do hash = { 'userName' => 'foo', - 'password' => 'staplebatteryhorsecorrect', 'name' => {'givenName' => 'Foo', 'familyName' => 'Bar'}, 'active' => true, 'emails' => [{'type' => 'work', 'primary' => true, 'value' => 'foo.bar@test.com'}], @@ -430,7 +430,6 @@ def self.scim_timestamps_map expect(instance.scim_uid ).to eql('AA02984') expect(instance.username ).to eql('foo') - expect(instance.password ).to eql('staplebatteryhorsecorrect') expect(instance.first_name ).to eql('Foo') expect(instance.last_name ).to eql('Bar') expect(instance.work_email_address).to eql('foo.bar@test.com') diff --git a/spec/requests/active_record_backed_resources_controller_spec.rb b/spec/requests/active_record_backed_resources_controller_spec.rb index c05e135..c6b352d 100644 --- a/spec/requests/active_record_backed_resources_controller_spec.rb +++ b/spec/requests/active_record_backed_resources_controller_spec.rb @@ -14,7 +14,7 @@ ids = 3.times.map { SecureRandom.uuid }.sort() @u1 = MockUser.create!(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1) - @u2 = MockUser.create!(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2) + @u2 = MockUser.create!(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2, password: "secret") @u3 = MockUser.create!(primary_key: ids.shift(), username: '3', first_name: 'Foo', home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3) @g1 = MockGroup.create!(display_name: 'Group 1') @@ -345,6 +345,7 @@ attributes = { userName: '4', + password: 'New Password', name: { givenName: 'Given', familyName: 'Family' @@ -381,6 +382,7 @@ expect(new_mock.username).to eql('4') expect(new_mock.first_name).to eql('Given') expect(new_mock.last_name).to eql('Family') + expect(new_mock.password).to eql('New Password') expect(new_mock.home_email_address).to eql('home_4@test.com') expect(new_mock.work_email_address).to eql('work_4@test.com') end @@ -720,6 +722,7 @@ @u2.reload expect(@u2.username).to eql('4') + expect(@u2.password).to eql('secret') expect(@u2.first_name).to eql('Foo') expect(@u2.last_name).to eql('Bar') expect(@u2.home_email_address).to eql('home_2@test.com')