rodauth 1.21.0 → 1.22.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: e2660e5b2f14afadab7d56a0a7329ff3f22cea0bb800fa952b3b40c3dcfbcb29
4
- data.tar.gz: 34b96d4d8bc26d641288af6bced9a42b9a895790b019a03b00c3f0e55b67843b
3
+ metadata.gz: 3379479fde31b70cd341f7d862088a1c29f850a4293bbf08a4ddced9f152a026
4
+ data.tar.gz: 3ec8abcd54a4da0a230d8d5a064d75febc247ee6ed35e3282b9b3ab2e1dde21d
5
5
  SHA512:
6
- metadata.gz: c1f90ea3269e61b7de2e336bbe08ccde86b9d343d4b773971fcda7c4f3294804d9b251087978a57ae6eb0fea0a435fd16f0dd239ae524c465739fa6a129b3584
7
- data.tar.gz: 6d8b86790ac1214bd48fffea3aaeffdce63351b8bf6fc40dce6ca96fa471f84a2d08be525d804f5138ba5664ac73bbc68bfda6f279ef17655fc4f24e64bd23d0
6
+ metadata.gz: 6617d309568f8292c01d00343fdea602692fc6ca0be38d9846f0e8415a6da13cee10007e3286e03e2eb9170f693d4b2420861167225aab35ebea87b111627819
7
+ data.tar.gz: 30efd30667ccb724c0796aa58123beb4d2711ac92a032be21d900ae74391901521d60b6b4806463494fc748461ecac3adb28ce3bf7c1956e6dcd4b72b0fea963
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ === 1.22.0 (2019-10-29)
2
+
3
+ * Add jwt_cors feature to handle Cross-Origin Resource Sharing when using the jwt feature (jeremyevans)
4
+
5
+ * Add space before newline after links in email, fixing issues with some webmail providers with broken autolinkers (jeremyevans)
6
+
1
7
  === 1.21.0 (2019-07-24)
2
8
 
3
9
  * Support rotp 5.1 in the otp feature (jeremyevans)
data/README.rdoc CHANGED
@@ -42,6 +42,7 @@ hashes by protecting access via database functions.
42
42
  * Single Session (Only one active session per account)
43
43
  * JWT (JSON API support for all other features)
44
44
  * JWT Refresh (Access & Refresh Token)
45
+ * JWT CORS (Cross-Origin Resource Sharing)
45
46
  * Update Password Hash (when hash cost changes)
46
47
  * HTTP Basic Auth
47
48
  * Change Password Notify
@@ -811,7 +812,8 @@ view the appropriate file in the doc directory.
811
812
  * {Session Expiration}[rdoc-ref:doc/session_expiration.rdoc]
812
813
  * {Single Session}[rdoc-ref:doc/single_session.rdoc]
813
814
  * {JWT}[rdoc-ref:doc/jwt.rdoc]
814
- * {JWT}[rdoc-ref:doc/jwt_refresh.rdoc]
815
+ * {JWT Refresh}[rdoc-ref:doc/jwt_refresh.rdoc]
816
+ * {JWT CORS}[rdoc-ref:doc/jwt_cors.rdoc]
815
817
  * {HTTP Basic Auth}[rdoc-ref:doc/http_basic_auth.rdoc]
816
818
 
817
819
  === Calling Rodauth in the Routing Tree
data/doc/base.rdoc CHANGED
@@ -64,7 +64,7 @@ input_field_label_suffix :: The suffix to use for all labels. Useful for noting
64
64
  input_field_error_class :: The CSS class to use for input fields with errors. Can be a
65
65
  space separated string for multiple CSS classes.
66
66
  input_field_error_message_class :: The CSS class to use for error messages. Can be a
67
- space separated string for multiple CSS classes.
67
+ space separated string for multiple CSS classes.
68
68
  invalid_field_error_status :: The response status to use for invalid field
69
69
  value errors, 422 by default.
70
70
  invalid_key_error_status :: The response status to use for invalid key codes,
