scimitar 2.7.0 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -3
- data/app/models/scimitar/resources/base.rb +12 -9
- data/app/models/scimitar/resources/mixin.rb +403 -33
- data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +140 -10
- data/lib/scimitar/version.rb +2 -2
- data/spec/models/scimitar/resources/base_spec.rb +9 -1
- data/spec/models/scimitar/resources/mixin_spec.rb +683 -112
- data/spec/requests/active_record_backed_resources_controller_spec.rb +81 -5
- data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +108 -0
- metadata +4 -18
@@ -14,7 +14,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
14
14
|
ids = 3.times.map { SecureRandom.uuid }.sort()
|
15
15
|
|
16
16
|
@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)
|
17
|
-
@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)
|
17
|
+
@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: 'oldpassword')
|
18
18
|
@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)
|
19
19
|
|
20
20
|
@g1 = MockGroup.create!(display_name: 'Group 1')
|
@@ -345,6 +345,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
345
345
|
|
346
346
|
attributes = {
|
347
347
|
userName: '4',
|
348
|
+
password: 'correcthorsebatterystaple',
|
348
349
|
name: {
|
349
350
|
givenName: 'Given',
|
350
351
|
familyName: 'Family'
|
@@ -379,6 +380,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
379
380
|
expect(result['id']).to eql(new_mock.id.to_s)
|
380
381
|
expect(result['meta']['resourceType']).to eql('User')
|
381
382
|
expect(new_mock.username).to eql('4')
|
383
|
+
expect(new_mock.password).to eql('correcthorsebatterystaple')
|
382
384
|
expect(new_mock.first_name).to eql('Given')
|
383
385
|
expect(new_mock.last_name).to eql('Family')
|
384
386
|
expect(new_mock.home_email_address).to eql('home_4@test.com')
|
@@ -523,14 +525,14 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
523
525
|
context '#replace' do
|
524
526
|
shared_examples 'a replacer' do | force_upper_case: |
|
525
527
|
it 'which replaces all attributes in an instance' do
|
526
|
-
attributes = { userName: '4' }
|
528
|
+
attributes = { userName: '4' } # Minimum required by schema
|
527
529
|
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
528
530
|
|
529
531
|
# Prove that certain known pathways are called; can then unit test
|
530
532
|
# those if need be and be sure that this covers #replace actions.
|
531
533
|
#
|
532
534
|
expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
|
533
|
-
expect_any_instance_of(MockUsersController).to receive(:save!
|
535
|
+
expect_any_instance_of(MockUsersController).to receive(:save! ).once.and_call_original
|
534
536
|
expect {
|
535
537
|
put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
|
536
538
|
}.to_not change { MockUser.count }
|
@@ -543,12 +545,44 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
543
545
|
expect(result['id']).to eql(@u2.primary_key.to_s)
|
544
546
|
expect(result['meta']['resourceType']).to eql('User')
|
545
547
|
|
548
|
+
expect(result).to have_key('name')
|
549
|
+
expect(result).to_not have_key('password')
|
550
|
+
|
546
551
|
@u2.reload
|
547
552
|
|
548
553
|
expect(@u2.username).to eql('4')
|
549
554
|
expect(@u2.first_name).to be_nil
|
550
555
|
expect(@u2.last_name).to be_nil
|
551
556
|
expect(@u2.home_email_address).to be_nil
|
557
|
+
expect(@u2.password).to be_nil
|
558
|
+
end
|
559
|
+
|
560
|
+
it 'can replace passwords' do
|
561
|
+
attributes = { userName: '4', password: 'correcthorsebatterystaple' }
|
562
|
+
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
563
|
+
|
564
|
+
expect {
|
565
|
+
put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
|
566
|
+
}.to_not change { MockUser.count }
|
567
|
+
|
568
|
+
expect(response.status ).to eql(200)
|
569
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
570
|
+
|
571
|
+
result = JSON.parse(response.body)
|
572
|
+
|
573
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
574
|
+
expect(result['meta']['resourceType']).to eql('User')
|
575
|
+
|
576
|
+
expect(result).to have_key('name')
|
577
|
+
expect(result).to_not have_key('password')
|
578
|
+
|
579
|
+
@u2.reload
|
580
|
+
|
581
|
+
expect(@u2.username).to eql('4')
|
582
|
+
expect(@u2.first_name).to be_nil
|
583
|
+
expect(@u2.last_name).to be_nil
|
584
|
+
expect(@u2.home_email_address).to be_nil
|
585
|
+
expect(@u2.password).to eql('correcthorsebatterystaple')
|
552
586
|
end
|
553
587
|
end # "shared_examples 'a replacer' do | force_upper_case: |"
|
554
588
|
|
@@ -626,7 +660,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
626
660
|
|
627
661
|
context 'with a block' do
|
628
662
|
it 'invokes the block' do
|
629
|
-
attributes = { userName: '4' }
|
663
|
+
attributes = { userName: '4' } # Minimum required by schema
|
630
664
|
|
631
665
|
expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
|
632
666
|
expect {
|
@@ -681,7 +715,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
681
715
|
|
682
716
|
context '#update' do
|
683
717
|
shared_examples 'an updater' do | force_upper_case: |
|
684
|
-
it 'which patches
|
718
|
+
it 'which patches regular attributes' do
|
685
719
|
payload = {
|
686
720
|
Operations: [
|
687
721
|
{
|
@@ -717,6 +751,9 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
717
751
|
expect(result['id']).to eql(@u2.primary_key.to_s)
|
718
752
|
expect(result['meta']['resourceType']).to eql('User')
|
719
753
|
|
754
|
+
expect(result).to have_key('name')
|
755
|
+
expect(result).to_not have_key('password')
|
756
|
+
|
720
757
|
@u2.reload
|
721
758
|
|
722
759
|
expect(@u2.username).to eql('4')
|
@@ -724,6 +761,45 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
724
761
|
expect(@u2.last_name).to eql('Bar')
|
725
762
|
expect(@u2.home_email_address).to eql('home_2@test.com')
|
726
763
|
expect(@u2.work_email_address).to eql('work_4@test.com')
|
764
|
+
expect(@u2.password).to eql('oldpassword')
|
765
|
+
end
|
766
|
+
|
767
|
+
it 'which patches "returned: \'never\'" fields' do
|
768
|
+
payload = {
|
769
|
+
Operations: [
|
770
|
+
{
|
771
|
+
op: 'replace',
|
772
|
+
path: 'password',
|
773
|
+
value: 'correcthorsebatterystaple'
|
774
|
+
}
|
775
|
+
]
|
776
|
+
}
|
777
|
+
|
778
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
779
|
+
|
780
|
+
expect {
|
781
|
+
patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
|
782
|
+
}.to_not change { MockUser.count }
|
783
|
+
|
784
|
+
expect(response.status ).to eql(200)
|
785
|
+
expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
|
786
|
+
|
787
|
+
result = JSON.parse(response.body)
|
788
|
+
|
789
|
+
expect(result['id']).to eql(@u2.primary_key.to_s)
|
790
|
+
expect(result['meta']['resourceType']).to eql('User')
|
791
|
+
|
792
|
+
expect(result).to have_key('name')
|
793
|
+
expect(result).to_not have_key('password')
|
794
|
+
|
795
|
+
@u2.reload
|
796
|
+
|
797
|
+
expect(@u2.username).to eql('2')
|
798
|
+
expect(@u2.first_name).to eql('Foo')
|
799
|
+
expect(@u2.last_name).to eql('Bar')
|
800
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
801
|
+
expect(@u2.work_email_address).to be_nil
|
802
|
+
expect(@u2.password).to eql('correcthorsebatterystaple')
|
727
803
|
end
|
728
804
|
|
729
805
|
context 'which clears attributes' do
|
@@ -37,6 +37,114 @@ RSpec.describe Scimitar::Support::HashWithIndifferentCaseInsensitiveAccess do
|
|
37
37
|
expect(subject()).to_not have_key('bar')
|
38
38
|
end
|
39
39
|
end # "context 'where keys set as symbols' do"
|
40
|
+
|
41
|
+
context 'access and merging' do
|
42
|
+
before :each do
|
43
|
+
@original_subject = subject().to_h().dup()
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns keys as Strings' do
|
47
|
+
subject()[:foo] = 1
|
48
|
+
subject()[:BAR] = 2
|
49
|
+
|
50
|
+
expect(subject().keys).to match_array(@original_subject.keys + ['foo', 'BAR'])
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'retains original case of keys' do
|
54
|
+
subject()[:foo ] = 1
|
55
|
+
subject()['FoO'] = 40 # (first-time-set case preservation test in passing)
|
56
|
+
subject()[:BAR ] = 2
|
57
|
+
subject()['Baz'] = 3
|
58
|
+
|
59
|
+
expectation = @original_subject.merge({
|
60
|
+
'foo' => 40,
|
61
|
+
'BAR' => 2,
|
62
|
+
'Baz' => 3
|
63
|
+
})
|
64
|
+
|
65
|
+
expect(subject()).to eql(expectation)
|
66
|
+
end
|
67
|
+
|
68
|
+
it '#merge does not mutate the receiver and retains case of first-set keys' do
|
69
|
+
subject()[:foo] = 1
|
70
|
+
subject()[:BAR] = 2
|
71
|
+
|
72
|
+
pre_merge_subject = subject().dup()
|
73
|
+
|
74
|
+
result = subject().merge({:FOO => { 'onE' => 40 }, :Baz => 3})
|
75
|
+
expectation = @original_subject.merge({
|
76
|
+
'foo' => { 'onE' => 40 },
|
77
|
+
'BAR' => 2,
|
78
|
+
'Baz' => 3
|
79
|
+
})
|
80
|
+
|
81
|
+
expect(subject()).to eql(pre_merge_subject)
|
82
|
+
expect(result).to eql(expectation)
|
83
|
+
end
|
84
|
+
|
85
|
+
it '#merge! mutates the receiver retains case of first-set keys' do
|
86
|
+
subject()[:foo] = 1
|
87
|
+
subject()[:BAR] = 2
|
88
|
+
|
89
|
+
subject().merge!({:FOO => { 'onE' => 40 }, :Baz => 3})
|
90
|
+
|
91
|
+
expectation = @original_subject.merge({
|
92
|
+
'foo' => { 'onE' => 40 },
|
93
|
+
'BAR' => 2,
|
94
|
+
'Baz' => 3
|
95
|
+
})
|
96
|
+
|
97
|
+
expect(subject()).to eql(expectation)
|
98
|
+
end
|
99
|
+
|
100
|
+
it '#deep_merge does not mutate the receiver and retains nested key cases' do
|
101
|
+
subject()[:foo] = { :one => 10 }
|
102
|
+
subject()[:BAR] = 2
|
103
|
+
|
104
|
+
pre_merge_subject = subject().dup()
|
105
|
+
|
106
|
+
result = subject().deep_merge({:FOO => { 'ONE' => 40, :TWO => 20 }, :Baz => 3})
|
107
|
+
expectation = @original_subject.merge({
|
108
|
+
'foo' => { 'one' => 40, 'TWO' => 20 },
|
109
|
+
'BAR' => 2,
|
110
|
+
'Baz' => 3
|
111
|
+
})
|
112
|
+
|
113
|
+
expect(subject()).to eql(pre_merge_subject)
|
114
|
+
expect(result).to eql(expectation)
|
115
|
+
end
|
116
|
+
|
117
|
+
it '#deep_merge! mutates the receiver and retains nested key cases' do
|
118
|
+
subject()[:foo] = { :one => 10 }
|
119
|
+
subject()[:BAR] = 2
|
120
|
+
|
121
|
+
subject().deep_merge!({:FOO => { 'ONE' => 40, :TWO => 20 }, :Baz => 3})
|
122
|
+
|
123
|
+
expectation = @original_subject.merge({
|
124
|
+
'foo' => { 'one' => 40, 'TWO' => 20 },
|
125
|
+
'BAR' => 2,
|
126
|
+
'Baz' => 3
|
127
|
+
})
|
128
|
+
|
129
|
+
expect(subject()).to eql(expectation)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'retains indifferent behaviour after duplication' do
|
133
|
+
subject()[:foo] = { 'onE' => 40 }
|
134
|
+
subject()[:BAR] = 2
|
135
|
+
|
136
|
+
duplicate = subject().dup()
|
137
|
+
duplicate.merge!({ 'FOO' => true, 'baz' => 3 })
|
138
|
+
|
139
|
+
expectation = @original_subject.merge({
|
140
|
+
'foo' => true,
|
141
|
+
'BAR' => 2,
|
142
|
+
'baz' => 3
|
143
|
+
})
|
144
|
+
|
145
|
+
expect(duplicate.to_h).to eql(expectation.to_h)
|
146
|
+
end
|
147
|
+
end # "context 'access and merging' do"
|
40
148
|
end # "shared_examples 'an indifferent access, case insensitive Hash' do"
|
41
149
|
|
42
150
|
context 'when created directly' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scimitar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RIPA Global
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-03-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -25,20 +25,6 @@ dependencies:
|
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '7.0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: nokogiri
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - '='
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: 1.15.5
|
35
|
-
type: :runtime
|
36
|
-
prerelease: false
|
37
|
-
version_requirements: !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - '='
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
version: 1.15.5
|
42
28
|
- !ruby/object:Gem::Dependency
|
43
29
|
name: rake
|
44
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -265,7 +251,7 @@ metadata:
|
|
265
251
|
homepage_uri: https://www.ripaglobal.com/
|
266
252
|
source_code_uri: https://github.com/RIPAGlobal/scimitar/
|
267
253
|
bug_tracker_uri: https://github.com/RIPAGlobal/scimitar/issues/
|
268
|
-
changelog_uri: https://github.com/RIPAGlobal/scimitar/blob/
|
254
|
+
changelog_uri: https://github.com/RIPAGlobal/scimitar/blob/main/CHANGELOG.md
|
269
255
|
post_install_message:
|
270
256
|
rdoc_options: []
|
271
257
|
require_paths:
|
@@ -281,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
267
|
- !ruby/object:Gem::Version
|
282
268
|
version: '0'
|
283
269
|
requirements: []
|
284
|
-
rubygems_version: 3.5.
|
270
|
+
rubygems_version: 3.5.4
|
285
271
|
signing_key:
|
286
272
|
specification_version: 4
|
287
273
|
summary: SCIM v2 for Rails
|