powerhome-scimitar 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +708 -0
  4. data/Rakefile +16 -0
  5. data/app/controllers/scimitar/active_record_backed_resources_controller.rb +257 -0
  6. data/app/controllers/scimitar/application_controller.rb +157 -0
  7. data/app/controllers/scimitar/resource_types_controller.rb +28 -0
  8. data/app/controllers/scimitar/resources_controller.rb +203 -0
  9. data/app/controllers/scimitar/schemas_controller.rb +21 -0
  10. data/app/controllers/scimitar/service_provider_configurations_controller.rb +8 -0
  11. data/app/models/scimitar/authentication_error.rb +9 -0
  12. data/app/models/scimitar/authentication_scheme.rb +18 -0
  13. data/app/models/scimitar/bulk.rb +8 -0
  14. data/app/models/scimitar/complex_types/address.rb +12 -0
  15. data/app/models/scimitar/complex_types/base.rb +83 -0
  16. data/app/models/scimitar/complex_types/email.rb +12 -0
  17. data/app/models/scimitar/complex_types/entitlement.rb +12 -0
  18. data/app/models/scimitar/complex_types/ims.rb +12 -0
  19. data/app/models/scimitar/complex_types/name.rb +12 -0
  20. data/app/models/scimitar/complex_types/phone_number.rb +12 -0
  21. data/app/models/scimitar/complex_types/photo.rb +12 -0
  22. data/app/models/scimitar/complex_types/reference_group.rb +12 -0
  23. data/app/models/scimitar/complex_types/reference_member.rb +12 -0
  24. data/app/models/scimitar/complex_types/role.rb +12 -0
  25. data/app/models/scimitar/complex_types/x509_certificate.rb +12 -0
  26. data/app/models/scimitar/engine_configuration.rb +32 -0
  27. data/app/models/scimitar/error_response.rb +32 -0
  28. data/app/models/scimitar/errors.rb +14 -0
  29. data/app/models/scimitar/filter.rb +11 -0
  30. data/app/models/scimitar/filter_error.rb +22 -0
  31. data/app/models/scimitar/invalid_syntax_error.rb +9 -0
  32. data/app/models/scimitar/lists/count.rb +64 -0
  33. data/app/models/scimitar/lists/query_parser.rb +745 -0
  34. data/app/models/scimitar/meta.rb +7 -0
  35. data/app/models/scimitar/not_found_error.rb +10 -0
  36. data/app/models/scimitar/resource_invalid_error.rb +9 -0
  37. data/app/models/scimitar/resource_type.rb +29 -0
  38. data/app/models/scimitar/resources/base.rb +190 -0
  39. data/app/models/scimitar/resources/group.rb +13 -0
  40. data/app/models/scimitar/resources/mixin.rb +1524 -0
  41. data/app/models/scimitar/resources/user.rb +13 -0
  42. data/app/models/scimitar/schema/address.rb +25 -0
  43. data/app/models/scimitar/schema/attribute.rb +132 -0
  44. data/app/models/scimitar/schema/base.rb +90 -0
  45. data/app/models/scimitar/schema/derived_attributes.rb +24 -0
  46. data/app/models/scimitar/schema/email.rb +10 -0
  47. data/app/models/scimitar/schema/entitlement.rb +10 -0
  48. data/app/models/scimitar/schema/group.rb +27 -0
  49. data/app/models/scimitar/schema/ims.rb +10 -0
  50. data/app/models/scimitar/schema/name.rb +20 -0
  51. data/app/models/scimitar/schema/phone_number.rb +10 -0
  52. data/app/models/scimitar/schema/photo.rb +10 -0
  53. data/app/models/scimitar/schema/reference_group.rb +23 -0
  54. data/app/models/scimitar/schema/reference_member.rb +21 -0
  55. data/app/models/scimitar/schema/role.rb +10 -0
  56. data/app/models/scimitar/schema/user.rb +52 -0
  57. data/app/models/scimitar/schema/vdtp.rb +18 -0
  58. data/app/models/scimitar/schema/x509_certificate.rb +22 -0
  59. data/app/models/scimitar/service_provider_configuration.rb +60 -0
  60. data/app/models/scimitar/supportable.rb +14 -0
  61. data/app/views/layouts/scimitar/application.html.erb +14 -0
  62. data/config/initializers/scimitar.rb +111 -0
  63. data/config/routes.rb +6 -0
  64. data/lib/scimitar/engine.rb +63 -0
  65. data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +216 -0
  66. data/lib/scimitar/support/utilities.rb +51 -0
  67. data/lib/scimitar/version.rb +13 -0
  68. data/lib/scimitar.rb +29 -0
  69. data/spec/apps/dummy/app/controllers/custom_create_mock_users_controller.rb +25 -0
  70. data/spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb +24 -0
  71. data/spec/apps/dummy/app/controllers/custom_replace_mock_users_controller.rb +25 -0
  72. data/spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb +30 -0
  73. data/spec/apps/dummy/app/controllers/custom_save_mock_users_controller.rb +24 -0
  74. data/spec/apps/dummy/app/controllers/custom_update_mock_users_controller.rb +25 -0
  75. data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +13 -0
  76. data/spec/apps/dummy/app/controllers/mock_users_controller.rb +13 -0
  77. data/spec/apps/dummy/app/models/mock_group.rb +83 -0
  78. data/spec/apps/dummy/app/models/mock_user.rb +132 -0
  79. data/spec/apps/dummy/config/application.rb +18 -0
  80. data/spec/apps/dummy/config/boot.rb +2 -0
  81. data/spec/apps/dummy/config/environment.rb +2 -0
  82. data/spec/apps/dummy/config/environments/test.rb +38 -0
  83. data/spec/apps/dummy/config/initializers/cookies_serializer.rb +3 -0
  84. data/spec/apps/dummy/config/initializers/scimitar.rb +61 -0
  85. data/spec/apps/dummy/config/initializers/session_store.rb +3 -0
  86. data/spec/apps/dummy/config/routes.rb +45 -0
  87. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +24 -0
  88. data/spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb +10 -0
  89. data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +13 -0
  90. data/spec/apps/dummy/db/schema.rb +48 -0
  91. data/spec/controllers/scimitar/application_controller_spec.rb +296 -0
  92. data/spec/controllers/scimitar/resource_types_controller_spec.rb +94 -0
  93. data/spec/controllers/scimitar/resources_controller_spec.rb +247 -0
  94. data/spec/controllers/scimitar/schemas_controller_spec.rb +83 -0
  95. data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +22 -0
  96. data/spec/models/scimitar/complex_types/address_spec.rb +18 -0
  97. data/spec/models/scimitar/complex_types/email_spec.rb +21 -0
  98. data/spec/models/scimitar/lists/count_spec.rb +147 -0
  99. data/spec/models/scimitar/lists/query_parser_spec.rb +830 -0
  100. data/spec/models/scimitar/resource_type_spec.rb +21 -0
  101. data/spec/models/scimitar/resources/base_spec.rb +485 -0
  102. data/spec/models/scimitar/resources/base_validation_spec.rb +86 -0
  103. data/spec/models/scimitar/resources/mixin_spec.rb +3562 -0
  104. data/spec/models/scimitar/resources/user_spec.rb +68 -0
  105. data/spec/models/scimitar/schema/attribute_spec.rb +99 -0
  106. data/spec/models/scimitar/schema/base_spec.rb +64 -0
  107. data/spec/models/scimitar/schema/group_spec.rb +87 -0
  108. data/spec/models/scimitar/schema/user_spec.rb +720 -0
  109. data/spec/requests/active_record_backed_resources_controller_spec.rb +1354 -0
  110. data/spec/requests/application_controller_spec.rb +61 -0
  111. data/spec/requests/controller_configuration_spec.rb +17 -0
  112. data/spec/requests/engine_spec.rb +45 -0
  113. data/spec/spec_helper.rb +101 -0
  114. data/spec/spec_helper_spec.rb +30 -0
  115. data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +169 -0
  116. metadata +321 -0
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Scimitar::Resources::User do
4
+ context '#name' do
5
+ it 'allows a setter for a valid name' do
6
+ user = described_class.new(name: Scimitar::ComplexTypes::Name.new(
7
+ familyName: 'Smith',
8
+ givenName: 'John',
9
+ formatted: 'John Smith'
10
+ ))
11
+
12
+ expect(user.name.familyName).to eql('Smith')
13
+ expect(user.name.givenName).to eql('John')
14
+ expect(user.name.formatted).to eql('John Smith')
15
+ expect(user.as_json['name']['errors']).to be_nil
16
+ end
17
+
18
+ it 'treats attributes as case-insensitive' do
19
+ user = described_class.new(name: Scimitar::ComplexTypes::Name.new(
20
+ FAMILYNAME: 'Smith',
21
+ GIVENNAME: 'John',
22
+ FORMATTED: 'John Smith'
23
+ ))
24
+
25
+ expect(user.name.familyName).to eql('Smith')
26
+ expect(user.name.givenName).to eql('John')
27
+ expect(user.name.formatted).to eql('John Smith')
28
+ expect(user.as_json['name']['errors']).to be_nil
29
+ end
30
+
31
+ it 'validates that the provided name matches the name schema' do
32
+ user = described_class.new(name: Scimitar::ComplexTypes::Email.new(
33
+ value: 'john@smoth.com',
34
+ primary: true
35
+ ))
36
+
37
+ expect(user.valid?).to be(false)
38
+ end
39
+ end
40
+
41
+ context '#add_errors_from_hash' do
42
+ let(:user) { described_class.new }
43
+
44
+ it 'adds the error when the value is a string' do
45
+ user.add_errors_from_hash(errors_hash: {key: 'some error'})
46
+ expect(user.errors.messages.to_h).to eql({key: ['some error']})
47
+ expect(user.errors.full_messages).to eql(['Key some error'])
48
+ end
49
+
50
+ it 'adds the error when the value is an array' do
51
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']})
52
+ expect(user.errors.messages.to_h).to eql({key: ['error1', 'error2']})
53
+ expect(user.errors.full_messages).to eql(['Key error1', 'Key error2'])
54
+ end
55
+
56
+ it 'adds the error with prefix when the value is a string' do
57
+ user.add_errors_from_hash(errors_hash: {key: 'some error'}, prefix: :pre)
58
+ expect(user.errors.messages.to_h).to eql({:'pre.key' => ['some error']})
59
+ expect(user.errors.full_messages).to eql(['Pre key some error'])
60
+ end
61
+
62
+ it 'adds the error wity prefix when the value is an array' do
63
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']}, prefix: :pre)
64
+ expect(user.errors.messages.to_h).to eql({:'pre.key' => ['error1', 'error2']})
65
+ expect(user.errors.full_messages).to eql(['Pre key error1', 'Pre key error2'])
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Scimitar::Schema::Attribute do
4
+ context '#initialize' do
5
+ it 'sets the default properties' do
6
+ attribute = described_class.new
7
+ expect(attribute.multiValued).to be(false)
8
+ expect(attribute.required).to be(false)
9
+ expect(attribute.caseExact).to be(false)
10
+ expect(attribute.mutability).to eql('readWrite')
11
+ expect(attribute.uniqueness).to eql('none')
12
+ expect(attribute.returned).to eql('default')
13
+ end
14
+ it 'lets override the default properties' do
15
+ attribute = described_class.new(multiValued: true)
16
+ expect(attribute.multiValued).to be(true)
17
+ end
18
+
19
+ it 'transforms complexTypes into subAttributes' do
20
+ name = described_class.new(name: 'name', complexType: Scimitar::ComplexTypes::Name)
21
+ expect(name.type).to eql('complex')
22
+ expect(name.subAttributes).to eql(Scimitar::Schema::Name.scim_attributes)
23
+ end
24
+ end
25
+
26
+ context '#valid?' do
27
+ it 'is invalid if attribute is required but value is blank' do
28
+ attribute = described_class.new(name: 'userName', type: 'string', required: true)
29
+ expect(attribute.valid?(nil)).to be(false)
30
+ expect(attribute.errors.messages.to_h).to eql({userName: ['is required']})
31
+ end
32
+
33
+ it 'is valid if attribute is not required and value is blank' do
34
+ attribute = described_class.new(name: 'userName', type: 'string', required: false)
35
+ expect(attribute.valid?(nil)).to be(true)
36
+ expect(attribute.errors.messages.to_h).to eql({})
37
+ end
38
+
39
+ it 'is valid if type is string and given value is string' do
40
+ expect(described_class.new(name: 'name', type: 'string').valid?('something')).to be(true)
41
+ end
42
+
43
+ it 'is invalid if type is string and given value is not string' do
44
+ attribute = described_class.new(name: 'userName', type: 'string')
45
+ expect(attribute.valid?(10)).to be(false)
46
+ expect(attribute.errors.messages.to_h).to eql({userName: ['has the wrong type. It has to be a(n) string.']})
47
+ end
48
+
49
+ it 'is valid if multi-valued and type is string and given value is an array of strings' do
50
+ attribute = described_class.new(name: 'scopes', multiValued: true, type: 'string')
51
+ expect(attribute.valid?(['something', 'something else'])).to be(true)
52
+ end
53
+
54
+ it 'is valid if multi-valued and type is string and given value is an empty array' do
55
+ attribute = described_class.new(name: 'scopes', multiValued: true, type: 'string')
56
+ expect(attribute.valid?([])).to be(true)
57
+ end
58
+
59
+ it 'is invalid if multi-valued and type is string and given value is not an array' do
60
+ attribute = described_class.new(name: 'scopes', multiValued: true, type: 'string')
61
+ expect(attribute.valid?('something')).to be(false)
62
+ expect(attribute.errors.messages.to_h).to eql({scopes: ['or one of its elements has the wrong type. It has to be an array of strings.']})
63
+ end
64
+
65
+ it 'is invalid if multi-valued and type is string and given value is an array containing another type' do
66
+ attribute = described_class.new(name: 'scopes', multiValued: true, type: 'string')
67
+ expect(attribute.valid?(['something', 123])).to be(false)
68
+ expect(attribute.errors.messages.to_h).to eql({scopes: ['or one of its elements has the wrong type. It has to be an array of strings.']})
69
+ end
70
+
71
+ it 'is valid if type is boolean and given value is boolean' do
72
+ expect(described_class.new(name: 'name', type: 'boolean').valid?(false)).to be(true)
73
+ expect(described_class.new(name: 'name', type: 'boolean').valid?(true)).to be(true)
74
+ end
75
+
76
+ it 'is valid if type is complex and the schema is same' do
77
+ expect(described_class.new(name: 'name', complexType: Scimitar::ComplexTypes::Name).valid?(Scimitar::ComplexTypes::Name.new(givenName: 'a', familyName: 'b'))).to be(true)
78
+ end
79
+
80
+ it 'is valid if type is integer and given value is integer (duh)' do
81
+ expect(described_class.new(name: 'quantity', type: 'integer').valid?(123)).to be(true)
82
+ end
83
+
84
+ it 'is not valid if type is integer and given value is not an integer' do
85
+ expect(described_class.new(name: 'quantity', type: 'integer').valid?(123.3)).to be(false)
86
+ end
87
+
88
+ it 'is valid if type is dateTime and given value is an ISO8601 date time' do
89
+ expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('2018-07-26T11:59:43-06:00')).to be(true)
90
+ end
91
+
92
+ it 'is not valid if type is dateTime and given value is a valid date but not in ISO8601 format' do
93
+ expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('2018-07-26')).to be(false)
94
+ end
95
+ it 'is not valid if type is dateTime and given value is not a valid date' do
96
+ expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('gaga')).to be(false)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Scimitar::Schema::Base do
4
+ context '#as_json' do
5
+ it 'converts the scim_attributes to attributes' do
6
+ attribute = Scimitar::Schema::Attribute.new(name: 'nickName')
7
+ schema = described_class.new(scim_attributes: [attribute])
8
+ expect(schema.as_json['attributes']).to eql([attribute.as_json])
9
+ expect(schema.as_json['scim_attributes']).to be_nil
10
+ end
11
+ end
12
+
13
+ context '#initialize' do
14
+ it 'creates "meta"' do
15
+ schema = described_class.new
16
+ expect(schema.meta.resourceType).to eql('Schema')
17
+ end
18
+ end
19
+
20
+ context '.find_attribute' do
21
+ it 'finds at top level' do
22
+ found = Scimitar::Schema::User.find_attribute('password')
23
+ expect(found).to be_present
24
+ expect(found.name).to eql('password')
25
+ expect(found.mutability).to eql('writeOnly')
26
+ end
27
+
28
+ it 'finds nested' do
29
+ found = Scimitar::Schema::User.find_attribute('name', 'givenName')
30
+ expect(found).to be_present
31
+ expect(found.name).to eql('givenName')
32
+ expect(found.mutability).to eql('readWrite')
33
+ end
34
+
35
+ it 'finds in multi-valued types, without index' do
36
+ found = Scimitar::Schema::User.find_attribute('groups', 'type')
37
+ expect(found).to be_present
38
+ expect(found.name).to eql('type')
39
+ expect(found.mutability).to eql('readOnly')
40
+ end
41
+
42
+ it 'finds in multi-valued types, ignoring index' do
43
+ found = Scimitar::Schema::User.find_attribute('groups', 42, 'type')
44
+ expect(found).to be_present
45
+ expect(found.name).to eql('type')
46
+ expect(found.mutability).to eql('readOnly')
47
+ end
48
+
49
+ it 'does not find bad names' do
50
+ found = Scimitar::Schema::User.find_attribute('foo')
51
+ expect(found).to be_nil
52
+ end
53
+
54
+ it 'does not find nested bad names' do
55
+ found = Scimitar::Schema::User.find_attribute('name', 'foo')
56
+ expect(found).to be_nil
57
+ end
58
+
59
+ it 'handles attempting to read sub-attributes when there are none' do
60
+ found = Scimitar::Schema::User.find_attribute('password', 'foo')
61
+ expect(found).to be_nil
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Scimitar::Schema::Group do
4
+ let(:expected_attributes) {
5
+ <<-EOJ
6
+ [
7
+ {
8
+ "multiValued": false,
9
+ "required": true,
10
+ "caseExact": false,
11
+ "mutability": "readWrite",
12
+ "uniqueness": "none",
13
+ "returned": "default",
14
+ "name": "displayName",
15
+ "type": "string"
16
+ },
17
+ {
18
+ "multiValued": true,
19
+ "required": false,
20
+ "caseExact": false,
21
+ "mutability": "readWrite",
22
+ "uniqueness": "none",
23
+ "returned": "default",
24
+ "name": "members",
25
+ "type": "complex",
26
+ "subAttributes": [
27
+ {
28
+ "multiValued": false,
29
+ "required": true,
30
+ "caseExact": false,
31
+ "mutability": "immutable",
32
+ "uniqueness": "none",
33
+ "returned": "default",
34
+ "name": "value",
35
+ "type": "string"
36
+ },
37
+ {
38
+ "multiValued": false,
39
+ "required": false,
40
+ "caseExact": false,
41
+ "mutability": "immutable",
42
+ "uniqueness": "none",
43
+ "returned": "default",
44
+ "name": "type",
45
+ "type": "string"
46
+ },
47
+ {
48
+ "multiValued": false,
49
+ "required": false,
50
+ "caseExact": false,
51
+ "mutability": "immutable",
52
+ "uniqueness": "none",
53
+ "returned": "default",
54
+ "name": "display",
55
+ "type": "string"
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ EOJ
61
+ }
62
+
63
+ let(:expected_full_schema) {
64
+ <<-EOJ
65
+ {
66
+ "name": "Group",
67
+ "id": "urn:ietf:params:scim:schemas:core:2.0:Group",
68
+ "description": "Represents a Group",
69
+ "meta": {
70
+ "resourceType": "Schema",
71
+ "location": "/Schemas?name=urn%3Aietf%3Aparams%3Ascim%3Aschemas%3Acore%3A2.0%3AGroup"
72
+ },
73
+ "attributes": #{expected_attributes()}
74
+ }
75
+ EOJ
76
+ }
77
+
78
+ it 'returns Group schema as JSON' do
79
+ actual_full_schema = Scimitar::Schema::Group.new
80
+ expect(JSON.parse(actual_full_schema.to_json)).to eql(JSON.parse(expected_full_schema()))
81
+ end
82
+
83
+ it 'returns the schema attributes as JSON' do
84
+ actual_attributes = Scimitar::Schema::Group.scim_attributes
85
+ expect(JSON.parse(actual_attributes.to_json)).to eql(JSON.parse(expected_attributes()))
86
+ end
87
+ end