Module: Rodauth

Defined in:
lib/rodauth/features/argon2.rb,
lib/rodauth.rb,
lib/rodauth/version.rb,
lib/rodauth/migrations.rb,
lib/rodauth/features/jwt.rb,
lib/rodauth/features/otp.rb,
lib/rodauth/features/base.rb,
lib/rodauth/features/json.rb,
lib/rodauth/features/login.rb,
lib/rodauth/features/logout.rb,
lib/rodauth/features/lockout.rb,
lib/rodauth/features/jwt_cors.rb,
lib/rodauth/features/remember.rb,
lib/rodauth/features/webauthn.rb,
lib/rodauth/features/sms_codes.rb,
lib/rodauth/features/email_auth.rb,
lib/rodauth/features/email_base.rb,
lib/rodauth/features/otp_unlock.rb,
lib/rodauth/features/jwt_refresh.rb,
lib/rodauth/features/change_login.rb,
lib/rodauth/features/audit_logging.rb,
lib/rodauth/features/close_account.rb,
lib/rodauth/features/create_account.rb,
lib/rodauth/features/recovery_codes.rb,
lib/rodauth/features/reset_password.rb,
lib/rodauth/features/single_session.rb,
lib/rodauth/features/verify_account.rb,
lib/rodauth/features/webauthn_login.rb,
lib/rodauth/features/active_sessions.rb,
lib/rodauth/features/change_password.rb,
lib/rodauth/features/http_basic_auth.rb,
lib/rodauth/features/password_pepper.rb,
lib/rodauth/features/two_factor_base.rb,
lib/rodauth/features/confirm_password.rb,
lib/rodauth/features/internal_request.rb,
lib/rodauth/features/otp_modify_email.rb,
lib/rodauth/features/otp_lockout_email.rb,
lib/rodauth/features/webauthn_autofill.rb,
lib/rodauth/features/account_expiration.rb,
lib/rodauth/features/path_class_methods.rb,
lib/rodauth/features/session_expiration.rb,
lib/rodauth/features/password_complexity.rb,
lib/rodauth/features/password_expiration.rb,
lib/rodauth/features/verify_login_change.rb,
lib/rodauth/features/update_password_hash.rb,
lib/rodauth/features/password_grace_period.rb,
lib/rodauth/features/reset_password_notify.rb,
lib/rodauth/features/webauthn_modify_email.rb,
lib/rodauth/features/change_password_notify.rb,
lib/rodauth/features/disallow_password_reuse.rb,
lib/rodauth/features/webauthn_verify_account.rb,
lib/rodauth/features/disallow_common_passwords.rb,
lib/rodauth/features/verify_account_grace_period.rb,
lib/rodauth/features/login_password_requirements_base.rb

Overview

:nocov:

Defined Under Namespace

Modules: ClassMethods, InstanceMethods, InternalRequestClassMethods, InternalRequestMethods, RequestMethods Classes: Auth, Configuration, ConfigurationError, Feature, FeatureConfiguration, InternalRequestError

Constant Summary collapse

FEATURES =
{}
MAJOR =

The major version of Rodauth, updated only for major changes that are likely to require modification to apps using Rodauth.

2
MINOR =

The minor version of Rodauth, updated for new feature releases of Rodauth.

42
TINY =

The patch version of Rodauth, updated only for bug fixes from the last feature release.

0
VERSION =

The full version of Rodauth as a string

"#{MAJOR}.#{MINOR}.#{TINY}".freeze
VERSION_NUMBER =

The full version of Rodauth as a number (1.17.0 => 11700)

MAJOR*10000 + MINOR*100 + TINY
INVALID_DOMAIN =
"invalidurl @@.com"

Class Method Summary collapse

Class Method Details

.configure(app, opts = {}, &block) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rodauth.rb', line 48

def self.configure(app, opts={}, &block)
  json_opt = app.opts[:rodauth_json] = opts.fetch(:json, app.opts[:rodauth_json])
  csrf = app.opts[:rodauth_csrf] = opts.fetch(:csrf, app.opts[:rodauth_csrf])
  app.opts[:rodauth_route_csrf] = case csrf
  when false, :rack_csrf
    false
  else
    json_opt != :only
  end
  auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth)
  if !auth_class.roda_class
    auth_class.roda_class = app
  elsif auth_class.roda_class != app
    auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class)
    auth_class.roda_class = app
  end
  auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)}
  auth_class.configure(&block) if block
  auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure)
end

.create_database_authentication_functions(db, opts = {}) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/rodauth/migrations.rb', line 4

def self.create_database_authentication_functions(db, opts={})
  table_name = opts[:table_name] || :account_password_hashes
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
  valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
  argon2 = opts[:argon2]

  case db.database_type
  when :postgres
    search_path = opts[:search_path] || 'public, pg_temp'
    primary_key_type =
      case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
      when 'uuid' then :uuid
      else :int8
      end
    table_name = db.literal(table_name) unless table_name.is_a?(String)

    argon_sql = <<END
CASE
  WHEN password_hash ~ '^\\$argon2id'
    THEN substring(password_hash from '\\$argon2id\\$v=\\d+\\$m=\\d+,t=\\d+,p=\\d+\\$.+\\$')
  ELSE substr(password_hash, 0, 30)
