scim_rails 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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