data/doc/jwt_cors.rdoc ADDED
@@ -0,0 +1,23 @@
1
+ = Documentation for JWT CORS Feature
2
+
3
+ The jwt_cors feature adds support for Cross-Origin Resource Sharing
4
+ to Rodauth's JSON API.
5
+
6
+ When this feature is used, CORS requests are handled. This includes
7
+ CORS preflight requests, which are required since Rodauth's JSON API
8
+ uses the application/json request content type.
9
+
10
+ This feature depends on the jwt feature.
11
+
12
+ == Auth Value Methods
13
+
14
+ jwt_cors_allow_origin :: Which origins are allowed to perform CORS requests. The default is +false+. This can be a String, Array of Strings, Regexp, or +true+ to allow CORS requests from any domain.
15
+ jwt_cors_allow_methods :: For allowed CORS-preflight requests, the value returned in the Access-Control-Allow-Methods header (default: 'POST'). This specifies which methods are allowed in CORS requests.
16
+ jwt_cors_allow_headers :: For allowed CORS-preflight requests, the value returned in the Access-Control-Allow-Headers header (default: 'Content-Type, Authorization, Accept'). This specifies which headers can be included in CORS requests.
17
+ jwt_cors_expose_headers :: For allowed CORS requests, the value returned in the Access-Control-Expose-Headers header (default: 'Authorization'). This specifies which headers the browser is allowed to access from a response to a CORS request.
18
+ jwt_cors_max_age :: For allowed CORS-preflight requests, the value returned in the Access-Control-Max-Age header (default: 86400). This specifies how long before the information returned should be considered stale and another CORS preflight request made.
19
+
20
+ == Auth Methods
21
+
22
+ jwt_cors_allow? :: Whether the request should be allowed. This is called for all requests for a Rodauth route that include an Origin header. It should return true or false for whether to specially handle the cross-origin request. By default, uses the +jwt_cors_allow_origin+ setting to check the origin.
23
+
@@ -0,0 +1,11 @@
1
+ = New Features
2
+
3
+ * A jwt_cors feature has been added, handling Cross-Origin Resource
4
+ Sharing when using the jwt feature, including supporting CORS
5
+ preflight requests.
6
+
7
+ = Other Improvements
8
+
9
+ * Mail templates that include links (e.g. for verifying accounts),
10
+ now add a space after the link and before the newline, fixing
11
+ issues with some web mail providers that have broken auto-linkers.
@@ -0,0 +1,53 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:jwt_cors, :JwtCors) do
5
+ depends :jwt
6
+
7
+ auth_value_method :jwt_cors_allow_origin, false
8
+ auth_value_method :jwt_cors_allow_methods, 'POST'
9
+ auth_value_method :jwt_cors_allow_headers, 'Content-Type, Authorization, Accept'
10
+ auth_value_method :jwt_cors_expose_headers, 'Authorization'
11
+ auth_value_method :jwt_cors_max_age, 86400
12
+
13
+ auth_methods(:jwt_cors_allow?)
14
+
15
+ def jwt_cors_allow?
16
+ if origin = request.env['HTTP_ORIGIN']
17
+ case allowed = jwt_cors_allow_origin
18
+ when String
19
+ timing_safe_eql?(origin, allowed)
20
+ when Array
21
+ allowed.any?{|s| timing_safe_eql?(origin, s)}
22
+ when Regexp
23
+ allowed =~ origin
24
+ when true
25
+ true
26
+ else
27
+ false
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def before_rodauth
35
+ if (origin = request.env['HTTP_ORIGIN']) && jwt_cors_allow?
36
+ response['Access-Control-Allow-Origin'] = origin
37
+
38
+ # Handle CORS preflight request
39
+ if request.request_method == 'OPTIONS'
40
+ response['Access-Control-Allow-Methods'] = jwt_cors_allow_methods
41
+ response['Access-Control-Allow-Headers'] = jwt_cors_allow_headers
42
+ response['Access-Control-Max-Age'] = jwt_cors_max_age.to_s
43
+ response.status = 204
44
+ request.halt(response.finish)
45
+ end
46
+
47
+ response['Access-Control-Expose-Headers'] = jwt_cors_expose_headers
48
+ end
49
+
50
+ super
51
+ end
52
+ end
53
+ end
@@ -6,7 +6,7 @@ module Rodauth
6
6
  MAJOR = 1
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 21
9
+ MINOR = 22
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
@@ -18,7 +18,7 @@ describe 'Rodauth disallow common passwords feature' do
18
18
  visit '/change-password'
19
19
 
20
20
  bad_password_file = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'dict', 'top-10_000-passwords.txt')
