challah 1.5.0 → 2.0.0.beta2
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +32 -0
- data/README.md +6 -39
- data/VERSION +1 -1
- data/app/controllers/sessions_controller.rb +11 -10
- data/app/models/authorization.rb +2 -0
- data/lib/challah/audit.rb +38 -36
- data/lib/challah/authenticators/api_key.rb +4 -2
- data/lib/challah/authenticators/password.rb +3 -1
- data/lib/challah/authenticators.rb +5 -3
- data/lib/challah/concerns/authorizeable.rb +4 -0
- data/lib/challah/concerns/user/attributeable.rb +37 -29
- data/lib/challah/concerns/user/authenticateable.rb +4 -2
- data/lib/challah/concerns/user/authorizable.rb +16 -12
- data/lib/challah/concerns/user/findable.rb +13 -10
- data/lib/challah/concerns/user/passwordable.rb +5 -3
- data/lib/challah/concerns/user/provideable.rb +22 -20
- data/lib/challah/concerns/user/statusable.rb +3 -21
- data/lib/challah/concerns/user/validateable.rb +3 -1
- data/lib/challah/concerns/userable.rb +1 -3
- data/lib/challah/controller.rb +69 -65
- data/lib/challah/cookie_store.rb +7 -5
- data/lib/challah/encrypter.rb +4 -2
- data/lib/challah/engine.rb +5 -18
- data/lib/challah/providers/password_provider.rb +9 -7
- data/lib/challah/providers.rb +3 -1
- data/lib/challah/random.rb +6 -4
- data/lib/challah/routes.rb +6 -6
- data/lib/challah/session.rb +27 -25
- data/lib/challah/signup.rb +5 -3
- data/lib/challah/simple_cookie_store.rb +82 -80
- data/lib/challah/techniques/api_key_technique.rb +2 -2
- data/lib/challah/techniques/password_technique.rb +2 -1
- data/lib/challah/techniques/token_technique.rb +3 -1
- data/lib/challah/techniques.rb +2 -0
- data/lib/challah/test.rb +6 -0
- data/lib/challah/validators/email_validator.rb +2 -0
- data/lib/challah/validators/password_validator.rb +5 -3
- data/lib/challah/version.rb +3 -1
- data/lib/challah.rb +2 -5
- data/lib/generators/challah_generator.rb +17 -0
- data/lib/generators/templates/migration.erb +39 -0
- data/lib/tasks/setup.rake +7 -3
- metadata +63 -26
- data/db/migrate/20120127150433_create_users.rb +0 -29
- data/db/migrate/20121116210759_create_authorizations.rb +0 -22
- data/lib/challah/plugins.rb +0 -54
@@ -1,12 +1,13 @@
|
|
1
1
|
module Challah
|
2
2
|
module UserStatusable
|
3
|
+
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
|
5
6
|
included do
|
6
7
|
begin
|
7
8
|
if columns.map(&:name).include?("status")
|
8
9
|
additional_statuses = Array(Challah.options[:additional_statuses])
|
9
|
-
enum status: [:active, :inactive, *additional_statuses]
|
10
|
+
enum status: [ :active, :inactive, *additional_statuses ].map(&:to_sym)
|
10
11
|
end
|
11
12
|
rescue ActiveRecord::StatementInvalid => exception
|
12
13
|
raise exception unless exception.message =~ /could not find table/i ||
|
@@ -14,26 +15,6 @@ module Challah
|
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
|
-
# Fallback to pre-enum active column (pre challah 1.4)
|
18
|
-
def active=(enabled)
|
19
|
-
if self.class.columns.map(&:name).include?("status")
|
20
|
-
self.status = (!!enabled ? :active : :inactive)
|
21
|
-
else
|
22
|
-
write_attribute(:active, !!enabled)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def active?
|
27
|
-
# enum-based status
|
28
|
-
if self.class.columns.map(&:name).include?("status")
|
29
|
-
read_attribute(:status).to_s == "active"
|
30
|
-
|
31
|
-
# support for non-enum status column (pre challah 1.4)
|
32
|
-
else
|
33
|
-
!!read_attribute(:active)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
18
|
def active
|
38
19
|
active?
|
39
20
|
end
|
@@ -41,5 +22,6 @@ module Challah
|
|
41
22
|
def valid_session?
|
42
23
|
active?
|
43
24
|
end
|
25
|
+
|
44
26
|
end
|
45
27
|
end
|
data/lib/challah/controller.rb
CHANGED
@@ -2,6 +2,7 @@ module Challah
|
|
2
2
|
# These methods are added into ActionController::Base and are available in all
|
3
3
|
# of your app's controllers.
|
4
4
|
module Controller
|
5
|
+
|
5
6
|
extend ActiveSupport::Concern
|
6
7
|
|
7
8
|
included do
|
@@ -9,6 +10,7 @@ module Challah
|
|
9
10
|
end
|
10
11
|
|
11
12
|
module ClassMethods
|
13
|
+
|
12
14
|
# Restrict the current controller to only users that have authenticated. All actions
|
13
15
|
# in the controller will be restricted unless otherwise stated. All normal options
|
14
16
|
# for a before_action are observed.
|
@@ -39,80 +41,82 @@ module Challah
|
|
39
41
|
restrict_to_authenticated(*args)
|
40
42
|
end
|
41
43
|
alias_method :login_required, :signin_required
|
44
|
+
|
42
45
|
end
|
43
46
|
|
44
47
|
protected
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
# Is there currently a logged in user? Returns true if it is safe to use
|
50
|
+
# the {#current_user current_user} method.
|
51
|
+
#
|
52
|
+
# @note This method is also available as a helper in your views.
|
53
|
+
#
|
54
|
+
# @see #current_user current_user
|
55
|
+
#
|
56
|
+
# @return [Boolean] Is there a user logged in?
|
57
|
+
def current_user?
|
58
|
+
!!current_user
|
59
|
+
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
# Alias for current_user?
|
62
|
+
def signed_in?
|
63
|
+
current_user?
|
64
|
+
end
|
65
|
+
alias_method :logged_in?, :signed_in?
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
# The user that is currently logged into this session. If there is no
|
68
|
+
# user logged in, nil will be returned.
|
69
|
+
#
|
70
|
+
# @note This method is also available as a helper in your views.
|
71
|
+
#
|
72
|
+
# @return [User, nil] The current authenticated user.
|
73
|
+
def current_user
|
74
|
+
@current_user ||= current_user_session.user
|
75
|
+
end
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
# The current authentication session, if one exists. A {Session} object will be
|
78
|
+
# returned regardless of its valid status. If an invalid session is returned, the
|
79
|
+
# {Session#user user} attribute will be nil.
|
80
|
+
#
|
81
|
+
# @return [Session] The current browser session.
|
82
|
+
def current_user_session
|
83
|
+
@current_user_session ||= Challah::Session.find(request, params, user_model)
|
84
|
+
end
|
82
85
|
|
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
|
-
|
86
|
+
# Restrict a controller to only authenticated users. If someone tries to access
|
87
|
+
# a restricted action and is not logged in, they will be redirected to the
|
88
|
+
# login page.
|
89
|
+
#
|
90
|
+
# This method is an alias for:
|
91
|
+
#
|
92
|
+
# restrict_to_authenticated
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# class YourController < ApplicationController
|
96
|
+
# before_action :login_required
|
97
|
+
#
|
98
|
+
# # ...
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @example Specifing certain actions.
|
102
|
+
# class YourOtherController < ApplicationController
|
103
|
+
# before_action :login_required, :only => [ :create, :update, :destroy ]
|
104
|
+
#
|
105
|
+
# # ...
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# @see Controller::ClassMethods#restrict_to_authenticated restrict_to_authenticated
|
109
|
+
def signin_required
|
110
|
+
unless signed_in?
|
111
|
+
session[:return_to] = request.url
|
112
|
+
redirect_to(signin_path) && (return)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
alias_method :login_required, :signin_required
|
116
|
+
|
117
|
+
def user_model
|
118
|
+
@_challah_user_model ||= Challah.user
|
110
119
|
end
|
111
|
-
end
|
112
|
-
alias_method :login_required, :signin_required
|
113
120
|
|
114
|
-
def user_model
|
115
|
-
@_challah_user_model ||= Challah.user
|
116
|
-
end
|
117
121
|
end
|
118
122
|
end
|
data/lib/challah/cookie_store.rb
CHANGED
@@ -9,15 +9,17 @@ module Challah
|
|
9
9
|
# a new class that responds to +read+, +save+ and +destroy+
|
10
10
|
#
|
11
11
|
class CookieStore < SimpleCookieStore
|
12
|
+
|
12
13
|
def inspect
|
13
|
-
"#<CookieStore:0x#{object_id.to_s(16)} valid=#{existing?}>"
|
14
|
+
"#<CookieStore:0x#{ object_id.to_s(16) } valid=#{ existing? }>"
|
14
15
|
end
|
15
16
|
|
16
17
|
protected
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
def validation_cookie_value(value = nil)
|
20
|
+
value = session_cookie_value unless value
|
21
|
+
Encrypter.md5(value, request.user_agent, request.remote_ip)
|
22
|
+
end
|
23
|
+
|
22
24
|
end
|
23
25
|
end
|
data/lib/challah/encrypter.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "digest/sha2"
|
2
|
+
require "bcrypt"
|
3
3
|
|
4
4
|
module Challah
|
5
5
|
# Handles all encryption, hashing and comparison necessary for tokens and passwords.
|
6
6
|
class Encrypter
|
7
|
+
|
7
8
|
attr_accessor :cost, :joiner
|
8
9
|
|
9
10
|
# The number of times to hash the given password.
|
@@ -54,5 +55,6 @@ module Challah
|
|
54
55
|
def self.md5(*args)
|
55
56
|
new().md5(*args)
|
56
57
|
end
|
58
|
+
|
57
59
|
end
|
58
60
|
end
|
data/lib/challah/engine.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Challah
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
|
4
|
-
initializer
|
5
|
-
app.routes_reloader.paths.insert(0, File.expand_path(File.join(File.dirname(__FILE__),
|
4
|
+
initializer "challah.router" do |app|
|
5
|
+
app.routes_reloader.paths.insert(0, File.expand_path(File.join(File.dirname(__FILE__), "routes.rb")))
|
6
6
|
end
|
7
7
|
|
8
|
-
initializer
|
8
|
+
initializer "challah.active_record" do
|
9
9
|
ActiveSupport.on_load :active_record do
|
10
10
|
Challah::Engine.setup_active_record!
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
initializer
|
14
|
+
initializer "challah.action_controller" do
|
15
15
|
ActiveSupport.on_load :action_controller do
|
16
16
|
Challah::Engine.setup_action_controller!
|
17
17
|
end
|
@@ -33,13 +33,6 @@ module Challah
|
|
33
33
|
if defined?(ActionController::API)
|
34
34
|
ActionController::API.send(:include, Challah::Controller)
|
35
35
|
end
|
36
|
-
|
37
|
-
# Load any ActionController/Challah plugins
|
38
|
-
Challah.plugins.values.each do |plugin|
|
39
|
-
plugin.action_controller.each do |proc|
|
40
|
-
proc.call
|
41
|
-
end
|
42
|
-
end
|
43
36
|
end
|
44
37
|
end
|
45
38
|
|
@@ -49,14 +42,8 @@ module Challah
|
|
49
42
|
Challah.options[:logger] = ActiveRecord::Base.logger
|
50
43
|
|
51
44
|
ActiveRecord::Base.send(:include, Challah::Audit)
|
52
|
-
|
53
|
-
# Load any ActiveRecord/Challah plugins
|
54
|
-
Challah.plugins.values.each do |plugin|
|
55
|
-
plugin.active_record.each do |proc|
|
56
|
-
proc.call
|
57
|
-
end
|
58
|
-
end
|
59
45
|
end
|
60
46
|
end
|
47
|
+
|
61
48
|
end
|
62
49
|
end
|
@@ -1,20 +1,21 @@
|
|
1
1
|
module Challah
|
2
2
|
class PasswordProvider
|
3
|
+
|
3
4
|
def self.save(user)
|
4
5
|
set(uid: user.username, token: user.password, user_id: user.id, authorization: user.class.authorization_model)
|
5
6
|
end
|
6
7
|
|
7
8
|
def self.set(options = {})
|
8
9
|
user_id = options.fetch(:user_id)
|
9
|
-
uid = options.fetch(:uid,
|
10
|
-
token = options.fetch(:token,
|
10
|
+
uid = options.fetch(:uid, "")
|
11
|
+
token = options.fetch(:token, "")
|
11
12
|
auth_model = options.fetch(:authorization, ::Authorization)
|
12
13
|
|
13
14
|
if token.to_s.blank?
|
14
|
-
authorization = auth_model.get(
|
15
|
+
authorization = auth_model.get(
|
15
16
|
user_id: user_id,
|
16
17
|
provider: :password
|
17
|
-
|
18
|
+
)
|
18
19
|
|
19
20
|
if authorization
|
20
21
|
token = authorization.token
|
@@ -23,12 +24,12 @@ module Challah
|
|
23
24
|
token = Challah::Encrypter.encrypt(token)
|
24
25
|
end
|
25
26
|
|
26
|
-
auth_model.set(
|
27
|
+
auth_model.set(
|
27
28
|
provider: :password,
|
28
29
|
user_id: user_id,
|
29
30
|
uid: uid,
|
30
31
|
token: token
|
31
|
-
|
32
|
+
)
|
32
33
|
end
|
33
34
|
|
34
35
|
def self.valid?(record)
|
@@ -36,5 +37,6 @@ module Challah
|
|
36
37
|
password_validator.new(force: true).validate(record)
|
37
38
|
record.errors[:password].size.zero?
|
38
39
|
end
|
40
|
+
|
39
41
|
end
|
40
|
-
end
|
42
|
+
end
|
data/lib/challah/providers.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Challah
|
2
2
|
module Providers
|
3
|
+
|
3
4
|
# Get a list of all authorization providers other than password provider
|
4
5
|
def custom_providers
|
5
6
|
providers.reject { |k, v| k == :password }
|
@@ -18,5 +19,6 @@ module Challah
|
|
18
19
|
def providers
|
19
20
|
@providers.dup
|
20
21
|
end
|
22
|
+
|
21
23
|
end
|
22
|
-
end
|
24
|
+
end
|
data/lib/challah/random.rb
CHANGED
@@ -2,17 +2,19 @@ module Challah
|
|
2
2
|
# Random string class, uses ActiveSupport's SecureRandom if possible, otherwise gives a fairly
|
3
3
|
# secure random string
|
4
4
|
class Random
|
5
|
+
|
5
6
|
# Returns a random string for use as a token at the given length.
|
6
7
|
def self.token(length = 30)
|
7
|
-
return SecureRandom.hex(length/2) if secure_random?
|
8
|
+
return SecureRandom.hex(length / 2) if secure_random?
|
8
9
|
|
9
|
-
c = [(0..9),(
|
10
|
-
(1..length).map{ c[rand(c.length)] }.join
|
10
|
+
c = [(0..9), ("a".."z"), ("A".."Z")].map { |i| i.to_a }.flatten
|
11
|
+
(1..length).map { c[rand(c.length)] }.join
|
11
12
|
end
|
12
13
|
|
13
14
|
# Is ActiveSupport::SecureRandom available. If so, we'll use it.
|
14
15
|
def self.secure_random?
|
15
16
|
defined?(::SecureRandom)
|
16
17
|
end
|
18
|
+
|
17
19
|
end
|
18
|
-
end
|
20
|
+
end
|
data/lib/challah/routes.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
2
|
unless Challah.options[:skip_routes]
|
3
|
-
post
|
4
|
-
get
|
5
|
-
get
|
3
|
+
post "/sign-in", to: "sessions#create", as: "authenticate"
|
4
|
+
get "/sign-in", to: "sessions#new", as: "signin"
|
5
|
+
get "/sign-out", to: "sessions#destroy", as: "signout"
|
6
6
|
|
7
|
-
post
|
8
|
-
get
|
9
|
-
get
|
7
|
+
post "/login", to: "sessions#create", as: "submit_login"
|
8
|
+
get "/login", to: "sessions#new", as: "login"
|
9
|
+
get "/logout", to: "sessions#destroy", as: "logout"
|
10
10
|
end
|
11
11
|
end
|
data/lib/challah/session.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Challah
|
2
2
|
class Session
|
3
|
+
|
3
4
|
extend ActiveModel::Naming
|
4
5
|
include ActiveModel::Conversion
|
5
6
|
|
@@ -34,7 +35,7 @@ module Challah
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def inspect
|
37
|
-
"#<Session:0x#{object_id.to_s(16)} valid=#{valid?} store=#{self.store.inspect} user=#{user_id || 'nil'}>"
|
38
|
+
"#<Session:0x#{ object_id.to_s(16) } valid=#{ valid? } store=#{ self.store.inspect } user=#{ user_id || 'nil' }>"
|
38
39
|
end
|
39
40
|
|
40
41
|
def persist?
|
@@ -43,7 +44,7 @@ module Challah
|
|
43
44
|
|
44
45
|
def read
|
45
46
|
persistence_token, user_id = self.store.read
|
46
|
-
return false if persistence_token.nil?
|
47
|
+
return false if persistence_token.nil? || user_id.nil?
|
47
48
|
|
48
49
|
store_user = nil
|
49
50
|
|
@@ -53,7 +54,7 @@ module Challah
|
|
53
54
|
nil
|
54
55
|
end
|
55
56
|
|
56
|
-
if store_user
|
57
|
+
if store_user && store_user.valid_session? && (store_user.persistence_token == persistence_token)
|
57
58
|
if store_user.valid_session?
|
58
59
|
self.user = store_user
|
59
60
|
@valid = true
|
@@ -66,7 +67,7 @@ module Challah
|
|
66
67
|
def save
|
67
68
|
return false unless valid?
|
68
69
|
|
69
|
-
if self.user
|
70
|
+
if self.user && persist?
|
70
71
|
self.store.save(self.user.persistence_token, user_id)
|
71
72
|
return true
|
72
73
|
end
|
@@ -90,7 +91,7 @@ module Challah
|
|
90
91
|
# Returns true if this session has been authenticated and is ready to save.
|
91
92
|
def valid?
|
92
93
|
return @valid if @valid != nil
|
93
|
-
return true if self.user
|
94
|
+
return true if self.user && self.user.valid_session?
|
94
95
|
authenticate!
|
95
96
|
end
|
96
97
|
|
@@ -126,7 +127,7 @@ module Challah
|
|
126
127
|
end
|
127
128
|
end
|
128
129
|
|
129
|
-
if user_record
|
130
|
+
if user_record && user_record.valid_session?
|
130
131
|
session.user = user_record
|
131
132
|
session.persist = true
|
132
133
|
end
|
@@ -157,32 +158,33 @@ module Challah
|
|
157
158
|
|
158
159
|
protected
|
159
160
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
161
|
+
# Try and authenticate against the various auth techniques. If one
|
162
|
+
# technique works, then just exit and make the session active.
|
163
|
+
def authenticate!
|
164
|
+
Challah.techniques.values.each do |klass|
|
165
|
+
technique = klass.new(self)
|
166
|
+
technique.user_model = user_model if technique.respond_to?(:"user_model=")
|
166
167
|
|
167
|
-
|
168
|
+
@user = technique.authenticate
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
|
170
|
+
if @user
|
171
|
+
@persist = technique.respond_to?(:persist?) ? technique.persist? : false
|
172
|
+
break
|
173
|
+
end
|
172
174
|
end
|
173
|
-
end
|
174
175
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
176
|
+
if @user
|
177
|
+
# Only update user record if persistence is on for the technique.
|
178
|
+
# Otherwise this builds up quick (one session for each API call)
|
179
|
+
if @persist
|
180
|
+
@user.successful_authentication!(ip)
|
181
|
+
end
|
182
|
+
|
183
|
+
return @valid = true
|
180
184
|
end
|
181
185
|
|
182
|
-
|
186
|
+
@valid = false
|
183
187
|
end
|
184
188
|
|
185
|
-
@valid = false
|
186
|
-
end
|
187
189
|
end
|
188
190
|
end
|
data/lib/challah/signup.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Challah
|
2
2
|
class Signup
|
3
|
+
|
3
4
|
extend ActiveModel::Naming
|
4
5
|
include ActiveModel::Conversion
|
5
6
|
|
@@ -17,7 +18,7 @@ module Challah
|
|
17
18
|
return unless Hash === value
|
18
19
|
|
19
20
|
value.each do |key, value|
|
20
|
-
self.send("#{key}=", value)
|
21
|
+
self.send("#{ key }=", value)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -60,7 +61,7 @@ module Challah
|
|
60
61
|
user.errors.each { |a, e| @errors.add(a, e) }
|
61
62
|
end
|
62
63
|
|
63
|
-
if !provider
|
64
|
+
if !provider || !valid_provider?
|
64
65
|
result = false
|
65
66
|
user.errors.each { |a, e| @errors.add(a, e) unless @errors.added?(a, e) }
|
66
67
|
end
|
@@ -71,5 +72,6 @@ module Challah
|
|
71
72
|
def self.model_name
|
72
73
|
ActiveModel::Name.new(Challah::Signup, Challah, "Signup")
|
73
74
|
end
|
75
|
+
|
74
76
|
end
|
75
|
-
end
|
77
|
+
end
|