scimitar 2.7.1 → 2.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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' } # Minimum required by schema
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! ).once.and_call_original
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' } # Minimum required by schema
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 specific attributes' do
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,145 @@ 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
+ context 'which' do
768
+ shared_examples 'it handles not-to-spec in-value Azure/Entra dotted attribute paths' do | operation |
769
+ it "and performs operation" do
770
+ payload = {
771
+ Operations: [
772
+ {
773
+ op: 'add',
774
+ value: {
775
+ 'name.givenName' => 'Foo!',
776
+ 'name.familyName' => 'Bar!',
777
+ 'name.formatted' => 'Foo! Bar!' # Unrecognised; should be ignored
778
+ },
779
+ },
780
+ ]
781
+ }
782
+
783
+ payload = spec_helper_hupcase(payload) if force_upper_case
784
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
785
+
786
+ expect(response.status ).to eql(200)
787
+ expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
788
+
789
+ @u2.reload
790
+ result = JSON.parse(response.body)
791
+
792
+ expect(@u2.first_name).to eql('Foo!')
793
+ expect(@u2.last_name ).to eql('Bar!')
794
+ end
795
+ end
796
+
797
+ it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'add'
798
+ it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'replace'
799
+
800
+ shared_examples 'it handles schema ID value keys without inline attributes' do | operation |
801
+ it "and performs operation" do
802
+ payload = {
803
+ Operations: [
804
+ {
805
+ op: operation,
806
+ value: {
807
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User': {
808
+ 'organization' => 'Foo Bar!',
809
+ 'department' => 'Bar Foo!'
810
+ },
811
+ },
812
+ },
813
+ ]
814
+ }
815
+
816
+ @u2.update!(organization: 'Old org')
817
+ payload = spec_helper_hupcase(payload) if force_upper_case
818
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
819
+
820
+ expect(response.status ).to eql(200)
821
+ expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
822
+
823
+ @u2.reload
824
+ result = JSON.parse(response.body)
825
+
826
+ expect(@u2.organization).to eql('Foo Bar!')
827
+ expect(@u2.department ).to eql('Bar Foo!')
828
+ end
829
+ end
830
+
831
+ it_behaves_like 'it handles schema ID value keys without inline attributes', 'add'
832
+ it_behaves_like 'it handles schema ID value keys without inline attributes', 'replace'
833
+
834
+ shared_examples 'it handles schema ID value keys with inline attributes' do
835
+ it "and performs operation" do
836
+ payload = {
837
+ Operations: [
838
+ {
839
+ op: 'add',
840
+ value: {
841
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization' => 'Foo Bar!',
842
+ 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department' => 'Bar Foo!'
843
+ },
844
+ },
845
+ ]
846
+ }
847
+
848
+ @u2.update!(organization: 'Old org')
849
+ payload = spec_helper_hupcase(payload) if force_upper_case
850
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
851
+
852
+ expect(response.status ).to eql(200)
853
+ expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
854
+
855
+ @u2.reload
856
+ result = JSON.parse(response.body)
857
+
858
+ expect(@u2.organization).to eql('Foo Bar!')
859
+ expect(@u2.department ).to eql('Bar Foo!')
860
+ end
861
+ end
862
+
863
+ it_behaves_like 'it handles schema ID value keys with inline attributes', 'add'
864
+ it_behaves_like 'it handles schema ID value keys with inline attributes', 'replace'
865
+ end
866
+
867
+ it 'which patches "returned: \'never\'" fields' do
868
+ payload = {
869
+ Operations: [
870
+ {
871
+ op: 'replace',
872
+ path: 'password',
873
+ value: 'correcthorsebatterystaple'
874
+ }
875
+ ]
876
+ }
877
+
878
+ payload = spec_helper_hupcase(payload) if force_upper_case
879
+
880
+ expect {
881
+ patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
882
+ }.to_not change { MockUser.count }
883
+
884
+ expect(response.status ).to eql(200)
885
+ expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
886
+
887
+ result = JSON.parse(response.body)
888
+
889
+ expect(result['id']).to eql(@u2.primary_key.to_s)
890
+ expect(result['meta']['resourceType']).to eql('User')
891
+
892
+ expect(result).to have_key('name')
893
+ expect(result).to_not have_key('password')
894
+
895
+ @u2.reload
896
+
897
+ expect(@u2.username).to eql('2')
898
+ expect(@u2.first_name).to eql('Foo')
899
+ expect(@u2.last_name).to eql('Bar')
900
+ expect(@u2.home_email_address).to eql('home_2@test.com')
901
+ expect(@u2.work_email_address).to be_nil
902
+ expect(@u2.password).to eql('correcthorsebatterystaple')
727
903
  end
728
904
 
729
905
  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.1
4
+ version: 2.7.3
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-01-16 00:00:00.000000000 Z
12
+ date: 2024-06-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails