monban 0.0.15 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de0ec82d19c70154d42f8f7190037d760b4646f6
4
- data.tar.gz: f48908fd2c9779d41abdc43ec9a275861c2b200a
3
+ metadata.gz: 4f3908fbff78befaa3da1b03f38c0d65d31727e0
4
+ data.tar.gz: 084f52a5bd3751d14600a9f7f54a3eb5a279f555
5
5
  SHA512:
6
- metadata.gz: 358aab452b6adc7615911edcde97bbaba49cf55ebd3c5d485be15ab723fc76c6a94dc89901a7718be1d3732ea3f60805cd83f5bd29d70928d81015cc8f2f5884
7
- data.tar.gz: bff158beadb5e27a3d4c1f32a37c138ac92ab358f3341139c175da0382644421c53ae57c9542f87a7de1c24e7c12dd07dea12544928fe503b47e98dc7023370d
6
+ metadata.gz: a71edfc56f62eaa9ce703b94e4475e1546a47da7960c426022f22d8f733989dad0106edf3a9c153ae9e4919e6f4ddfc27c0b71e2015a5219b9484d7bd82d50fc
7
+ data.tar.gz: f8aa579c7a1e9c9f1b4dfaeb3c13f0173853487f12e63b7abbb618f19f9bddd1d71c11ff928e6ee68fa972ca715a04a5b303d7ca29856ba8b81b0eb9e033c591
data/.gitignore CHANGED
@@ -2,3 +2,6 @@ spec/rails_app/log/*
2
2
  spec/rails_app/tmp/*
3
3
  *.sqlite3
4
4
  pkg
5
+ /.yardoc/
6
+ /_yardoc/
7
+ /doc/
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- monban (0.0.15)
4
+ monban (0.1.0)
5
5
  bcrypt
6
6
  rails
7
7
  warden
data/NEWS.rdoc CHANGED
@@ -1,3 +1,16 @@
1
+ == 0.1.0
2
+ * Fix PasswordStrategy to use configuration options
3
+ * Documentation
4
+ * Renamed encryption to hashing
5
+ * Renamed encrypted to digested
6
+ * Renamed unencrypted to undigested
7
+ * A configuration for `no_login_redirect` was added. This accepts anything that
8
+ can be passed to `redirect_to` and is used when `require_login` is called with
9
+ no logged in user.
10
+ * A configuration for `no_login_handler` was added. This allows developers to
11
+ completely customize the response when `require_login` is called with no
12
+ logged in user.
13
+
1
14
  == 0.0.15
2
15
  * Delegate user_class correctly so that config returns class
3
16
  * Fixed issue authenticate session not allowing for multiple fields
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  # Monban 門番
2
3
 
3
4
  [![Build Status](https://travis-ci.org/halogenandtoast/monban.png?branch=master)](https://travis-ci.org/halogenandtoast/monban)
@@ -36,7 +37,7 @@ And you're ready to start designing your authentication system.
36
37
 
37
38
  ## Generators
38
39
 
39
- If you'd like a good starting point for building an app using Monban, it is suggested to use the [monban generators](https://github.com/halogenandtoast/monban-generators).
40
+ If you'd like a good starting point for building an app using Monban, it is suggested to use the [monban generators]
40
41
 
41
42
  ## Usage
42
43
 
@@ -78,7 +79,7 @@ Monban provides the following:
78
79
  Monban.test_mode!
79
80
  ```
80
81
 
81
- Which will change password encryption to provide plaintext responses instead of using BCrypt. This will allow you to write factories using the password_digest field:
82
+ Which will change password hashing method to provide plaintext responses instead of using BCrypt. This will allow you to write factories using the password_digest field:
82
83
 
83
84
  ```ruby
84
85
  FactoryGirl.define do
@@ -169,6 +170,26 @@ end
169
170
 
170
171
  ## Advanced Functionality
171
172
 
173
+ ### Authentication with username instead of email
174
+
175
+ If you want to sign in with username instead of email just change the configuration option
176
+
177
+ ```ruby
178
+ # config/initializers/monban.rb
179
+ Monban.configure do |config|
180
+ config.user_lookup_field = :username
181
+ end
182
+ ```
183
+
184
+ If you used the monban:scaffold generator from [monban generators] you'll have to change the following four references to email.
185
+
186
+ * In SessionsController#session_params
187
+ * In UsersController#user_params
188
+ * The email form field on sessions#new
189
+ * The email form field on users#new
190
+
191
+ ### Using multiple lookup fields
192
+
172
193
  You may perform a look up on a user using multiple fields by doing something like the following:
173
194
 
174
195
  ```ruby
@@ -194,7 +215,43 @@ end
194
215
 
195
216
  This will allow the user to enter either their username or email to login
196
217
 
197
- ### Limitations
218
+ ## Configuration
219
+
220
+ Monban::Configuration has lots of options for changing how monban works. Currently the options you can change are as follows:
221
+
222
+ ### User values
223
+
224
+ * **user_lookup_field**: (default `:email`) Field in the database to lookup a user by.
225
+ * **user_token_field**: (default `:password`) Field the form submits containing the undigested password.
226
+ * **user_token_store_field**: (default: `:password_digest`) Field in the database that stores the user's digested password.
227
+ * **user_class**: (default: `User`) The user class.
228
+
229
+ ### Services
230
+
231
+ * **sign_in_notice**: (default: `You must be signed in`) Rails flash message to set when user signs in.
232
+ * **sign_in_service**: (default: `Monban::Services::SignIn`) Service for signing a user in.
233
+ * **sign_up_service**: (default: `Monban::Services::SignUp`) Service for signing a user up.
234
+ * **sign_out_service**: (default: `Monban::Services::SignOut`) Service for signing a user out.
235
+ * **authentication_service**: (default: `Monban::Services::Authentication`) Service for authenticated a user.
236
+ * **password_reset_service**: (default: `Monban::Services::PasswordReset`) Service for resetting a user's password.
237
+
238
+ ### Rails values
239
+
240
+ * **no_login_handler**: A before_action for rails that handles when a user is not signed in.
241
+ * **no_login_redirect**: Used by the no_login_handler to redirect the user
242
+
243
+ ### Methods
244
+
245
+ * **hashing_method**: Method to hash an undigested password.
246
+ * **token_comparison**: Method to compare a digested and undigested password.
247
+ * **creation_method**: Method for creating a user.
248
+ * **find_method**: Method for finding a user.
249
+
250
+ ### Warden Settings
251
+
252
+ * **failure_app**: Necessary for warden to work. A rack app that handles failures in authentication.
253
+
254
+ ## Limitations
198
255
 
199
256
  Here are a few of the current limitations of monban:
200
257
 
@@ -207,3 +264,5 @@ Here are a few of the current limitations of monban:
207
264
  3. Commit your changes (`git commit -am 'Add some feature'`)
208
265
  4. Push to the branch (`git push origin my-new-feature`)
209
266
  5. Create new Pull Request
267
+
268
+ [monban generators]: https://github.com/halogenandtoast/monban-generators
data/lib/monban.rb CHANGED
@@ -10,6 +10,10 @@ require "monban/field_map"
10
10
  require "monban/strategies/password_strategy"
11
11
  require "active_support/core_ext/module/attribute_accessors"
12
12
 
13
+ # Monban is an authentication toolkit designed to allow developers to create their own
14
+ # authentication solutions. If you're interested in a default implementation try
15
+ # {http://github.com/halogenandtoast/monban-generators Monban Generators}
16
+ # @since 0.0.15
13
17
  module Monban
14
18
  mattr_accessor :warden_config
15
19
  mattr_accessor :config
@@ -19,42 +23,82 @@ module Monban
19
23
  autoload :ControllerHelpers, "monban/test/controller_helpers"
20
24
  end
21
25
 
22
- def self.initialize warden_config, &block
23
- setup_config(&block)
26
+ # initialize Monban. Sets up warden and the default configuration.
27
+ #
28
+ # @note This is used in {Monban::Railtie} in order to bootstrap Monban
29
+ # @param warden_config [Warden::Config] the configuration from warden
30
+ # @see Monban::Railtie
31
+ # @see Monban::Configuration
32
+ def self.initialize warden_config
33
+ setup_config
24
34
  setup_warden_config(warden_config)
25
35
  end
26
36
 
37
+ # compares the token (undigested password) to a digested password
38
+ #
39
+ # @param digest [String] A digested password
40
+ # @param token [String] An undigested password
41
+ # @see Monban::Configuration#default_token_comparison
42
+ # @return [Boolean] whether the token and digest match
27
43
  def self.compare_token(digest, token)
28
44
  config.token_comparison.call(digest, token)
29
45
  end
30
46
 
31
- def self.encrypt_token(token)
32
- config.encryption_method.call(token)
47
+ # hashes a token
48
+ #
49
+ # @param token [String] the password in undigested form
50
+ # @see Monban::Configuration#default_hashing_method
51
+ # @return [String] a digest of the token
52
+ def self.hash_token(token)
53
+ config.hashing_method.call(token)
33
54
  end
34
55
 
56
+ # the user class
57
+ #
58
+ # @see Monban::Configuration#setup_class_defaults
59
+ # @return [Class] the User class
35
60
  def self.user_class
36
61
  config.user_class
37
62
  end
38
63
 
64
+ # finds a user based on their credentials
65
+ #
66
+ # @param params [Hash] a hash of user parameters
67
+ # @param field_map [FieldMap] a field map in order to allow multiple lookup fields
68
+ # @see Monban::Configuration#default_find_method
69
+ # @return [User] if user is found
70
+ # @return [nil] if no user is found
39
71
  def self.lookup(params, field_map)
40
72
  fields = FieldMap.new(params, field_map).to_fields
41
73
  self.config.find_method.call(fields)
42
74
  end
43
75
 
76
+ # Puts monban into test mode. This will disable hashing passwords
77
+ # @note You must call this if you want to use monban in your tests
44
78
  def self.test_mode!
45
79
  Warden.test_mode!
46
80
  self.config ||= Monban::Configuration.new
47
- config.encryption_method = ->(password) { password }
48
- config.token_comparison = ->(digest, unencrypted_password) do
49
- digest == unencrypted_password
81
+ config.hashing_method = ->(password) { password }
82
+ config.token_comparison = ->(digest, undigested_password) do
83
+ digest == undigested_password
50
84
  end
51
85
  end
52
86
 
87
+ # Configures monban
88
+ #
89
+ # @yield [configuration] Yield the current configuration
90
+ # @example A custom configuration
91
+ # Monban.configure do |config|
92
+ # config.user_lookup_field = :username
93
+ # config.user_token_store_field = :hashed_password
94
+ # end
53
95
  def self.configure(&block)
54
96
  self.config ||= Monban::Configuration.new
55
97
  yield self.config
56
98
  end
57
99
 
100
+ # Resets monban in between tests.
101
+ # @note You must call this between tests
58
102
  def self.test_reset!
59
103
  Warden.test_reset!
60
104
  end
@@ -63,9 +107,6 @@ module Monban
63
107
 
64
108
  def self.setup_config
65
109
  self.config ||= Monban::Configuration.new
66
- if block_given?
67
- yield config
68
- end
69
110
  end
70
111
 
71
112
  def self.setup_warden_config(warden_config)
@@ -1,9 +1,26 @@
1
1
  module Monban
2
+ # Middleware used in tests to allow users to be signed in directly, without
3
+ # having to load and submit the sign in form. The user should be provided by
4
+ # using the key :as in a hash passed to the path.
5
+ #
6
+ # @note This should only be used for testing purposes
7
+ # @since 0.0.15
8
+ # @example Using the backdoor in an rspec feature spec
9
+ # feature "User dashboard" do
10
+ # scenario "user visits dashboard" do
11
+ # user = create(:user)
12
+ # visit dashboard_path(as: user)
13
+ # expect(page).to have_css("#dashboard")
14
+ # end
15
+ # end
2
16
  class BackDoor
17
+ # Create the a new BackDoor middleware for test purposes
18
+ # @return [BackDoor]
3
19
  def initialize(app)
4
20
  @app = app
5
21
  end
6
22
 
23
+ # Execute the BackDoor middleware signing in the user specified with :as
7
24
  def call(env)
8
25
  sign_in_through_the_back_door(env)
9
26
  @app.call(env)
@@ -1,29 +1,37 @@
1
1
  module Monban
2
+ # Configuration options for Monban
3
+ # @since 0.0.15
2
4
  class Configuration
3
-
4
5
  attr_accessor :user_token_field, :user_token_store_field
5
- attr_accessor :encryption_method, :token_comparison, :user_lookup_field
6
+ attr_accessor :hashing_method, :token_comparison, :user_lookup_field
6
7
  attr_accessor :sign_in_notice
7
8
  attr_accessor :sign_in_service, :sign_up_service, :sign_out_service
8
9
  attr_accessor :authentication_service, :password_reset_service
9
10
  attr_accessor :failure_app
10
11
  attr_accessor :creation_method, :find_method
12
+ attr_accessor :no_login_handler, :no_login_redirect
11
13
 
12
14
  attr_writer :user_class
13
15
 
14
16
  def initialize
15
17
  setup_class_defaults
16
- setup_token_encryption
18
+ setup_token_hashing
17
19
  setup_notices
18
20
  setup_services
19
21
  setup_requirements
20
22
  end
21
23
 
24
+ # Default creation method. Can be overriden via {Monban.configure}
25
+ #
26
+ # @see #creation_method=
22
27
  def default_creation_method
23
28
  ->(params) { Monban.user_class.create(params) }
24
29
  end
25
30
 
26
- def default_encryption_method
31
+ # Default hashing method. Can be overriden via {Monban.configure}
32
+ #
33
+ # @see #hashing_method=
34
+ def default_hashing_method
27
35
  ->(token) do
28
36
  if token.present?
29
37
  BCrypt::Password.create(token)
@@ -33,25 +41,47 @@ module Monban
33
41
  end
34
42
  end
35
43
 
44
+ # Default find method. Can be overriden via {Monban.configure}
45
+ #
46
+ # @see #find_method=
47
+ # @see Monban.user_class
36
48
  def default_find_method
37
49
  ->(params) { Monban.user_class.find_by(params) }
38
50
  end
39
51
 
40
- def default_password_comparison
41
- ->(digest, unencrypted_token) do
42
- BCrypt::Password.new(digest) == unencrypted_token
52
+ # Default token comparison method. Can be overriden via {Monban.configure}
53
+ #
54
+ # @see #token_comparison=
55
+ def default_token_comparison
56
+ ->(digest, undigested_token) do
57
+ BCrypt::Password.new(digest) == undigested_token
43
58
  end
44
59
  end
45
60
 
61
+ # Default handler when user is not logged in. Can be overriden via {Monban.configure}
62
+ #
63
+ # @see #no_login_handler=
64
+ # @see #sign_in_notice
65
+ # @see #no_login_redirect
66
+ def default_no_login_handler
67
+ ->(controller) do
68
+ controller.flash.notice = Monban.config.sign_in_notice
69
+ controller.redirect_to Monban.config.no_login_redirect
70
+ end
71
+ end
72
+
73
+ # User class. Can be overriden via {Monban.configure}
74
+ #
75
+ # @see #user_class=
46
76
  def user_class
47
77
  @user_class.constantize
48
78
  end
49
79
 
50
80
  private
51
81
 
52
- def setup_token_encryption
53
- @encryption_method = default_encryption_method
54
- @token_comparison = default_password_comparison
82
+ def setup_token_hashing
83
+ @hashing_method = default_hashing_method
84
+ @token_comparison = default_token_comparison
55
85
  end
56
86
 
57
87
  def setup_notices
@@ -65,19 +95,20 @@ module Monban
65
95
  @user_lookup_field = :email
66
96
  @creation_method = default_creation_method
67
97
  @find_method = default_find_method
98
+ @no_login_redirect = { controller: '/session', action: 'new' }
99
+ @no_login_handler = default_no_login_handler
68
100
  end
69
101
 
70
102
  def setup_services
71
- @authentication_service = Monban::Authentication
72
- @sign_in_service = Monban::SignIn
73
- @sign_up_service = Monban::SignUp
74
- @sign_out_service = Monban::SignOut
75
- @password_reset_service = Monban::PasswordReset
103
+ @authentication_service = Monban::Services::Authentication
104
+ @sign_in_service = Monban::Services::SignIn
105
+ @sign_up_service = Monban::Services::SignUp
106
+ @sign_out_service = Monban::Services::SignOut
107
+ @password_reset_service = Monban::Services::PasswordReset
76
108
  end
77
109
 
78
110
  def setup_requirements
79
111
  @failure_app = lambda{|e|[401, {"Content-Type" => "text/plain"}, ["Authorization Failed"]] }
80
112
  end
81
113
  end
82
-
83
114
  end
@@ -1,6 +1,10 @@
1
1
  module Monban
2
2
  module Constraints
3
+ # Rails route constraint for signed in users
3
4
  class SignedIn
5
+ # Checks to see if the constraint is matched by having a user signed in
6
+ #
7
+ # @param request [Rack::Request] A rack request
4
8
  def matches?(request)
5
9
  warden = request.env["warden"]
6
10
  warden && warden.authenticated?
@@ -1,6 +1,10 @@
1
1
  module Monban
2
2
  module Constraints
3
+ # Rails route constraint for signed out users
3
4
  class SignedOut
5
+ # Checks to see if the constraint is matched by not having a user signed in
6
+ #
7
+ # @param request [Rack::Request] A rack request
4
8
  def matches?(request)
5
9
  warden = request.env["warden"]
6
10
  warden && warden.unauthenticated?
@@ -2,12 +2,21 @@ require 'bcrypt'
2
2
  require 'active_support/concern'
3
3
 
4
4
  module Monban
5
+ # Mixin to be included in Rails controllers.
6
+ # @since 0.0.15
5
7
  module ControllerHelpers
6
8
  extend ActiveSupport::Concern
7
9
  included do
8
10
  helper_method :current_user, :signed_in?
9
11
  end
10
12
 
13
+ # Sign in a user
14
+ #
15
+ # @note Uses the {Monban::Services::SignIn} service to create a session
16
+ #
17
+ # @param user [User] the user object to sign in
18
+ # @yield Yields to the block if the user is successfully signed in
19
+ # @return [Object] returns the value from calling perform on the {Monban::Services::SignIn} service
11
20
  def sign_in user
12
21
  Monban.config.sign_in_service.new(user, warden).perform.tap do |status|
13
22
  if status && block_given?
@@ -16,10 +25,22 @@ module Monban
16
25
  end
17
26
  end
18
27
 
28
+ # Sign out the current session
29
+ #
30
+ # @note Uses the {Monban::Services::SignOut} service to destroy the session
31
+ #
32
+ # @return [Object] returns the value from calling perform on the {Monban::Services::SignOut} service
19
33
  def sign_out
20
34
  Monban.config.sign_out_service.new(warden).perform
21
35
  end
22
36
 
37
+ # Sign up a user
38
+ #
39
+ # @note Uses the {Monban::Services::SignUp} service to create a user
40
+ #
41
+ # @param user_params [Hash] params containing lookup and token fields
42
+ # @yield Yields to the block if the user is signed up successfully
43
+ # @return [Object] returns the value from calling perform on the {Monban::Services::SignUp} service
23
44
  def sign_up user_params
24
45
  Monban.config.sign_up_service.new(user_params).perform.tap do |status|
25
46
  if status && block_given?
@@ -28,6 +49,53 @@ module Monban
28
49
  end
29
50
  end
30
51
 
52
+ # Authenticates a session.
53
+ #
54
+ # @note Uses the {Monban::Services::Authentication} service to verify the user's details
55
+ #
56
+ # @param session_params [Hash] params containing lookup and token fields
57
+ # @param field_map [Hash] Field map used for allowing users to sign in with multiple fields e.g. email and username
58
+ # @return [User] if authentication succeeded
59
+ # @return [nil] if authentication failed
60
+ # @example Basic usage
61
+ # class SessionsController < ApplicationController
62
+ # def create
63
+ # user = authenticate_session(session_params)
64
+ #
65
+ # if sign_in(user)
66
+ # redirect_to(root_path)
67
+ # else
68
+ # render :new
69
+ # end
70
+ # end
71
+ #
72
+ # private
73
+ #
74
+ # def session_params
75
+ # params.require(:session).permit(:email, :password)
76
+ # end
77
+ #
78
+ # end
79
+ # @example Using the field map to authenticate using multiple lookup fields
80
+ # class SessionsController < ApplicationController
81
+ # def create
82
+ # user = authenticate_session(session_params, email_or_username: [:email, :username])
83
+ #
84
+ # if sign_in(user)
85
+ # redirect_to(root_path)
86
+ # else
87
+ # render :new
88
+ # end
89
+ # end
90
+ #
91
+ # private
92
+ #
93
+ # def session_params
94
+ # params.require(:session).permit(:email_or_username, :password)
95
+ # end
96
+ #
97
+ # end
98
+
31
99
  def authenticate_session session_params, field_map = nil
32
100
  token_field = Monban.config.user_token_field
33
101
  password = session_params.fetch(token_field)
@@ -35,30 +103,55 @@ module Monban
35
103
  authenticate(user, password)
36
104
  end
37
105
 
106
+ # Authenticates a user given a password
107
+ #
108
+ # @note Uses the {Monban::Services::Authentication} service to verify the user's credentials
109
+ #
110
+ # @param user [User] the user
111
+ # @param password [String] the password
112
+ # @return [User] if authentication succeeded
113
+ # @return [nil] if authentication failed
38
114
  def authenticate user, password
39
115
  Monban.config.authentication_service.new(user, password).perform
40
116
  end
41
117
 
118
+ # Resets a user's password
119
+ #
120
+ # @note Uses the {Monban::Services::PasswordReset} service to change a user's password
121
+ #
122
+ # @param user [User] the user
123
+ # @param password [String] the password
42
124
  def reset_password user, password
43
125
  Monban.config.password_reset_service.new(user, password).perform
44
126
  end
45
127
 
128
+ # @api private
46
129
  def warden
47
130
  request.env['warden']
48
131
  end
49
132
 
133
+ # helper_method that returns the current user
134
+ #
135
+ # @return [User] if user is signed in
136
+ # @return [nil] if user is not signed in
50
137
  def current_user
51
138
  @current_user ||= warden.user
52
139
  end
53
140
 
141
+ # helper_method that checks if there is a user signed in
142
+ #
143
+ # @return [User] if user is signed in
144
+ # @return [nil] if user is not signed in
54
145
  def signed_in?
55
146
  warden.user
56
147
  end
57
148
 
149
+ # before_action that determines what to do when the user is not signed in
150
+ #
151
+ # @note Uses the no login handler
58
152
  def require_login
59
153
  unless signed_in?
60
- flash.notice = Monban.config.sign_in_notice
61
- redirect_to controller: '/sessions', action: 'new'
154
+ Monban.config.no_login_handler.call(self)
62
155
  end
63
156
  end
64
157
  end
@@ -1,10 +1,20 @@
1
1
  module Monban
2
+ # FieldMap is used to allow multiple lookup fields. For instance if you
3
+ # wanted to allow a user to sign in via email or username. This is used
4
+ # internally by the authenticate_session controller helper
5
+ # @since 0.0.15
2
6
  class FieldMap
7
+ # @param params [Hash] hash of parameters
8
+ # @param field_map [Hash] hash of values to map
3
9
  def initialize params, field_map
4
10
  @params = params
5
11
  @field_map = field_map
6
12
  end
7
13
 
14
+ # converts params into values that can be passed into a where clause
15
+ #
16
+ # @return [Array] if initialized with field_map
17
+ # @return [Hash] if not initialized with field_map
8
18
  def to_fields
9
19
  if @field_map
10
20
  params_from_field_map
@@ -1,6 +1,8 @@
1
1
  require 'warden'
2
2
 
3
3
  module Monban
4
+ # Railtie for Monban. Injects the Warden middleware and initializes Monban.
5
+ # @since 0.0.15
4
6
  class Railtie < Rails::Railtie
5
7
  config.app_middleware.use Warden::Manager do |config|
6
8
  Monban.initialize(config)
@@ -1,26 +1,38 @@
1
1
  module Monban
2
- class Authentication
3
- def initialize user, unencrypted_token
4
- @user = user
5
- @unencrypted_token = unencrypted_token
6
- end
2
+ module Services
3
+ # Authentication service. Checks to see if the credentials provided are valid
4
+ # @since 0.0.15
5
+ class Authentication
6
+ # Initialize service
7
+ #
8
+ # @param user [User] A user object
9
+ # @param undigested_token [String] An undigested password
10
+ def initialize user, undigested_token
11
+ @user = user
12
+ @undigested_token = undigested_token
13
+ end
7
14
 
8
- def perform
9
- if authenticated?
10
- @user
11
- else
12
- false
15
+ # Perform the service
16
+ #
17
+ # @return [User] if authentication succeeds
18
+ # @return [false] if authentication fails
19
+ def perform
20
+ if authenticated?
21
+ @user
22
+ else
23
+ false
24
+ end
13
25
  end
14
- end
15
26
 
16
- private
27
+ private
17
28
 
18
- def authenticated?
19
- @user && Monban.compare_token(@user.send(token_store_field), @unencrypted_token)
20
- end
29
+ def authenticated?
30
+ @user && Monban.compare_token(@user.send(token_store_field), @undigested_token)
31
+ end
21
32
 
22
- def token_store_field
23
- Monban.config.user_token_store_field
33
+ def token_store_field
34
+ Monban.config.user_token_store_field
35
+ end
24
36
  end
25
37
  end
26
38
  end
@@ -1,14 +1,23 @@
1
1
  module Monban
2
- class PasswordReset
3
- def initialize user, password
4
- @user = user
5
- @password = password
6
- end
2
+ module Services
3
+ # Password reset service. Updates the password on a User
4
+ # @since 0.0.15
5
+ class PasswordReset
6
+ # Initialize service
7
+ #
8
+ # @param user [User] A user object
9
+ # @param new_password [String] The new undigested password for a user
10
+ def initialize user, new_password
11
+ @user = user
12
+ @new_password = new_password
13
+ end
7
14
 
8
- def perform
9
- field = Monban.config.user_token_store_field
10
- encrypted_password = Monban.encrypt_token(@password)
11
- @user[field] = encrypted_password
15
+ # Perform the service.
16
+ def perform
17
+ field = Monban.config.user_token_store_field
18
+ digested_password = Monban.hash_token(@new_password)
19
+ @user[field] = digested_password
20
+ end
12
21
  end
13
22
  end
14
23
  end
@@ -1,12 +1,21 @@
1
1
  module Monban
2
- class SignIn
3
- def initialize user, warden
4
- @user = user
5
- @warden = warden
6
- end
2
+ module Services
3
+ # Sign in service. Signs the user in via warden
4
+ # @since 0.0.15
5
+ class SignIn
6
+ # Initialize service
7
+ #
8
+ # @param user [User] A user object
9
+ # @param warden [Warden] warden
10
+ def initialize user, warden
11
+ @user = user
12
+ @warden = warden
13
+ end
7
14
 
8
- def perform
9
- @warden.set_user(@user)
15
+ # Perform the service
16
+ def perform
17
+ @warden.set_user(@user)
18
+ end
10
19
  end
11
20
  end
12
21
  end
@@ -1,11 +1,19 @@
1
1
  module Monban
2
- class SignOut
3
- def initialize warden
4
- @warden = warden
5
- end
2
+ module Services
3
+ # Sign out service. Signs the user out via warden
4
+ # @since 0.0.15
5
+ class SignOut
6
+ # Initialize service
7
+ #
8
+ # @param warden [Warden] warden
9
+ def initialize warden
10
+ @warden = warden
11
+ end
6
12
 
7
- def perform
8
- @warden.logout
13
+ # Perform the service
14
+ def perform
15
+ @warden.logout
16
+ end
9
17
  end
10
18
  end
11
19
  end
@@ -1,31 +1,40 @@
1
1
  module Monban
2
- class SignUp
3
- def initialize user_params
4
- encrypted_token = token_digest(user_params)
5
- @user_params = user_params.
6
- except(token_field).
7
- merge(token_store_field.to_sym => encrypted_token)
8
- end
2
+ module Services
3
+ # Sign up service. Signs the user up
4
+ # @since 0.0.15
5
+ class SignUp
6
+ # Initialize service
7
+ #
8
+ # @param user_params [Hash] A hash of user credentials. Should contain the lookup and token fields
9
+ def initialize user_params
10
+ digested_token = token_digest(user_params)
11
+ @user_params = user_params.
12
+ except(token_field).
13
+ merge(token_store_field.to_sym => digested_token)
14
+ end
9
15
 
10
- def perform
11
- Monban.config.creation_method.call(@user_params.to_hash)
12
- end
16
+ # Performs the service
17
+ # @see Monban::Configuration.default_creation_method
18
+ def perform
19
+ Monban.config.creation_method.call(@user_params.to_hash)
20
+ end
13
21
 
14
- private
22
+ private
15
23
 
16
- def token_digest(user_params)
17
- unencrypted_token = user_params[token_field]
18
- unless unencrypted_token.empty?
19
- Monban.encrypt_token(unencrypted_token)
24
+ def token_digest(user_params)
25
+ undigested_token = user_params[token_field]
26
+ unless undigested_token.empty?
27
+ Monban.hash_token(undigested_token)
28
+ end
20
29
  end
21
- end
22
30
 
23
- def token_store_field
24
- Monban.config.user_token_store_field
25
- end
31
+ def token_store_field
32
+ Monban.config.user_token_store_field
33
+ end
26
34
 
27
- def token_field
28
- Monban.config.user_token_field
35
+ def token_field
36
+ Monban.config.user_token_field
37
+ end
29
38
  end
30
39
  end
31
40
  end
@@ -2,16 +2,41 @@ require 'warden'
2
2
 
3
3
  module Monban
4
4
  module Strategies
5
+ # Password strategy for warden
6
+ # @since 0.0.15
5
7
  class PasswordStrategy < ::Warden::Strategies::Base
8
+
9
+ # Checks if strategy should be executed
10
+ # @return [Boolean]
6
11
  def valid?
7
- params[:email] || params[:password]
12
+ lookup_field_value || token_field_value
8
13
  end
9
14
 
15
+
16
+ # Authenticates for warden
10
17
  def authenticate!
11
- user = Monban.user_class.find_by(email: params[:email])
12
- auth = Authentication.new(user, params[:password])
18
+ user = Monban.user_class.find_by(lookup_field => lookup_field_value)
19
+ auth = Monban.config.authentication_service.new(user, token_field_value)
13
20
  auth.authenticated? ? success!(user) : fail!("Could not log in")
14
21
  end
22
+
23
+ private
24
+
25
+ def lookup_field_value
26
+ params[lookup_field]
27
+ end
28
+
29
+ def token_field_value
30
+ params[token_field]
31
+ end
32
+
33
+ def lookup_field
34
+ Monban.config.user_lookup_field
35
+ end
36
+
37
+ def token_field
38
+ Monban.config.user_token_field
39
+ end
15
40
  end
16
41
  end
17
42
  end
@@ -2,6 +2,9 @@ require 'warden'
2
2
 
3
3
  module Monban
4
4
  module Test
5
+ # These are test helpers for controller specs
6
+ # @note these have only been tested with rspec controller specs
7
+ # @since 0.0.15
5
8
  module ControllerHelpers
6
9
  def self.included(base)
7
10
  base.class_eval do
@@ -9,18 +12,18 @@ module Monban
9
12
  end
10
13
  end
11
14
 
12
- def store_controller_for_warden
13
- @request.env['action_controller.instance'] = @controller
14
- end
15
-
15
+ # Signs a user in for tests
16
+ # @param user [User] the user to sign in
16
17
  def sign_in(user)
17
18
  @controller.sign_in(user)
18
19
  end
19
20
 
21
+ # Signs the user out in tests
20
22
  def sign_out
21
23
  @controller.sign_out
22
24
  end
23
25
 
26
+ # A mock of warden for tests
24
27
  def warden
25
28
  @warden ||= begin
26
29
  manager = Warden::Manager.new(nil) do |config|
@@ -29,6 +32,12 @@ module Monban
29
32
  @request.env['warden'] = Warden::Proxy.new(@request.env, manager)
30
33
  end
31
34
  end
35
+
36
+ private
37
+ def store_controller_for_warden
38
+ @request.env['action_controller.instance'] = @controller
39
+ end
40
+
32
41
  end
33
42
  end
34
43
  end
@@ -1,12 +1,18 @@
1
1
  module Monban
2
2
  module Test
3
+ # Helpers for integration or feature specs
4
+ # @note these have only been tested with rspec integration and feature specs
5
+ # @since 0.0.15
3
6
  module Helpers
4
7
  include Warden::Test::Helpers
5
8
 
9
+ # Sign a user in
10
+ # @param user [User] user to sign in
6
11
  def sign_in user
7
12
  login_as user
8
13
  end
9
14
 
15
+ # Sign a user out
10
16
  def sign_out
11
17
  logout
12
18
  end
@@ -1,3 +1,4 @@
1
1
  module Monban
2
- VERSION = "0.0.15"
2
+ # 0.1.0
3
+ VERSION = "0.1.0"
3
4
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  feature 'Visitor signs in with invalid form' do
4
4
  scenario 'is not signed in' do
5
- Monban::SignUp.new(email: 'email@example.com', password: 'password').perform
5
+ Monban::Services::SignUp.new(email: 'email@example.com', password: 'password').perform
6
6
  visit invalid_sign_in_path
7
7
  fill_in "session_password", with: 'password'
8
8
  click_button 'go'
@@ -16,7 +16,7 @@ module Monban
16
16
  end
17
17
 
18
18
  class Dummy
19
- attr_reader :redirected, :flash, :request
19
+ attr_reader :redirected, :redirected_to, :flash, :request
20
20
  def initialize warden
21
21
  @warden = warden
22
22
  @flash = Flash.new
@@ -25,6 +25,7 @@ module Monban
25
25
  end
26
26
  def redirect_to path
27
27
  @redirected = true
28
+ @redirected_to = path
28
29
  end
29
30
  def env
30
31
  { "warden" => @warden }
@@ -59,7 +60,7 @@ module Monban
59
60
  it 'performs a sign out' do
60
61
  sign_out = double()
61
62
  sign_out.should_receive(:perform)
62
- SignOut.should_receive(:new).with(@warden).and_return(sign_out)
63
+ Services::SignOut.should_receive(:new).with(@warden).and_return(sign_out)
63
64
  @dummy.sign_out
64
65
  end
65
66
 
@@ -88,7 +89,7 @@ module Monban
88
89
  authentication = double()
89
90
  authentication.should_receive(:perform).and_return(user)
90
91
  Monban.should_receive(:lookup).with({email: 'a@b.com'}, nil).and_return(user)
91
- Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
92
+ Services::Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
92
93
  @dummy.authenticate_session(session_params).should == user
93
94
  end
94
95
 
@@ -99,7 +100,7 @@ module Monban
99
100
  authentication = double()
100
101
  authentication.should_receive(:perform).and_return(user)
101
102
  Monban.should_receive(:lookup).with(session_params.except(:password), field_map).and_return(user)
102
- Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
103
+ Services::Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
103
104
  @dummy.authenticate_session(session_params, field_map).should == user
104
105
  end
105
106
 
@@ -111,7 +112,7 @@ module Monban
111
112
  authentication = double()
112
113
  authentication.should_receive(:perform).and_return(false)
113
114
  Monban.should_receive(:lookup).with(session_params, nil).and_return(user)
114
- Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
115
+ Services::Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
115
116
  @dummy.authenticate_session(session_params).should == false
116
117
  end
117
118
 
@@ -120,7 +121,7 @@ module Monban
120
121
  password = double()
121
122
  authentication = double()
122
123
  authentication.should_receive(:perform)
123
- Authentication.should_receive(:new).with(user, password).and_return(authentication)
124
+ Services::Authentication.should_receive(:new).with(user, password).and_return(authentication)
124
125
  @dummy.authenticate user, password
125
126
  end
126
127
 
@@ -139,6 +140,7 @@ module Monban
139
140
  @warden.should_receive(:user).and_return(false)
140
141
  @dummy.require_login
141
142
  expect(@dummy.redirected).to eq(true)
143
+ expect(@dummy.redirected_to).to eq(Monban.config.no_login_redirect)
142
144
  expect(@dummy.flash.notice).to eq(Monban.config.sign_in_notice)
143
145
  end
144
146
 
@@ -156,7 +158,7 @@ module Monban
156
158
  user = double()
157
159
  sign_in = double()
158
160
  sign_in.should_receive(:perform).and_return(success)
159
- SignIn.should_receive(:new).with(user, @warden).and_return(sign_in)
161
+ Services::SignIn.should_receive(:new).with(user, @warden).and_return(sign_in)
160
162
  user
161
163
  end
162
164
 
@@ -164,7 +166,7 @@ module Monban
164
166
  user_params = double()
165
167
  sign_up = double()
166
168
  sign_up.should_receive(:perform).and_return(success)
167
- SignUp.should_receive(:new).with(user_params).and_return(sign_up)
169
+ Services::SignUp.should_receive(:new).with(user_params).and_return(sign_up)
168
170
  user_params
169
171
  end
170
172
  end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
  require 'monban/services/authentication'
3
3
 
4
- describe Monban::Authentication, '#authentication' do
4
+ describe Monban::Services::Authentication, '#authentication' do
5
5
  it 'is authenticated for a valid password' do
6
6
  password_digest = BCrypt::Password.create('password')
7
7
  user = double(password_digest: password_digest)
8
- auth = Monban::Authentication.new(user, 'password')
8
+ auth = Monban::Services::Authentication.new(user, 'password')
9
9
 
10
10
  expect(auth.perform).to eq(user)
11
11
  end
@@ -13,13 +13,13 @@ describe Monban::Authentication, '#authentication' do
13
13
  it 'is not authenticated for the wrong password' do
14
14
  password_digest = BCrypt::Password.create('password')
15
15
  user = double(password_digest: password_digest)
16
- auth = Monban::Authentication.new(user, 'drowssap')
16
+ auth = Monban::Services::Authentication.new(user, 'drowssap')
17
17
 
18
18
  expect(auth.perform).to eq(false)
19
19
  end
20
20
 
21
21
  it 'is not authenticated without a user' do
22
- auth = Monban::Authentication.new(nil, 'password')
22
+ auth = Monban::Services::Authentication.new(nil, 'password')
23
23
  expect(auth.perform).to eq(false)
24
24
  end
25
25
  end
@@ -2,22 +2,22 @@ require 'spec_helper'
2
2
  require 'monban/services/password_reset'
3
3
  require 'ostruct'
4
4
 
5
- describe Monban::PasswordReset do
5
+ describe Monban::Services::PasswordReset do
6
6
  before do
7
- Monban.config.encryption_method = ->(password) { password + "secret" }
7
+ Monban.config.hashing_method = ->(password) { password + "secret" }
8
8
  end
9
9
 
10
- it 'updates the password with the encryption strategy' do
11
- password_digest = Monban.encrypt_token('password')
10
+ it 'updates the password with the hashing strategy' do
11
+ password_digest = Monban.hash_token('password')
12
12
  user = double()
13
13
  field = Monban.config.user_token_store_field
14
14
  user.should_receive(:[]=).with(field, 'passwordsecret')
15
- password_reset = Monban::PasswordReset.new(user, 'password')
15
+ password_reset = Monban::Services::PasswordReset.new(user, 'password')
16
16
 
17
17
  password_reset.perform
18
18
  end
19
19
 
20
20
  after do
21
- Monban.config.encryption_method = Monban.config.default_encryption_method
21
+ Monban.config.hashing_method = Monban.config.default_hashing_method
22
22
  end
23
23
  end
@@ -1,12 +1,12 @@
1
1
  require 'spec_helper'
2
2
  require 'monban/services/sign_in'
3
3
 
4
- describe Monban::SignIn, '#perform' do
4
+ describe Monban::Services::SignIn, '#perform' do
5
5
  it 'signs the user in' do
6
6
  user = double()
7
7
  warden = double()
8
8
  warden.should_receive(:set_user).with(user)
9
9
 
10
- Monban::SignIn.new(user, warden).perform
10
+ Monban::Services::SignIn.new(user, warden).perform
11
11
  end
12
12
  end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
  require 'monban/services/sign_out'
3
3
 
4
- describe Monban::SignOut, '#perform' do
4
+ describe Monban::Services::SignOut, '#perform' do
5
5
  it 'signs out the user' do
6
6
  warden = double()
7
7
  warden.should_receive(:logout)
8
8
 
9
- Monban::SignOut.new(warden).perform
9
+ Monban::Services::SignOut.new(warden).perform
10
10
  end
11
11
  end
@@ -1,12 +1,12 @@
1
1
  require 'spec_helper'
2
2
  require 'monban/services/sign_up'
3
3
 
4
- describe Monban::SignUp, '#perform' do
4
+ describe Monban::Services::SignUp, '#perform' do
5
5
  it 'creates a user with the right parameters' do
6
6
  allow(User).to receive(:create)
7
7
  user_params = { email: 'email@example.com', password: 'password' }
8
8
 
9
- Monban::SignUp.new(user_params).perform
9
+ Monban::Services::SignUp.new(user_params).perform
10
10
  expect(User).to have_received(:create) do |args|
11
11
  expect(args[:email]).to eq(user_params[:email])
12
12
  expect(Monban.compare_token(args[:password_digest], 'password')).to be_true
@@ -19,7 +19,7 @@ describe Monban::SignUp, '#perform' do
19
19
  user_params = { email: 'email@example.com', password: 'password' }
20
20
 
21
21
  with_monban_config(creation_method: user_create_double) do
22
- Monban::SignUp.new(user_params).perform
22
+ Monban::Services::SignUp.new(user_params).perform
23
23
  end
24
24
 
25
25
  expect(user_create_double).to have_received(:call) do |args|
@@ -31,7 +31,7 @@ describe Monban::SignUp, '#perform' do
31
31
  allow(User).to receive(:create)
32
32
  user_params = { email: 'email@example.com', password: '' }
33
33
 
34
- Monban::SignUp.new(user_params).perform
34
+ Monban::Services::SignUp.new(user_params).perform
35
35
  expect(User).to have_received(:create) do |args|
36
36
  expect(args[:password_digest]).to be_nil
37
37
  end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Monban
4
+ module Strategies
5
+ describe PasswordStrategy do
6
+ it "bases lookup and token on config values" do
7
+ params = HashWithIndifferentAccess.new(username: 'test', the_password: 'password')
8
+
9
+ with_monban_config(user_lookup_field: "username", user_token_field: "the_password") do
10
+ env = Rack::MockRequest.env_for("/", params: params)
11
+ strategy = PasswordStrategy.new(env)
12
+ expect(strategy).to be_valid
13
+ end
14
+ end
15
+
16
+ it "it doesn't trigger if params are not provided" do
17
+ env = Rack::MockRequest.env_for("/")
18
+ strategy = PasswordStrategy.new(env)
19
+ expect(strategy).not_to be_valid
20
+ end
21
+ end
22
+ end
23
+ end
data/spec/monban_spec.rb CHANGED
@@ -8,7 +8,7 @@ describe 'Monban' do
8
8
 
9
9
  it "provides a .test_mode!" do
10
10
  Monban.test_mode!
11
- expect(Monban.encrypt_token('password')).to eql('password')
11
+ expect(Monban.hash_token('password')).to eql('password')
12
12
  expect(Monban.compare_token('password', 'password')).to be_true
13
13
  end
14
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monban
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.15
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - halogenandtoast
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-12 00:00:00.000000000 Z
12
+ date: 2014-07-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -186,6 +186,7 @@ files:
186
186
  - spec/monban/services/sign_in_spec.rb
187
187
  - spec/monban/services/sign_out_spec.rb
188
188
  - spec/monban/services/sign_up_spec.rb
189
+ - spec/monban/strategies/password_strategy_spec.rb
189
190
  - spec/monban/test_controller_helpers_spec.rb
190
191
  - spec/monban/test_helpers_spec.rb
191
192
  - spec/monban_spec.rb
@@ -266,6 +267,7 @@ test_files:
266
267
  - spec/monban/services/sign_in_spec.rb
267
268
  - spec/monban/services/sign_out_spec.rb
268
269
  - spec/monban/services/sign_up_spec.rb
270
+ - spec/monban/strategies/password_strategy_spec.rb
269
271
  - spec/monban/test_controller_helpers_spec.rb
270
272
  - spec/monban/test_helpers_spec.rb
271
273
  - spec/monban_spec.rb