scimaenaga 0.7.0 → 0.9.1
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 +4 -4
- data/README.md +22 -7
- data/Rakefile +6 -8
- data/app/controllers/concerns/{scim_rails → scimaenaga}/exception_handler.rb +47 -37
- data/app/controllers/concerns/scimaenaga/response.rb +94 -0
- data/app/controllers/scimaenaga/application_controller.rb +72 -0
- data/app/controllers/{scim_rails → scimaenaga}/scim_groups_controller.rb +26 -26
- data/app/controllers/scimaenaga/scim_schemas_controller.rb +42 -0
- data/app/controllers/scimaenaga/scim_users_controller.rb +104 -0
- data/app/helpers/{scim_rails → scimaenaga}/application_helper.rb +1 -1
- data/app/libraries/scim_patch.rb +16 -9
- data/app/libraries/scim_patch_operation.rb +51 -141
- data/app/libraries/scim_patch_operation_converter.rb +90 -0
- data/app/libraries/scim_patch_operation_group.rb +100 -0
- data/app/libraries/scim_patch_operation_user.rb +53 -0
- data/app/models/{scim_rails → scimaenaga}/application_record.rb +1 -1
- data/app/models/scimaenaga/authorize_api_request.rb +39 -0
- data/app/models/{scim_rails → scimaenaga}/scim_count.rb +8 -4
- data/app/models/scimaenaga/scim_query_parser.rb +49 -0
- data/config/routes.rb +15 -13
- data/lib/generators/scimaenaga/USAGE +8 -0
- data/lib/generators/scimaenaga/scimaenaga_generator.rb +7 -0
- data/lib/generators/{scim_rails → scimaenaga}/templates/initializer.rb +128 -22
- data/lib/{scim_rails → scimaenaga}/config.rb +9 -7
- data/lib/scimaenaga/encoder.rb +27 -0
- data/lib/scimaenaga/engine.rb +12 -0
- data/lib/scimaenaga/version.rb +5 -0
- data/lib/scimaenaga.rb +6 -0
- data/lib/tasks/{scim_rails_tasks.rake → scimaenaga_tasks.rake} +1 -1
- data/spec/controllers/{scim_rails → scimaenaga}/scim_groups_controller_spec.rb +8 -8
- data/spec/controllers/{scim_rails → scimaenaga}/scim_groups_request_spec.rb +18 -18
- data/spec/controllers/scimaenaga/scim_schemas_controller_spec.rb +238 -0
- data/spec/controllers/scimaenaga/scim_schemas_request_spec.rb +39 -0
- data/spec/controllers/{scim_rails → scimaenaga}/scim_users_controller_spec.rb +14 -15
- data/spec/controllers/{scim_rails → scimaenaga}/scim_users_request_spec.rb +20 -20
- data/spec/dummy/app/assets/config/manifest.js +1 -1
- data/spec/dummy/config/application.rb +1 -2
- data/spec/dummy/config/initializers/{scim_rails_config.rb → scimaenaga_config.rb} +25 -25
- data/spec/dummy/config/routes.rb +1 -1
- data/spec/factories/company.rb +3 -3
- data/spec/lib/scimaenaga/encoder_spec.rb +64 -0
- data/spec/libraries/scim_patch_operation_group_spec.rb +165 -0
- data/spec/libraries/scim_patch_operation_user_spec.rb +101 -0
- data/spec/libraries/scim_patch_spec.rb +129 -45
- data/spec/models/scim_query_parser_spec.rb +5 -6
- metadata +107 -108
- data/app/controllers/concerns/scim_rails/response.rb +0 -94
- data/app/controllers/scim_rails/application_controller.rb +0 -72
- data/app/controllers/scim_rails/scim_users_controller.rb +0 -107
- data/app/models/scim_rails/authorize_api_request.rb +0 -40
- data/app/models/scim_rails/scim_query_parser.rb +0 -49
- data/lib/generators/scim_rails/USAGE +0 -8
- data/lib/generators/scim_rails/scim_rails_generator.rb +0 -7
- data/lib/scim_rails/encoder.rb +0 -25
- data/lib/scim_rails/engine.rb +0 -12
- data/lib/scim_rails/version.rb +0 -5
- data/lib/scim_rails.rb +0 -6
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +0 -5770
- data/spec/dummy/put_group.http +0 -5
- data/spec/dummy/tmp/restart.txt +0 -0
- data/spec/lib/scim_rails/encoder_spec.rb +0 -62
- data/spec/libraries/scim_patch_operation_spec.rb +0 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41335471b09b4e09028210f9c05586a1f171cf2e6cf3d12d03b07bddd0022dc3
|
4
|
+
data.tar.gz: 12c2072c1bc4a57274cb427a4507ed2b4a13a038b9e311bfdb7e29fabc94e524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc91680912294a747970e1ede984f5bbe4ad555bfacace02cc844abd6d13b124cb07f62481a6e7a7aaf1258253ca5e3d9fc83d4606cebfc44e50041dbd0e881f
|
7
|
+
data.tar.gz: 87da8ec3d1a3295d1766f13dc8474e525dc0a0c96fc4a458e79279ff48b05d7e8d628311880b10aa15925f149a59a4f54991154eeea605304df4338779dc03d3
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ The goal of the Gem is to offer a relatively painless way of adding SCIM 2.0 to
|
|
17
17
|
Add this line to your application's Gemfile:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
gem 'scimaenaga'
|
20
|
+
gem 'scimaenaga'
|
21
21
|
```
|
22
22
|
|
23
23
|
And then execute:
|
@@ -35,13 +35,13 @@ $ gem install scimaenaga
|
|
35
35
|
Generate the config file with:
|
36
36
|
|
37
37
|
```bash
|
38
|
-
$ rails generate
|
38
|
+
$ rails generate scimaenaga config
|
39
39
|
```
|
40
40
|
|
41
41
|
The config file will be located at:
|
42
42
|
|
43
43
|
```
|
44
|
-
config/initializers/
|
44
|
+
config/initializers/scimaenaga_config.rb
|
45
45
|
```
|
46
46
|
|
47
47
|
Please update the config file with the models and attributes of your app.
|
@@ -50,7 +50,7 @@ Mount the gem in your routes file:
|
|
50
50
|
|
51
51
|
```ruby
|
52
52
|
Application.routes.draw do
|
53
|
-
mount
|
53
|
+
mount Scimaenaga::Engine => "/"
|
54
54
|
end
|
55
55
|
```
|
56
56
|
|
@@ -95,7 +95,7 @@ The config setting `basic_auth_model_authenticatable_attribute` is the model att
|
|
95
95
|
|
96
96
|
Assuming the attribute is `:api_token`, generate the password using:
|
97
97
|
```ruby
|
98
|
-
token =
|
98
|
+
token = Scimaenaga::Encoder.encode(company)
|
99
99
|
# use the token as password for requests
|
100
100
|
company.api_token = token # required
|
101
101
|
company.save! # don't forget to persist the company record
|
@@ -119,7 +119,7 @@ In the config settings, ensure you set `signing_secret` to a secret key that wil
|
|
119
119
|
|
120
120
|
If you have already generated the `api_token` in the "Basic Auth" section, then use that as your bearer token and ignore the steps below:
|
121
121
|
```ruby
|
122
|
-
token =
|
122
|
+
token = Scimaenaga::Encoder.encode(company)
|
123
123
|
# use the token as bearer token for requests
|
124
124
|
company.api_token = token #required
|
125
125
|
company.save! # don't forget to persist the company record
|
@@ -245,11 +245,26 @@ If you would like, you can supply a custom handler for exceptions in the initial
|
|
245
245
|
For example, you might want to notify Honeybadger:
|
246
246
|
|
247
247
|
```ruby
|
248
|
-
|
248
|
+
Scimaenaga.configure do |config|
|
249
249
|
config.on_error = ->(e) { Honeybadger.notify(e) }
|
250
250
|
end
|
251
251
|
```
|
252
252
|
|
253
|
+
### Schemas endpoint
|
254
|
+
|
255
|
+
If you need Schemas endpoint configure `schemas`.
|
256
|
+
(Azure AD requires Schemas endpoint when registering to Application Gallery.)
|
257
|
+
|
258
|
+
You have to configure `schemas` as
|
259
|
+
- corresponding with other configurations.
|
260
|
+
e.g.) When `userName` is defined in `mutable_user_attributes`, configure `userName` as `mutability: 'readWrite'`.
|
261
|
+
|
262
|
+
- corresponding with your model.
|
263
|
+
e.g.) When `userName` must be specified configure `userName` as `required: true`
|
264
|
+
|
265
|
+
Sample config (with comment) is written in lib/generators/scimaenaga/templates/initializer.rb.
|
266
|
+
For more details, read [Schema Definition](https://datatracker.ietf.org/doc/html/rfc7643#section-7), and [Schema Representation](https://datatracker.ietf.org/doc/html/rfc7643#section-8.7)
|
267
|
+
|
253
268
|
## Contributing
|
254
269
|
|
255
270
|
### [Code of Conduct](https://github.com/StudistCorporation/scimaenaga/blob/master/CODE_OF_CONDUCT.md)
|
data/Rakefile
CHANGED
@@ -8,27 +8,25 @@ require 'rdoc/task'
|
|
8
8
|
|
9
9
|
RDoc::Task.new(:rdoc) do |rdoc|
|
10
10
|
rdoc.rdoc_dir = 'rdoc'
|
11
|
-
rdoc.title = '
|
11
|
+
rdoc.title = 'Scimaenaga'
|
12
12
|
rdoc.options << '--line-numbers'
|
13
13
|
rdoc.rdoc_files.include('README.md')
|
14
14
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
15
|
end
|
16
16
|
|
17
|
-
APP_RAKEFILE = File.expand_path(
|
17
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
18
18
|
load 'rails/tasks/engine.rake'
|
19
19
|
|
20
|
-
|
21
20
|
load 'rails/tasks/statistics.rake'
|
22
21
|
|
23
|
-
|
24
22
|
Bundler::GemHelper.install_tasks
|
25
23
|
|
26
|
-
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
24
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
|
27
25
|
|
28
26
|
require 'rspec/core'
|
29
27
|
require 'rspec/core/rake_task'
|
30
28
|
|
31
|
-
desc
|
32
|
-
RSpec::Core::RakeTask.new(:
|
29
|
+
desc 'Run all specs in spec directory (excluding plugin specs)'
|
30
|
+
RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
|
33
31
|
|
34
|
-
task :
|
32
|
+
task default: :spec
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module Scimaenaga
|
4
4
|
module ExceptionHandler
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
@@ -25,10 +25,19 @@ module ScimRails
|
|
25
25
|
class UnexpectedError < StandardError
|
26
26
|
end
|
27
27
|
|
28
|
+
class ResourceNotFound < StandardError
|
29
|
+
attr_reader :id
|
30
|
+
|
31
|
+
def initialize(id)
|
32
|
+
super
|
33
|
+
@id = id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
28
37
|
included do
|
29
38
|
if Rails.env.production?
|
30
39
|
rescue_from StandardError do |exception|
|
31
|
-
on_error =
|
40
|
+
on_error = Scimaenaga.config.on_error
|
32
41
|
if on_error.respond_to?(:call)
|
33
42
|
on_error.call(exception)
|
34
43
|
else
|
@@ -37,98 +46,99 @@ module ScimRails
|
|
37
46
|
|
38
47
|
json_response(
|
39
48
|
{
|
40
|
-
schemas: [
|
41
|
-
status:
|
49
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
50
|
+
status: '500',
|
42
51
|
},
|
43
52
|
:internal_server_error
|
44
53
|
)
|
45
54
|
end
|
46
55
|
end
|
47
56
|
|
48
|
-
rescue_from
|
57
|
+
rescue_from Scimaenaga::ExceptionHandler::InvalidCredentials do
|
49
58
|
json_response(
|
50
59
|
{
|
51
|
-
schemas: [
|
52
|
-
detail:
|
53
|
-
status:
|
60
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
61
|
+
detail: 'Authorization failure. The authorization header is invalid or missing.',
|
62
|
+
status: '401',
|
54
63
|
},
|
55
64
|
:unauthorized
|
56
65
|
)
|
57
66
|
end
|
58
67
|
|
59
|
-
rescue_from
|
68
|
+
rescue_from Scimaenaga::ExceptionHandler::InvalidRequest do |e|
|
60
69
|
json_response(
|
61
70
|
{
|
62
|
-
schemas: [
|
71
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
63
72
|
detail: "Invalid request. #{e.message}",
|
64
|
-
status:
|
73
|
+
status: '400',
|
65
74
|
},
|
66
75
|
:bad_request
|
67
76
|
)
|
68
77
|
end
|
69
78
|
|
70
|
-
rescue_from
|
79
|
+
rescue_from Scimaenaga::ExceptionHandler::InvalidQuery do
|
71
80
|
json_response(
|
72
81
|
{
|
73
|
-
schemas: [
|
74
|
-
scimType:
|
75
|
-
detail:
|
76
|
-
status:
|
82
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
83
|
+
scimType: 'invalidFilter',
|
84
|
+
detail: 'The specified filter syntax was invalid, or the specified attribute and filter comparison combination is not supported.',
|
85
|
+
status: '400',
|
77
86
|
},
|
78
87
|
:bad_request
|
79
88
|
)
|
80
89
|
end
|
81
90
|
|
82
|
-
rescue_from
|
91
|
+
rescue_from Scimaenaga::ExceptionHandler::UnsupportedPatchRequest do
|
83
92
|
json_response(
|
84
93
|
{
|
85
|
-
schemas: [
|
86
|
-
detail:
|
87
|
-
status:
|
94
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
95
|
+
detail: 'Invalid PATCH request.',
|
96
|
+
status: '422',
|
88
97
|
},
|
89
98
|
:unprocessable_entity
|
90
99
|
)
|
91
100
|
end
|
92
101
|
|
93
|
-
rescue_from
|
102
|
+
rescue_from Scimaenaga::ExceptionHandler::UnsupportedDeleteRequest do
|
94
103
|
json_response(
|
95
104
|
{
|
96
|
-
schemas: [
|
97
|
-
detail:
|
98
|
-
status:
|
105
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
106
|
+
detail: 'Delete operation is disabled for the requested resource.',
|
107
|
+
status: '501',
|
99
108
|
},
|
100
109
|
:not_implemented
|
101
110
|
)
|
102
111
|
end
|
103
112
|
|
104
|
-
rescue_from
|
113
|
+
rescue_from Scimaenaga::ExceptionHandler::InvalidConfiguration do |e|
|
105
114
|
json_response(
|
106
115
|
{
|
107
|
-
schemas: [
|
116
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
108
117
|
detail: "Invalid configuration. #{e.message}",
|
109
|
-
status:
|
118
|
+
status: '500',
|
110
119
|
},
|
111
120
|
:internal_server_error
|
112
121
|
)
|
113
122
|
end
|
114
123
|
|
115
|
-
rescue_from
|
124
|
+
rescue_from Scimaenaga::ExceptionHandler::UnexpectedError do |e|
|
116
125
|
json_response(
|
117
126
|
{
|
118
|
-
schemas: [
|
127
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
119
128
|
detail: "Unexpected Error. #{e.message}",
|
120
|
-
status:
|
129
|
+
status: '500',
|
121
130
|
},
|
122
131
|
:internal_server_error
|
123
132
|
)
|
124
133
|
end
|
125
134
|
|
126
|
-
rescue_from ActiveRecord::RecordNotFound
|
135
|
+
rescue_from ActiveRecord::RecordNotFound,
|
136
|
+
Scimaenaga::ExceptionHandler::ResourceNotFound do |e|
|
127
137
|
json_response(
|
128
138
|
{
|
129
|
-
schemas: [
|
139
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
130
140
|
detail: "Resource #{e.id} not found.",
|
131
|
-
status:
|
141
|
+
status: '404',
|
132
142
|
},
|
133
143
|
:not_found
|
134
144
|
)
|
@@ -139,18 +149,18 @@ module ScimRails
|
|
139
149
|
when /has already been taken/
|
140
150
|
json_response(
|
141
151
|
{
|
142
|
-
schemas: [
|
152
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
143
153
|
detail: e.message,
|
144
|
-
status:
|
154
|
+
status: '409',
|
145
155
|
},
|
146
156
|
:conflict
|
147
157
|
)
|
148
158
|
else
|
149
159
|
json_response(
|
150
160
|
{
|
151
|
-
schemas: [
|
161
|
+
schemas: ['urn:ietf:params:scim:api:messages:2.0:Error'],
|
152
162
|
detail: e.message,
|
153
|
-
status:
|
163
|
+
status: '422',
|
154
164
|
},
|
155
165
|
:unprocessable_entity
|
156
166
|
)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scimaenaga
|
4
|
+
module Response
|
5
|
+
CONTENT_TYPE = 'application/scim+json'
|
6
|
+
|
7
|
+
def json_response(object, status = :ok)
|
8
|
+
render \
|
9
|
+
json: object,
|
10
|
+
status: status,
|
11
|
+
content_type: CONTENT_TYPE
|
12
|
+
end
|
13
|
+
|
14
|
+
def json_scim_response(object:, status: :ok, counts: nil)
|
15
|
+
case params[:action]
|
16
|
+
when 'index'
|
17
|
+
render \
|
18
|
+
json: list_response(object, counts),
|
19
|
+
status: status,
|
20
|
+
content_type: CONTENT_TYPE
|
21
|
+
when 'show', 'create', 'put_update', 'patch_update'
|
22
|
+
render \
|
23
|
+
json: object_response(object),
|
24
|
+
status: status,
|
25
|
+
content_type: CONTENT_TYPE
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def list_response(object, counts)
|
32
|
+
object = object
|
33
|
+
.order(:id)
|
34
|
+
.offset(counts.offset)
|
35
|
+
.limit(counts.limit)
|
36
|
+
{
|
37
|
+
schemas: [
|
38
|
+
'urn:ietf:params:scim:api:messages:2.0:ListResponse'
|
39
|
+
],
|
40
|
+
totalResults: counts.total,
|
41
|
+
startIndex: counts.start_index,
|
42
|
+
itemsPerPage: counts.limit,
|
43
|
+
Resources: list_objects(object),
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def list_objects(objects)
|
48
|
+
objects.map do |object|
|
49
|
+
object_response(object)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def object_response(object)
|
54
|
+
schema = case object
|
55
|
+
when Scimaenaga.config.scim_users_model
|
56
|
+
Scimaenaga.config.user_schema
|
57
|
+
when Scimaenaga.config.scim_groups_model
|
58
|
+
Scimaenaga.config.group_schema
|
59
|
+
else
|
60
|
+
raise Scimaenaga::ExceptionHandler::InvalidQuery,
|
61
|
+
"Unknown model: #{object}"
|
62
|
+
end
|
63
|
+
find_value(object, schema)
|
64
|
+
end
|
65
|
+
|
66
|
+
# `find_value` is a recursive method that takes a "user" and a
|
67
|
+
# "user schema" and replaces any symbols in the schema with the
|
68
|
+
# corresponding value from the user. Given a schema with symbols,
|
69
|
+
# `find_value` will search through the object for the symbols,
|
70
|
+
# send those symbols to the model, and replace the symbol with
|
71
|
+
# the return value.
|
72
|
+
|
73
|
+
def find_value(object, schema)
|
74
|
+
case schema
|
75
|
+
when Hash
|
76
|
+
schema.each.with_object({}) do |(key, value), hash|
|
77
|
+
hash[key] = find_value(object, value)
|
78
|
+
end
|
79
|
+
when Array, ActiveRecord::Associations::CollectionProxy
|
80
|
+
schema.map do |value|
|
81
|
+
find_value(object, value)
|
82
|
+
end
|
83
|
+
when Scimaenaga.config.scim_users_model
|
84
|
+
find_value(schema, Scimaenaga.config.user_abbreviated_schema)
|
85
|
+
when Scimaenaga.config.scim_groups_model
|
86
|
+
find_value(schema, Scimaenaga.config.group_abbreviated_schema)
|
87
|
+
when Symbol
|
88
|
+
find_value(object, object.public_send(schema))
|
89
|
+
else
|
90
|
+
schema
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scimaenaga
|
4
|
+
class ApplicationController < ActionController::API
|
5
|
+
include ActionController::HttpAuthentication::Basic::ControllerMethods
|
6
|
+
include ExceptionHandler
|
7
|
+
include Response
|
8
|
+
|
9
|
+
before_action :authorize_request
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def authorize_request
|
14
|
+
send(authentication_strategy) do |searchable_attribute, authentication_attribute|
|
15
|
+
authorization = AuthorizeApiRequest.new(
|
16
|
+
searchable_attribute: searchable_attribute,
|
17
|
+
authentication_attribute: authentication_attribute
|
18
|
+
)
|
19
|
+
@company = authorization.company
|
20
|
+
end
|
21
|
+
raise Scimaenaga::ExceptionHandler::InvalidCredentials if @company.blank?
|
22
|
+
end
|
23
|
+
|
24
|
+
def authentication_strategy
|
25
|
+
if request.headers['Authorization']&.include?('Bearer')
|
26
|
+
:authenticate_with_oauth_bearer
|
27
|
+
else
|
28
|
+
:authenticate_with_http_basic
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def authenticate_with_oauth_bearer
|
33
|
+
authentication_attribute = request.headers['Authorization'].split.last
|
34
|
+
payload = Scimaenaga::Encoder.decode(authentication_attribute).with_indifferent_access
|
35
|
+
searchable_attribute = payload[Scimaenaga.config.basic_auth_model_searchable_attribute]
|
36
|
+
|
37
|
+
yield searchable_attribute, authentication_attribute
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_value_for(attribute)
|
41
|
+
params.dig(*path_for(attribute))
|
42
|
+
end
|
43
|
+
|
44
|
+
# `path_for` is a recursive method used to find the "path" for
|
45
|
+
# `.dig` to take when looking for a given attribute in the
|
46
|
+
# params.
|
47
|
+
#
|
48
|
+
# Example: `path_for(:name)` should return an array that looks
|
49
|
+
# like [:names, 0, :givenName]. `.dig` can then use that path
|
50
|
+
# against the params to translate the :name attribute to "John".
|
51
|
+
|
52
|
+
def path_for(attribute, object = controller_schema, path = [])
|
53
|
+
at_path = path.empty? ? object : object.dig(*path)
|
54
|
+
return path if at_path == attribute
|
55
|
+
|
56
|
+
case at_path
|
57
|
+
when Hash
|
58
|
+
at_path.each do |key, _value|
|
59
|
+
found_path = path_for(attribute, object, [*path, key])
|
60
|
+
return found_path if found_path
|
61
|
+
end
|
62
|
+
nil
|
63
|
+
when Array
|
64
|
+
at_path.each_with_index do |_value, index|
|
65
|
+
found_path = path_for(attribute, object, [*path, index])
|
66
|
+
return found_path if found_path
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,27 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
class ScimGroupsController <
|
3
|
+
module Scimaenaga
|
4
|
+
class ScimGroupsController < Scimaenaga::ApplicationController
|
5
5
|
def index
|
6
6
|
if params[:filter].present?
|
7
|
-
query =
|
8
|
-
params[:filter],
|
7
|
+
query = Scimaenaga::ScimQueryParser.new(
|
8
|
+
params[:filter], Scimaenaga.config.queryable_group_attributes
|
9
9
|
)
|
10
10
|
|
11
11
|
groups = @company
|
12
|
-
.public_send(
|
12
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
13
13
|
.where(
|
14
|
-
"#{
|
14
|
+
"#{Scimaenaga.config.scim_groups_model
|
15
15
|
.connection.quote_column_name(query.attribute)}
|
16
16
|
#{query.operator} ?",
|
17
17
|
query.parameter
|
18
18
|
)
|
19
|
-
.order(
|
19
|
+
.order(Scimaenaga.config.scim_groups_list_order)
|
20
20
|
else
|
21
21
|
groups = @company
|
22
|
-
.public_send(
|
22
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
23
23
|
.preload(:users)
|
24
|
-
.order(
|
24
|
+
.order(Scimaenaga.config.scim_groups_list_order)
|
25
25
|
end
|
26
26
|
|
27
27
|
counts = ScimCount.new(
|
@@ -35,14 +35,14 @@ module ScimRails
|
|
35
35
|
|
36
36
|
def show
|
37
37
|
group = @company
|
38
|
-
.public_send(
|
38
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
39
39
|
.find(params[:id])
|
40
40
|
json_scim_response(object: group)
|
41
41
|
end
|
42
42
|
|
43
43
|
def create
|
44
44
|
group = @company
|
45
|
-
.public_send(
|
45
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
46
46
|
.create!(permitted_group_params)
|
47
47
|
|
48
48
|
json_scim_response(object: group, status: :created)
|
@@ -50,7 +50,7 @@ module ScimRails
|
|
50
50
|
|
51
51
|
def put_update
|
52
52
|
group = @company
|
53
|
-
.public_send(
|
53
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
54
54
|
.find(params[:id])
|
55
55
|
group.update!(permitted_group_params)
|
56
56
|
json_scim_response(object: group)
|
@@ -58,32 +58,32 @@ module ScimRails
|
|
58
58
|
|
59
59
|
def patch_update
|
60
60
|
group = @company
|
61
|
-
.public_send(
|
61
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
62
62
|
.find(params[:id])
|
63
|
-
patch = ScimPatch.new(params,
|
63
|
+
patch = ScimPatch.new(params, :group)
|
64
64
|
patch.save(group)
|
65
65
|
|
66
66
|
json_scim_response(object: group)
|
67
67
|
end
|
68
68
|
|
69
69
|
def destroy
|
70
|
-
unless
|
71
|
-
raise
|
70
|
+
unless Scimaenaga.config.group_destroy_method
|
71
|
+
raise Scimaenaga::ExceptionHandler::InvalidConfiguration
|
72
72
|
end
|
73
73
|
|
74
74
|
group = @company
|
75
|
-
.public_send(
|
75
|
+
.public_send(Scimaenaga.config.scim_groups_scope)
|
76
76
|
.find(params[:id])
|
77
77
|
raise ActiveRecord::RecordNotFound unless group
|
78
78
|
|
79
79
|
begin
|
80
|
-
group.public_send(
|
80
|
+
group.public_send(Scimaenaga.config.group_destroy_method)
|
81
81
|
rescue NoMethodError => e
|
82
|
-
raise
|
82
|
+
raise Scimaenaga::ExceptionHandler::InvalidConfiguration, e.message
|
83
83
|
rescue ActiveRecord::RecordNotDestroyed => e
|
84
|
-
raise
|
85
|
-
rescue => e
|
86
|
-
raise
|
84
|
+
raise Scimaenaga::ExceptionHandler::InvalidRequest, e.message
|
85
|
+
rescue StandardError => e
|
86
|
+
raise Scimaenaga::ExceptionHandler::UnexpectedError, e.message
|
87
87
|
end
|
88
88
|
|
89
89
|
head :no_content
|
@@ -102,19 +102,19 @@ module ScimRails
|
|
102
102
|
|
103
103
|
def member_params
|
104
104
|
{
|
105
|
-
|
105
|
+
Scimaenaga.config.group_member_relation_attribute =>
|
106
106
|
params[:members].map do |member|
|
107
|
-
member[
|
107
|
+
member[Scimaenaga.config.group_member_relation_schema.keys.first]
|
108
108
|
end,
|
109
109
|
}
|
110
110
|
end
|
111
111
|
|
112
112
|
def mutable_attributes
|
113
|
-
|
113
|
+
Scimaenaga.config.mutable_group_attributes
|
114
114
|
end
|
115
115
|
|
116
116
|
def controller_schema
|
117
|
-
|
117
|
+
Scimaenaga.config.mutable_group_attributes_schema
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scimaenaga
|
4
|
+
class ScimSchemasController < Scimaenaga::ApplicationController
|
5
|
+
def index
|
6
|
+
schemas = Scimaenaga.config.schemas
|
7
|
+
|
8
|
+
counts = ScimCount.new(
|
9
|
+
start_index: params[:startIndex],
|
10
|
+
limit: params[:count],
|
11
|
+
total: schemas.count
|
12
|
+
)
|
13
|
+
|
14
|
+
list_schemas_response(schemas, counts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
schema = Scimaenaga.config.schemas.find do |s|
|
19
|
+
s[:id] == params[:id]
|
20
|
+
end
|
21
|
+
|
22
|
+
raise Scimaenaga::ExceptionHandler::ResourceNotFound, params[:id] if schema.nil?
|
23
|
+
|
24
|
+
json_response(schema)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def list_schemas_response(schemas, counts)
|
30
|
+
response = {
|
31
|
+
schemas: [
|
32
|
+
'urn:ietf:params:scim:api:messages:2.0:ListResponse'
|
33
|
+
],
|
34
|
+
totalResults: counts.total,
|
35
|
+
startIndex: counts.start_index,
|
36
|
+
itemsPerPage: counts.limit,
|
37
|
+
Resources: schemas[counts.offset...counts.offset + counts.limit],
|
38
|
+
}
|
39
|
+
json_response(response)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|