challah 1.6.1 → 2.0.0.beta1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +5 -38
  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 +35 -33
  13. data/lib/challah/concerns/user/authenticateable.rb +2 -0
  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 +1 -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 +2 -8
  42. data/lib/generators/templates/{migration.rb → migration.erb} +3 -6
  43. metadata +42 -19
  44. data/lib/challah/plugins.rb +0 -54
@@ -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
@@ -4,6 +4,7 @@ module Challah
4
4
  # To use a different storage method for persisting a session, just create
5
5
  # a new class that responds to +read+, +save+ and +destroy+
6
6
  class SimpleCookieStore
7
+
7
8
  def initialize(session)
8
9
  @session = session
9
10
  end
@@ -13,7 +14,7 @@ module Challah
13
14
  end
14
15
 
15
16
  def inspect
16
- "#<SimpleCookieStore:0x#{object_id.to_s(16)} valid=#{existing?}>"
17
+ "#<SimpleCookieStore:0x#{ object_id.to_s(16) } valid=#{ existing? }>"
17
18
  end
18
19
 
19
20
  def read
@@ -29,107 +30,108 @@ module Challah
29
30
 
30
31
  private
31
32
 
32
- def clear
33
- cookies.delete(session_cookie_name, domain: domain)
34
- cookies.delete(validation_cookie_name, domain: domain)
35
- end
33
+ def clear
34
+ cookies.delete(session_cookie_name, domain: domain)
35
+ cookies.delete(validation_cookie_name, domain: domain)
36
+ end
36
37
 
37
- def cookie_values
38
- session_cookie && session_cookie.to_s.split(joiner)
39
- end
38
+ def cookie_values
39
+ session_cookie && session_cookie.to_s.split(joiner)
40
+ end
40
41
 
41
- def cookies
42
- request.cookie_jar
43
- end
42
+ def cookies
43
+ request.cookie_jar
44
+ end
44
45
 
45
- def default_cookie_prefix
46
- Challah.options[:cookie_prefix]
47
- end
46
+ def default_cookie_prefix
47
+ Challah.options[:cookie_prefix]
48
+ end
48
49
 
49
- def domain
50
- request.session_options[:domain]
51
- end
50
+ def domain
51
+ request.session_options[:domain]
52
+ end
52
53
 
53
- # Do the cookies exist, and are they valid?
54
- def existing?
55
- exists = false
54
+ # Do the cookies exist, and are they valid?
55
+ def existing?
56
+ exists = false
56
57
 
57
- if session_cookie and validation_cookie
58
- session_tmp = session_cookie.to_s
59
- validation_tmp = validation_cookie.to_s
58
+ if session_cookie && validation_cookie
59
+ session_tmp = session_cookie.to_s
60
+ validation_tmp = validation_cookie.to_s
60
61
 
61
- if validation_tmp == validation_cookie_value(session_tmp)
62
- exists = true
62
+ if validation_tmp == validation_cookie_value(session_tmp)
63
+ exists = true
64
+ end
63
65
  end
66
+
67
+ exists
64
68
  end
65
69
 
66
- exists
67
- end
70
+ def expiration
71
+ @expiration ||= 1.month.from_now
72
+ end
68
73
 
69
- def expiration
70
- @expiration ||= 1.month.from_now
71
- end
74
+ def joiner
75
+ "@"
76
+ end
72
77
 
73
- def joiner
74
- '@'
75
- end
78
+ def prefix
79
+ @prefix ||= [ default_cookie_prefix, user_model_id ].compact.join("-")
80
+ end
76
81
 
77
- def prefix
78
- @prefix ||= [ default_cookie_prefix, user_model_id ].compact.join('-')
79
- end
82
+ def request
83
+ raise "No Request Provided" unless @session && @session.request
84
+ @session.request
85
+ end
80
86
 
81
- def request
82
- raise "No Request Provided" unless @session and @session.request
83
- @session.request
84
- end
87
+ def session_cookie
88
+ cookies[session_cookie_name]
89
+ end
85
90
 
86
- def session_cookie
87
- cookies[session_cookie_name]
88
- end
91
+ def session_cookie_name
92
+ "#{ prefix }-s"
93
+ end
89
94
 
90
- def session_cookie_name
91
- "#{prefix}-s"
92
- end
95
+ def session_cookie_value
96
+ "#@token#{ joiner }#@user_id"
97
+ end
93
98
 
94
- def session_cookie_value
95
- "#@token#{joiner}#@user_id"
96
- end
99
+ def user_model_id
100
+ if @session && @session.user_model && @session.user_model.table_name != "users"
101
+ Encrypter.md5(@session.user_model.table_name).slice(0..5)
102
+ end
103
+ end
97
104
 
98
- def user_model_id
99
- if @session && @session.user_model && @session.user_model.table_name != 'users'
100
- Encrypter.md5(@session.user_model.table_name).slice(0..5)
105
+ def validation_cookie
106
+ cookies[validation_cookie_name]
101
107
  end
102
- end
103
108
 
104
- def validation_cookie
105
- cookies[validation_cookie_name]
106
- end
109
+ def validation_cookie_name
110
+ "#{ prefix }-v"
111
+ end
107
112
 
108
- def validation_cookie_name
109
- "#{prefix}-v"
110
- end
113
+ def validation_cookie_value(value = nil)
114
+ value = session_cookie_value unless value
115
+ Encrypter.md5(value)
116
+ end
111
117
 
112
- def validation_cookie_value(value = nil)
113
- value = session_cookie_value unless value
114
- Encrypter.md5(value)
115
- end
118
+ def write_cookies!
119
+ cookies[session_cookie_name] = {
120
+ value: session_cookie_value,
121
+ expires: expiration,
122
+ secure: false,
123
+ httponly: true,
124
+ domain: domain
125
+ }
126
+
127
+ cookies[validation_cookie_name] = {
128
+ value: validation_cookie_value,
129
+ expires: expiration,
130
+ secure: false,
131
+ httponly: true,
132
+ domain: domain
133
+ }
134
+ end
116
135
 
117
- def write_cookies!
118
- cookies[session_cookie_name] = {
119
- value: session_cookie_value,
120
- expires: expiration,
121
- secure: false,
122
- httponly: true,
123
- domain: domain
124
- }
125
-
126
- cookies[validation_cookie_name] = {
127
- value: validation_cookie_value,
128
- expires: expiration,
129
- secure: false,
130
- httponly: true,
131
- domain: domain
132
- }
133
- end
134
136
  end
135
137
  end