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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +32 -0
  3. data/README.md +6 -39
  4. data/VERSION +1 -1
  5. data/app/controllers/sessions_controller.rb +11 -10
  6. data/app/models/authorization.rb +2 -0
  7. data/lib/challah/audit.rb +38 -36
  8. data/lib/challah/authenticators/api_key.rb +4 -2
  9. data/lib/challah/authenticators/password.rb +3 -1
  10. data/lib/challah/authenticators.rb +5 -3
  11. data/lib/challah/concerns/authorizeable.rb +4 -0
  12. data/lib/challah/concerns/user/attributeable.rb +37 -29
  13. data/lib/challah/concerns/user/authenticateable.rb +4 -2
  14. data/lib/challah/concerns/user/authorizable.rb +16 -12
  15. data/lib/challah/concerns/user/findable.rb +13 -10
  16. data/lib/challah/concerns/user/passwordable.rb +5 -3
  17. data/lib/challah/concerns/user/provideable.rb +22 -20
  18. data/lib/challah/concerns/user/statusable.rb +3 -21
  19. data/lib/challah/concerns/user/validateable.rb +3 -1
  20. data/lib/challah/concerns/userable.rb +1 -3
  21. data/lib/challah/controller.rb +69 -65
  22. data/lib/challah/cookie_store.rb +7 -5
  23. data/lib/challah/encrypter.rb +4 -2
  24. data/lib/challah/engine.rb +5 -18
  25. data/lib/challah/providers/password_provider.rb +9 -7
  26. data/lib/challah/providers.rb +3 -1
  27. data/lib/challah/random.rb +6 -4
  28. data/lib/challah/routes.rb +6 -6
  29. data/lib/challah/session.rb +27 -25
  30. data/lib/challah/signup.rb +5 -3
  31. data/lib/challah/simple_cookie_store.rb +82 -80
  32. data/lib/challah/techniques/api_key_technique.rb +2 -2
  33. data/lib/challah/techniques/password_technique.rb +2 -1
  34. data/lib/challah/techniques/token_technique.rb +3 -1
  35. data/lib/challah/techniques.rb +2 -0
  36. data/lib/challah/test.rb +6 -0
  37. data/lib/challah/validators/email_validator.rb +2 -0
  38. data/lib/challah/validators/password_validator.rb +5 -3
  39. data/lib/challah/version.rb +3 -1
  40. data/lib/challah.rb +2 -5
  41. data/lib/generators/challah_generator.rb +17 -0
  42. data/lib/generators/templates/migration.erb +39 -0
  43. data/lib/tasks/setup.rake +7 -3
  44. metadata +63 -26
  45. data/db/migrate/20120127150433_create_users.rb +0 -29
  46. data/db/migrate/20121116210759_create_authorizations.rb +0 -22
  47. 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
@@ -1,5 +1,6 @@
1
1
  module Challah
2
2
  module UserValidateable
3
+
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  included do
@@ -17,5 +18,6 @@ module Challah
17
18
 
18
19
  validates_with Challah.options[:password_validator], force: false
19
20
  end
21
+
20
22
  end
21
- end
23
+ end
@@ -1,5 +1,6 @@
1
1
  module Challah
2
2
  module Userable
3
+
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  include UserAttributeable
@@ -14,8 +15,5 @@ module Challah
14
15
  include UserValidateable
15
16
  end
16
17
 
17
- included do
18
- Challah.include_user_plugins!
19
- end
20
18
  end
21
19
  end
@@ -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
- # Is there currently a logged in user? Returns true if it is safe to use
47
- # the {#current_user current_user} method.
48
- #
49
- # @note This method is also available as a helper in your views.
50
- #
51
- # @see #current_user current_user
52
- #
53
- # @return [Boolean] Is there a user logged in?
54
- def current_user?
55
- !!current_user
56
- end
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
- # Alias for current_user?
59
- def signed_in?
60
- current_user?
61
- end
62
- alias_method :logged_in?, :signed_in?
61
+ # Alias for current_user?
62
+ def signed_in?
63
+ current_user?
64
+ end
65
+ alias_method :logged_in?, :signed_in?
63
66
 
