rodauth 2.2.0 → 2.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: f672a0312d4a0c6103bb763d339161119551a0e64cc248546a0cb52f4e6784c1
4
- data.tar.gz: 530d322cc3ddba655959238aafe155512de11f090afb9465980181dcc729d1b9
3
+ metadata.gz: 4ea58deac2998a11cd0cd4b17545eab5fce6fb0acd7751416fd521cbd77d6add
4
+ data.tar.gz: c113c1131f5d756fd3aa5c1dbf083f40506b8822fb1c9b67537069c9e8695fba
5
5
  SHA512:
6
- metadata.gz: d8018f5e02d077bd8625c17c8c5a1f607b986c1916296132ef68e2651d2af26d65ddbdfdc9b5f1352431c74caa30e94d270cc03840207303f8fa0e6a44910a0b
7
- data.tar.gz: 5f24ce4d800d3bbe914dac40dba1cd68b78af4aff5efdaf976b32702d45b5fe7809453d2a650f407437afb1b9d6206ff3d79e6f703fcd85d8d55a0f0658464eb
6
+ metadata.gz: 1fa2ce1d08a9f09e21d2fb1fe5b71ea3c6fc55181452302a35929229f0fad8c3c5d3d3324d73bee9c69daf0ff7a2d14ddbb731ef994bd3317512627233a69d28
7
+ data.tar.gz: 7a188457006390730f00bc4a2ea5159c002dba66b300fd993929a5b5a491f2f4be0191c7e7c8e4b9e82b2e709b1a4e8ea8f5d335613f262671e1d68507cc2e26
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ === 2.3.0 (2020-08-21)
2
+
3
+ * Return an error status instead of an invalid access token when trying to refresh JWT without an access token in the jwt_refresh feature (jeremyevans)
4
+
5
+ * Allow {create,drop}_database_authentication_functions to work with UUID keys (monorkin, janko) (#117)
6
+
7
+ * Add rodauth.login('login_type') for logging in after setting a valid account (janko) (#114)
8
+
9
+ * Make new refresh token available to the after_refresh_token hook by setting it in the response first (jeremyevans)
10
+
11
+ * Make the jwt_refresh plugin call before_jwt_refresh_route hook (previously the configuration method was ignored) (AlexeyMatskevich) (#110)
12
+
13
+ * Add login_email_regexp, login_not_valid_email_message, and log_valid_email? configuration methods (janko) (#107)
14
+
1
15
  === 2.2.0 (2020-07-20)
2
16
 
3
17
  * Allow removing all jwt_refresh tokens when logging out by providing a value of "all" as the token to remove (jeremyevans)
@@ -19,6 +19,9 @@ when logging out, provide the refresh token when submitting the JSON request to
19
19
  If you would like to remove all refresh tokens for the account when logging out, provide
20
20
  a value of <tt>all</tt> as the token value.
21
21
 
22
+ When using the refresh token, you must provide a valid access token, as that contains
23
+ information about the current session, which is used to create the new access token.
24
+
22
25
  This feature depends on the jwt feature.
23
26
 
24
27
  == Auth Value Methods
@@ -36,6 +39,8 @@ jwt_refresh_token_key :: Name of the key in the response json holding the refres
36
39
  jwt_refresh_token_key_column :: The column name in the +jwt_refresh_token_table+ holding the refresh token key value.
37
40
  jwt_refresh_token_key_param :: Name of parameter in which the refresh token is provided when requesting a new token. Default is +refresh_token+.
38
41
  jwt_refresh_token_table :: Name of the table holding refresh token keys.
42
+ jwt_refresh_without_access_token_message :: Error message when trying to refresh with providing an access token.
43
+ jwt_refresh_without_access_token_status :: The HTTP status code to use when trying to refresh without providing an access token.
39
44
 
40
45
  == Auth Methods
41
46
 
@@ -3,6 +3,13 @@
3
3
  The login feature implements a login page. It's the most commonly
4
4
  used feature.
5
5
 
6
+ In addition to the auth methods below, it provides a +login+ method that wraps
7
+ +login_session+, running login hooks and redirecting to the configured
8
+ location.
9
+
10
+ rodauth.account #=> { id: 123, ... }
11
+ rodauth.login('password') # login the current account
12
+
6
13
  == Auth Value Methods
7
14
 
8
15
  login_additional_form_tags :: HTML fragment containing additional form tags to use on the login form.
@@ -9,8 +9,10 @@ already_an_account_with_this_login_message :: The error message to display when
9
9
  login_confirm_label :: The label to use for login confirmations.
10
10
  login_confirm_param :: The parameter name to use for login confirmations.
11
11
  login_does_not_meet_requirements_message :: The error message to display when the login does not meet the requirements you have set.
12
+ login_email_regexp :: The regular expression used to validate whether login is a valid email address.
12
13
  login_maximum_length :: The maximum length for logins, 255 by default.
13
14
  login_minimum_length :: The minimum length for logins, 3 by default.
15
+ login_not_valid_email_message :: The error message to display when login is not a valid email address.
14
16
  login_too_long_message :: The error message fragment to show if the login is too long.
15
17
  login_too_short_message :: The error message fragment to show if the login is too short.
16
18
  logins_do_not_match_message :: The error message to display when login and login confirmation do not match.
@@ -29,6 +31,7 @@ same_as_existing_password_message :: The error message to display when a new pas
29
31
  == Auth Methods
30
32
 
31
33
  login_meets_requirements?(login) :: Whether the given login meets the requirements. By default, just checks that the login is a valid email address.
34
+ login_valid_email?(login) :: Whether the login is a valid email address.
32
35
  password_hash(password) :: A hash of the given password.
33
36
  password_meets_requirements?(password) :: Whether the given password meets the requirements. Can be used to implement complexity requirements for passwords.
34
37
  set_password(password) :: Set the password for the current account to the given password.
@@ -0,0 +1,37 @@
1
+ = New Features
2
+
3
+ * Configuration methods have been added for easier validation of
4
+ logins when logins must be valid email addresses (the default):
5
+
6
+ * login_valid_email?(login) can be used for full control of
7
+ determining whether the login is valid.
8
+
9
+ * login_email_regexp can be used to set the regexp used in the
10
+ default login_valid_email? check.
11
+
12
+ * login_not_valid_email_message can be used to set the field
13
+ error message if the login is not a valid email. Previously, this
14
+ value was hardcoded and not translatable.
15
+
16
+ * The {create,drop}_database_authentication_functions now work
17
+ correctly with uuid keys on PostgreSQL. All other parts of
18
+ Rodauth already worked correctly with uuid keys.
19
+
20
+ = Other Improvements
21
+
22
+ * The before_jwt_refresh_route hook is now called before the route
23
+ is taken. Previously, the configuration method had no effect.
24
+
25
+ * rodauth.login can now be used by external code to login the current
26
+ account (the account that rodauth.account returns). This should be
27
+ passed the authentication type string used to login, such as
28
+ password.
29
+
30
+ * The jwt_refresh route now returns an error for requests where a
31
+ valid access token for a logged in session is not provided. You
32
+ can use the jwt_refresh_without_access_token_message and
33
+ jwt_refresh_without_access_token_status configuration methods
34
+ to configure the error response.
35
+
36
+ * The new refresh token is now available to the after_refresh_token
37
+ hook by looking in json_response[jwt_refresh_token_key].
@@ -33,7 +33,11 @@ module Rodauth
33
33
  end
34
34
 
35
35
  r.post do
36
- if !close_account_requires_password? || password_match?(param(password_param))
36
+ catch_error do
37
+ if close_account_requires_password? && !password_match?(param(password_param))
38
+ throw_error_status(invalid_password_error_status, password_param, invalid_password_message)
39
+ end
40
+
37
41
  transaction do
38
42
  before_close_account
39
43
  close_account
@@ -46,12 +50,10 @@ module Rodauth
46
50
 
47
51
  set_notice_flash close_account_notice_flash
48
52
  redirect close_account_redirect
49
- else
50
- set_response_error_status(invalid_password_error_status)
51
- set_field_error(password_param, invalid_password_message)
52
- set_error_flash close_account_error_flash
53
- close_account_view
54
53
  end
54
+
55
+ set_error_flash close_account_error_flash
56
+ close_account_view
55
57
  end
56
58
  end
57
59
 
@@ -94,7 +94,7 @@ module Rodauth
94
94
  redirect email_auth_email_sent_redirect
95
95
  end
96
96
 
97
- _login('email_auth')
97
+ login('email_auth')
98
98
  end
99
99
  end
100
100
 
@@ -19,23 +19,29 @@ module Rodauth
19
19
  auth_value_method :jwt_refresh_token_key_column, :key
20
20
  auth_value_method :jwt_refresh_token_key_param, 'refresh_token'
21
21
  auth_value_method :jwt_refresh_token_table, :account_jwt_refresh_keys
22
+ translatable_method :jwt_refresh_without_access_token_message, 'no JWT access token provided during refresh'
23
+ auth_value_method :jwt_refresh_without_access_token_status, 401
22
24
 
23
25
  auth_private_methods(
24
26
  :account_from_refresh_token
25
27
  )
26
28
 
27
29
  route do |r|
30
+ before_jwt_refresh_route
31
+
28
32
  r.post do
29
- if (refresh_token = param_or_nil(jwt_refresh_token_key_param)) && account_from_refresh_token(refresh_token)
30
- formatted_token = nil
33
+ if !session_value
34
+ response.status ||= jwt_refresh_without_access_token_status
35
+ json_response[json_response_error_key] = jwt_refresh_without_access_token_message
36
+ elsif (refresh_token = param_or_nil(jwt_refresh_token_key_param)) && account_from_refresh_token(refresh_token)
31
37
  transaction do
32
38
  before_refresh_token
33
39
  formatted_token = generate_refresh_token
34
40
  remove_jwt_refresh_token_key(refresh_token)
41
+ json_response[jwt_refresh_token_key] = formatted_token
42
+ json_response[jwt_access_token_key] = session_jwt
35
43
  after_refresh_token
36
44
  end
37
- json_response[jwt_refresh_token_key] = formatted_token
38
- json_response[jwt_access_token_key] = session_jwt
39
45
  else
40
46
  json_response[json_response_error_key] = jwt_refresh_invalid_token_message
41
47
  response.status ||= json_response_error_status
@@ -62,7 +62,7 @@ module Rodauth
62
62
  throw_error_status(login_error_status, password_param, invalid_password_message)
63
63
  end
64
64
 
65
- _login('password')
65
+ login('password')
66
66
  end
67
67
 
68
68
  set_error_flash login_error_flash unless skip_error_flash
@@ -72,6 +72,18 @@ module Rodauth
72
72
 
73
73
  attr_reader :login_form_header
74
74
 
75
+ def login(auth_type)
76
+ saved_login_redirect = remove_session_value(login_redirect_session_key)
77
+ transaction do
78
+ before_login
79
+ login_session(auth_type)
80
+ yield if block_given?
81
+ after_login
82
+ end
83
+ set_notice_flash login_notice_flash
84
+ redirect(saved_login_redirect || login_redirect)
85
+ end
86
+
75
87
  def login_required
76
88
  if login_return_to_requested_location?
77
89
  set_session_value(login_redirect_session_key, request.fullpath)
@@ -126,15 +138,8 @@ module Rodauth
126
138
  end
127
139
 
128
140
  def _login(auth_type)
129
- saved_login_redirect = remove_session_value(login_redirect_session_key)
130
- transaction do
131
- before_login
132
- login_session(auth_type)
133
- yield if block_given?
134
- after_login
135
- end
136
- set_notice_flash login_notice_flash
137
- redirect(saved_login_redirect || login_redirect)
141
+ warn("Deprecated #_login method called, use #login instead.")
142
+ login(auth_type)
138
143
  end
139
144
  end
140
145
  end
@@ -4,8 +4,10 @@ module Rodauth
4
4
  Feature.define(:login_password_requirements_base, :LoginPasswordRequirementsBase) do
5
5
  translatable_method :already_an_account_with_this_login_message, 'already an account with this login'
6
6
  auth_value_method :login_confirm_param, 'login-confirm'
7
+ auth_value_method :login_email_regexp, /\A[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+\z/
7
8
  auth_value_method :login_minimum_length, 3
8
9
  auth_value_method :login_maximum_length, 255
10
+ translatable_method :login_not_valid_email_message, 'not a valid email address'
9
11
  translatable_method :logins_do_not_match_message, 'logins do not match'
10
12
  auth_value_method :password_confirm_param, 'password-confirm'
11
13
  auth_value_method :password_minimum_length, 6
@@ -28,6 +30,7 @@ module Rodauth
28
30
 
29
31
  auth_methods(
30
32
  :login_meets_requirements?,
33
+ :login_valid_email?,
31
34
  :password_hash,
32
35
  :password_meets_requirements?,
33
36
  :set_password
@@ -104,13 +107,15 @@ module Rodauth
104
107
 
105
108
  def login_meets_email_requirements?(login)
106
109
  return true unless require_email_address_logins?
107
- if login =~ /\A[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+\z/
108
- return true
109
- end
110
- @login_requirement_message = 'not a valid email address'
110
+ return true if login_valid_email?(login)
111
+ @login_requirement_message = login_not_valid_email_message
111
112
  return false
112
113
  end
113
114
 
115
+ def login_valid_email?(login)
116
+ login =~ login_email_regexp
117
+ end
118
+
114
119
  def password_meets_length_requirements?(password)
115
120
  return true if password_minimum_length <= password.length
116
121
  @password_requirement_message = password_too_short_message
@@ -3,6 +3,7 @@
3
3
  module Rodauth
4
4
  Feature.define(:session_expiration, :SessionExpiration) do
5
5
  error_flash "This session has expired, please login again"
6
+ redirect{require_login_redirect}
6
7
 
7
8
  auth_value_method :max_session_lifetime, 86400
8
9
  session_key :session_created_session_key, :session_created_at
@@ -11,8 +12,6 @@ module Rodauth
11
12
  auth_value_method :session_inactivity_timeout, 1800
12
13
  session_key :session_last_activity_session_key, :last_session_activity_at
13
14
 
14
- auth_value_methods :session_expiration_redirect
15
-
16
15
  def check_session_expiration
17
16
  return unless logged_in?
18
17
 
@@ -43,10 +42,6 @@ module Rodauth
43
42
  redirect session_expiration_redirect
44
43
  end
45
44
 
46
- def session_expiration_redirect
47
- require_login_redirect
48
- end
49
-
50
45
  def update_session
51
46
  super
52
47
  t = Time.now.to_i
@@ -22,7 +22,7 @@ module Rodauth
22
22
 
23
23
  webauthn_credential = webauthn_auth_credential_from_form_submission
24
24
  before_webauthn_login
25
- _login('webauthn') do
25
+ login('webauthn') do
26
26
  webauthn_update_session(webauthn_credential.id)
27
27
  end
28
28
  end
@@ -9,9 +9,14 @@ module Rodauth
9
9
  case db.database_type
10
10
  when :postgres
11
11
  search_path = opts[:search_path] || 'public, pg_temp'
12
+ primary_key_type =
13
+ case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
14
+ when 'uuid' then :uuid
15
+ else :int8
16
+ end
12
17
 
13
18
  db.run <<END
14
- CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id int8) RETURNS text AS $$
19
+ CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
15
20
  DECLARE salt text;
16
21
  BEGIN
17
22
  SELECT substr(password_hash, 0, 30) INTO salt
@@ -25,7 +30,7 @@ SET search_path = #{search_path};
25
30
  END
26
31
 
27
32
  db.run <<END
28
- CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id int8, hash text) RETURNS boolean AS $$
33
+ CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id #{primary_key_type}, hash text) RETURNS boolean AS $$
29
34
  DECLARE valid boolean;
30
35
  BEGIN
31
36
  SELECT password_hash = hash INTO valid
@@ -100,13 +105,19 @@ END
100
105
  end
101
106
 
102
107
  def self.drop_database_authentication_functions(db, opts={})
108
+ table_name = opts[:table_name] || :account_password_hashes
103
109
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
104
110
  valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
105
111
 
106
112
  case db.database_type
107
113
  when :postgres
108
- db.run "DROP FUNCTION #{get_salt_name}(int8)"
109
- db.run "DROP FUNCTION #{valid_hash_name}(int8, text)"
114
+ primary_key_type =
115
+ case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
116
+ when 'uuid' then :uuid
117
+ else :int8
118
+ end
119
+ db.run "DROP FUNCTION #{get_salt_name}(#{primary_key_type})"
120
+ db.run "DROP FUNCTION #{valid_hash_name}(#{primary_key_type}, text)"
110
121
  when :mysql, :mssql
111
122
  db.run "DROP FUNCTION #{get_salt_name}"
112
123
  db.run "DROP FUNCTION #{valid_hash_name}"
@@ -118,6 +129,6 @@ END
118
129
  end
119
130
 
120
131
  def self.drop_database_previous_password_check_functions(db, opts={})
121
- drop_database_authentication_functions(db, {:get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
132
+ drop_database_authentication_functions(db, {:table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
122
133
  end
123
134
  end
@@ -6,7 +6,7 @@ module Rodauth
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 2
9
+ MINOR = 3
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
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: 2.2.0
4
+ version: 2.3.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: 2020-07-20 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -304,6 +304,7 @@ extra_rdoc_files:
304
304
  - doc/release_notes/2.0.0.txt
305
305
  - doc/release_notes/2.1.0.txt
306
306
  - doc/release_notes/2.2.0.txt
307
+ - doc/release_notes/2.3.0.txt
307
308
  files:
308
309
  - CHANGELOG
309
310
  - MIT-LICENSE
@@ -384,6 +385,7 @@ files:
384
385
  - doc/release_notes/2.0.0.txt
385
386
  - doc/release_notes/2.1.0.txt
386
387
  - doc/release_notes/2.2.0.txt
388
+ - doc/release_notes/2.3.0.txt
387
389
  - doc/remember.rdoc
388
390
  - doc/reset_password.rdoc
389
391
  - doc/session_expiration.rdoc