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,13 @@
1
+ module Scimitar
2
+ module Resources
3
+ class User < Base
4
+
5
+ set_schema Schema::User
6
+
7
+ def self.endpoint
8
+ '/Users'
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Address complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Address
7
+ #
8
+ class Address < Base
9
+
10
+ def self.scim_attributes
11
+ @scim_attributes ||= [
12
+ Attribute.new(name: 'type', type: 'string'),
13
+ Attribute.new(name: 'primary', type: 'boolean'),
14
+ Attribute.new(name: 'formatted', type: 'string'),
15
+ Attribute.new(name: 'streetAddress', type: 'string'),
16
+ Attribute.new(name: 'locality', type: 'string'),
17
+ Attribute.new(name: 'region', type: 'string'),
18
+ Attribute.new(name: 'postalCode', type: 'string'),
19
+ Attribute.new(name: 'country', type: 'string'),
20
+ ]
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,132 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents an attribute of a SCIM resource that is declared in its
5
+ # schema.
6
+ #
7
+ # Attributes can be simple or complex. A complex attribute needs to have
8
+ # its own schema that is passed to the initialize method when the attribute
9
+ # is instantiated.
10
+ #
11
+ # Examples:
12
+ #
13
+ # Attribute.new(name: 'userName', type: 'string', uniqueness: 'server')
14
+ # Attribute.new(name: 'name', complexType: Scimitar::ComplexTypes::Name)
15
+ #
16
+ class Attribute
17
+ include ActiveModel::Model
18
+ include Scimitar::Errors
19
+
20
+ attr_accessor :name,
21
+ :type,
22
+ :multiValued,
23
+ :required,
24
+ :caseExact,
25
+ :mutability,
26
+ :returned,
27
+ :uniqueness,
28
+ :subAttributes,
29
+ :complexType,
30
+ :canonicalValues
31
+
32
+ # +options+:: Hash of values to be used for instantiating the attribute
33
+ # object. Some of the instance variables of the objects will
34
+ # have default values if this hash does not contain anything
35
+ # for them.
36
+ #
37
+ def initialize(options = {})
38
+ defaults = {
39
+ multiValued: false,
40
+ required: false,
41
+ caseExact: false,
42
+ mutability: 'readWrite',
43
+ uniqueness: 'none',
44
+ returned: 'default',
45
+ canonicalValues: []
46
+ }
47
+
48
+ if options[:complexType]
49
+ defaults.merge!(type: 'complex', subAttributes: options[:complexType].schema.scim_attributes)
50
+ end
51
+
52
+ super(defaults.merge(options || {}))
53
+ end
54
+
55
+ # Validates a value against this attribute object. For simple attributes,
56
+ # it checks if blank is valid or not and if the type matches. For complex
57
+ # attributes, it delegates it to the valid? method of the complex type
58
+ # schema.
59
+ #
60
+ # If the value is not valid, validation message(s) are added to the
61
+ # #errors attribute of this object.
62
+ #
63
+ # +value+:: Value to check.
64
+ #
65
+ # Returns +true+ if value is valid for this attribute, else +false+.
66
+ #
67
+ def valid?(value)
68
+ return valid_blank? if value.blank? && !value.is_a?(FalseClass)
69
+
70
+ if type == 'complex'
71
+ return all_valid?(complexType, value) if multiValued
72
+ valid_complex_type?(value)
73
+ else
74
+ valid_simple_type?(value)
75
+ end
76
+ end
77
+
78
+ def valid_blank?
79
+ return true unless self.required
80
+ errors.add(self.name, 'is required')
81
+ false
82
+ end
83
+
84
+ def valid_complex_type?(value)
85
+ if !value.class.respond_to?(:schema) || value.class.schema != complexType.schema
86
+ errors.add(self.name, 'has to follow the complexType format.')
87
+ return false
88
+ end
89
+ value.class.schema.valid?(value)
90
+ return true if value.errors.empty?
91
+ add_errors_from_hash(errors_hash: value.errors.to_hash, prefix: self.name)
92
+ false
93
+ end
94
+
95
+ def valid_simple_type?(value)
96
+ if multiValued
97
+ valid = value.is_a?(Array) && value.all? { |v| simple_type?(v) }
98
+ errors.add(self.name, "or one of its elements has the wrong type. It has to be an array of #{self.type}s.") unless valid
99
+ else
100
+ valid = simple_type?(value)
101
+ errors.add(self.name, "has the wrong type. It has to be a(n) #{self.type}.") unless valid
102
+ end
103
+ valid
104
+ end
105
+
106
+ def simple_type?(value)
107
+ (type == 'string' && value.is_a?(String)) ||
108
+ (type == 'boolean' && (value.is_a?(TrueClass) || value.is_a?(FalseClass))) ||
109
+ (type == 'integer' && (value.is_a?(Integer))) ||
110
+ (type == 'dateTime' && valid_date_time?(value))
111
+ end
112
+
113
+ def valid_date_time?(value)
114
+ !!Time.iso8601(value)
115
+ rescue ArgumentError
116
+ false
117
+ end
118
+
119
+ def all_valid?(complex_type, value)
120
+ validations = value.map {|value_in_array| valid_complex_type?(value_in_array)}
121
+ validations.all?
122
+ end
123
+
124
+ def as_json(options = {})
125
+ options[:except] ||= ['complexType']
126
+ options[:except] << 'canonicalValues' if canonicalValues.empty?
127
+ super.except(options)
128
+ end
129
+
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,90 @@
1
+ module Scimitar
2
+ module Schema
3
+ # The base class that each schema class must inherit from.
4
+ # These classes represent the schema of a SCIM resource or a complex type that could be used in a resource.
5
+ class Base
6
+ include ActiveModel::Model
7
+ attr_accessor :id, :name, :description, :scim_attributes, :meta
8
+
9
+ def initialize(options = {})
10
+ super
11
+ @meta = Meta.new(resourceType: 'Schema')
12
+ end
13
+
14
+ # Converts the schema to its json representation that will be returned by /SCHEMAS end-point of a SCIM service provider.
15
+ def as_json(options = {})
16
+ @meta.location ||= Scimitar::Engine.routes.url_helpers.scim_schemas_path(name: id)
17
+ original = super
18
+ original.merge('attributes' => original.delete('scim_attributes'))
19
+ end
20
+
21
+ # Validates the resource against specific validations of each attribute,
22
+ # for example if the type of the attribute matches the one defined in the
23
+ # schema.
24
+ #
25
+ # +resource+:: A resource object that uses this schema.
26
+ #
27
+ def self.valid?(resource)
28
+ cloned_scim_attributes.each do |scim_attribute|
29
+ unless scim_attribute.valid?(resource.send(scim_attribute.name))
30
+ resource.add_errors_from_hash(errors_hash: scim_attribute.errors.to_hash)
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.cloned_scim_attributes
36
+ scim_attributes.map { |scim_attribute| scim_attribute.clone }
37
+ end
38
+
39
+ # Find a given attribute this schema, travelling down a path to any
40
+ # sub-attributes within. Given that callers might be dealing with paths
41
+ # into actual SCIM data, array indices for multi-value attributes are
42
+ # allowed (as integers) and simply skipped - only the names are of
43
+ # interest.
44
+ #
45
+ # This is typically used to access attribute properties such as intended
46
+ # mutability ('readOnly', 'readWrite', 'immutable', 'writeOnly').
47
+ #
48
+ # Returns the found Scimitar::Schema::Attribute or "nil".
49
+ #
50
+ # *path:: One or more attribute names as Strings, or Integer indices.
51
+ #
52
+ # For example, in a User schema, passing "name", "givenName" would find
53
+ # the "givenName" attribute. Passing "emails", 0, "value" would find the
54
+ # schema attribute for "value" under "emails", ignoring the array index
55
+ # (since the schema is identical for each item in an array of values).
56
+ #
57
+ # See also Scimitar::Resources::Base::find_attribute
58
+ #
59
+ def self.find_attribute(*path)
60
+ found_attribute = nil
61
+ current_attributes = self.scim_attributes()
62
+
63
+ until path.empty? do
64
+ current_path_entry = path.shift()
65
+ next if current_path_entry.is_a?(Integer) # Skip array indicies arising from multi-value attributes
66
+
67
+ current_path_entry = current_path_entry.to_s.downcase
68
+
69
+ found_attribute = current_attributes.find do | attribute_to_check |
70
+ attribute_to_check.name.to_s.downcase == current_path_entry
71
+ end
72
+
73
+ if found_attribute && path.present? # Any sub-attributes to check?...
74
+ if found_attribute.subAttributes.present? # ...and are any defined?
75
+ current_attributes = found_attribute.subAttributes
76
+ else
77
+ found_attribute = nil
78
+ break # NOTE EARLY EXIT - tried to find a sub-attribute but there are none
79
+ end
80
+ else
81
+ break # NOTE EARLY EXIT - no found attribute, or found target item at end of path
82
+ end
83
+ end # "until path.empty() do"
84
+
85
+ return found_attribute
86
+ end
87
+
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,24 @@
1
+ module Scimitar
2
+ module Schema
3
+ module DerivedAttributes
4
+ extend ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def set_schema(schema)
8
+ @schema = schema
9
+ derive_attributes_from_schema(schema)
10
+ schema
11
+ end
12
+
13
+ def derive_attributes_from_schema(schema)
14
+ attr_accessor *schema.scim_attributes.map(&:name)
15
+ end
16
+
17
+ def schema
18
+ @schema
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Email complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Email
7
+ #
8
+ class Email < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Entitlement complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Entitlement
7
+ #
8
+ class Entitlement < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ module Scimitar
2
+ module Schema
3
+ # Represents the schema for the Group resource
4
+ # See also Scimitar::Resources::Group
5
+ class Group < Base
6
+
7
+ def initialize(options = {})
8
+ super(name: 'Group',
9
+ id: self.class.id,
10
+ description: 'Represents a Group',
11
+ scim_attributes: self.class.scim_attributes)
12
+ end
13
+
14
+ def self.id
15
+ 'urn:ietf:params:scim:schemas:core:2.0:Group'
16
+ end
17
+
18
+ def self.scim_attributes
19
+ [
20
+ Attribute.new(name: 'displayName', type: 'string', required: true),
21
+ Attribute.new(name: 'members', multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceMember, mutability: 'readWrite')
22
+ ]
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Ims (Instant Messaging) complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Ims
7
+ #
8
+ class Ims < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ module Scimitar
2
+ module Schema
3
+ # Represents the schema for the Name complex type
4
+ # See also Scimitar::ComplexTypes::Name
5
+ class Name < Base
6
+
7
+ def self.scim_attributes
8
+ @scim_attributes ||= [
9
+ Attribute.new(name: 'familyName', type: 'string', required: true),
10
+ Attribute.new(name: 'givenName', type: 'string', required: true),
11
+ Attribute.new(name: 'middleName', type: 'string'),
12
+ Attribute.new(name: 'formatted', type: 'string'),
13
+ Attribute.new(name: 'honorificPrefix', type: 'string'),
14
+ Attribute.new(name: 'honorificSuffix', type: 'string'),
15
+ ]
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the PhoneNumber complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::PhoneNumber
7
+ #
8
+ class PhoneNumber < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Photo complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Photo
7
+ #
8
+ class Photo < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the ReferenceGroup complex type,
5
+ # referring to a group of which a user is a member - used in
6
+ # a User SCIM resource's "groups" array.
7
+ #
8
+ # These are always read-only, with no ability to change the
9
+ # membership list through a User. Change via Groups instead.
10
+ #
11
+ # See also Scimitar::ComplexTypes::ReferenceGroup
12
+ #
13
+ class ReferenceGroup < Base
14
+ def self.scim_attributes
15
+ @scim_attributes ||= [
16
+ Attribute.new(name: 'value', type: 'string', mutability: 'readOnly', required: true),
17
+ Attribute.new(name: 'display', type: 'string', mutability: 'readOnly'),
18
+ Attribute.new(name: 'type', type: 'string', mutability: 'readOnly'),
19
+ ]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the ReferenceMember complex type,
5
+ # referring to a member of a group (where members can themselves
6
+ # be Users or Groups, identified by the "type" attribute). Used
7
+ # by the Group SCIM resource's "members" array.
8
+ #
9
+ # See also Scimitar::ComplexTypes::ReferenceMember
10
+ #
11
+ class ReferenceMember < Base
12
+ def self.scim_attributes
13
+ @scim_attributes ||= [
14
+ Attribute.new(name: 'value', type: 'string', mutability: 'immutable', required: true),
15
+ Attribute.new(name: 'type', type: 'string', mutability: 'immutable'),
16
+ Attribute.new(name: 'display', type: 'string', mutability: 'immutable'),
17
+ ]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the Role complex type.
5
+ #
6
+ # See also Scimitar::ComplexTypes::Role
7
+ #
8
+ class Role < Vdtp; end
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ module Scimitar
2
+ module Schema
3
+ # Represents the schema for the User resource
4
+ # See also Scimitar::Resources::User
5
+ class User < Base
6
+
7
+ def initialize(options = {})
8
+ super(name: 'User',
9
+ id: self.class.id,
10
+ description: 'Represents a User',
11
+ scim_attributes: self.class.scim_attributes)
12
+
13
+ end
14
+
15
+ def self.id
16
+ 'urn:ietf:params:scim:schemas:core:2.0:User'
17
+ end
18
+
19
+ def self.scim_attributes
20
+ [
21
+ Attribute.new(name: 'userName', type: 'string', uniqueness: 'server', required: true),
22
+
23
+ Attribute.new(name: 'name', complexType: Scimitar::ComplexTypes::Name),
24
+
25
+ Attribute.new(name: 'displayName', type: 'string'),
26
+ Attribute.new(name: 'nickName', type: 'string'),
27
+ Attribute.new(name: 'profileUrl', type: 'string'),
28
+ Attribute.new(name: 'title', type: 'string'),
29
+ Attribute.new(name: 'userType', type: 'string'),
30
+ Attribute.new(name: 'preferredLanguage', type: 'string'),
31
+ Attribute.new(name: 'locale', type: 'string'),
32
+ Attribute.new(name: 'timezone', type: 'string'),
33
+
34
+ Attribute.new(name: 'active', type: 'boolean'),
35
+
36
+ Attribute.new(name: 'password', type: 'string', mutability: 'writeOnly', returned: 'never'),
37
+
38
+ Attribute.new(name: 'emails', multiValued: true, complexType: Scimitar::ComplexTypes::Email),
39
+ Attribute.new(name: 'phoneNumbers', multiValued: true, complexType: Scimitar::ComplexTypes::PhoneNumber),
40
+ Attribute.new(name: 'ims', multiValued: true, complexType: Scimitar::ComplexTypes::Ims),
41
+ Attribute.new(name: 'photos', multiValued: true, complexType: Scimitar::ComplexTypes::Photo),
42
+ Attribute.new(name: 'addresses', multiValued: true, complexType: Scimitar::ComplexTypes::Address),
43
+ Attribute.new(name: 'groups', multiValued: true, complexType: Scimitar::ComplexTypes::ReferenceGroup, mutability: 'readOnly'),
44
+ Attribute.new(name: 'entitlements', multiValued: true, complexType: Scimitar::ComplexTypes::Entitlement),
45
+ Attribute.new(name: 'roles', multiValued: true, complexType: Scimitar::ComplexTypes::Role),
46
+ Attribute.new(name: 'x509Certificates', multiValued: true, complexType: Scimitar::ComplexTypes::X509Certificate),
47
+ ]
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents a common schema for a few complex types; base class DRYs up
5
+ # code. "Vdtp" - Value, Display, Type, Primary.
6
+ #
7
+ class Vdtp < Base
8
+ def self.scim_attributes
9
+ @scim_attributes ||= [
10
+ Attribute.new(name: 'value', type: 'string', required: Scimitar.engine_configuration.optional_value_fields_required),
11
+ Attribute.new(name: 'display', type: 'string', mutability: 'readOnly'),
12
+ Attribute.new(name: 'type', type: 'string'),
13
+ Attribute.new(name: 'primary', type: 'boolean'),
14
+ ]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Scimitar
2
+ module Schema
3
+
4
+ # Represents the schema for the X509Certificate complex type.
5
+ # The 'value' holds the certificate data.
6
+ #
7
+ # Similar to the Vdtp class, but the "value" field is of type "binary".
8
+ #
9
+ # See also Scimitar::ComplexTypes::X509Certificate
10
+ #
11
+ class X509Certificate < Base
12
+ def self.scim_attributes
13
+ @scim_attributes ||= [
14
+ Attribute.new(name: 'value', type: 'binary', required: true),
15
+ Attribute.new(name: 'display', type: 'string', mutability: 'readOnly'),
16
+ Attribute.new(name: 'type', type: 'string'),
17
+ Attribute.new(name: 'primary', type: 'boolean'),
18
+ ]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,60 @@
1
+ module Scimitar
2
+
3
+ # Represents the service provider info. Used by the /ServiceProviderConfig
4
+ # endpoint to provide specification compliance, authentication schemes and
5
+ # data models. Renders to JSON as a SCIM ServiceProviderConfig type.
6
+ #
7
+ # See config/initializers/scimitar.rb for more information.
8
+ #
9
+ class ServiceProviderConfiguration
10
+ include ActiveModel::Model
11
+
12
+ attr_accessor(
13
+ :uses_defaults,
14
+ :patch,
15
+ :bulk,
16
+ :filter,
17
+ :changePassword,
18
+ :sort,
19
+ :etag,
20
+ :authenticationSchemes,
21
+ :schemas,
22
+ :meta,
23
+ )
24
+
25
+ def initialize(attributes = {})
26
+ @uses_defaults = attributes.empty?
27
+
28
+ defaults = {
29
+ bulk: Supportable.unsupported,
30
+ changePassword: Supportable.unsupported,
31
+ sort: Supportable.unsupported,
32
+ etag: Supportable.unsupported,
33
+
34
+ patch: Supportable.supported,
35
+
36
+ filter: Scimitar::Filter.new(
37
+ supported: true,
38
+ maxResults: Scimitar::Filter::MAX_RESULTS_DEFAULT
39
+ ),
40
+
41
+ schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
42
+
43
+ meta: Meta.new(
44
+ resourceType: 'ServiceProviderConfig',
45
+ created: Time.now,
46
+ lastModified: Time.now,
47
+ version: '1'
48
+ ),
49
+
50
+ authenticationSchemes: [
51
+ AuthenticationScheme.basic,
52
+ AuthenticationScheme.bearer
53
+ ]
54
+ }
55
+
56
+ super(defaults.merge(attributes))
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,14 @@
1
+ module Scimitar
2
+ class Supportable
3
+ include ActiveModel::Model
4
+ attr_accessor :supported
5
+
6
+ def self.supported
7
+ new(supported: true)
8
+ end
9
+
10
+ def self.unsupported
11
+ new(supported: false)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Scimitar</title>
5
+ <%= stylesheet_link_tag "scimitar/application", media: "all" %>
6
+ <%= javascript_include_tag "scimitar/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>