scimitar 1.0.0

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.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +16 -0
  3. data/app/controllers/scimitar/active_record_backed_resources_controller.rb +180 -0
  4. data/app/controllers/scimitar/application_controller.rb +129 -0
  5. data/app/controllers/scimitar/resource_types_controller.rb +28 -0
  6. data/app/controllers/scimitar/resources_controller.rb +203 -0
  7. data/app/controllers/scimitar/schemas_controller.rb +16 -0
  8. data/app/controllers/scimitar/service_provider_configurations_controller.rb +8 -0
  9. data/app/models/scimitar/authentication_error.rb +9 -0
  10. data/app/models/scimitar/authentication_scheme.rb +18 -0
  11. data/app/models/scimitar/bulk.rb +8 -0
  12. data/app/models/scimitar/complex_types/address.rb +18 -0
  13. data/app/models/scimitar/complex_types/base.rb +41 -0
  14. data/app/models/scimitar/complex_types/email.rb +12 -0
  15. data/app/models/scimitar/complex_types/entitlement.rb +12 -0
  16. data/app/models/scimitar/complex_types/ims.rb +12 -0
  17. data/app/models/scimitar/complex_types/name.rb +12 -0
  18. data/app/models/scimitar/complex_types/phone_number.rb +12 -0
  19. data/app/models/scimitar/complex_types/photo.rb +12 -0
  20. data/app/models/scimitar/complex_types/reference_group.rb +12 -0
  21. data/app/models/scimitar/complex_types/reference_member.rb +12 -0
  22. data/app/models/scimitar/complex_types/role.rb +12 -0
  23. data/app/models/scimitar/complex_types/x509_certificate.rb +12 -0
  24. data/app/models/scimitar/engine_configuration.rb +24 -0
  25. data/app/models/scimitar/error_response.rb +20 -0
  26. data/app/models/scimitar/errors.rb +14 -0
  27. data/app/models/scimitar/filter.rb +11 -0
  28. data/app/models/scimitar/filter_error.rb +22 -0
  29. data/app/models/scimitar/invalid_syntax_error.rb +9 -0
  30. data/app/models/scimitar/lists/count.rb +64 -0
  31. data/app/models/scimitar/lists/query_parser.rb +730 -0
  32. data/app/models/scimitar/meta.rb +7 -0
  33. data/app/models/scimitar/not_found_error.rb +10 -0
  34. data/app/models/scimitar/resource_invalid_error.rb +9 -0
  35. data/app/models/scimitar/resource_type.rb +29 -0
  36. data/app/models/scimitar/resources/base.rb +159 -0
  37. data/app/models/scimitar/resources/group.rb +13 -0
  38. data/app/models/scimitar/resources/mixin.rb +964 -0
  39. data/app/models/scimitar/resources/user.rb +13 -0
  40. data/app/models/scimitar/schema/address.rb +24 -0
  41. data/app/models/scimitar/schema/attribute.rb +123 -0
  42. data/app/models/scimitar/schema/base.rb +86 -0
  43. data/app/models/scimitar/schema/derived_attributes.rb +24 -0
  44. data/app/models/scimitar/schema/email.rb +10 -0
  45. data/app/models/scimitar/schema/entitlement.rb +10 -0
  46. data/app/models/scimitar/schema/group.rb +27 -0
  47. data/app/models/scimitar/schema/ims.rb +10 -0
  48. data/app/models/scimitar/schema/name.rb +20 -0
  49. data/app/models/scimitar/schema/phone_number.rb +10 -0
  50. data/app/models/scimitar/schema/photo.rb +10 -0
  51. data/app/models/scimitar/schema/reference_group.rb +23 -0
  52. data/app/models/scimitar/schema/reference_member.rb +21 -0
  53. data/app/models/scimitar/schema/role.rb +10 -0
  54. data/app/models/scimitar/schema/user.rb +52 -0
  55. data/app/models/scimitar/schema/vdtp.rb +18 -0
  56. data/app/models/scimitar/schema/x509_certificate.rb +22 -0
  57. data/app/models/scimitar/service_provider_configuration.rb +49 -0
  58. data/app/models/scimitar/supportable.rb +14 -0
  59. data/app/views/layouts/scimitar/application.html.erb +14 -0
  60. data/config/initializers/scimitar.rb +82 -0
  61. data/config/routes.rb +6 -0
  62. data/lib/scimitar.rb +23 -0
  63. data/lib/scimitar/engine.rb +63 -0
  64. data/lib/scimitar/version.rb +13 -0
  65. data/spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb +24 -0
  66. data/spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb +30 -0
  67. data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +13 -0
  68. data/spec/apps/dummy/app/controllers/mock_users_controller.rb +13 -0
  69. data/spec/apps/dummy/app/models/mock_group.rb +83 -0
  70. data/spec/apps/dummy/app/models/mock_user.rb +104 -0
  71. data/spec/apps/dummy/config/application.rb +17 -0
  72. data/spec/apps/dummy/config/boot.rb +2 -0
  73. data/spec/apps/dummy/config/environment.rb +2 -0
  74. data/spec/apps/dummy/config/environments/test.rb +15 -0
  75. data/spec/apps/dummy/config/initializers/cookies_serializer.rb +3 -0
  76. data/spec/apps/dummy/config/initializers/scimitar.rb +14 -0
  77. data/spec/apps/dummy/config/initializers/session_store.rb +3 -0
  78. data/spec/apps/dummy/config/routes.rb +24 -0
  79. data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +15 -0
  80. data/spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb +10 -0
  81. data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +8 -0
  82. data/spec/apps/dummy/db/schema.rb +42 -0
  83. data/spec/controllers/scimitar/application_controller_spec.rb +173 -0
  84. data/spec/controllers/scimitar/resource_types_controller_spec.rb +94 -0
  85. data/spec/controllers/scimitar/resources_controller_spec.rb +247 -0
  86. data/spec/controllers/scimitar/schemas_controller_spec.rb +75 -0
  87. data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +22 -0
  88. data/spec/models/scimitar/complex_types/address_spec.rb +19 -0
  89. data/spec/models/scimitar/complex_types/email_spec.rb +23 -0
  90. data/spec/models/scimitar/lists/count_spec.rb +147 -0
  91. data/spec/models/scimitar/lists/query_parser_spec.rb +763 -0
  92. data/spec/models/scimitar/resource_type_spec.rb +21 -0
  93. data/spec/models/scimitar/resources/base_spec.rb +289 -0
  94. data/spec/models/scimitar/resources/base_validation_spec.rb +61 -0
  95. data/spec/models/scimitar/resources/mixin_spec.rb +2127 -0
  96. data/spec/models/scimitar/resources/user_spec.rb +55 -0
  97. data/spec/models/scimitar/schema/attribute_spec.rb +80 -0
  98. data/spec/models/scimitar/schema/base_spec.rb +64 -0
  99. data/spec/models/scimitar/schema/group_spec.rb +87 -0
  100. data/spec/models/scimitar/schema/user_spec.rb +710 -0
  101. data/spec/requests/active_record_backed_resources_controller_spec.rb +569 -0
  102. data/spec/requests/application_controller_spec.rb +49 -0
  103. data/spec/requests/controller_configuration_spec.rb +17 -0
  104. data/spec/requests/engine_spec.rb +20 -0
  105. data/spec/spec_helper.rb +66 -0
  106. metadata +315 -0
