monban 0.0.15 → 0.1.0

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