authem 1.5.0 → 2.0.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 +4 -4
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Appraisals +12 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile +10 -0
- data/README.markdown +15 -1
- data/Rakefile +11 -5
- data/authem.gemspec +25 -0
- data/gemfiles/rails_4.0.gemfile +16 -0
- data/gemfiles/rails_4.1.gemfile +15 -0
- data/lib/authem.rb +4 -10
- data/lib/authem/controller.rb +50 -0
- data/lib/authem/errors/ambigous_role.rb +8 -0
- data/lib/authem/errors/unknown_role.rb +7 -0
- data/lib/authem/railtie.rb +12 -0
- data/lib/authem/role.rb +62 -0
- data/lib/authem/session.rb +41 -0
- data/lib/authem/support.rb +129 -0
- data/lib/authem/token.rb +5 -5
- data/lib/authem/user.rb +27 -13
- data/lib/authem/version.rb +1 -1
- data/lib/generators/authem/session/session_generator.rb +12 -0
- data/lib/generators/authem/session/templates/create_sessions.rb +15 -0
- data/lib/generators/authem/user/templates/create_table_migration.rb +22 -0
- data/lib/generators/authem/user/templates/model.rb +11 -0
- data/lib/generators/authem/user/user_generator.rb +13 -0
- data/spec/controller_spec.rb +413 -0
- data/spec/session_spec.rb +52 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/active_record.rb +45 -0
- data/spec/support/i18n.rb +1 -0
- data/spec/support/time.rb +1 -0
- data/spec/token_spec.rb +10 -0
- data/spec/user_spec.rb +115 -0
- metadata +42 -112
- data/lib/authem/base_user.rb +0 -54
- data/lib/authem/config.rb +0 -21
- data/lib/authem/controller_support.rb +0 -51
- data/lib/authem/sorcery_user.rb +0 -24
- data/lib/generators/authem/model/model_generator.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67a264031f3e96725f18dc7e77c427ebd4708716
|
4
|
+
data.tar.gz: 09c0291510e6909b13ea9c5a3efa473e3054020b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 771d1413927363e3c2426d0cee8d28fabf08389ce16a4aa6eefd06c6da6499177213f271fbc5ef2c111e48a2433c9695c1747877a6bb1b32e15aca99e4f491b7
|
7
|
+
data.tar.gz: 6e3adc3d207f865439888550c6aeb1d8f84f202e6a2ec31f132a23e4a254a218d5bd905a148309966e6917426cb89544cc4501c0208a81a955ab6ee7c198cf81
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
authem
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.2
|
data/.travis.yml
ADDED
data/Appraisals
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
appraise "rails-4.1" do
|
2
|
+
gem "activerecord", "~> 4.1.0", require: "active_record"
|
3
|
+
gem "bcrypt", "~> 3.1"
|
4
|
+
gem "railties", "~> 4.0"
|
5
|
+
end
|
6
|
+
|
7
|
+
appraise "rails-4.0" do
|
8
|
+
gem "activerecord", "~> 4.0.0", require: "active_record"
|
9
|
+
gem "bcrypt", "~> 3.1"
|
10
|
+
gem "railties", "~> 4.0"
|
11
|
+
gem "protected_attributes"
|
12
|
+
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
### 2.0.0 ###
|
2
|
+
|
3
|
+
* Complete rewrite from scratch
|
4
|
+
* Store sessions in the database
|
5
|
+
* Multiple sessions and models support
|
6
|
+
* Drop Sorcery support
|
7
|
+
* Initializer is no longer needed
|
8
|
+
|
9
|
+
### 1.4.0 ###
|
10
|
+
|
11
|
+
* Use SecureRandom for token generation
|
12
|
+
* Lots of support file cleanup
|
13
|
+
* Some code cleanup
|
14
|
+
* All of this is thanks to Pavel Pravosud (@rwz). This guy is fantastic.
|
15
|
+
|
16
|
+
### 1.3.3 ###
|
17
|
+
|
18
|
+
* Regenerate session token when user signs out (Issue #21)
|
19
|
+
|
20
|
+
### 1.3.2 ###
|
21
|
+
|
22
|
+
* Prevent duplicate password validations on Authem::User
|
23
|
+
|
24
|
+
### 1.3.1 ###
|
25
|
+
|
26
|
+
* Bump bcrypt dependency for Rails 4.0.1 compatibility
|
27
|
+
|
28
|
+
### 1.3.0 ###
|
29
|
+
|
30
|
+
* Check for presence of password in authenticate
|
31
|
+
* Reconstantize user class when requested
|
32
|
+
* Add remember token to generated migrations
|
33
|
+
* Remove weird commas from generated migrations
|
34
|
+
|
35
|
+
### 1.2.0 ###
|
36
|
+
|
37
|
+
* Use `sign_in?` helper in `require_user` (Issue #13)
|
38
|
+
* Update rails dependencies to final release versions
|
39
|
+
|
40
|
+
### 1.1.1 ###
|
41
|
+
|
42
|
+
* Lock down bcrypt version per Rails' requirements
|
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -6,7 +6,7 @@ Authem is an email-based authentication library for ruby web apps.
|
|
6
6
|
|
7
7
|
## Compatibility
|
8
8
|
|
9
|
-
Authem
|
9
|
+
Authem requires Ruby 2.0.0 or newer
|
10
10
|
|
11
11
|
[](http://travis-ci.org/paulelliott/authem)
|
12
12
|
[](https://codeclimate.com/github/paulelliott/authem)
|
@@ -14,3 +14,17 @@ Authem is tested against Ruby 1.9.3, 2.0.0, and Rubinius.
|
|
14
14
|
## Documentation
|
15
15
|
|
16
16
|
Please see the Authem website for up-to-date documentation: http://authem.org
|
17
|
+
|
18
|
+
## Upgrading to 2.0
|
19
|
+
|
20
|
+
- Specify the latest alpha release in your Gemfile: `gem 'authem', '2.0.0.alpha.3'`
|
21
|
+
- Remove references to the old Authem::Config object.
|
22
|
+
- Create the new sessions table with `rails g authem:session`.
|
23
|
+
- Replace `include Authem::ControllerSupport` with `authem_for :user`.
|
24
|
+
- Rename `signed_in?` to `user_signed_in? OR `alias_method :signed_in?, :user_signed_in?` in your controller.
|
25
|
+
- Rename column `User#reset_password_token` to `User#password_reset_token` OR `alias_attribute :password_reset_token, :reset_password_token` in your `User` model.
|
26
|
+
- Replace calls to `user#reset_password_token!` with `user#password_reset_token`. Tokens are now generated automatically and the bang method is deprecated.
|
27
|
+
- Rename `sign_out` to `sign_out_user` OR `alias_method :sign_out, :sign_out_user`
|
28
|
+
- If you were passing a remember flag as the second argument to `sign_in`, you need to provide an options hash instead. For example, `sign_in(user, params[:remember])` would become `sign_in(user, remember: params[:remember])`.
|
29
|
+
- Blank email addresses will now produce the proper "can't be blank" validation message". Update your tests accordingly.
|
30
|
+
- Email addresses are no longer automatically downcased when calling `find_by_email` on your model. You will need to downcase the value manually if you wish to retain this behavior.
|
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
spec.pattern = "spec/**/*_spec.rb"
|
4
|
-
end
|
1
|
+
require "bundler/setup"
|
2
|
+
require "bundler/gem_tasks"
|
5
3
|
|
6
|
-
|
4
|
+
if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
|
5
|
+
require "appraisal/task"
|
6
|
+
Appraisal::Task.new
|
7
|
+
task default: :appraisal
|
8
|
+
else
|
9
|
+
require "rspec/core/rake_task"
|
10
|
+
RSpec::Core::RakeTask.new
|
11
|
+
task default: :spec
|
12
|
+
end
|
data/authem.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'authem/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "authem"
|
8
|
+
spec.version = Authem::VERSION
|
9
|
+
spec.authors = ["Paul Elliott", "Pavel Pravosud"]
|
10
|
+
spec.email = ["paul@codingfrontier.com", "pavel@pravosud.com"]
|
11
|
+
spec.summary = "Authem authenticates them by email"
|
12
|
+
spec.description = "Authem provides a simple solution for email-based authentication"
|
13
|
+
spec.homepage = "https://github.com/paulelliott/authem"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.required_ruby_version = ">= 2.0.0"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.test_files = spec.files.grep("spec")
|
20
|
+
spec.require_path = "lib"
|
21
|
+
|
22
|
+
spec.add_dependency "activesupport", ">= 4.0.4"
|
23
|
+
spec.add_dependency "railties", "~> 4.0"
|
24
|
+
spec.add_dependency "bcrypt", "~> 3.1"
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "activerecord", "~> 4.0.0", :require => "active_record"
|
7
|
+
gem "bcrypt", "~> 3.1"
|
8
|
+
gem "railties", "~> 4.0"
|
9
|
+
gem "protected_attributes"
|
10
|
+
|
11
|
+
group :test do
|
12
|
+
gem "rspec", "~> 3.0"
|
13
|
+
gem "rake", "~> 10.3"
|
14
|
+
gem "sqlite3"
|
15
|
+
gem "pry"
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "activerecord", "~> 4.1.0", :require => "active_record"
|
7
|
+
gem "bcrypt", "~> 3.1"
|
8
|
+
gem "railties", "~> 4.0"
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem "rspec", "~> 3.0"
|
12
|
+
gem "rake", "~> 10.3"
|
13
|
+
gem "sqlite3"
|
14
|
+
gem "pry"
|
15
|
+
end
|
data/lib/authem.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
autoload :SorceryUser, 'authem/sorcery_user'
|
5
|
-
autoload :Config, 'authem/config'
|
6
|
-
autoload :ControllerSupport, 'authem/controller_support'
|
7
|
-
autoload :Token, 'authem/token'
|
1
|
+
require "authem/railtie"
|
2
|
+
require "authem/user"
|
3
|
+
require "authem/version"
|
8
4
|
|
9
|
-
|
10
|
-
Config.configure(&block)
|
11
|
-
end
|
5
|
+
module Authem
|
12
6
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "authem/role"
|
3
|
+
|
4
|
+
module Authem
|
5
|
+
module Controller
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included{ class_attribute :authem_roles }
|
9
|
+
|
10
|
+
module SessionManagementMethods
|
11
|
+
def sign_in(model, **options)
|
12
|
+
role = options.fetch(:as){ self.class.authem_role_for(model) }
|
13
|
+
public_send "sign_in_#{role}", model, options
|
14
|
+
end
|
15
|
+
|
16
|
+
def sign_out(model, **options)
|
17
|
+
role = options.fetch(:as){ self.class.authem_role_for(model) }
|
18
|
+
public_send "sign_out_#{role}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_all_sessions_for(model, **options)
|
22
|
+
role = options.fetch(:as){ self.class.authem_role_for(model) }
|
23
|
+
public_send "clear_all_#{role}_sessions_for", model
|
24
|
+
end
|
25
|
+
|
26
|
+
def redirect_back_or_to(url, **options)
|
27
|
+
url = session.delete(:return_to_url) || url
|
28
|
+
redirect_to url, options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
def authem_for(role_name, **options)
|
34
|
+
include SessionManagementMethods
|
35
|
+
Authem::Role.new(self, role_name, options).setup!
|
36
|
+
end
|
37
|
+
|
38
|
+
def authem_role_for(record)
|
39
|
+
fail ArgumentError if record.nil?
|
40
|
+
|
41
|
+
matches = authem_roles.select{ |role| record.class == role.klass }
|
42
|
+
|
43
|
+
fail UnknownRoleError.build(record) if matches.empty?
|
44
|
+
fail AmbigousRoleError.build(record, matches) unless matches.one?
|
45
|
+
|
46
|
+
matches.first.name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/authem/role.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "authem/support"
|
2
|
+
|
3
|
+
module Authem
|
4
|
+
class Role
|
5
|
+
attr_reader :controller, :name, :options
|
6
|
+
|
7
|
+
METHODS = %i[current sign_in signed_in? require sign_out clear_for deny_access]
|
8
|
+
|
9
|
+
METHODS.each do |method_name|
|
10
|
+
define_method method_name do |controller, *args|
|
11
|
+
Support.new(self, controller).public_send(method_name, *args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(controller, name, **options)
|
16
|
+
@controller, @name, @options = controller, name.to_s, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def klass
|
20
|
+
@klass ||= options.fetch(:model){ name.classify.constantize }
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup!
|
24
|
+
setup_controller_settings
|
25
|
+
setup_controller_instance_methods
|
26
|
+
setup_view_helpers
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def setup_controller_settings
|
32
|
+
controller.authem_roles ||= []
|
33
|
+
controller.authem_roles += [self]
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup_controller_instance_methods
|
37
|
+
role = self
|
38
|
+
|
39
|
+
method_mapping.each do |inner_method, exposed_method|
|
40
|
+
define_controller_method exposed_method do |*args|
|
41
|
+
role.public_send(inner_method, self, *args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_view_helpers
|
47
|
+
controller.helper_method *%I[current_#{name} #{name}_signed_in?]
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_controller_method(*args, &block)
|
51
|
+
controller.instance_eval{ define_method *args, &block }
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_mapping
|
55
|
+
exposed_methods = %I[current_#{name} sign_in_#{name}
|
56
|
+
#{name}_signed_in? require_#{name} sign_out_#{name}
|
57
|
+
clear_all_#{name}_sessions_for deny_#{name}_access]
|
58
|
+
|
59
|
+
Hash[[METHODS, exposed_methods].transpose]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "authem/token"
|
3
|
+
|
4
|
+
module Authem
|
5
|
+
class Session < ::ActiveRecord::Base
|
6
|
+
self.table_name = :authem_sessions
|
7
|
+
|
8
|
+
belongs_to :subject, polymorphic: true
|
9
|
+
|
10
|
+
before_create do
|
11
|
+
self.token ||= Authem::Token.generate
|
12
|
+
self.ttl ||= 30.days
|
13
|
+
self.expires_at ||= ttl_from_now
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def by_subject(record)
|
18
|
+
where(subject_type: record.class.name, subject_id: record.id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def active
|
22
|
+
where(arel_table[:expires_at].gteq(Time.zone.now))
|
23
|
+
end
|
24
|
+
|
25
|
+
def expired
|
26
|
+
where(arel_table[:expires_at].lt(Time.zone.now))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh
|
31
|
+
self.expires_at = ttl_from_now
|
32
|
+
save!
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def ttl_from_now
|
38
|
+
ttl.to_i.seconds.from_now
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
require "authem/session"
|
3
|
+
require "authem/errors/ambigous_role"
|
4
|
+
require "authem/errors/unknown_role"
|
5
|
+
|
6
|
+
module Authem
|
7
|
+
class Support
|
8
|
+
attr_reader :role, :controller
|
9
|
+
|
10
|
+
def initialize(role, controller)
|
11
|
+
@role, @controller = role, controller
|
12
|
+
end
|
13
|
+
|
14
|
+
def current
|
15
|
+
if ivar_defined?
|
16
|
+
ivar_get
|
17
|
+
else
|
18
|
+
ivar_set fetch_subject_by_token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign_in(record, **options)
|
23
|
+
check_record! record
|
24
|
+
ivar_set record
|
25
|
+
auth_session = create_auth_session(record, options)
|
26
|
+
save_session auth_session
|
27
|
+
save_cookie auth_session if options[:remember]
|
28
|
+
auth_session
|
29
|
+
end
|
30
|
+
|
31
|
+
def signed_in?
|
32
|
+
current.present?
|
33
|
+
end
|
34
|
+
|
35
|
+
def sign_out
|
36
|
+
ivar_set nil
|
37
|
+
Authem::Session.where(role: role_name, token: current_auth_token).delete_all
|
38
|
+
cookies.delete key, domain: :all
|
39
|
+
session.delete key
|
40
|
+
end
|
41
|
+
|
42
|
+
def clear_for(record)
|
43
|
+
check_record! record
|
44
|
+
sign_out
|
45
|
+
Authem::Session.by_subject(record).where(role: role_name).delete_all
|
46
|
+
end
|
47
|
+
|
48
|
+
def require
|
49
|
+
unless signed_in?
|
50
|
+
session[:return_to_url] = request.url unless request.xhr?
|
51
|
+
controller.send "deny_#{role_name}_access"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def deny_access
|
56
|
+
# default landing point for deny_#{role_name}_access
|
57
|
+
fail NotImplementedError, "No strategy for require_#{role_name} defined. Please define `deny_#{role_name}_access` method in your controller"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
delegate :name, to: :role, prefix: true
|
63
|
+
|
64
|
+
def check_record!(record)
|
65
|
+
fail ArgumentError if record.nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_subject_by_token
|
69
|
+
return if current_auth_token.blank?
|
70
|
+
auth_session = get_auth_session_by_token(current_auth_token)
|
71
|
+
return nil unless auth_session
|
72
|
+
auth_session.refresh
|
73
|
+
save_cookie auth_session if cookies.signed[key].present?
|
74
|
+
auth_session.subject
|
75
|
+
end
|
76
|
+
|
77
|
+
def current_auth_token
|
78
|
+
session[key] || cookies.signed[key]
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_auth_session(record, options)
|
82
|
+
Authem::Session.create!(role: role_name, subject: record, ttl: options[:ttl])
|
83
|
+
end
|
84
|
+
|
85
|
+
def save_session(auth_session)
|
86
|
+
session[key] = auth_session.token
|
87
|
+
end
|
88
|
+
|
89
|
+
def save_cookie(auth_session)
|
90
|
+
cookie_value = {
|
91
|
+
value: auth_session.token,
|
92
|
+
expires: auth_session.expires_at,
|
93
|
+
domain: :all
|
94
|
+
}
|
95
|
+
cookies.signed[key] = cookie_value
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_auth_session_by_token(token)
|
99
|
+
Authem::Session.active.find_by(role: role_name, token: token)
|
100
|
+
end
|
101
|
+
|
102
|
+
def key
|
103
|
+
"_authem_current_#{role_name}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def ivar_defined?
|
107
|
+
controller.instance_variable_defined?(ivar_name)
|
108
|
+
end
|
109
|
+
|
110
|
+
def ivar_set(value)
|
111
|
+
controller.instance_variable_set ivar_name, value
|
112
|
+
end
|
113
|
+
|
114
|
+
def ivar_get
|
115
|
+
controller.instance_variable_get ivar_name
|
116
|
+
end
|
117
|
+
|
118
|
+
def ivar_name
|
119
|
+
@ivar_name ||= "@_#{key}".to_sym
|
120
|
+
end
|
121
|
+
|
122
|
+
# exposing private controller methods
|
123
|
+
%i[cookies session redirect_to request].each do |method_name|
|
124
|
+
define_method method_name do |*args|
|
125
|
+
controller.send(method_name, *args)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|