64
- # The user that is currently logged into this session. If there is no
65
- # user logged in, nil will be returned.
66
- #
67
- # @note This method is also available as a helper in your views.
68
- #
69
- # @return [User, nil] The current authenticated user.
70
- def current_user
71
- @current_user ||= current_user_session.user
72
- end
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
- # The current authentication session, if one exists. A {Session} object will be
75
- # returned regardless of its valid status. If an invalid session is returned, the
76
- # {Session#user user} attribute will be nil.
77
- #
78
- # @return [Session] The current browser session.
79
- def current_user_session
80
- @current_user_session ||= Challah::Session.find(request, params, user_model)
81
- end
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
- # Restrict a controller to only authenticated users. If someone tries to access
84
- # a restricted action and is not logged in, they will be redirected to the
85
- # login page.
86
- #
87
- # This method is an alias for:
88
- #
89
- # restrict_to_authenticated
90
- #
91
- # @example
92
- # class YourController < ApplicationController
93
- # before_action :login_required
94
- #
95
- # # ...
96
- # end
97
- #
98
- # @example Specifing certain actions.
99
- # class YourOtherController < ApplicationController
100
- # before_action :login_required, :only => [ :create, :update, :destroy ]
101
- #
102
- # # ...
103
- # end
104
- #
105
- # @see Controller::ClassMethods#restrict_to_authenticated restrict_to_authenticated
106
- def signin_required
107
- unless signed_in?
108
- session[:return_to] = request.url
109
- redirect_to signin_path and return
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
@@ -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
- def validation_cookie_value(value = nil)
19
- value = session_cookie_value unless value
20
- Encrypter.md5(value, request.user_agent, request.remote_ip)
21
- end
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
@@ -1,9 +1,10 @@
1
- require 'digest/sha2'
2
- require 'bcrypt'
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
@@ -1,17 +1,17 @@
1
1
  module Challah
2
2
  class Engine < ::Rails::Engine
3
3
 
4
- initializer 'challah.router' do |app|
5
- app.routes_reloader.paths.insert(0, File.expand_path(File.join(File.dirname(__FILE__), 'routes.rb')))
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 'challah.active_record' do
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 'challah.action_controller' do
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
@@ -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
@@ -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),('a'..'z'),('A'..'Z')].map {|i| i.to_a }.flatten
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
@@ -1,11 +1,11 @@
1
1
  Rails.application.routes.draw do
2
2
  unless Challah.options[:skip_routes]
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'
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 '/login', to: 'sessions#create', as: 'submit_login'
8
- get '/login', to: 'sessions#new', as: 'login'
9
- get '/logout', to: 'sessions#destroy', as: 'logout'
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
@@ -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? or user_id.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 and store_user.valid_session? and store_user.persistence_token == persistence_token
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 and persist?
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 and self.user.valid_session?
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 and user_record.valid_session?
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
- # Try and authenticate against the various auth techniques. If one
161
- # technique works, then just exit and make the session active.
162
- def authenticate!
163
- Challah.techniques.values.each do |klass|
164
- technique = klass.new(self)
165
- technique.user_model = user_model if technique.respond_to?(:"user_model=")
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
- @user = technique.authenticate
168
+ @user = technique.authenticate
168
169
 
169
- if @user
170
- @persist = technique.respond_to?(:persist?) ? technique.persist? : false
171
- break
170
+ if @user
171
+ @persist = technique.respond_to?(:persist?) ? technique.persist? : false
172
+ break
173
+ end
172
174
  end
173
- end
174
175
 
175
- if @user
176
- # Only update user record if persistence is on for the technique.
177
- # Otherwise this builds up quick (one session for each API call)
178
- if @persist
179
- @user.successful_authentication!(ip)
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
- return @valid = true
186
+ @valid = false
183
187
  end
184
188
 
185
- @valid = false
186
- end
187
189
  end
188
190
  end
@@ -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 or !valid_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