authentication-logic 0.1.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/bin/console +11 -0
  3. data/bin/setup +8 -0
  4. data/lib/auth/logic/acts_as_authentic/base.rb +118 -0
  5. data/lib/auth/logic/acts_as_authentic/email.rb +32 -0
  6. data/lib/auth/logic/acts_as_authentic/logged_in_status.rb +87 -0
  7. data/lib/auth/logic/acts_as_authentic/login.rb +65 -0
  8. data/lib/auth/logic/acts_as_authentic/magic_columns.rb +40 -0
  9. data/lib/auth/logic/acts_as_authentic/password.rb +362 -0
  10. data/lib/auth/logic/acts_as_authentic/perishable_token.rb +125 -0
  11. data/lib/auth/logic/acts_as_authentic/persistence_token.rb +72 -0
  12. data/lib/auth/logic/acts_as_authentic/queries/case_sensitivity.rb +55 -0
  13. data/lib/auth/logic/acts_as_authentic/queries/find_with_case.rb +85 -0
  14. data/lib/auth/logic/acts_as_authentic/session_maintenance.rb +189 -0
  15. data/lib/auth/logic/acts_as_authentic/single_access_token.rb +85 -0
  16. data/lib/auth/logic/config.rb +41 -0
  17. data/lib/auth/logic/controller_adapters/abstract_adapter.rb +121 -0
  18. data/lib/auth/logic/controller_adapters/rack_adapter.rb +74 -0
  19. data/lib/auth/logic/controller_adapters/rails_adapter.rb +49 -0
  20. data/lib/auth/logic/controller_adapters/sinatra_adapter.rb +69 -0
  21. data/lib/auth/logic/cookie_credentials.rb +65 -0
  22. data/lib/auth/logic/crypto_providers/bcrypt.rb +116 -0
  23. data/lib/auth/logic/crypto_providers/md5/v2.rb +37 -0
  24. data/lib/auth/logic/crypto_providers/md5.rb +38 -0
  25. data/lib/auth/logic/crypto_providers/scrypt.rb +96 -0
  26. data/lib/auth/logic/crypto_providers/sha1/v2.rb +42 -0
  27. data/lib/auth/logic/crypto_providers/sha1.rb +43 -0
  28. data/lib/auth/logic/crypto_providers/sha256/v2.rb +60 -0
  29. data/lib/auth/logic/crypto_providers/sha256.rb +61 -0
  30. data/lib/auth/logic/crypto_providers/sha512/v2.rb +41 -0
  31. data/lib/auth/logic/crypto_providers/sha512.rb +40 -0
  32. data/lib/auth/logic/crypto_providers.rb +89 -0
  33. data/lib/auth/logic/errors.rb +52 -0
  34. data/lib/auth/logic/i18n/translator.rb +20 -0
  35. data/lib/auth/logic/i18n.rb +100 -0
  36. data/lib/auth/logic/random.rb +18 -0
  37. data/lib/auth/logic/session/base.rb +2205 -0
  38. data/lib/auth/logic/session/magic_column/assigns_last_request_at.rb +49 -0
  39. data/lib/auth/logic/test_case/mock_api_controller.rb +53 -0
  40. data/lib/auth/logic/test_case/mock_controller.rb +59 -0
  41. data/lib/auth/logic/test_case/mock_cookie_jar.rb +112 -0
  42. data/lib/auth/logic/test_case/mock_logger.rb +14 -0
  43. data/lib/auth/logic/test_case/mock_request.rb +36 -0
  44. data/lib/auth/logic/test_case/rails_request_adapter.rb +40 -0
  45. data/lib/auth/logic/test_case.rb +216 -0
  46. data/lib/auth/logic/version.rb +7 -0
  47. data/lib/auth/logic.rb +46 -0
  48. metadata +426 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e3ea405dd232236ede943193d4e88d29b71325cfbd49792a1172d9ffa7d61f3a