@@ -0,0 +1,16 @@
1
+ require_dependency "scimitar/application_controller"
2
+
3
+ module Scimitar
4
+ class SchemasController < ApplicationController
5
+ def index
6
+ schemas = Scimitar::Engine.schemas
7
+ schemas_by_id = schemas.reduce({}) do |hash, schema|
8
+ hash[schema.id] = schema
9
+ hash
10
+ end
11
+
12
+ render json: schemas_by_id[params[:name]] || schemas
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require_dependency "scimitar/application_controller"
2
+ module Scimitar
3
+ class ServiceProviderConfigurationsController < ApplicationController
4
+ def show
5
+ render json: Scimitar.service_provider_configuration(location: request.url)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Scimitar
2
+
3
+ class AuthenticationError < ErrorResponse
4
+ def initialize
5
+ super(status: 401, detail: 'Requires authentication')
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Scimitar
2
+ class AuthenticationScheme
3
+ include ActiveModel::Model
4
+ attr_accessor :type, :name, :description
5
+
6
+ def self.basic
7
+ new type: 'httpbasic',
8
+ name: 'HTTP Basic',
9
+ description: 'Authentication scheme using the HTTP Basic Standard'
10
+ end
11
+
12
+ def self.bearer
13
+ new type: 'oauthbearertoken',
14
+ name: 'OAuth Bearer Token',
15
+ description: 'Authentication scheme using the OAuth Bearer Token Standard'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ module Scimitar
2
+
3
+ # This isn't supported yet; the class is a placeholder.
4
+ #
5
+ class Bulk < Supportable
6
+ attr_accessor :maxOperations, :maxPayloadSize
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Address type.
5
+ #
6
+ # See also Scimitar::Schema::Address
7
+ #
8
+ class Address < Base
9
+ set_schema Scimitar::Schema::Address
10
+
11
+ # Returns the JSON representation of an Address.
12
+ #
13
+ def as_json(options = {})
14
+ {'type' => 'work'}.merge(super(options))
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # This class represents complex types that could be used inside SCIM
5
+ # resources. Each complex type must inherit from this class. They also need
6
+ # to have their own schema defined. For example:
7
+ #
8
+ # module Scimitar
9
+ # module ComplexTypes
10
+ # class Email < Base
11
+ # set_schema Scimitar::Schema::Email
12
+ #
13
+ # def as_json(options = {})
14
+ # {'type' => 'work', 'primary' => true}.merge(super(options))
15
+ # end
16
+ # end
17
+ # end
18
+ # end
19
+ #
20
+ class Base
21
+ include ActiveModel::Model
22
+ include Scimitar::Schema::DerivedAttributes
23
+ include Scimitar::Errors
24
+
25
+ def initialize(options={})
26
+ super
27
+ @errors = ActiveModel::Errors.new(self)
28
+ end
29
+
30
+ # Converts the object to its SCIM representation, which is always JSON.
31
+ #
32
+ # +options+:: A hash that could provide default values for some of the
33
+ # attributes of this complex type object.
34
+ #
35
+ def as_json(options={})
36
+ options[:except] ||= ['errors']
37
+ super.except(options)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Email type.
5
+ #
6
+ # See also Scimitar::Schema::Email
7
+ #
8
+ class Email < Base
9
+ set_schema Scimitar::Schema::Email
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Entitlement type.
5
+ #
6
+ # See also Scimitar::Schema::Entitlement
7
+ #
8
+ class Entitlement < Base
9
+ set_schema Scimitar::Schema::Entitlement
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Instant Messaging type.
5
+ #
6
+ # See also Scimitar::Schema::Ims
7
+ #
8
+ class Ims < Base
9
+ set_schema Scimitar::Schema::Ims
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Name type.
5
+ #
6
+ # See also Scimitar::Schema::Name
7
+ #
8
+ class Name < Base
9
+ set_schema Scimitar::Schema::Name
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex PhoneNumber type.
5
+ #
6
+ # See also Scimitar::Schema::PhoneNumber
7
+ #
8
+ class PhoneNumber < Base
9
+ set_schema Scimitar::Schema::PhoneNumber
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Photo type.
5
+ #
6
+ # See also Scimitar::Schema::Photo
7
+ #
8
+ class Photo < Base
9
+ set_schema Scimitar::Schema::Photo
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex reference-a-group type.
5
+ #
6
+ # See also Scimitar::Schema::ReferenceGroup
7
+ #
8
+ class ReferenceGroup < Base
9
+ set_schema Scimitar::Schema::ReferenceGroup
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex reference-a-member type.
5
+ #
6
+ # See also Scimitar::Schema::ReferenceMember
7
+ #
8
+ class ReferenceMember < Base
9
+ set_schema Scimitar::Schema::ReferenceMember
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex Role type.
5
+ #
6
+ # See also Scimitar::Schema::Role
7
+ #
8
+ class Role < Base
9
+ set_schema Scimitar::Schema::Role
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Scimitar
2
+ module ComplexTypes
3
+
4
+ # Represents the complex X509Certificate type.
5
+ #
6
+ # See also Scimitar::Schema::X509Certificate
7
+ #
8
+ class X509Certificate < Base
9
+ set_schema Scimitar::Schema::X509Certificate
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Scimitar
2
+
3
+ # Scimitar general configuration.
4
+ #
5
+ # See config/initializers/scimitar.rb for more information.
6
+ #
7
+ class EngineConfiguration
8
+ include ActiveModel::Model
9
+
10
+ attr_accessor :basic_authenticator,
11
+ :token_authenticator,
12
+ :application_controller_mixin
13
+
14
+ def initialize(attributes = {})
15
+
16
+ # No defaults yet - reserved for future use.
17
+ #
18
+ defaults = {}
19
+
20
+ super(defaults.merge(attributes))
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ module Scimitar
2
+ class ErrorResponse < StandardError
3
+ include ActiveModel::Model
4
+
5
+ attr_accessor :status,
6
+ :detail,
7
+ :scimType
8
+
9
+ def as_json(options = {})
10
+ data = {
11
+ 'schemas': ['urn:ietf:params:scim:api:messages:2.0:Error'],
12
+ 'detail': detail,
13
+ 'status': "#{status}"
14
+ }
15
+
16
+ data['scimType'] = scimType if scimType
17
+ data
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module Scimitar
2
+ module Errors
3
+ def add_errors_from_hash(errors_hash, prefix: nil)
4
+ errors_hash.each_pair do |key, value|
5
+ new_key = prefix.nil? ? key : "#{prefix}.#{key}".to_sym
6
+ if value.is_a?(Array)
7
+ value.each {|error| errors.add(new_key, error)}
8
+ else
9
+ errors.add(new_key, value)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Scimitar
2
+
3
+ # Used to configure filters via
4
+ # app/models/scimitar/service_provider_configuration.rb.
5
+ #
6
+ class Filter < Supportable
7
+ MAX_RESULTS_DEFAULT = 100
8
+
9
+ attr_accessor :maxResults
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ module Scimitar
2
+
3
+ # Raised when an invalid query is attempted, either by being malformed or by
4
+ # being unsupported in some way.
5
+ #
6
+ class FilterError < ErrorResponse
7
+ def initialize(message = nil)
8
+ detail = 'The specified filter syntax was invalid, or the specified attribute and filter comparison combination is not supported'
9
+
10
+ if message.present?
11
+ detail << ":\n#{message}"
12
+ end
13
+
14
+ super(
15
+ status: 400,
16
+ scimType: 'invalidFilter',
17
+ detail: detail
18
+ )
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,9 @@
1
+ module Scimitar
2
+ class InvalidSyntaxError < ErrorResponse
3
+
4
+ def initialize(error_message)
5
+ super(status: 400, scimType: 'invalidSyntax', detail: error_message)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ module Scimitar
2
+ module Lists
3
+ class Count
4
+ include ActiveModel::Model
5
+
6
+ attr_accessor :limit, :start_index, :total
7
+ attr_reader :offset
8
+
9
+ def initialize(*args)
10
+ @limit = 100
11
+ @start_index = 1
12
+
13
+ super(*args)
14
+ end
15
+
16
+ # Set a limit (page size) value.
17
+ #
18
+ # +value+:: Integer value held in a String. Must be >= 1.
19
+ #
20
+ # Raises exceptions if given non-numeric, zero or negative input.
21
+ #
22
+ def limit=(value)
23
+ value = value&.to_s
24
+ return if value.blank? # NOTE EARLY EXIT
25
+
26
+ validate_numericality(value)
27
+ input = value.to_i
28
+ raise if input < 1
29
+ @limit = input
30
+ end
31
+
32
+ # Set a start index (offset) value. Values start at 1. See also #offset.
33
+ #
34
+ # +value+:: Integer value held in a String. Must be >= 1.
35
+ #
36
+ # Raises exceptions if given non-numeric or negative input. Corrects an
37
+ # input value of zero to 1.
38
+ #
39
+ def start_index=(value)
40
+ value = value&.to_s
41
+ return if value.blank? # NOTE EARLY EXIT
42
+
43
+ validate_numericality(value)
44
+ input = value.to_i
45
+ input = 1 if input < 1
46
+ @start_index = input
47
+ end
48
+
49
+ # Read-only accessor that represents #start_index as a zero-based offset,
50
+ # rather than 1-based. This is useful for most storage engines.
51
+ #
52
+ def offset
53
+ start_index - 1
54
+ end
55
+
56
+ private
57
+
58
+ def validate_numericality(input)
59
+ raise unless input.match?(/\A\d+\z/)
60
+ end
61
+
62
+ end
63
+ end
64
+ end