END INTO salt
END
    db.run <<END
CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
DECLARE salt text;
BEGIN
SELECT
#{argon2 ? argon_sql : "substr(password_hash, 0, 30) INTO salt"}
FROM #{table_name}
WHERE acct_id = id;
RETURN salt;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = #{search_path};
END

    db.run <<END
CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id #{primary_key_type}, hash text) RETURNS boolean AS $$
DECLARE valid boolean;
BEGIN
SELECT password_hash = hash INTO valid 
FROM #{table_name}
WHERE acct_id = id;
RETURN valid;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = #{search_path};
END
  when :mysql
    argon_sql = <<END
CASE
WHEN password_hash REGEXP '^.argon2id'
  THEN left(password_hash, CHAR_LENGTH(password_hash) - INSTR(REVERSE(password_hash), '$'))
ELSE substr(password_hash, 1, 30)
END
END
    db.run <<END
CREATE FUNCTION #{get_salt_name}(acct_id int8) RETURNS varchar(255)
SQL SECURITY DEFINER
READS SQL DATA
BEGIN
RETURN (SELECT
#{argon2 ? argon_sql : "substr(password_hash, 1, 30)"}
FROM #{table_name}
WHERE acct_id = id);
END;
END

    db.run <<END
CREATE FUNCTION #{valid_hash_name}(acct_id int8, hash varchar(255)) RETURNS tinyint(1)
SQL SECURITY DEFINER
READS SQL DATA
BEGIN
DECLARE valid tinyint(1);
DECLARE csr CURSOR FOR 
SELECT password_hash = hash
FROM #{table_name}
WHERE acct_id = id;
OPEN csr;
FETCH csr INTO valid;
CLOSE csr;
RETURN valid;
END;
END
  when :mssql
    argon_sql = <<END
CASE
WHEN password_hash LIKE '[$]argon2id%'
  THEN left(password_hash, len(password_hash) - charindex('$', reverse(password_hash)))
ELSE substring(password_hash, 0, 30)
END
END
    db.run <<END
CREATE FUNCTION #{get_salt_name}(@account_id bigint) RETURNS nvarchar(255)
WITH EXECUTE AS OWNER
AS
BEGIN
DECLARE @salt nvarchar(255);
SELECT @salt =
#{argon2 ? argon_sql : "substring(password_hash, 0, 30)"}
FROM #{table_name}
WHERE id = @account_id;
RETURN @salt;
END;
END

    db.run <<END
CREATE FUNCTION #{valid_hash_name}(@account_id bigint, @hash nvarchar(255)) RETURNS bit
WITH EXECUTE AS OWNER
AS
BEGIN
DECLARE @valid bit;
DECLARE @ph nvarchar(255);
SELECT @ph = password_hash
FROM #{table_name}
WHERE id = @account_id;
IF(@hash = @ph)
SET @valid = 1;
ELSE
SET @valid = 0
RETURN @valid;
END;
END
  end
end

.create_database_previous_password_check_functions(db, opts = {}) ⇒ Object



153
154
155
# File 'lib/rodauth/migrations.rb', line 153

def self.create_database_previous_password_check_functions(db, opts={})
  create_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))
end

.drop_database_authentication_functions(db, opts = {}) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/rodauth/migrations.rb', line 133

def self.drop_database_authentication_functions(db, opts={})
  table_name = opts[:table_name] || :account_password_hashes
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
  valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash

  case db.database_type
  when :postgres
    primary_key_type =
      case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
      when 'uuid' then :uuid
      else :int8
      end
    db.run "DROP FUNCTION #{get_salt_name}(#{primary_key_type})"
    db.run "DROP FUNCTION #{valid_hash_name}(#{primary_key_type}, text)"
  when :mysql, :mssql
    db.run "DROP FUNCTION #{get_salt_name}"
    db.run "DROP FUNCTION #{valid_hash_name}"
  end
end

.drop_database_previous_password_check_functions(db, opts = {}) ⇒ Object



157
158
159
# File 'lib/rodauth/migrations.rb', line 157

def self.drop_database_previous_password_check_functions(db, opts={})
  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))
end

.lib(opts = {}, &block) ⇒ Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/rodauth.rb', line 8

def self.lib(opts={}, &block) 
  require 'roda'
  c = Class.new(Roda)
  c.plugin(:rodauth, opts) do
    enable :internal_request
    instance_exec(&block)
  end
  c.freeze
  c.rodauth
end

.load_dependencies(app, opts = {}, &_) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/rodauth.rb', line 19

def self.load_dependencies(app, opts={}, &_)
  json_opt = opts.fetch(:json, app.opts[:rodauth_json])
  if json_opt
    app.plugin :json
    app.plugin :json_parser
  end

  unless json_opt == :only
    unless opts[:render] == false
      require 'tilt/string'
      app.plugin :render
    end

    case opts.fetch(:csrf, app.opts[:rodauth_csrf])
    when false
      # nothing
    when :rack_csrf
      # :nocov:
      app.plugin :csrf
      # :nocov:
    else
      app.plugin :route_csrf
    end

    app.plugin :flash unless opts[:flash] == false
    app.plugin :h
  end
end

.versionObject



21
22
23
# File 'lib/rodauth/version.rb', line 21

def self.version
  VERSION
end