entrance 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3320468261098d87b1536592f636be4d39b25403
4
+ data.tar.gz: f3b36768c497d6af747677461693920860a52c71
5
+ SHA512:
6
+ metadata.gz: e6169c56830fe1d3d430c854d2b0847c70d8776fc634bf0b72bfbf9124fedc0bb808a89cbc57d0bffdddcfe901697c474786703416c056d85c48985d36d9c06d
7
+ data.tar.gz: 3aa89b3005787082ae19173da9e8ba64135e9bc1521e49f1ed06ad5466d885b983c34a56d615906710b6d2e9c7d0a807f2b3adf60309c628c41e8f8a310a7872
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/entrance.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/entrance/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "entrance"
6
+ s.version = Entrance::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Tomás Pollak']
9
+ s.email = ['tomas@forkhq.com']
10
+ s.homepage = "https://github.com/tomas/entrance"
11
+ s.summary = "Lean authentication alternative for Rails and Sinatra."
12
+ s.description = "Doesn't fiddle with your controllers and routes."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "entrance"
16
+
17
+ s.add_runtime_dependency "bcrypt", "~> 3.0"
18
+ s.add_runtime_dependency "activesupport", "~> 3.0"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
22
+ s.require_path = 'lib'
23
+ # s.bindir = 'bin'
24
+ end
@@ -0,0 +1,45 @@
1
+ require 'digest/sha1'
2
+ require 'bcrypt'
3
+
4
+ module Entrance
5
+
6
+ module Ciphers
7
+
8
+ module SHA1
9
+
10
+ JOIN_STRING = '--'
11
+
12
+ def self.read(password)
13
+ password
14
+ end
15
+
16
+ # same logic as restful authentication
17
+ def self.encrypt(password, salt)
18
+ digest = Entrance.config.secret
19
+ raise "Secret not set!" if digest.blank?
20
+
21
+ Entrance.config.stretches.times do
22
+ str = [digest, salt, password, Entrance.config.secret].join(JOIN_STRING)
23
+ digest = Digest::SHA1.hexdigest(str)
24
+ end
25
+
26
+ digest
27
+ end
28
+
29
+ end
30
+
31
+ module BCrypt
32
+
33
+ def self.read(password)
34
+ BCrypt::Password.new(password)
35
+ end
36
+
37
+ def self.encrypt(password, salt = nil)
38
+ BCrypt::Password.create(password)
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,129 @@
1
+ module Entrance
2
+
3
+ module Controller
4
+
5
+ REMEMBER_ME_TOKEN = 'auth_token'.freeze
6
+
7
+ def self.included(base)
8
+ base.send(:helper_method, :current_user, :logged_in?, :logged_out?) if base.respond_to?(:helper_method)
9
+ end
10
+
11
+ def authenticate_and_login(username, password, remember_me = false)
12
+ if user = Entrance.config.model.constantize.authenticate(username, password)
13
+ login!(user, remember_me)
14
+ user
15
+ end
16
+ end
17
+
18
+ def login!(user, remember_me = false)
19
+ self.current_user = user
20
+ remember_or_forget(remember_me)
21
+ end
22
+
23
+ def logout!
24
+ if logged_in?
25
+ current_user.forget_me!
26
+ self.current_user = nil
27
+ end
28
+ delete_remember_cookie
29
+ end
30
+
31
+ def login_required
32
+ logged_in? || access_denied
33
+ end
34
+
35
+ def current_user
36
+ @current_user ||= (login_from_session || login_from_cookie)
37
+ end
38
+
39
+ def logged_in?
40
+ !!current_user
41
+ end
42
+
43
+ def logged_out?
44
+ !logged_in?
45
+ end
46
+
47
+ private
48
+
49
+ def current_user=(new_user)
50
+ raise "Invalid user: #{new_user}" unless new_user.nil? or new_user.is_a?(Entrance.config.model.constantize)
51
+ session[:user_id] = new_user ? new_user.id : nil
52
+ @current_user = new_user # should be nil when logging out
53
+ end
54
+
55
+ def remember_or_forget(remember_me)
56
+ if remember_me
57
+ current_user.remember_me!
58
+ set_remember_cookie
59
+ else
60
+ current_user.forget_me!
61
+ delete_remember_cookie
62
+ end
63
+ end
64
+
65
+ def access_denied
66
+ store_location
67
+ if request.xhr?
68
+ render :nothing => true, :status => 401
69
+ else
70
+ flash[:notice] = I18n.t(Entrance.config.access_denied_message_key)
71
+ redirect_to Entrance.config.access_denied_redirect_to
72
+ end
73
+ end
74
+
75
+ def login_from_session
76
+ self.current_user = User.find(session[:user_id]) if session[:user_id]
77
+ end
78
+
79
+ def login_from_cookie
80
+ return unless cookies[REMEMBER_ME_TOKEN]
81
+
82
+ query = {}
83
+ query[Entrance.config.remember_token_attr] = cookies[REMEMBER_ME_TOKEN]
84
+ if user = User.where(query).first \
85
+ and user.send(Entrance.config.remember_until_attr) > Time.now
86
+ self.current_user = user
87
+ # user.update_remember_token_expiration!
88
+ user
89
+ end
90
+ end
91
+
92
+ def store_location
93
+ session[:return_to] = request.request_uri
94
+ end
95
+
96
+ def redirect_to_stored_or(default_path)
97
+ redirect_to(session[:return_to] || default_path)
98
+ session[:return_to] = nil
99
+ end
100
+
101
+ def redirect_to_back_or(default_path)
102
+ redirect_to(request.env['HTTP_REFERER'] || default_path)
103
+ end
104
+
105
+ def set_remember_cookie
106
+ values = {
107
+ :expires => Entrance.config.remember_for.from_now,
108
+ :httponly => Entrance.config.cookie_httponly,
109
+ :path => Entrance.config.cookie_path,
110
+ :secure => Entrance.config.cookie_secure,
111
+ :value => current_user.send(Entrance.config.remember_token_attr)
112
+ }
113
+ values[:domain] = Entrance.config.cookie_domain if Entrance.config.cookie_domain
114
+
115
+ cookies[REMEMBER_ME_TOKEN] = values
116
+ end
117
+
118
+ def delete_remember_cookie
119
+ cookies.delete(REMEMBER_ME_TOKEN)
120
+ # cookies.delete(REMEMBER_ME_TOKEN, :domain => AppConfig.cookie_domain)
121
+ end
122
+
123
+ # def cookies
124
+ # @cookies ||= @env['action_dispatch.cookies'] || Rack::Request.new(@env).cookies
125
+ # end
126
+
127
+ end
128
+
129
+ end
@@ -0,0 +1,103 @@
1
+ require 'active_support/concern'
2
+
3
+ module Model
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # verify that username/password attributes are present
8
+ attrs = Entrance.config.model.constantize.columns.collect(&:name)
9
+ %w(username_attr password_attr).each do |key|
10
+ attr = Entrance.config.send(key)
11
+ raise "Couldn't find '#{attr}' in #{Entrance.config.model} model." unless attrs.include?(attr)
12
+ end
13
+
14
+ validates :password, :presence => true, :length => 6..32, :if => :password_required?
15
+ validates :password, :confirmation => true, :if => :password_required?
16
+ validates :password_confirmation, :presence => true, :if => :password_required?
17
+ end
18
+
19
+ module ClassMethods
20
+
21
+ def authenticate(username, password)
22
+ return if username.blank? or password.blank?
23
+
24
+ query = {}
25
+ query[Entrance.config.username_attr] = username.downcase.strip
26
+ if u = where(query).first
27
+ return u.authenticated?(password) ? u : nil
28
+ end
29
+ end
30
+
31
+ def with_password_reset_token(token)
32
+ return if token.blank?
33
+
34
+ query = {}
35
+ query[Entrance.config.reset_token_attr] = token.strip
36
+ if u = where(query).first and u.send(Entrance.config.reset_until_attr) > Time.now
37
+ return u
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ def authenticated?(string)
44
+ password === encrypt_password(string)
45
+ end
46
+
47
+ def remember_me!(until_date = nil)
48
+ update_attribute(Entrance.config.remember_token_attr, Entrance.generate_token)
49
+ update_remember_token_expiration!(until_date)
50
+ end
51
+
52
+ def update_remember_token_expiration!(until_date = nil)
53
+ timestamp = until_date || Entrance.config.remember_for
54
+ update_attribute(Entrance.config.remember_until_attr, timestamp.from_now)
55
+ end
56
+
57
+ def forget_me!
58
+ update_attribute(Entrance.config.remember_token_attr, nil)
59
+ update_attribute(Entrance.config.remember_until_attr, nil)
60
+ end
61
+
62
+ def password
63
+ @password || Entrance.config.cipher.read(send(Entrance.config.password_attr))
64
+ end
65
+
66
+ def password=(new_password)
67
+ return if new_password.blank?
68
+
69
+ @password = new_password # for validation
70
+ @password_changed = true
71
+
72
+ # if we're using salt and it is empty, generate one
73
+ if Entrance.config.salt_attr \
74
+ and send(Entrance.config.salt_attr).blank?
75
+ self.send(Entrance.config.salt_attr + '=', Entrance.generate_token)
76
+ end
77
+
78
+ self.send(Entrance.config.password_attr + '=', encrypt_password(new_password))
79
+ end
80
+
81
+ def request_password_reset!
82
+ send(Entrance.config.reset_token_attr + '=', Entrance.generate_token)
83
+ update_attribute(Entrance.config.reset_until_attr, Entrance.config.reset_password_window.from_now)
84
+ if save(:validate => false)
85
+ Entrance.config.mailer_class.constantize.reset_password_request(self).deliver
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def get_salt
92
+ Entrance.config.salt_attr && send(Entrance.config.salt_attr)
93
+ end
94
+
95
+ def encrypt_password(string)
96
+ Entrance.config.cipher.encrypt(string, get_salt)
97
+ end
98
+
99
+ def password_required?
100
+ password.blank? or @password_changed
101
+ end
102
+
103
+ end
@@ -0,0 +1,7 @@
1
+ module Entrance
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 1
5
+
6
+ VERSION = [MAJOR, MINOR, PATCH].join('.')
7
+ end
data/lib/entrance.rb ADDED
@@ -0,0 +1,83 @@
1
+ ####################################
2
+ # Entrance
3
+ #
4
+ # By Tomas Pollak
5
+ # Simple Ruby Authentication Library
6
+ ###################################
7
+
8
+ =begin
9
+
10
+ In your controller:
11
+ include Entrance::Controller
12
+
13
+ - Provides authenticate_and_login, login!(user), logout! methods
14
+ - Provices login_required, logged_in? and logged_out? helpers
15
+
16
+ In your model:
17
+
18
+ include Entrance::Model
19
+
20
+ - Provides Model.authenticate(username, password)
21
+ - Provices Model#remember_me! and Model#forget_me!
22
+ - Provides Model#password getter and setter
23
+ - Provides Model#request_password_reset!
24
+ =end
25
+
26
+ require 'entrance/controller'
27
+ require 'entrance/model'
28
+ require 'entrance/ciphers'
29
+
30
+ require 'active_support/time'
31
+
32
+ module Entrance
33
+
34
+ REMEMBER_ME_TOKEN = 'auth_token'
35
+
36
+ def self.config
37
+ @config ||= Config.new
38
+ end
39
+
40
+ def self.configure
41
+ yield config
42
+ end
43
+
44
+ def self.generate_token(length = 40)
45
+ SecureRandom.hex(length/2).encode('UTF-8')
46
+ end
47
+
48
+ class Config
49
+
50
+ attr_accessor *%w(
51
+ model mailer_class cipher secret stretches
52
+ username_attr password_attr salt_attr
53
+ remember_token_attr remember_until_attr reset_token_attr reset_until_attr
54
+ access_denied_redirect_to access_denied_message_key reset_password_window remember_for
55
+ cookie_domain cookie_secure cookie_path cookie_httponly
56
+ )
57
+
58
+ def initialize
59
+ @model = 'User'
60
+ @mailer_class = 'UserMailer'
61
+ @cipher = Ciphers::SHA1
62
+ @secret = nil
63
+ @stretches = 1
64
+ @username_attr = 'email'
65
+ @password_attr = 'password_hash'
66
+ @salt_attr = nil
67
+ @remember_token_attr = 'remember_token'
68
+ @remember_until_attr = 'remember_token_expires_at'
69
+ @reset_token_attr = 'reset_token'
70
+ @reset_until_attr = 'reset_token_expires_at'
71
+ @access_denied_redirect_to = '/'
72
+ @access_denied_message_key = 'messages.access_denied'
73
+ @reset_password_window = 1.hour
74
+ @remember_for = 2.weeks
75
+ @cookie_domain = nil
76
+ @cookie_secure = true
77
+ @cookie_path = '/'
78
+ @cookie_httponly = false
79
+ end
80
+
81
+ end
82
+
83
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entrance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomás Pollak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bcrypt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ description: Doesn't fiddle with your controllers and routes.
42
+ email:
43
+ - tomas@forkhq.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Rakefile
50
+ - entrance.gemspec
51
+ - lib/entrance.rb
52
+ - lib/entrance/ciphers.rb
53
+ - lib/entrance/controller.rb
54
+ - lib/entrance/model.rb
55
+ - lib/entrance/version.rb
56
+ homepage: https://github.com/tomas/entrance
57
+ licenses: []
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.6
73
+ requirements: []
74
+ rubyforge_project: entrance
75
+ rubygems_version: 2.2.0
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: Lean authentication alternative for Rails and Sinatra.
79
+ test_files: []