scim_rails 0.2.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffb0afaa648c6bf2e0daf0836313e1b5c37203642588922296489b7f87dc8a78
4
- data.tar.gz: e4d007055da4ae528a9953ad43737df15b85c44490d5c7ca5bb97954ddf7d9cf
3
+ metadata.gz: 69ddfe751f11e33adf91e96b99ccaaa7258bb1468ea8463511ae0b04eaeca4bf
4
+ data.tar.gz: ac7a95b8e8f7c5455bdf7b9a64d3b2c88c015974530077f80ab37a1e6751e468
5
5
  SHA512:
6
- metadata.gz: 1293946a2411ad4855301adb70c24350808f3a35c38c5b66ee833f7330e622efee2ebacc94218ce577551bda4794e1e329cee311518fc60316289022738f4e4f
7
- data.tar.gz: a6317481acf35c7640c834a81b8676dc80899c9cebaa36ff74dd2139cbfafa7b9167d423f4e201f7a637284458981a942315a842f14dcfd224736c422e139259
6
+ metadata.gz: 05d83eb5dee1ecbfa49116f5d1f70e5f4e600393419055d93dfdff8e735e865ef2eeedf15b73c5899543f47168763b7f7cf13b897ba53e80332d7d813b427219
7
+ data.tar.gz: be1735ff8d1de4d79d5aa70c39b0cffdbc22f26d7adc105e5c03ec2fc61953d00a7ba363b0e59b57adf3aae3f32a880275eac00eaf5874adae789954ea22c3f1
data/README.md CHANGED
@@ -78,6 +78,57 @@ When sending requests to the server the `Content-Type` should be set to `applica
78
78
 
79
79
  All responses will be sent with a `Content-Type` of `application/scim+json`.
80
80
 
81
+ #### Authentication
82
+
83
+ This gem supports both basic and OAuth bearer authentication.
84
+
85
+ ##### Basic Auth
86
+ ###### Username
87
+ The config setting `basic_auth_model_searchable_attribute` is the model attribute used to authenticate as the `username`. It defaults to `:subdomain`.
88
+
89
+ Ensure it is unique to the model records.
90
+
91
+ ###### Password
92
+ The config setting `basic_auth_model_authenticatable_attribute` is the model attribute used to authenticate as `password`. Defaults to `:api_token`.
93
+
94
+ Assuming the attribute is `:api_token`, generate the password using:
95
+ ```ruby
96
+ token = ScimRails::Encoder.encode(company)
97
+ # use the token as password for requests
98
+ company.api_token = token # required
99
+ company.save! # don't forget to persist the company record
100
+ ```
101
+
102
+ This is necessary irrespective of your authentication choice(s) - basic auth, oauth bearer or both.
103
+
104
+ ###### Sample Request
105
+
106
+ ```bash
107
+ $ curl -X GET 'http://username:password@localhost:3000/scim/v2/Users'
108
+ ```
109
+
110
+ ##### OAuth Bearer
111
+
112
+ ###### Signing Algorithm
113
+ In the config settings, ensure you set `signing_algorithm` to a valid JWT signing algorithm, e.g "HS256". Defaults to `"none"` when not set.
114
+
115
+ ###### Signing Secret
116
+ In the config settings, ensure you set `signing_secret` to a secret key that will be used to encode and decode tokens. Defaults to `nil` when not set.
117
+
118
+ 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:
119
+ ```ruby
120
+ token = ScimRails::Encoder.encode(company)
121
+ # use the token as bearer token for requests
122
+ company.api_token = token #required
123
+ company.save! # don't forget to persist the company record
124
+ ```
125
+
126
+ ##### Sample Request
127
+
128
+ ```bash
129
+ $ curl -H 'Authorization: Bearer xxxxxxx.xxxxxx' -X GET 'http://localhost:3000/scim/v2/Users'
130
+ ```
131
+
81
132
  ### List
82
133
 
83
134
  ##### All
@@ -9,14 +9,30 @@ module ScimRails
9
9
  private
10
10
 
11
11
  def authorize_request
12
- authenticate_with_http_basic do |username, password|
12
+ send(authentication_strategy) do |searchable_attribute, authentication_attribute|
13
13
  authorization = AuthorizeApiRequest.new(
14
- searchable_attribute: username,
15
- authentication_attribute: password
14
+ searchable_attribute: searchable_attribute,
15
+ authentication_attribute: authentication_attribute
16
16
  )
17
17
  @company = authorization.company
18
18
  end
19
- raise ScimRails::ExceptionHandler::InvalidCredentials if @company.blank?
19
+ raise ScimRails::ExceptionHandler::InvalidCredentials if @company.blank?
20
+ end
21
+
22
+ def authentication_strategy
23
+ if request.headers["Authorization"]&.include?("Bearer")
24
+ :authenticate_with_oauth_bearer
25
+ else
26
+ :authenticate_with_http_basic
27
+ end
28
+ end
29
+
30
+ def authenticate_with_oauth_bearer
31
+ authentication_attribute = request.headers["Authorization"].split(" ").last
32
+ payload = ScimRails::Encoder.decode(authentication_attribute).with_indifferent_access
33
+ searchable_attribute = payload[ScimRails.config.basic_auth_model_searchable_attribute]
34
+
35
+ yield searchable_attribute, authentication_attribute
20
36
  end
21
37
  end
22
38
  end
@@ -22,6 +22,18 @@ ScimRails.configure do |config|
22
22
  # or throws an error (returning 409 Conflict in accordance with SCIM spec)
23
23
  config.scim_user_prevent_update_on_create = false
24
24
 
25
+ # Cryptographic algorithm used for signing the auth tokens.
26
+ # It supports all algorithms supported by the jwt gem.
27
+ # See https://github.com/jwt/ruby-jwt#algorithms-and-usage for supported algorithms
28
+ # It is "none" by default, hence generated tokens are unsigned
29
+ # The tokens do not need to be signed if you only need basic authentication.
30
+ # config.signing_algorithm = "HS256"
31
+
32
+ # Secret token used to sign authorization tokens
33
+ # It is `nil` by default, hence generated tokens are unsigned
34
+ # The tokens do not need to be signed if you only need basic authentication.
35
+ # config.signing_secret = SECRET_TOKEN
36
+
25
37
  # Default sort order for pagination is by id. If you
26
38
  # use non sequential ids for user records, uncomment
27
39
  # the below line and configure a determinate order.
@@ -10,6 +10,8 @@ module ScimRails
10
10
  end
11
11
 
12
12
  class Config
13
+ ALGO_NONE = "none".freeze
14
+
13
15
  attr_accessor \
14
16
  :basic_auth_model,
15
17
  :basic_auth_model_authenticatable_attribute,
@@ -21,6 +23,8 @@ module ScimRails
21
23
  :scim_users_model,
22
24
  :scim_users_scope,
23
25
  :scim_user_prevent_update_on_create,
26
+ :signing_secret,
27
+ :signing_algorithm,
24
28
  :user_attributes,
25
29
  :user_deprovision_method,
26
30
  :user_reprovision_method,
@@ -30,6 +34,7 @@ module ScimRails
30
34
  @basic_auth_model = "Company"
31
35
  @scim_users_list_order = :id
32
36
  @scim_users_model = "User"
37
+ @signing_algorithm = ALGO_NONE
33
38
  @user_schema = {}
34
39
  @user_attributes = []
35
40
  end
@@ -0,0 +1,25 @@
1
+ require "jwt"
2
+
3
+ module ScimRails
4
+ module Encoder
5
+ extend self
6
+
7
+ def encode(company)
8
+ payload = {
9
+ iat: Time.current.to_i,
10
+ ScimRails.config.basic_auth_model_searchable_attribute =>
11
+ company.public_send(ScimRails.config.basic_auth_model_searchable_attribute)
12
+ }
13
+
14
+ JWT.encode(payload, ScimRails.config.signing_secret, ScimRails.config.signing_algorithm)
15
+ end
16
+
17
+ def decode(token)
18
+ verify = ScimRails.config.signing_algorithm != ScimRails::Config::ALGO_NONE
19
+
20
+ JWT.decode(token, ScimRails.config.signing_secret, verify, algorithm: ScimRails.config.signing_algorithm).first
21
+ rescue JWT::VerificationError, JWT::DecodeError
22
+ raise ScimRails::ExceptionHandler::InvalidCredentials
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module ScimRails
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/scim_rails.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "scim_rails/engine"
2
2
  require "scim_rails/config"
3
+ require "scim_rails/encoder"
3
4
 
4
5
  module ScimRails
5
6
  end
@@ -537,7 +537,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :controller do
537
537
  expect(user.archived?).to eq true
538
538
  end
539
539
 
540
- it "sucessfully restores user" do
540
+ it "successfully restores user" do
541
541
  expect(company.users.count).to eq 1
542
542
  user = company.users.first.tap(&:archive!)
543
543
  expect(user.archived?).to eq true
@@ -5,7 +5,7 @@ RSpec.describe ScimRails::ScimUsersController, type: :request do
5
5
  let(:credentials) { Base64::encode64("#{company.subdomain}:#{company.api_token}") }
6
6
  let(:authorization) { "Basic #{credentials}" }
7
7
 
8
- def post_request(content_type)
8
+ def post_request(content_type = "application/scim+json")
9
9
  # params need to be transformed into a string to test if they are being parsed by Rack
10
10
 
11
11
  post "/scim_rails/scim/v2/Users",
@@ -48,4 +48,26 @@ RSpec.describe ScimRails::ScimUsersController, type: :request do
48
48
  expect(company.users.count).to eq 0
49
49
  end
50
50
  end
51
+
52
+ context "OAuth Bearer Authorization" do
53
+ context "with valid token" do
54
+ let(:authorization) { "Bearer #{company.api_token}" }
55
+
56
+ it "supports OAuth bearer authorization and succeeds" do
57
+ expect { post_request }.to change(company.users, :count).from(0).to(1)
58
+
59
+ expect(response.status).to eq 201
60
+ end
61
+ end
62
+
63
+ context "with invalid token" do
64
+ let(:authorization) { "Bearer #{SecureRandom.hex}" }
65
+
66
+ it "The request fails" do
67
+ expect { post_request }.not_to change(company.users, :count)
68
+
69
+ expect(response.status).to eq 401
70
+ end
71
+ end
72
+ end
51
73
  end
@@ -7,6 +7,9 @@ ScimRails.configure do |config|
7
7
  config.scim_users_scope = :users
8
8
  config.scim_users_list_order = :id
9
9
 
10
+ config.signing_algorithm = "HS256"
11
+ config.signing_secret = "2d6806dd11c2fece2e81b8ca76dcb0062f5b08e28e3264e8ba1c44bbd3578b70"
12
+
10
13
  config.user_deprovision_method = :archive!
11
14
  config.user_reprovision_method = :unarchive!
12
15