scim_rails 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/app/controllers/concerns/scim_rails/exception_handler.rb +14 -0
- data/app/controllers/scim_rails/scim_users_controller.rb +15 -5
- data/lib/scim_rails/version.rb +1 -1
- data/spec/controllers/scim_rails/scim_users_controller_spec.rb +55 -8
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/log/development.log +476 -0
- data/spec/dummy/log/test.log +50125 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 852439c711dfe1b37da47988060c40324985ff23
|
4
|
+
data.tar.gz: ef61b7184ed183976e3c48833ba8b330e0ef773e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b056aceb8507e3587bc33dcc6387f0cf0ec2f29bbfce0d00102056676285fd1dad15a1387e3b2d2041a918ec1b9fbf7e0a150b614757ade503196558e6ff6f93
|
7
|
+
data.tar.gz: 1d2340490c77f232f87e05f7775d1c482333aba9974a74efc599e55a8819f3b8df439249300c5d1c6c3adc3ef23d6426048c7779d21f6a90b5abfe1b91666c15
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ NOTE: This Gem is not yet fully SCIM complaint. It was developed with the main f
|
|
4
4
|
|
5
5
|
#### What is SCIM?
|
6
6
|
|
7
|
-
SCIM stands for System for Cross-domain Identity Management. At its core, it is a set of rules defining how apps should interact for the purpose of creating, updating, and deprovisioning users. SCIM requests and responses can be sent in XML or JSON and this Gem uses JSON for ease of
|
7
|
+
SCIM stands for System for Cross-domain Identity Management. At its core, it is a set of rules defining how apps should interact for the purpose of creating, updating, and deprovisioning users. SCIM requests and responses can be sent in XML or JSON and this Gem uses JSON for ease of readability.
|
8
8
|
|
9
9
|
To learn more about SCIM 2.0 you can read the documentation at [RFC 7643](https://tools.ietf.org/html/rfc7643) and [RFC 7644](https://tools.ietf.org/html/rfc7644).
|
10
10
|
|
@@ -92,7 +92,7 @@ $ curl -X GET 'http://username:password@localhost:3000/scim/v2/Users'
|
|
92
92
|
|
93
93
|
This Gem provides two pagination filters; `startIndex` and `count`.
|
94
94
|
|
95
|
-
`startIndex` is the positional number you would like to start at. This parameter can accept any integer but anything less than 1 will be interpreted as 1. If you visualize an array with all your user records in the array, `startIndex` is basically what element you would like to start at. If you are familiar with SQL this parameter is directly correlated to the query offset. **The default value for this
|
95
|
+
`startIndex` is the positional number you would like to start at. This parameter can accept any integer but anything less than 1 will be interpreted as 1. If you visualize an array with all your user records in the array, `startIndex` is basically what element you would like to start at. If you are familiar with SQL this parameter is directly correlated to the query offset. **The default value for this filter is 1.**
|
96
96
|
|
97
97
|
`count` is the number of records you would like present in the response. **The default value for this filter is 100.**
|
98
98
|
|
@@ -108,7 +108,7 @@ The pagination filters may be used on their own or in addition to the query filt
|
|
108
108
|
|
109
109
|
##### Querying
|
110
110
|
|
111
|
-
Currently the only filter supported is a single level `eq`. More operators can be added
|
111
|
+
Currently the only filter supported is a single level `eq`. More operators can be added fairly easily in future releases. The SCIM RFC documents nested querying which is something we would like to implement in the future.
|
112
112
|
|
113
113
|
**Queryable attributes can be mapped in the configuration file.**
|
114
114
|
|
@@ -121,7 +121,7 @@ filter=formattedName eq Test User
|
|
121
121
|
filter=id eq 1
|
122
122
|
```
|
123
123
|
|
124
|
-
|
124
|
+
Unsupported filter:
|
125
125
|
|
126
126
|
```
|
127
127
|
filter=(email eq test@example.com) or (userName eq test@example.com)
|
@@ -183,16 +183,16 @@ Sample request:
|
|
183
183
|
$ curl -X PUT 'http://username:password@localhost:3000/scim/v2/Users/1' -d '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"userName":"test@example.com","name":{"givenName":"Test","familyName":"User"},"emails":[{"primary":true,"value":"test@example.com","type":"work"}],"displayName":"Test User","active":true}' -H 'Content-Type: application/scim+json'
|
184
184
|
```
|
185
185
|
|
186
|
-
### Deprovision
|
186
|
+
### Deprovision / Reprovision
|
187
187
|
|
188
|
-
The PATCH request was implemented to work with Okta. Okta updates profiles with PUT and deprovisions with PATCH. This
|
188
|
+
The PATCH request was implemented to work with Okta. Okta updates profiles with PUT and deprovisions / reprovisions with PATCH. This implementation of PATCH is not SCIM compliant as it does not update a single attribute on the user profile but instead only sends a status update request to the record.
|
189
189
|
|
190
190
|
We would like to implement PATCH to be fully SCIM compliant in future releases.
|
191
191
|
|
192
192
|
Sample request:
|
193
193
|
|
194
194
|
```bash
|
195
|
-
$ curl -X PATCH 'http://username:password@localhost:3000/scim/v2/Users/1'
|
195
|
+
$ curl -X PATCH 'http://username:password@localhost:3000/scim/v2/Users/1' -d '{"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{"op": "replace", "value": { "active": false }}]}' -H 'Content-Type: application/scim+json'
|
196
196
|
```
|
197
197
|
|
198
198
|
## Contributing
|
@@ -8,6 +8,9 @@ module ScimRails
|
|
8
8
|
class InvalidQuery < StandardError
|
9
9
|
end
|
10
10
|
|
11
|
+
class UnsupportedPatchRequest < StandardError
|
12
|
+
end
|
13
|
+
|
11
14
|
included do
|
12
15
|
rescue_from ScimRails::ExceptionHandler::InvalidCredentials do
|
13
16
|
json_response(
|
@@ -32,6 +35,17 @@ module ScimRails
|
|
32
35
|
)
|
33
36
|
end
|
34
37
|
|
38
|
+
rescue_from ScimRails::ExceptionHandler::UnsupportedPatchRequest do
|
39
|
+
json_response(
|
40
|
+
{
|
41
|
+
schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
|
42
|
+
detail: "Invalid PATCH request. This PATCH endpoint only supports deprovisioning and reprovisioning records.",
|
43
|
+
status: "422"
|
44
|
+
},
|
45
|
+
:unprocessable_entity
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
35
49
|
rescue_from ActiveRecord::RecordNotFound do |e|
|
36
50
|
json_response(
|
37
51
|
{
|
@@ -28,7 +28,7 @@ module ScimRails
|
|
28
28
|
|
29
29
|
def create
|
30
30
|
user = @company.public_send(ScimRails.config.scim_users_scope).create!(permitted_user_params)
|
31
|
-
update_status(user) unless
|
31
|
+
update_status(user) unless put_active_param.nil?
|
32
32
|
json_scim_response(object: user, status: :created)
|
33
33
|
end
|
34
34
|
|
@@ -39,16 +39,15 @@ module ScimRails
|
|
39
39
|
|
40
40
|
def put_update
|
41
41
|
user = @company.public_send(ScimRails.config.scim_users_scope).find(params[:id])
|
42
|
-
update_status(user) unless
|
42
|
+
update_status(user) unless put_active_param.nil?
|
43
43
|
user.update!(permitted_user_params)
|
44
44
|
json_scim_response(object: user)
|
45
45
|
end
|
46
46
|
|
47
|
-
# TODO: PATCH will only deprovision
|
47
|
+
# TODO: PATCH will only deprovision or reprovision users.
|
48
48
|
# This will work just fine for Okta but is not SCIM compliant.
|
49
49
|
def patch_update
|
50
50
|
user = @company.public_send(ScimRails.config.scim_users_scope).find(params[:id])
|
51
|
-
params[:active] = false
|
52
51
|
update_status(user)
|
53
52
|
json_scim_response(object: user)
|
54
53
|
end
|
@@ -99,7 +98,8 @@ module ScimRails
|
|
99
98
|
end
|
100
99
|
|
101
100
|
def active?
|
102
|
-
|
101
|
+
active = put_active_param || patch_active_param
|
102
|
+
case active
|
103
103
|
when true, "true", 1
|
104
104
|
true
|
105
105
|
when false, "false", 0
|
@@ -108,5 +108,15 @@ module ScimRails
|
|
108
108
|
raise ActiveRecord::RecordInvalid
|
109
109
|
end
|
110
110
|
end
|
111
|
+
|
112
|
+
def put_active_param
|
113
|
+
params[:active]
|
114
|
+
end
|
115
|
+
|
116
|
+
def patch_active_param
|
117
|
+
active = params.dig("Operations", 0, "value", "active")
|
118
|
+
raise ScimRails::ExceptionHandler::UnsupportedPatchRequest if active.nil?
|
119
|
+
active
|
120
|
+
end
|
111
121
|
end
|
112
122
|
end
|
data/lib/scim_rails/version.rb
CHANGED
@@ -457,13 +457,13 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
457
457
|
|
458
458
|
context "when unauthorized" do
|
459
459
|
it "returns scim+json content type" do
|
460
|
-
patch :patch_update, params:
|
460
|
+
patch :patch_update, params: patch_params(id: 1)
|
461
461
|
|
462
462
|
expect(response.content_type).to eq "application/scim+json, application/json"
|
463
463
|
end
|
464
464
|
|
465
465
|
it "fails with no credentials" do
|
466
|
-
patch :patch_update, params:
|
466
|
+
patch :patch_update, params: patch_params(id: 1)
|
467
467
|
|
468
468
|
expect(response.status).to eq 401
|
469
469
|
end
|
@@ -471,7 +471,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
471
471
|
it "fails with invalid credentials" do
|
472
472
|
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials("unauthorized","123456")
|
473
473
|
|
474
|
-
patch :patch_update, params:
|
474
|
+
patch :patch_update, params: patch_params(id: 1)
|
475
475
|
|
476
476
|
expect(response.status).to eq 401
|
477
477
|
end
|
@@ -485,19 +485,19 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
485
485
|
end
|
486
486
|
|
487
487
|
it "returns scim+json content type" do
|
488
|
-
patch :patch_update, params:
|
488
|
+
patch :patch_update, params: patch_params(id: 1)
|
489
489
|
|
490
490
|
expect(response.content_type).to eq "application/scim+json, application/json"
|
491
491
|
end
|
492
492
|
|
493
493
|
it "is successful with valid credentials" do
|
494
|
-
patch :patch_update, params:
|
494
|
+
patch :patch_update, params: patch_params(id: 1)
|
495
495
|
|
496
496
|
expect(response.status).to eq 200
|
497
497
|
end
|
498
498
|
|
499
499
|
it "returns :not_found for id that cannot be found" do
|
500
|
-
get :patch_update, params:
|
500
|
+
get :patch_update, params: patch_params(id: "fake_id")
|
501
501
|
|
502
502
|
expect(response.status).to eq 404
|
503
503
|
end
|
@@ -506,7 +506,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
506
506
|
new_company = create(:company)
|
507
507
|
create(:user, company: new_company, id: 1000)
|
508
508
|
|
509
|
-
get :patch_update, params:
|
509
|
+
get :patch_update, params: patch_params(id: 1000)
|
510
510
|
|
511
511
|
expect(response.status).to eq 404
|
512
512
|
end
|
@@ -516,13 +516,60 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
|
|
516
516
|
user = company.users.first
|
517
517
|
expect(user.archived?).to eq false
|
518
518
|
|
519
|
-
patch :patch_update, params:
|
519
|
+
patch :patch_update, params: patch_params(id: 1)
|
520
520
|
|
521
521
|
expect(response.status).to eq 200
|
522
522
|
expect(company.users.count).to eq 1
|
523
523
|
user.reload
|
524
524
|
expect(user.archived?).to eq true
|
525
525
|
end
|
526
|
+
|
527
|
+
it "sucessfully restores user" do
|
528
|
+
expect(company.users.count).to eq 1
|
529
|
+
user = company.users.first.tap(&:archive!)
|
530
|
+
expect(user.archived?).to eq true
|
531
|
+
|
532
|
+
patch :patch_update, params: patch_params(id: 1, active: true)
|
533
|
+
|
534
|
+
expect(response.status).to eq 200
|
535
|
+
expect(company.users.count).to eq 1
|
536
|
+
user.reload
|
537
|
+
expect(user.archived?).to eq false
|
538
|
+
end
|
539
|
+
|
540
|
+
it "throws an error for non status updates" do
|
541
|
+
patch :patch_update, params: {
|
542
|
+
id: 1,
|
543
|
+
Operations: [
|
544
|
+
{
|
545
|
+
op: "replace",
|
546
|
+
value: {
|
547
|
+
name: {
|
548
|
+
givenName: "Francis"
|
549
|
+
}
|
550
|
+
}
|
551
|
+
}
|
552
|
+
]
|
553
|
+
}
|
554
|
+
|
555
|
+
expect(response.status).to eq 422
|
556
|
+
response_body = JSON.parse(response.body)
|
557
|
+
expect(response_body.dig("schemas", 0)).to eq "urn:ietf:params:scim:api:messages:2.0:Error"
|
558
|
+
end
|
526
559
|
end
|
527
560
|
end
|
561
|
+
|
562
|
+
def patch_params(id:, active: false)
|
563
|
+
{
|
564
|
+
id: id,
|
565
|
+
Operations: [
|
566
|
+
{
|
567
|
+
op: "replace",
|
568
|
+
value: {
|
569
|
+
active: active
|
570
|
+
}
|
571
|
+
}
|
572
|
+
]
|
573
|
+
}
|
574
|
+
end
|
528
575
|
end
|
Binary file
|