challah 1.5.0 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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