4
+ data.tar.gz: 0e4feb942a405eeca6c22b6ccfe0aa435652b8ec725945ba04a1fba309482de8
5
+ SHA512:
6
+ metadata.gz: b656538ef705d09b62b9f8ac6cf710814f549d6a0d74459076ff37be764ad2a61b518696a337d8c37670fa83d2f679eb158e2fab03550f7d06ca9a7e8e9534f9
7
+ data.tar.gz: c7780765733051e7c3317497bddce4dcf87415a029301170c54ee7c64ae001b99edb66239e28db0871b81de6a3216c9c84c87ef91935af2b4a92dfdfa1590e09
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "auth/logic"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authentication
4
+ module Logic
5
+ module ActsAsAuthentic
6
+ # Provides the base functionality for acts_as_authentic
7
+ module Base
8
+ def self.included(klass)
9
+ klass.class_eval do
10
+ class_attribute :acts_as_authentic_modules
11
+ self.acts_as_authentic_modules ||= []
12
+ extend Authentication::Logic::Config
13
+ extend Config
14
+ end
15
+ end
16
+
17
+ # The primary configuration of a model (often, `User`) for use with
18
+ # auth-logic. These methods become class methods of ::ActiveRecord::Base.
19
+ module Config
20
+ # This includes a lot of helpful methods for authenticating records
21
+ # which the Authentication::Logic::Session module relies on. To use it just do:
22
+ #
23
+ # class User < ApplicationRecord
24
+ # acts_as_authentic
25
+ # end
26
+ #
27
+ # Configuration is easy:
28
+ #
29
+ # acts_as_authentic do |c|
30
+ # c.my_configuration_option = my_value
31
+ # end
32
+ #
33
+ # See the various sub modules for the configuration they provide.
34
+ def acts_as_authentic
35
+ yield self if block_given?
36
+ return unless db_setup?
37
+
38
+ acts_as_authentic_modules.each { |mod| include mod }
39
+ end
40
+
41
+ # Since this part of Authentication::Logic deals with another class, ActiveRecord,
42
+ # we can't just start including things in ActiveRecord itself. A lot of
43
+ # these module includes need to be triggered by the acts_as_authentic
44
+ # method call. For example, you don't want to start adding in email
45
+ # validations and what not into a model that has nothing to do with
46
+ # Authentication::Logic.
47
+ #
48
+ # That being said, this is your tool for extending Authentication::Logic and
49
+ # "hooking" into the acts_as_authentic call.
50
+ def add_acts_as_authentic_module(mod, action = :append)
51
+ modules = acts_as_authentic_modules.clone
52
+ case action
53
+ when :append
54
+ modules << mod
55
+ when :prepend
56
+ modules = [mod] + modules
57
+ end
58
+ modules.uniq!
59
+ self.acts_as_authentic_modules = modules
60
+ end
61
+
62
+ # This is the same as add_acts_as_authentic_module, except that it
63
+ # removes the module from the list.
64
+ def remove_acts_as_authentic_module(mod)
65
+ modules = acts_as_authentic_modules.clone
66
+ modules.delete(mod)
67
+ self.acts_as_authentic_modules = modules
68
+ end
69
+
70
+ # Some Authentication::Logic modules requires a database connection with a existing
71
+ # users table by the moment when you call the `acts_as_authentic`
72
+ # method. If you try to call `acts_as_authentic` without a database
73
+ # connection, it will raise a `Authentication::Logic::ModelSetupError`.
74
+ #
75
+ # If you rely on the User model before the database is setup correctly,
76
+ # set this field to false.
77
+ # * <tt>Default:</tt> false
78
+ # * <tt>Accepts:</tt> Boolean
79
+ def raise_on_model_setup_error(value = nil)
80
+ rw_config(:raise_on_model_setup_error, value, false)
81
+ end
82
+ alias raise_on_model_setup_error= raise_on_model_setup_error
83
+
84
+ private
85
+
86
+ def db_setup?
87
+ column_names
88
+ true
89
+ rescue StandardError
90
+ raise ModelSetupError if raise_on_model_setup_error
91
+
92
+ false
93
+ end
94
+
95
+ def first_column_to_exist(*columns_to_check)
96
+ if db_setup?
97
+ columns_to_check.each do |column_name|
98
+ return column_name.to_sym if column_names.include?(column_name.to_s)
99
+ end
100
+ end
101
+ columns_to_check.first&.to_sym
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::Base
110
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::Email
111
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::LoggedInStatus
112
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::Login
113
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::MagicColumns
114
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::Password
115
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::PerishableToken
116
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::PersistenceToken
117
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::SessionMaintenance
118
+ ::ActiveRecord::Base.include Authentication::Logic::ActsAsAuthentic::SingleAccessToken
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authentication
4
+ module Logic
5
+ module ActsAsAuthentic
6
+ # Sometimes models won't have an explicit "login" or "username" field.
7
+ # Instead they want to use the email field. In this case, auth-logic provides
8
+ # validations to make sure the email submited is actually a valid email.
9
+ # Don't worry, if you do have a login or username field, Authentication::Logic will
10
+ # still validate your email field. One less thing you have to worry about.
11
+ module Email
12
+ def self.included(klass)
13
+ klass.class_eval do
14
+ extend Config
15
+ end
16
+ end
17
+
18
+ # Configuration to modify how Authentication::Logic handles the email field.
19
+ module Config
20
+ # The name of the field that stores email addresses.
21
+ #
22
+ # * <tt>Default:</tt> :email, if it exists
23
+ # * <tt>Accepts:</tt> Symbol
24
+ def email_field(value = nil)
25
+ rw_config(:email_field, value, first_column_to_exist(nil, :email, :email_address))
26
+ end
27
+ alias email_field= email_field
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authentication
4
+ module Logic
5
+ module ActsAsAuthentic
6
+ # Since web applications are stateless there is not sure fire way to tell if
7
+ # a user is logged in or not, from the database perspective. The best way to
8
+ # do this is to provide a "timeout" based on inactivity. So if that user is
9
+ # inactive for a certain amount of time we assume they are logged out.
10
+ # That's what this module is all about.
11
+ module LoggedInStatus
12
+ def self.included(klass)
13
+ klass.class_eval do
14
+ extend Config
15
+ add_acts_as_authentic_module(Methods)
16
+ end
17
+ end
18
+
19
+ # All configuration for the logged in status feature set.
20
+ module Config
21
+ # The timeout to determine when a user is logged in or not.
22
+ #
23
+ # * <tt>Default:</tt> 10.minutes
24
+ # * <tt>Accepts:</tt> Fixnum
25
+ def logged_in_timeout(value = nil)
26
+ rw_config(:logged_in_timeout, (!value.nil? && value.to_i) || value, 10.minutes.to_i)
27
+ end
28
+ alias logged_in_timeout= logged_in_timeout
29
+ end
30
+
31
+ # All methods for the logged in status feature seat.
32
+ module Methods
33
+ def self.included(klass)
34
+ return unless klass.column_names.include?("last_request_at")
35
+
36
+ klass.class_eval do
37
+ include InstanceMethods
38
+ scope(
39
+ :logged_in,
40
+ lambda do
41
+ where(
42
+ "last_request_at > ? and current_login_at IS NOT NULL",
43
+ logged_in_timeout.seconds.ago
44
+ )
45
+ end
46
+ )
47
+ scope(
48
+ :logged_out,
49
+ lambda do
50
+ where(
51
+ "last_request_at is NULL or last_request_at <= ?",
52
+ logged_in_timeout.seconds.ago
53
+ )
54
+ end
55
+ )
56
+ end
57
+ end
58
+
59
+ # :nodoc:
60
+ module InstanceMethods
61
+ # Returns true if the last_request_at > logged_in_timeout.
62
+ def logged_in?
63
+ unless respond_to?(:last_request_at)
64
+ raise(
65
+ "Can not determine the records login state because " \
66
+ "there is no last_request_at column"
67
+ )
68
+ end
69
+ !last_request_at.nil? && last_request_at > logged_in_timeout.seconds.ago
70
+ end
71
+
72
+ # Opposite of logged_in?
73
+ def logged_out?
74
+ !logged_in?
75
+ end
76
+
77
+ private
78
+
79
+ def logged_in_timeout
80
+ self.class.logged_in_timeout
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "auth/logic/acts_as_authentic/queries/case_sensitivity"
4
+ require "auth/logic/acts_as_authentic/queries/find_with_case"
5
+
6
+ module Authentication
7
+ module Logic
8
+ module ActsAsAuthentic
9
+ # Handles everything related to the login field.
10
+ module Login
11
+ def self.included(klass)
12
+ klass.class_eval do
13
+ extend Config
14
+ end
15
+ end
16
+
17
+ # Configuration for the login field.
18
+ module Config
19
+ # The name of the login field in the database.
20
+ #
21
+ # * <tt>Default:</tt> :login or :username, if they exist
22
+ # * <tt>Accepts:</tt> Symbol
23
+ def login_field(value = nil)
24
+ rw_config(:login_field, value, first_column_to_exist(nil, :login, :username))
25
+ end
26
+ alias login_field= login_field
27
+
28
+ # This method allows you to find a record with the given login. If you
29
+ # notice, with Active Record you have the UniquenessValidator class.
30
+ # They give you a :case_sensitive option. I handle this in the same
31
+ # manner that they handle that. If you are using the login field, set
32
+ # false for the :case_sensitive option in
33
+ # validates_uniqueness_of_login_field_options and the column doesn't
34
+ # have a case-insensitive collation, this method will modify the query
35
+ # to look something like:
36
+ #
37
+ # "LOWER(#{quoted_table_name}.#{login_field}) = LOWER(#{login})"
38
+ #
39
+ # If you don't specify this it just uses a regular case-sensitive search
40
+ # (with the binary modifier if necessary):
41
+ #
42
+ # "BINARY #{login_field} = #{login}"
43
+ #
44
+ # The above also applies for using email as your login, except that you
45
+ # need to set the :case_sensitive in
46
+ # validates_uniqueness_of_email_field_options to false.
47
+ #
48
+ # @api public
49
+ def find_by_smart_case_login_field(login)
50
+ field = login_field || email_field
51
+ sensitive = Queries::CaseSensitivity.new(self, field).sensitive?
52
+ find_with_case(field, login, sensitive)
53
+ end
54
+
55
+ private
56
+
57
+ # @api private
58
+ def find_with_case(field, value, sensitive)
59
+ Queries::FindWithCase.new(self, field, value, sensitive).execute
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authentication
4
+ module Logic
5
+ module ActsAsAuthentic
6
+ # Magic columns are like ActiveRecord's created_at and updated_at columns.
7
+ # They are "magically" maintained for you. Authentication::Logic has the same thing, but
8
+ # these are maintained on the session side. Please see "Magic Columns" in
9
+ # `Session::Base` for more details. This module merely adds validations for
10
+ # the magic columns if they exist.
11
+ module MagicColumns
12
+ def self.included(klass)
13
+ klass.class_eval do
14
+ add_acts_as_authentic_module(Methods)
15
+ end
16
+ end
17
+
18
+ # Methods relating to the magic columns
19
+ module Methods
20
+ def self.included(klass)
21
+ klass.class_eval do
22
+ if column_names.include?("login_count")
23
+ validates_numericality_of :login_count,
24
+ only_integer: true,
25
+ greater_than_or_equal_to: 0,
26
+ allow_nil: true
27
+ end
28
+ if column_names.include?("failed_login_count")
29
+ validates_numericality_of :failed_login_count,
30
+ only_integer: true,
31
+ greater_than_or_equal_to: 0,
32
+ allow_nil: true
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end