21
- File.read(bad_password_file).split.shuffle.take(5).each do |pass|
21
+ (File.read(bad_password_file).split.shuffle - ['0123456789']).take(5).each do |pass|
22
22
  fill_in 'New Password', :with=>pass
23
23
  fill_in 'Confirm Password', :with=>pass
24
24
  click_button 'Change Password'
@@ -0,0 +1,57 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe 'Rodauth jwt_cors feature' do
4
+ it "should support CORS logins if allowed" do
5
+ origin = false
6
+ rodauth do
7
+ enable :login, :jwt_cors
8
+ jwt_secret '1'
9
+ json_response_success_key 'success'
10
+ jwt_cors_allow_origin{origin}
11
+ end
12
+ roda(:csrf=>false, :json=>true) do |r|
13
+ r.rodauth
14
+ rodauth.require_authentication
15
+ response['Content-Type'] = 'application/json'
16
+ '1'
17
+ end
18
+
19
+ # CORS Preflight Request
20
+ preflight_request = {
21
+ :method=>'OPTIONS',
22
+ :headers=>{
23
+ "HTTP_ACCESS_CONTROL_REQUEST_METHOD"=>"POST",
24
+ "HTTP_ORIGIN"=>"https://foo.example.com",
25
+ "HTTP_ACCESS_CONTROL_REQUEST_HEADERS"=>"content-type",
26
+ "CONTENT_TYPE"=>' application/json'
27
+ }
28
+ }
29
+
30
+ res = json_request("/login", preflight_request.dup)
31
+ res.must_equal [405, ["{\"error\":\"non-POST method used in JSON API\"}"]]
32
+
33
+ origin = Object.new
34
+ res = json_request("/login", preflight_request.dup)
35
+ res.must_equal [405, ["{\"error\":\"non-POST method used in JSON API\"}"]]
36
+
37
+ ["https://foo.example.com", ["https://foo.example.com"], %r{https://foo.example.com}, true].each do |orig|
38
+ origin = orig
39
+
40
+ res = json_request("/login", preflight_request.merge(:include_headers=>true))
41
+ res[0].must_equal 204
42
+ res[1]['Access-Control-Allow-Origin'].must_equal "https://foo.example.com"
43
+ res[1]['Access-Control-Allow-Methods'].must_equal "POST"
44
+ res[1]['Access-Control-Allow-Headers'].must_equal "Content-Type, Authorization, Accept"
45
+ res[1]['Access-Control-Max-Age'].must_equal "86400"
46
+ res[2].must_equal []
47
+
48
+ res = json_request("/login", :login=>'foo@example.com', :password=>'0123456789', :headers=>{"HTTP_ORIGIN"=>"https://foo.example.com"}, :include_headers=>true)
49
+ res[0].must_equal 200
50
+ res[1]['Access-Control-Allow-Origin'].must_equal "https://foo.example.com"
51
+ res[1]['Access-Control-Expose-Headers'].must_equal "Authorization"
52
+ res[2].must_equal("success"=>"You have been logged in")
53
+
54
+ json_request("/foo").must_equal [200, 1]
55
+ end
56
+ end
57
+ end
data/spec/spec_helper.rb CHANGED
@@ -33,7 +33,7 @@ require 'securerandom'
33
33
 
34
34
  ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
35
35
  gem 'minitest'
36
- require 'minitest/autorun'
36
+ require 'minitest/global_expectations/autorun'
37
37
  require 'minitest/hooks/default'
38
38
 
39
39
  require 'roda'
@@ -198,7 +198,7 @@ class Minitest::HooksSpec
198
198
  msgs.length.must_equal 1
199
199
  msgs.first.to.first.must_equal to
200
200
 
201
- link = msgs.first.body.to_s[regexp]
201
+ link = msgs.first.body.to_s.gsub(/ $/, '')[regexp]
202
202
  msgs.clear
203
203
  link.must_be_kind_of(String)
204
204
  link
@@ -1,5 +1,5 @@
1
1
  Someone has requested a login link for the account with this email
2
2
  address. If you did not request a login link, please ignore this
3
3
  message. If you requested a login link, please go to
4
- #{rodauth.email_auth_email_link}
4
+ #{rodauth.email_auth_email_link}
5
5
  to login to this account.
@@ -1,5 +1,5 @@
1
1
  Someone has requested a password reset for the account with this email
2
2
  address. If you did not request a password reset, please ignore this
3
3
  message. If you requested a password reset, please go to
4
- #{rodauth.reset_password_email_link}
4
+ #{rodauth.reset_password_email_link}
5
5
  to reset the password for the account.
@@ -1,5 +1,5 @@
1
1
  Someone has requested a that the account with this email be unlocked.
2
2
  If you did not request the unlocking of this account, please ignore this
3
3
  message. If you requested the unlocking of this account, please go to
4
- #{rodauth.unlock_account_email_link}
4
+ #{rodauth.unlock_account_email_link}
5
5
  to unlock this account.
@@ -1,4 +1,4 @@
1
1
  Someone has created an account with this email address. If you did not create
2
2
  this account, please ignore this message. If you created this account, please go to
3
- #{rodauth.verify_account_email_link}
3
+ #{rodauth.verify_account_email_link}
4
4
  to verify the account.
@@ -1,9 +1,10 @@
1
1
  Someone with an account has requested their login be changed to this email address:
2
2
 
3
3
  Old Login: #{rodauth.verify_login_change_old_login}
4
+
4
5
  New Login: #{rodauth.verify_login_change_new_login}
5
6
 
6
7
  If you did not request this login change, please ignore this message. If you
7
8
  requested this login change, please go to
8
- #{rodauth.verify_login_change_email_link}
9
+ #{rodauth.verify_login_change_email_link}
9
10
  to verify the login change.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.21.0
4
+ version: 1.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-24 00:00:00.000000000 Z
11
+ date: 2019-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - ">="
151
151
  - !ruby/object:Gem::Version
152
152
  version: 5.0.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: minitest-global_expectations
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: minitest-hooks
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -232,6 +246,7 @@ extra_rdoc_files:
232
246
  - doc/jwt_refresh.rdoc
233
247
  - doc/verify_account_grace_period.rdoc
234
248
  - doc/verify_login_change.rdoc
249
+ - doc/jwt_cors.rdoc
235
250
  - doc/release_notes/1.17.0.txt
236
251
  - doc/release_notes/1.0.0.txt
237
252
  - doc/release_notes/1.1.0.txt
@@ -254,6 +269,7 @@ extra_rdoc_files:
254
269
  - doc/release_notes/1.19.0.txt
255
270
  - doc/release_notes/1.20.0.txt
256
271
  - doc/release_notes/1.21.0.txt
272
+ - doc/release_notes/1.22.0.txt
257
273
  files:
258
274
  - CHANGELOG
259
275
  - MIT-LICENSE
@@ -275,6 +291,7 @@ files:
275
291
  - doc/http_basic_auth.rdoc
276
292
  - doc/internals.rdoc
277
293
  - doc/jwt.rdoc
294
+ - doc/jwt_cors.rdoc
278
295
  - doc/jwt_refresh.rdoc
279
296
  - doc/lockout.rdoc
280
297
  - doc/login.rdoc
@@ -300,6 +317,7 @@ files:
300
317
  - doc/release_notes/1.2.0.txt
301
318
  - doc/release_notes/1.20.0.txt
302
319
  - doc/release_notes/1.21.0.txt
320
+ - doc/release_notes/1.22.0.txt
303
321
  - doc/release_notes/1.3.0.txt
304
322
  - doc/release_notes/1.4.0.txt
305
323
  - doc/release_notes/1.5.0.txt
@@ -334,6 +352,7 @@ files:
334
352
  - lib/rodauth/features/email_base.rb
335
353
  - lib/rodauth/features/http_basic_auth.rb
336
354
  - lib/rodauth/features/jwt.rb
355
+ - lib/rodauth/features/jwt_cors.rb
337
356
  - lib/rodauth/features/jwt_refresh.rb
338
357
  - lib/rodauth/features/lockout.rb
339
358
  - lib/rodauth/features/login.rb
@@ -369,6 +388,7 @@ files:
369
388
  - spec/disallow_password_reuse_spec.rb
370
389
  - spec/email_auth_spec.rb
371
390
  - spec/http_basic_auth_spec.rb
391
+ - spec/jwt_cors_spec.rb
372
392
  - spec/jwt_refresh_spec.rb
373
393
  - spec/jwt_spec.rb
374
394
  - spec/lockout_spec.rb