monban 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +149 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +6 -0
- data/lib/generators/monban/controllers/controllers_generator.rb +30 -0
- data/lib/generators/monban/scaffold/scaffold_generator.rb +42 -0
- data/lib/generators/monban/templates/app/controllers/sessions_controller.rb +30 -0
- data/lib/generators/monban/templates/app/controllers/users_controller.rb +26 -0
- data/lib/generators/monban/templates/app/models/user.rb +7 -0
- data/lib/generators/monban/templates/app/views/sessions/new.html.erb +13 -0
- data/lib/generators/monban/templates/app/views/users/new.html.erb +13 -0
- data/lib/generators/monban/templates/db/migrate/create_users.rb +10 -0
- data/lib/generators/monban/templates/scaffold_readme +4 -0
- data/lib/monban.rb +38 -0
- data/lib/monban/configuration.rb +27 -0
- data/lib/monban/controller_helpers.rb +56 -0
- data/lib/monban/controller_helpers/authentication.rb +26 -0
- data/lib/monban/controller_helpers/sign_in.rb +12 -0
- data/lib/monban/controller_helpers/sign_out.rb +11 -0
- data/lib/monban/controller_helpers/sign_up.rb +23 -0
- data/lib/monban/field_map.rb +38 -0
- data/lib/monban/railtie.rb +9 -0
- data/lib/monban/strategies/password_strategy.rb +15 -0
- data/lib/monban/version.rb +3 -0
- data/lib/monban/warden_setup.rb +11 -0
- data/monban.gemspec +29 -0
- data/spec/features/visitor/visitor_signs_up_spec.rb +12 -0
- data/spec/monban/controller_helpers/authentication_spec.rb +25 -0
- data/spec/monban/controller_helpers/sign_in_spec.rb +12 -0
- data/spec/monban/controller_helpers/sign_out_spec.rb +11 -0
- data/spec/monban/controller_helpers/sign_up_spec.rb +17 -0
- data/spec/monban/controller_helpers_spec.rb +124 -0
- data/spec/monban/field_map_spec.rb +18 -0
- data/spec/monban_spec.rb +7 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +13 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/application_controller.rb +4 -0
- data/spec/rails_app/app/controllers/posts_controller.rb +7 -0
- data/spec/rails_app/app/controllers/sessions_controller.rb +18 -0
- data/spec/rails_app/app/controllers/users_controller.rb +15 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/models/user.rb +4 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/app/views/users/new.html.erb +5 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +58 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +29 -0
- data/spec/rails_app/config/environments/production.rb +54 -0
- data/spec/rails_app/config/environments/test.rb +29 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/routes.rb +8 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/spec_helper.rb +7 -0
- metadata +299 -0
data/lib/monban.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "monban/version"
|
2
|
+
require "monban/configuration"
|
3
|
+
require "monban/controller_helpers"
|
4
|
+
require "monban/railtie"
|
5
|
+
require "monban/warden_setup"
|
6
|
+
require "monban/field_map"
|
7
|
+
require "monban/strategies/password_strategy"
|
8
|
+
require "active_support/core_ext/module/attribute_accessors"
|
9
|
+
|
10
|
+
module Monban
|
11
|
+
mattr_accessor :warden_config
|
12
|
+
mattr_accessor :config
|
13
|
+
|
14
|
+
def self.initialize warden_config
|
15
|
+
self.warden_config = warden_config
|
16
|
+
self.config = Monban::Configuration.new
|
17
|
+
if block_given?
|
18
|
+
yield config
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.compare_token(digest, token)
|
23
|
+
config.token_comparison.call(digest, token)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.encrypt_token(token)
|
27
|
+
config.encryption_method.call(token)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.user_class
|
31
|
+
config.user_class.constantize
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.lookup(params, field_map)
|
35
|
+
fields = FieldMap.new(params, field_map).to_fields
|
36
|
+
user_class.where(fields).first
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Monban
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :user_class, :user_token_field, :user_token_store_field
|
4
|
+
attr_accessor :encryption_method, :token_comparison, :user_lookup_field
|
5
|
+
attr_accessor :sign_in_notice
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@user_class = 'User'
|
9
|
+
@user_token_field = 'password'
|
10
|
+
@user_token_store_field = 'password_digest'
|
11
|
+
@user_lookup_field = 'email'
|
12
|
+
@encryption_method = default_encryption_method
|
13
|
+
@token_comparison = default_password_comparison
|
14
|
+
@sign_in_notice = 'You must be signed in'
|
15
|
+
end
|
16
|
+
|
17
|
+
def default_encryption_method
|
18
|
+
->(token) { BCrypt::Password.create(token) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_password_comparison
|
22
|
+
->(digest, unencrypted_token) do
|
23
|
+
BCrypt::Password.new(digest) == unencrypted_token
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
require 'monban/controller_helpers/sign_in'
|
3
|
+
require 'monban/controller_helpers/sign_out'
|
4
|
+
require 'monban/controller_helpers/sign_up'
|
5
|
+
require 'monban/controller_helpers/authentication'
|
6
|
+
require 'active_support/concern'
|
7
|
+
|
8
|
+
module Monban
|
9
|
+
module ControllerHelpers
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
included do
|
12
|
+
helper_method :current_user, :signed_in?
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign_in user
|
16
|
+
SignIn.new(user, warden).perform
|
17
|
+
end
|
18
|
+
|
19
|
+
def sign_out
|
20
|
+
SignOut.new(warden).perform
|
21
|
+
end
|
22
|
+
|
23
|
+
def sign_up user_params
|
24
|
+
SignUp.new(user_params).perform
|
25
|
+
end
|
26
|
+
|
27
|
+
def authenticate_session session_params, field_map = nil
|
28
|
+
user = Monban.lookup(session_params, field_map)
|
29
|
+
password = session_params.delete(Monban.config.user_token_field)
|
30
|
+
authenticate(user, password)
|
31
|
+
end
|
32
|
+
|
33
|
+
def authenticate user, password
|
34
|
+
Authentication.new(user, password).perform
|
35
|
+
end
|
36
|
+
|
37
|
+
def warden
|
38
|
+
env['warden']
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_user
|
42
|
+
warden.user
|
43
|
+
end
|
44
|
+
|
45
|
+
def signed_in?
|
46
|
+
current_user
|
47
|
+
end
|
48
|
+
|
49
|
+
def require_login
|
50
|
+
unless signed_in?
|
51
|
+
flash.notice = Monban.config.sign_in_notice
|
52
|
+
redirect_to '/sign_in'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Monban
|
2
|
+
class Authentication
|
3
|
+
def initialize user, unencrypted_token
|
4
|
+
@user = user
|
5
|
+
@unencrypted_token = unencrypted_token
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform
|
9
|
+
if authenticated?
|
10
|
+
@user
|
11
|
+
else
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def authenticated?
|
19
|
+
@user && Monban.compare_token(@user.send(token_store_field), @unencrypted_token)
|
20
|
+
end
|
21
|
+
|
22
|
+
def token_store_field
|
23
|
+
Monban.config.user_token_store_field
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Monban
|
2
|
+
class SignUp
|
3
|
+
def initialize user_params
|
4
|
+
unencrypted_token = user_params.delete(token_field)
|
5
|
+
token_digest = Monban.encrypt_token(unencrypted_token)
|
6
|
+
@user_params = user_params.merge(token_store_field.to_sym => token_digest)
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform
|
10
|
+
Monban.user_class.create(@user_params.to_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def token_store_field
|
16
|
+
Monban.config.user_token_store_field
|
17
|
+
end
|
18
|
+
|
19
|
+
def token_field
|
20
|
+
Monban.config.user_token_field
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Monban
|
2
|
+
class FieldMap
|
3
|
+
def initialize params, field_map
|
4
|
+
@params = params
|
5
|
+
@field_map = field_map
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_fields
|
9
|
+
if @field_map
|
10
|
+
params_from_field_map
|
11
|
+
else
|
12
|
+
@params
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def params_from_field_map
|
19
|
+
[query_string, *([value] * lookup_keys.length)]
|
20
|
+
end
|
21
|
+
|
22
|
+
def query_string
|
23
|
+
lookup_keys.map { |key| "#{key} = ?" }.join(" OR ")
|
24
|
+
end
|
25
|
+
|
26
|
+
def session_key
|
27
|
+
@field_map.keys.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def lookup_keys
|
31
|
+
@field_map.values.first
|
32
|
+
end
|
33
|
+
|
34
|
+
def value
|
35
|
+
@params[session_key]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Monban
|
2
|
+
module Strategies
|
3
|
+
class PasswordStrategy < ::Warden::Strategies::Base
|
4
|
+
def valid?
|
5
|
+
params[:email] || params[:password]
|
6
|
+
end
|
7
|
+
|
8
|
+
def authenticate!
|
9
|
+
user = User.find_by_email(params[:email])
|
10
|
+
auth = Authentication.new(user, params[:password])
|
11
|
+
auth.authenticated? ? success!(user) : fail!("Could not log in")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "monban/strategies/password_strategy"
|
2
|
+
|
3
|
+
Warden::Manager.serialize_into_session do |user|
|
4
|
+
user.id
|
5
|
+
end
|
6
|
+
|
7
|
+
Warden::Manager.serialize_from_session do |id|
|
8
|
+
User.find(id)
|
9
|
+
end
|
10
|
+
|
11
|
+
Warden::Strategies.add(:password_strategy, Monban::Strategies::PasswordStrategy)
|
data/monban.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monban/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "monban"
|
8
|
+
gem.version = Monban::VERSION
|
9
|
+
gem.authors = ["halogenandtoast", "calebthompson"]
|
10
|
+
gem.email = ["halogenandtoast@gmail.com"]
|
11
|
+
gem.description = %q{simple rails authentication}
|
12
|
+
gem.summary = %q{Making rails authentication as simple as possible}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'rails'
|
21
|
+
gem.add_dependency 'bcrypt-ruby'
|
22
|
+
gem.add_dependency 'warden'
|
23
|
+
gem.add_development_dependency 'rspec'
|
24
|
+
gem.add_development_dependency 'rspec-rails'
|
25
|
+
gem.add_development_dependency 'capybara'
|
26
|
+
gem.add_development_dependency 'pry'
|
27
|
+
gem.add_development_dependency 'sqlite3'
|
28
|
+
gem.add_development_dependency 'active_hash'
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'Visitor signs up' do
|
4
|
+
scenario 'with an email and password' do
|
5
|
+
visit sign_up_path
|
6
|
+
fill_in 'user_email', with: 'email@example.com'
|
7
|
+
fill_in 'user_password', with: 'password'
|
8
|
+
click_on 'go'
|
9
|
+
|
10
|
+
page.current_path.should eq(posts_path)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'monban/controller_helpers/authentication'
|
3
|
+
|
4
|
+
describe Monban::Authentication, '#authentication' do
|
5
|
+
it 'is authenticated for a valid password' do
|
6
|
+
password_digest = BCrypt::Password.create('password')
|
7
|
+
user = stub(password_digest: password_digest)
|
8
|
+
auth = Monban::Authentication.new(user, 'password')
|
9
|
+
|
10
|
+
expect(auth.perform).to eq(user)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'is not authenticated for the wrong password' do
|
14
|
+
password_digest = BCrypt::Password.create('password')
|
15
|
+
user = stub(password_digest: password_digest)
|
16
|
+
auth = Monban::Authentication.new(user, 'drowssap')
|
17
|
+
|
18
|
+
expect(auth.perform).to eq(false)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'is not authenticated without a user' do
|
22
|
+
auth = Monban::Authentication.new(nil, 'password')
|
23
|
+
expect(auth.perform).to eq(false)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'monban/controller_helpers/sign_in'
|
3
|
+
|
4
|
+
describe Monban::SignIn, '#perform' do
|
5
|
+
it 'signs the user in' do
|
6
|
+
user = double()
|
7
|
+
warden = double()
|
8
|
+
warden.should_receive(:set_user).with(user)
|
9
|
+
|
10
|
+
Monban::SignIn.new(user, warden).perform
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'monban/controller_helpers/sign_up'
|
3
|
+
|
4
|
+
describe Monban::SignUp, '#perform' do
|
5
|
+
it 'creates a user with the right parameters' do
|
6
|
+
create = double
|
7
|
+
stub_const('User', create)
|
8
|
+
user_params = { email: 'email@example.com', password: 'password' }
|
9
|
+
|
10
|
+
create.should_receive(:create) do |args|
|
11
|
+
args[:email].should eq(user_params[:email])
|
12
|
+
args.should have_key(:password_digest)
|
13
|
+
end
|
14
|
+
|
15
|
+
Monban::SignUp.new(user_params).perform
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Monban
|
4
|
+
describe ControllerHelpers do
|
5
|
+
class WardenMock
|
6
|
+
def user; end
|
7
|
+
end
|
8
|
+
class Flash < Struct.new(:notice)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Dummy
|
12
|
+
attr_reader :redirected, :flash
|
13
|
+
def initialize warden
|
14
|
+
@warden = warden
|
15
|
+
@flash = Flash.new
|
16
|
+
@redirected = false
|
17
|
+
end
|
18
|
+
def redirect_to path
|
19
|
+
@redirected = true
|
20
|
+
end
|
21
|
+
def env
|
22
|
+
{ "warden" => @warden }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
@warden = WardenMock.new
|
28
|
+
@dummy = Dummy.new(@warden)
|
29
|
+
@dummy.extend(ControllerHelpers)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'performs a sign in' do
|
33
|
+
user = double()
|
34
|
+
sign_in = double()
|
35
|
+
sign_in.should_receive(:perform)
|
36
|
+
SignIn.should_receive(:new).with(user, @warden).and_return(sign_in)
|
37
|
+
@dummy.sign_in user
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'performs a sign out' do
|
41
|
+
sign_out = double()
|
42
|
+
sign_out.should_receive(:perform)
|
43
|
+
SignOut.should_receive(:new).with(@warden).and_return(sign_out)
|
44
|
+
@dummy.sign_out
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'performs a sign_up' do
|
48
|
+
user_params = double()
|
49
|
+
sign_up = double()
|
50
|
+
sign_up.should_receive(:perform)
|
51
|
+
SignUp.should_receive(:new).with(user_params).and_return(sign_up)
|
52
|
+
@dummy.sign_up user_params
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'authenticates a session' do
|
56
|
+
session_params = double()
|
57
|
+
session_params.should_receive(:delete).with('password').and_return('password')
|
58
|
+
user = double()
|
59
|
+
authentication = double()
|
60
|
+
authentication.should_receive(:perform).and_return(user)
|
61
|
+
Monban.should_receive(:lookup).with(session_params, nil).and_return(user)
|
62
|
+
Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
|
63
|
+
@dummy.authenticate_session(session_params).should == user
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'authenticates a session against multiple fields' do
|
67
|
+
session_params = { 'email_or_username' => 'foo', 'password' => 'password' }
|
68
|
+
field_map = { email_or_username: [:email, :username] }
|
69
|
+
user = double()
|
70
|
+
authentication = double()
|
71
|
+
authentication.should_receive(:perform).and_return(user)
|
72
|
+
Monban.should_receive(:lookup).with(session_params, field_map).and_return(user)
|
73
|
+
Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
|
74
|
+
@dummy.authenticate_session(session_params, field_map).should == user
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns false when it could not authenticate the user' do
|
78
|
+
session_params = double()
|
79
|
+
session_params.should_receive(:delete).with('password').and_return('password')
|
80
|
+
user = double()
|
81
|
+
authentication = double()
|
82
|
+
authentication.should_receive(:perform).and_return(false)
|
83
|
+
Monban.should_receive(:lookup).with(session_params, nil).and_return(user)
|
84
|
+
Authentication.should_receive(:new).with(user, 'password').and_return(authentication)
|
85
|
+
@dummy.authenticate_session(session_params).should == false
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'performs an authenticate' do
|
89
|
+
user = double()
|
90
|
+
password = double()
|
91
|
+
authentication = double()
|
92
|
+
authentication.should_receive(:perform)
|
93
|
+
Authentication.should_receive(:new).with(user, password).and_return(authentication)
|
94
|
+
@dummy.authenticate user, password
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns the current user' do
|
98
|
+
@warden.should_receive(:user)
|
99
|
+
@dummy.current_user
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns signed_in?' do
|
103
|
+
@warden.should_receive(:user)
|
104
|
+
@dummy.signed_in?
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'redirects when not signed_in' do
|
108
|
+
@warden.should_receive(:user).and_return(false)
|
109
|
+
@dummy.require_login
|
110
|
+
expect(@dummy.redirected).to eq(true)
|
111
|
+
expect(@dummy.flash.notice).to eq(Monban.config.sign_in_notice)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'does not redirect when signed_in' do
|
115
|
+
@warden.should_receive(:user).and_return(true)
|
116
|
+
@dummy.require_login
|
117
|
+
expect(@dummy.redirected).to eq(false)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns warden' do
|
121
|
+
@dummy.warden.should == @warden
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|