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.
- checksums.yaml +7 -0
- data/Rakefile +16 -0
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +180 -0
- data/app/controllers/scimitar/application_controller.rb +129 -0
- data/app/controllers/scimitar/resource_types_controller.rb +28 -0
- data/app/controllers/scimitar/resources_controller.rb +203 -0
- data/app/controllers/scimitar/schemas_controller.rb +16 -0
- data/app/controllers/scimitar/service_provider_configurations_controller.rb +8 -0
- data/app/models/scimitar/authentication_error.rb +9 -0
- data/app/models/scimitar/authentication_scheme.rb +18 -0
- data/app/models/scimitar/bulk.rb +8 -0
- data/app/models/scimitar/complex_types/address.rb +18 -0
- data/app/models/scimitar/complex_types/base.rb +41 -0
- data/app/models/scimitar/complex_types/email.rb +12 -0
- data/app/models/scimitar/complex_types/entitlement.rb +12 -0
- data/app/models/scimitar/complex_types/ims.rb +12 -0
- data/app/models/scimitar/complex_types/name.rb +12 -0
- data/app/models/scimitar/complex_types/phone_number.rb +12 -0
- data/app/models/scimitar/complex_types/photo.rb +12 -0
- data/app/models/scimitar/complex_types/reference_group.rb +12 -0
- data/app/models/scimitar/complex_types/reference_member.rb +12 -0
- data/app/models/scimitar/complex_types/role.rb +12 -0
- data/app/models/scimitar/complex_types/x509_certificate.rb +12 -0
- data/app/models/scimitar/engine_configuration.rb +24 -0
- data/app/models/scimitar/error_response.rb +20 -0
- data/app/models/scimitar/errors.rb +14 -0
- data/app/models/scimitar/filter.rb +11 -0
- data/app/models/scimitar/filter_error.rb +22 -0
- data/app/models/scimitar/invalid_syntax_error.rb +9 -0
- data/app/models/scimitar/lists/count.rb +64 -0
- data/app/models/scimitar/lists/query_parser.rb +730 -0
- data/app/models/scimitar/meta.rb +7 -0
- data/app/models/scimitar/not_found_error.rb +10 -0
- data/app/models/scimitar/resource_invalid_error.rb +9 -0
- data/app/models/scimitar/resource_type.rb +29 -0
- data/app/models/scimitar/resources/base.rb +159 -0
- data/app/models/scimitar/resources/group.rb +13 -0
- data/app/models/scimitar/resources/mixin.rb +964 -0
- data/app/models/scimitar/resources/user.rb +13 -0
- data/app/models/scimitar/schema/address.rb +24 -0
- data/app/models/scimitar/schema/attribute.rb +123 -0
- data/app/models/scimitar/schema/base.rb +86 -0
- data/app/models/scimitar/schema/derived_attributes.rb +24 -0
- data/app/models/scimitar/schema/email.rb +10 -0
- data/app/models/scimitar/schema/entitlement.rb +10 -0
- data/app/models/scimitar/schema/group.rb +27 -0
- data/app/models/scimitar/schema/ims.rb +10 -0
- data/app/models/scimitar/schema/name.rb +20 -0
- data/app/models/scimitar/schema/phone_number.rb +10 -0
- data/app/models/scimitar/schema/photo.rb +10 -0
- data/app/models/scimitar/schema/reference_group.rb +23 -0
- data/app/models/scimitar/schema/reference_member.rb +21 -0
- data/app/models/scimitar/schema/role.rb +10 -0
- data/app/models/scimitar/schema/user.rb +52 -0
- data/app/models/scimitar/schema/vdtp.rb +18 -0
- data/app/models/scimitar/schema/x509_certificate.rb +22 -0
- data/app/models/scimitar/service_provider_configuration.rb +49 -0
- data/app/models/scimitar/supportable.rb +14 -0
- data/app/views/layouts/scimitar/application.html.erb +14 -0
- data/config/initializers/scimitar.rb +82 -0
- data/config/routes.rb +6 -0
- data/lib/scimitar.rb +23 -0
- data/lib/scimitar/engine.rb +63 -0
- data/lib/scimitar/version.rb +13 -0
- data/spec/apps/dummy/app/controllers/custom_destroy_mock_users_controller.rb +24 -0
- data/spec/apps/dummy/app/controllers/custom_request_verifiers_controller.rb +30 -0
- data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +13 -0
- data/spec/apps/dummy/app/controllers/mock_users_controller.rb +13 -0
- data/spec/apps/dummy/app/models/mock_group.rb +83 -0
- data/spec/apps/dummy/app/models/mock_user.rb +104 -0
- data/spec/apps/dummy/config/application.rb +17 -0
- data/spec/apps/dummy/config/boot.rb +2 -0
- data/spec/apps/dummy/config/environment.rb +2 -0
- data/spec/apps/dummy/config/environments/test.rb +15 -0
- data/spec/apps/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/apps/dummy/config/initializers/scimitar.rb +14 -0
- data/spec/apps/dummy/config/initializers/session_store.rb +3 -0
- data/spec/apps/dummy/config/routes.rb +24 -0
- data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +15 -0
- data/spec/apps/dummy/db/migrate/20210308020313_create_mock_groups.rb +10 -0
- data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +8 -0
- data/spec/apps/dummy/db/schema.rb +42 -0
- data/spec/controllers/scimitar/application_controller_spec.rb +173 -0
- data/spec/controllers/scimitar/resource_types_controller_spec.rb +94 -0
- data/spec/controllers/scimitar/resources_controller_spec.rb +247 -0
- data/spec/controllers/scimitar/schemas_controller_spec.rb +75 -0
- data/spec/controllers/scimitar/service_provider_configurations_controller_spec.rb +22 -0
- data/spec/models/scimitar/complex_types/address_spec.rb +19 -0
- data/spec/models/scimitar/complex_types/email_spec.rb +23 -0
- data/spec/models/scimitar/lists/count_spec.rb +147 -0
- data/spec/models/scimitar/lists/query_parser_spec.rb +763 -0
- data/spec/models/scimitar/resource_type_spec.rb +21 -0
- data/spec/models/scimitar/resources/base_spec.rb +289 -0
- data/spec/models/scimitar/resources/base_validation_spec.rb +61 -0
- data/spec/models/scimitar/resources/mixin_spec.rb +2127 -0
- data/spec/models/scimitar/resources/user_spec.rb +55 -0
- data/spec/models/scimitar/schema/attribute_spec.rb +80 -0
- data/spec/models/scimitar/schema/base_spec.rb +64 -0
- data/spec/models/scimitar/schema/group_spec.rb +87 -0
- data/spec/models/scimitar/schema/user_spec.rb +710 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +569 -0
- data/spec/requests/application_controller_spec.rb +49 -0
- data/spec/requests/controller_configuration_spec.rb +17 -0
- data/spec/requests/engine_spec.rb +20 -0
- data/spec/spec_helper.rb +66 -0
- metadata +315 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class MockGroup < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
# ===========================================================================
|
|
4
|
+
# TEST ATTRIBUTES - see db/migrate/20210308020313_create_mock_groups.rb etc.
|
|
5
|
+
# ===========================================================================
|
|
6
|
+
|
|
7
|
+
READWRITE_ATTRS = %w{
|
|
8
|
+
id
|
|
9
|
+
scim_uid
|
|
10
|
+
display_name
|
|
11
|
+
scim_users_and_groups
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
has_and_belongs_to_many :mock_users
|
|
15
|
+
|
|
16
|
+
has_many :child_mock_groups, class_name: 'MockGroup', foreign_key: 'parent_id'
|
|
17
|
+
|
|
18
|
+
# ===========================================================================
|
|
19
|
+
# SCIM ADAPTER ACCESSORS
|
|
20
|
+
#
|
|
21
|
+
# Groups in SCIM can contain users or other groups. That's why the :find_with
|
|
22
|
+
# key in the Hash returned by ::scim_attributes_map has to check the type of
|
|
23
|
+
# thing it needs to find. Since the mappings only support a single read/write
|
|
24
|
+
# accessor, we need custom accessors to do what SCIM is expecting by turning
|
|
25
|
+
# the Rails associations to/from mixed, flat arrays of mock users and groups.
|
|
26
|
+
# ===========================================================================
|
|
27
|
+
|
|
28
|
+
def scim_users_and_groups
|
|
29
|
+
self.mock_users.to_a + self.child_mock_groups.to_a
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def scim_users_and_groups=(mixed_array)
|
|
33
|
+
self.mock_users = mixed_array.select { |item| item.is_a?(MockUser) }
|
|
34
|
+
self.child_mock_groups = mixed_array.select { |item| item.is_a?(MockGroup) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# ===========================================================================
|
|
38
|
+
# SCIM MIXIN AND REQUIRED METHODS
|
|
39
|
+
# ===========================================================================
|
|
40
|
+
|
|
41
|
+
def self.scim_resource_type
|
|
42
|
+
return Scimitar::Resources::Group
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.scim_attributes_map
|
|
46
|
+
return {
|
|
47
|
+
id: :id,
|
|
48
|
+
externalId: :scim_uid,
|
|
49
|
+
displayName: :display_name,
|
|
50
|
+
members: [ # NB read-write, though individual items' attributes are immutable
|
|
51
|
+
list: :scim_users_and_groups, # See adapter accessors, earlier in this file
|
|
52
|
+
using: {
|
|
53
|
+
value: :id
|
|
54
|
+
},
|
|
55
|
+
find_with: -> (scim_list_entry) {
|
|
56
|
+
id = scim_list_entry['value']
|
|
57
|
+
type = scim_list_entry['type' ] || 'User' # Some online examples omit 'type' and believe 'User' will be assumed
|
|
58
|
+
|
|
59
|
+
case type.downcase
|
|
60
|
+
when 'user'
|
|
61
|
+
MockUser.find_by_id(id)
|
|
62
|
+
when 'group'
|
|
63
|
+
MockGroup.find_by_id(id)
|
|
64
|
+
else
|
|
65
|
+
raise Scimitar::InvalidSyntaxError.new("Unrecognised type #{type.inspect}")
|
|
66
|
+
end
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.scim_mutable_attributes
|
|
73
|
+
return nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.scim_queryable_attributes
|
|
77
|
+
return {
|
|
78
|
+
displayName: :display_name
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
include Scimitar::Resources::Mixin
|
|
83
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
class MockUser < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
# ===========================================================================
|
|
4
|
+
# TEST ATTRIBUTES - see db/migrate/20210304014602_create_mock_users.rb etc.
|
|
5
|
+
# ===========================================================================
|
|
6
|
+
|
|
7
|
+
READWRITE_ATTRS = %w{
|
|
8
|
+
id
|
|
9
|
+
scim_uid
|
|
10
|
+
username
|
|
11
|
+
first_name
|
|
12
|
+
last_name
|
|
13
|
+
work_email_address
|
|
14
|
+
home_email_address
|
|
15
|
+
work_phone_number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
has_and_belongs_to_many :mock_groups
|
|
19
|
+
|
|
20
|
+
# A fixed value read-only attribute, in essence.
|
|
21
|
+
#
|
|
22
|
+
def is_active
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# A test hook to force validation failures.
|
|
27
|
+
#
|
|
28
|
+
INVALID_USERNAME = 'invalid username'
|
|
29
|
+
validates :username, uniqueness: true, exclusion: { in: [INVALID_USERNAME] }
|
|
30
|
+
|
|
31
|
+
# ===========================================================================
|
|
32
|
+
# SCIM MIXIN AND REQUIRED METHODS
|
|
33
|
+
# ===========================================================================
|
|
34
|
+
|
|
35
|
+
def self.scim_resource_type
|
|
36
|
+
return Scimitar::Resources::User
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.scim_attributes_map
|
|
40
|
+
return {
|
|
41
|
+
id: :id,
|
|
42
|
+
externalId: :scim_uid,
|
|
43
|
+
userName: :username,
|
|
44
|
+
name: {
|
|
45
|
+
givenName: :first_name,
|
|
46
|
+
familyName: :last_name
|
|
47
|
+
},
|
|
48
|
+
emails: [
|
|
49
|
+
{
|
|
50
|
+
match: 'type',
|
|
51
|
+
with: 'work',
|
|
52
|
+
using: {
|
|
53
|
+
value: :work_email_address,
|
|
54
|
+
primary: true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
match: 'type',
|
|
59
|
+
with: 'home',
|
|
60
|
+
using: {
|
|
61
|
+
value: :home_email_address,
|
|
62
|
+
primary: false
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
phoneNumbers: [
|
|
67
|
+
{
|
|
68
|
+
match: 'type',
|
|
69
|
+
with: 'work',
|
|
70
|
+
using: {
|
|
71
|
+
value: :work_phone_number,
|
|
72
|
+
primary: false
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
groups: [ # NB read-only, so no :find_with key
|
|
77
|
+
{
|
|
78
|
+
list: :mock_groups,
|
|
79
|
+
using: {
|
|
80
|
+
value: :id,
|
|
81
|
+
display: :display_name
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
active: :is_active
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.scim_mutable_attributes
|
|
90
|
+
return nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.scim_queryable_attributes
|
|
94
|
+
return {
|
|
95
|
+
'name.givenName' => { column: :first_name },
|
|
96
|
+
'name.familyName' => { column: :last_name },
|
|
97
|
+
'emails' => { columns: [ :work_email_address, :home_email_address ] },
|
|
98
|
+
'emails.value' => { columns: [ :work_email_address, :home_email_address ] },
|
|
99
|
+
'emails.type' => { ignore: true } # We can't filter on that; it'll just search all e-mails
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
include Scimitar::Resources::Mixin
|
|
104
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative 'boot'
|
|
2
|
+
|
|
3
|
+
require 'rails'
|
|
4
|
+
require 'active_model/railtie'
|
|
5
|
+
require 'active_record/railtie'
|
|
6
|
+
require 'action_controller/railtie'
|
|
7
|
+
require 'action_view/railtie'
|
|
8
|
+
|
|
9
|
+
Bundler.require(*Rails.groups)
|
|
10
|
+
|
|
11
|
+
require 'scimitar'
|
|
12
|
+
|
|
13
|
+
module Dummy
|
|
14
|
+
class Application < Rails::Application
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Rails.application.configure do
|
|
2
|
+
config.cache_classes = true
|
|
3
|
+
config.eager_load = false
|
|
4
|
+
config.serve_static_files = true
|
|
5
|
+
config.static_cache_control = 'public, max-age=3600'
|
|
6
|
+
config.consider_all_requests_local = true
|
|
7
|
+
|
|
8
|
+
config.action_dispatch.show_exceptions = false
|
|
9
|
+
|
|
10
|
+
config.action_controller.perform_caching = false
|
|
11
|
+
config.action_controller.allow_forgery_protection = false
|
|
12
|
+
|
|
13
|
+
config.active_support.test_order = :random
|
|
14
|
+
config.active_support.deprecation = :stderr
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Test app configuration.
|
|
2
|
+
#
|
|
3
|
+
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new({
|
|
4
|
+
|
|
5
|
+
application_controller_mixin: Module.new do
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.class_eval do
|
|
8
|
+
def test_hook; end
|
|
9
|
+
before_action :test_hook
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# This test app mounts everything at the root level, but you'd usually be doing
|
|
2
|
+
# more in your Rails app than just SCIM! Wrapping with 'namespace :foo do' is
|
|
3
|
+
# strongly recommended to avoid routing namespace collisions. See README.md for
|
|
4
|
+
# an example.
|
|
5
|
+
#
|
|
6
|
+
Rails.application.routes.draw do
|
|
7
|
+
mount Scimitar::Engine, at: '/'
|
|
8
|
+
|
|
9
|
+
get 'Users', to: 'mock_users#index'
|
|
10
|
+
get 'Users/:id', to: 'mock_users#show'
|
|
11
|
+
post 'Users', to: 'mock_users#create'
|
|
12
|
+
put 'Users/:id', to: 'mock_users#replace'
|
|
13
|
+
patch 'Users/:id', to: 'mock_users#update'
|
|
14
|
+
delete 'Users/:id', to: 'mock_users#destroy'
|
|
15
|
+
|
|
16
|
+
# For testing blocks passed to ActiveRecordBackedResourcesController#destroy
|
|
17
|
+
#
|
|
18
|
+
delete 'CustomDestroyUsers/:id', to: 'custom_destroy_mock_users#destroy'
|
|
19
|
+
|
|
20
|
+
# For testing environment inside Scimitar::ApplicationController subclasses.
|
|
21
|
+
#
|
|
22
|
+
get 'CustomRequestVerifiers', to: 'custom_request_verifiers#index'
|
|
23
|
+
post 'CustomRequestVerifiers', to: 'custom_request_verifiers#create'
|
|
24
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateMockUsers < ActiveRecord::Migration[6.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :mock_users do |t|
|
|
4
|
+
|
|
5
|
+
t.text :scim_uid
|
|
6
|
+
t.text :username
|
|
7
|
+
t.text :first_name
|
|
8
|
+
t.text :last_name
|
|
9
|
+
t.text :work_email_address
|
|
10
|
+
t.text :home_email_address
|
|
11
|
+
t.text :work_phone_number
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
4
|
+
#
|
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
|
9
|
+
# migrations use external dependencies or application code.
|
|
10
|
+
#
|
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Schema.define(version: 2021_03_08_044214) do
|
|
14
|
+
|
|
15
|
+
# These are extensions that must be enabled in order to support this database
|
|
16
|
+
enable_extension "plpgsql"
|
|
17
|
+
|
|
18
|
+
create_table "mock_groups", force: :cascade do |t|
|
|
19
|
+
t.text "scim_uid"
|
|
20
|
+
t.text "display_name"
|
|
21
|
+
t.bigint "parent_id"
|
|
22
|
+
t.index ["parent_id"], name: "index_mock_groups_on_parent_id"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
create_table "mock_groups_users", id: false, force: :cascade do |t|
|
|
26
|
+
t.bigint "mock_group_id", null: false
|
|
27
|
+
t.bigint "mock_user_id", null: false
|
|
28
|
+
t.index ["mock_group_id", "mock_user_id"], name: "index_mock_groups_users_on_mock_group_id_and_mock_user_id"
|
|
29
|
+
t.index ["mock_user_id", "mock_group_id"], name: "index_mock_groups_users_on_mock_user_id_and_mock_group_id"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
create_table "mock_users", force: :cascade do |t|
|
|
33
|
+
t.text "scim_uid"
|
|
34
|
+
t.text "username"
|
|
35
|
+
t.text "first_name"
|
|
36
|
+
t.text "last_name"
|
|
37
|
+
t.text "work_email_address"
|
|
38
|
+
t.text "home_email_address"
|
|
39
|
+
t.text "work_phone_number"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Scimitar::ApplicationController do
|
|
4
|
+
context 'basic authentication' do
|
|
5
|
+
before do
|
|
6
|
+
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new(
|
|
7
|
+
basic_authenticator: Proc.new do | username, password |
|
|
8
|
+
username == 'A' && password == 'B'
|
|
9
|
+
end
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
controller do
|
|
14
|
+
rescue_from StandardError, with: :handle_resource_not_found
|
|
15
|
+
|
|
16
|
+
def index
|
|
17
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'renders success when valid creds are given' do
|
|
22
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('A', 'B')
|
|
23
|
+
|
|
24
|
+
get :index, params: { format: :scim }
|
|
25
|
+
expect(response).to be_ok
|
|
26
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
|
27
|
+
expect(response.headers['WWW_AUTHENTICATE']).to eql('Basic')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'renders failure with bad password' do
|
|
31
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('A', 'C')
|
|
32
|
+
|
|
33
|
+
get :index, params: { format: :scim }
|
|
34
|
+
expect(response).not_to be_ok
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'renders failure with bad user name' do
|
|
38
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('C', 'B')
|
|
39
|
+
|
|
40
|
+
get :index, params: { format: :scim }
|
|
41
|
+
expect(response).not_to be_ok
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'renders failure with bad user name and password' do
|
|
45
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('C', 'D')
|
|
46
|
+
|
|
47
|
+
get :index, params: { format: :scim }
|
|
48
|
+
expect(response).not_to be_ok
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'renders failure with blank password' do
|
|
52
|
+
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('A', '')
|
|
53
|
+
|
|
54
|
+
get :index, params: { format: :scim }
|
|
55
|
+
expect(response).not_to be_ok
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'renders failure with missing header' do
|
|
59
|
+
get :index, params: { format: :scim }
|
|
60
|
+
expect(response).not_to be_ok
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'token authentication' do
|
|
65
|
+
before do
|
|
66
|
+
Scimitar.engine_configuration = Scimitar::EngineConfiguration.new(
|
|
67
|
+
token_authenticator: Proc.new do | token, options |
|
|
68
|
+
token == 'A'
|
|
69
|
+
end
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
controller do
|
|
74
|
+
rescue_from StandardError, with: :handle_resource_not_found
|
|
75
|
+
|
|
76
|
+
def index
|
|
77
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'renders success when valid creds are given' do
|
|
82
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer A'
|
|
83
|
+
|
|
84
|
+
get :index, params: { format: :scim }
|
|
85
|
+
expect(response).to be_ok
|
|
86
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
|
87
|
+
expect(response.headers['WWW_AUTHENTICATE']).to eql('Bearer')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'renders failure with bad token' do
|
|
91
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer Invalid'
|
|
92
|
+
|
|
93
|
+
get :index, params: { format: :scim }
|
|
94
|
+
expect(response).not_to be_ok
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'renders failure with blank token' do
|
|
98
|
+
request.env['HTTP_AUTHORIZATION'] = 'Bearer'
|
|
99
|
+
|
|
100
|
+
get :index, params: { format: :scim }
|
|
101
|
+
expect(response).not_to be_ok
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'renders failure with missing header' do
|
|
105
|
+
get :index, params: { format: :scim }
|
|
106
|
+
expect(response).not_to be_ok
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'authenticated' do
|
|
111
|
+
controller do
|
|
112
|
+
rescue_from StandardError, with: :handle_resource_not_found
|
|
113
|
+
|
|
114
|
+
def index
|
|
115
|
+
render json: { 'message' => 'cool, cool!' }, format: :scim
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def authenticated?
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context 'authenticate' do
|
|
124
|
+
it 'renders index if authenticated' do
|
|
125
|
+
get :index, params: { format: :scim }
|
|
126
|
+
expect(response).to be_ok
|
|
127
|
+
expect(JSON.parse(response.body)).to eql({ 'message' => 'cool, cool!' })
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'renders not authorized response if not authenticated' do
|
|
131
|
+
allow(controller()).to receive(:authenticated?) { false }
|
|
132
|
+
get :index, params: { format: :scim }
|
|
133
|
+
expect(response).to have_http_status(:unauthorized)
|
|
134
|
+
parsed_body = JSON.parse(response.body)
|
|
135
|
+
expect(parsed_body).to include('schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'])
|
|
136
|
+
expect(parsed_body).to include('detail' => 'Requires authentication')
|
|
137
|
+
expect(parsed_body).to include('status' => '401')
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'renders resource not found response when resource cannot be found for the given id' do
|
|
141
|
+
allow(controller()).to receive(:index).and_raise(StandardError)
|
|
142
|
+
get :index, params: { id: 10, format: :scim }
|
|
143
|
+
expect(response).to have_http_status(:not_found)
|
|
144
|
+
parsed_body = JSON.parse(response.body)
|
|
145
|
+
expect(parsed_body).to include('schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'])
|
|
146
|
+
expect(parsed_body).to include('detail' => 'Resource "10" not found')
|
|
147
|
+
expect(parsed_body).to include('status' => '404')
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
context 'error handling' do
|
|
153
|
+
controller do
|
|
154
|
+
def index
|
|
155
|
+
raise 'Bang'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def authenticated?
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it 'handles general exceptions automatically' do
|
|
164
|
+
get :index, params: { format: :scim }
|
|
165
|
+
|
|
166
|
+
expect(response).to have_http_status(:internal_server_error)
|
|
167
|
+
parsed_body = JSON.parse(response.body)
|
|
168
|
+
expect(parsed_body).to include('schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'])
|
|
169
|
+
expect(parsed_body).to include('status' => '500')
|
|
170
|
+
expect(parsed_body).to include('detail' => 'Bang')
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|