cz_auth 0.0.1 → 0.3.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 +7 -0
- data/README.md +45 -0
- data/Rakefile +9 -15
- data/app/controllers/cz_auth/application_controller.rb +1 -22
- data/app/controllers/cz_auth/authentication_controller.rb +26 -0
- data/app/controllers/cz_auth/concerns/authentication.rb +118 -0
- data/app/views/cz_auth/authentication/create.json.erb +4 -0
- data/app/views/cz_auth/authentication/invalid.json +1 -0
- data/config/environment.rb +1 -0
- data/config/routes.rb +3 -11
- data/lib/cz_auth.rb +1 -4
- data/lib/cz_auth/engine.rb +6 -0
- data/lib/cz_auth/requires_authentication.rb +39 -8
- data/lib/cz_auth/version.rb +1 -1
- data/lib/generators/cz_auth/install_generator.rb +1 -1
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +28 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +13 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/migrate/20130725213651_create_users.rb +12 -0
- data/spec/dummy/db/schema.rb +25 -0
- data/spec/dummy/log/test.log +1180 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/factories/user.rb +9 -0
- data/spec/dummy/spec/models/user_spec.rb +40 -0
- data/spec/dummy/spec/spec_helper.rb +37 -0
- data/spec/spec_helper.rb +37 -0
- metadata +58 -26
- data/README.rdoc +0 -19
- data/app/controllers/cz_auth/registrations_controller.rb +0 -19
- data/app/controllers/cz_auth/sessions_controller.rb +0 -22
- data/app/helpers/cz_auth/application_helper.rb +0 -11
- data/app/views/cz_auth/registrations/new.html.erb +0 -1
- data/app/views/cz_auth/sessions/new.html.erb +0 -1
- data/lib/tasks/cz_auth_tasks.rake +0 -4
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: de5f5d2313c8fe1d61a0454ecfd85b1ab9c737e6
|
4
|
+
data.tar.gz: 61a829f603db0d2f05686bd677148808facae640
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d5e514cfe066dd17dcf5f4b6f18428c2eb8dcb9c125166466b3a1332a8232ff9e42778563e63f67fc1fd6cf6e235c0170c2426f5c98db4180a9584ac1ac7eab
|
7
|
+
data.tar.gz: ee4f054b34eb411bd3ebcf4faa469538fa2e278a9e4c74e60a80f4fa492212ebee62d005422094b45fb3339b477bd136f87d67c32b3651b9b770430b117213c0
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
CzAuth
|
2
|
+
=
|
3
|
+
|
4
|
+
Simple authentication functionality packaged into gem
|
5
|
+
|
6
|
+
Usage
|
7
|
+
=
|
8
|
+
|
9
|
+
```shell
|
10
|
+
rails g cz_auth:install <model_name>
|
11
|
+
rake db:migrate
|
12
|
+
```
|
13
|
+
|
14
|
+
You can also work with existing models, by adding the following to an existing model
|
15
|
+
```ruby
|
16
|
+
requires_authentication
|
17
|
+
```
|
18
|
+
|
19
|
+
Add the following to the top of `application_controller.rb`
|
20
|
+
```ruby
|
21
|
+
include CzAuth::Concerns::Authentication
|
22
|
+
```
|
23
|
+
|
24
|
+
Add the following to the top of `routes.rb`
|
25
|
+
```ruby
|
26
|
+
mount CzAuth::Engine => "/"
|
27
|
+
```
|
28
|
+
|
29
|
+
Details
|
30
|
+
=
|
31
|
+
|
32
|
+
The generator will generate a model with the following attributes:
|
33
|
+
|
34
|
+
* email
|
35
|
+
* password_digest
|
36
|
+
* auth_token
|
37
|
+
* password_reset_token
|
38
|
+
|
39
|
+
It will then place a `requires_authentication` method at the top of the model. This will trigger `has_secure_password`, and before filter used to generate an auth_token.
|
40
|
+
|
41
|
+
The model will gain the following methods, and can be override at will:
|
42
|
+
|
43
|
+
__generate_token__: This method takes one argument, which is the column that the token will be placed in. By default, this can be `auth_token`, or `password_reset_token`
|
44
|
+
|
45
|
+
It does not create controllers, or mailers. These are left to the main application to implement.
|
data/Rakefile
CHANGED
@@ -4,24 +4,18 @@ begin
|
|
4
4
|
rescue LoadError
|
5
5
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
6
|
end
|
7
|
-
begin
|
8
|
-
require 'rdoc/task'
|
9
|
-
rescue LoadError
|
10
|
-
require 'rdoc/rdoc'
|
11
|
-
require 'rake/rdoctask'
|
12
|
-
RDoc::Task = Rake::RDocTask
|
13
|
-
end
|
14
7
|
|
15
|
-
|
16
|
-
|
17
|
-
rdoc.title = 'CzAuth'
|
18
|
-
rdoc.options << '--line-numbers'
|
19
|
-
rdoc.rdoc_files.include('README.rdoc')
|
20
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
-
end
|
8
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
9
|
+
load 'rails/tasks/engine.rake'
|
22
10
|
|
11
|
+
Bundler::GemHelper.install_tasks
|
23
12
|
|
13
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
24
14
|
|
15
|
+
require 'rspec/core'
|
16
|
+
require 'rspec/core/rake_task'
|
25
17
|
|
26
|
-
|
18
|
+
desc "Run all specs in spec directory (excluding plugin specs)"
|
19
|
+
RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
|
27
20
|
|
21
|
+
task :default => :spec
|
@@ -1,26 +1,5 @@
|
|
1
1
|
module CzAuth
|
2
2
|
class ApplicationController < ActionController::Base
|
3
|
-
|
4
|
-
|
5
|
-
protected
|
6
|
-
|
7
|
-
# Specify when a controller requires authentication
|
8
|
-
def require_authentication(options={})
|
9
|
-
unless current_user
|
10
|
-
flash[:error] = options[:error]
|
11
|
-
redirect_to options[:redirect]
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# Load the resource, via params[:resource]
|
16
|
-
def fetch_resource
|
17
|
-
begin
|
18
|
-
klass = params[:resource].classify
|
19
|
-
@klass = klass.constantize
|
20
|
-
rescue
|
21
|
-
raise AbstractController::ActionNotFound
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
3
|
+
include CzAuth::Concerns::Authentication
|
25
4
|
end
|
26
5
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CzAuth
|
2
|
+
class AuthenticationController < ApplicationController
|
3
|
+
skip_before_filter { :"authorize_#{model}!" }
|
4
|
+
|
5
|
+
def create
|
6
|
+
resource = model.where(email: params[:email]).first
|
7
|
+
@authenticated = perform_authentication_for(resource || model)
|
8
|
+
render @authenticated ? 'create' : 'invalid'
|
9
|
+
end
|
10
|
+
|
11
|
+
def destroy
|
12
|
+
delete_session_cookie
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def resource_param
|
18
|
+
"#{params[:resource]}".classify
|
19
|
+
end
|
20
|
+
|
21
|
+
def model
|
22
|
+
resource = "#{resource_param}".constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module CzAuth
|
2
|
+
module Concerns
|
3
|
+
module Authentication
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
authentication_models.each do |model|
|
9
|
+
create_helper_methods_for(model)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# Determine which models are used for authentication
|
16
|
+
def authentication_models
|
17
|
+
@authentication_models ||= begin
|
18
|
+
Dir.glob("app/models/*.rb").map do |file|
|
19
|
+
model = File.basename(file, File.extname(file))
|
20
|
+
resource = model.classify.constantize
|
21
|
+
if resource.instance_methods.include?(:password)
|
22
|
+
model
|
23
|
+
end
|
24
|
+
end.compact!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Defines helper methods, such as current_user
|
29
|
+
def create_helper_methods_for(model)
|
30
|
+
define_method(:"current_#{model}") { current_resource(model) }
|
31
|
+
define_method(:"#{model}_signed_in?") { resource_signed_in?(model) }
|
32
|
+
define_method(:"authenticate_#{model}!") { authenticate_resource!(model) }
|
33
|
+
helper_method :"current_#{model}", :"#{model}_signed_in?"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
# Convenience method to wrap authentication method, and cookie creation
|
41
|
+
def perform_authentication_for(resource)
|
42
|
+
authenticated = resource.try(:authenticate, credentials[:password] || auth_token)
|
43
|
+
create_session_cookie_for(authenticated)
|
44
|
+
return authenticated
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a cookie with the auth_token as the value
|
48
|
+
# Will set expiration to the return value of session_length
|
49
|
+
def create_session_cookie_for(resource)
|
50
|
+
if defined?(cookies) && !!resource
|
51
|
+
cookies.signed[:auth_token] = {
|
52
|
+
value: resource.auth_token,
|
53
|
+
expires: resource.session_length.from_now,
|
54
|
+
domain: :all
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Removes the session cookie
|
60
|
+
def delete_session_cookie
|
61
|
+
cookies.delete(:auth_token, domain: :all)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Fetch the auth_token from either the URL, or HTTP Header
|
65
|
+
def auth_token
|
66
|
+
auth_token = request.headers["X-AUTH-TOKEN"] || params[:auth_token]
|
67
|
+
auth_token ||= cookies.signed[:auth_token] if defined?(cookies)
|
68
|
+
auth_token
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Simple credentials object
|
74
|
+
def credentials
|
75
|
+
{ email: params[:email], password: params[:password], auth_token: auth_token }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Simple finder to get resource, or return the class
|
79
|
+
def query_resource(model)
|
80
|
+
klass = model.classify.constantize
|
81
|
+
klass.where(auth_token: auth_token).first || klass
|
82
|
+
rescue NoMethodError => e
|
83
|
+
klass
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns an instance of the currently authenticated resource, or nil
|
87
|
+
def current_resource(var)
|
88
|
+
variable = "@current_#{var}"
|
89
|
+
if instance_variable_defined?(variable)
|
90
|
+
instance_variable_get(variable)
|
91
|
+
else
|
92
|
+
instance_variable_set(variable, perform_authentication_for(query_resource(var)))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns boolean describing whether or not a resource is currently signed in
|
97
|
+
def resource_signed_in?(var)
|
98
|
+
variable = "@#{var}_signed_in"
|
99
|
+
if instance_variable_defined?(variable)
|
100
|
+
instance_variable_get(variable)
|
101
|
+
else
|
102
|
+
instance_variable_set(variable, !!current_resource(var))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Called to protect a controller
|
107
|
+
# Throws a CzAuth::UnauthorizedAccess if unable to verify existing authentication
|
108
|
+
# Can be rescued from to customize handling
|
109
|
+
def authenticate_resource!(var)
|
110
|
+
raise CzAuth::UnauthorizedAccess unless resource_signed_in?(var)
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Simple exception object, can be rescued from to handle response
|
118
|
+
class CzAuth::UnauthorizedAccess < StandardError;end
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "error": "Invalid Credentials" }
|
@@ -0,0 +1 @@
|
|
1
|
+
# This file exists for CodeClimate security scans
|
data/config/routes.rb
CHANGED
@@ -1,12 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
match '/:resource/sessions(/:action)', controller: 'cz_auth/sessions'
|
5
|
-
match '/:resource/registrations(/:action)', controller: 'cz_auth/registrations'
|
6
|
-
|
7
|
-
# Helpers
|
8
|
-
match '/:resource/login', to: 'cz_auth/sessions#new', as: 'login'
|
9
|
-
match '/:resource/logout', to: 'cz_auth/sessions#destroy', as: 'logout'
|
10
|
-
match '/:resource/signup', to: 'cz_auth/registrations#new', as: 'signup'
|
11
|
-
|
1
|
+
CzAuth::Engine.routes.draw do
|
2
|
+
post '/:resource/authentication', to: 'cz_auth/authentication#create', as: :login
|
3
|
+
delete '/:resource/authentication', to: 'cz_auth/authentication#destroy', as: :logout
|
12
4
|
end
|
data/lib/cz_auth.rb
CHANGED
data/lib/cz_auth/engine.rb
CHANGED
@@ -2,22 +2,53 @@ module CzAuth
|
|
2
2
|
module RequiresAuthentication
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def requires_authentication(options = {})
|
7
8
|
has_secure_password
|
8
9
|
before_create { generate_token(:auth_token) }
|
9
10
|
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generate a token for the specified field
|
15
|
+
def generate_token(column)
|
16
|
+
begin
|
17
|
+
self[column] = SecureRandom.uuid
|
18
|
+
end while self.class.exists?(column => self[column])
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generate a token for the specified field, and save
|
22
|
+
def generate_token!(column)
|
23
|
+
generate_token(column)
|
24
|
+
save
|
10
25
|
end
|
11
26
|
|
12
|
-
|
27
|
+
# Set the default length of the session
|
28
|
+
# Override to set different value
|
29
|
+
# Default: 2.weeks
|
30
|
+
def session_length
|
31
|
+
2.weeks
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Override authenticate in order to allow for auth_token
|
39
|
+
#
|
40
|
+
module ActiveModel
|
41
|
+
module SecurePassword
|
42
|
+
module InstanceMethodsOnActivation
|
13
43
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
44
|
+
def authenticate(unencrypted_password)
|
45
|
+
(auth_token == unencrypted_password ||
|
46
|
+
BCrypt::Password.new(password_digest) == unencrypted_password) &&
|
47
|
+
self
|
18
48
|
end
|
19
49
|
|
50
|
+
end
|
20
51
|
end
|
21
52
|
end
|
22
53
|
|
23
|
-
ActiveRecord::Base.send :include, CzAuth::RequiresAuthentication
|
54
|
+
ActiveRecord::Base.send :include, CzAuth::RequiresAuthentication
|
data/lib/cz_auth/version.rb
CHANGED
@@ -9,7 +9,7 @@ class CzAuth::InstallGenerator < Rails::Generators::Base
|
|
9
9
|
|
10
10
|
def create_models
|
11
11
|
model = user_model.singularize
|
12
|
-
generate("model", "#{model}
|
12
|
+
generate("model", "#{model} email password_digest auth_token password_reset_token")
|
13
13
|
if File.exists?("app/models/#{user_model}.rb")
|
14
14
|
inject_into_file "app/models/#{user_model}.rb", :after => "ActiveRecord::Base" do
|
15
15
|
"\n\trequires_authentication"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
== README
|
2
|
+
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
4
|
+
application up and running.
|
5
|
+
|
6
|
+
Things you may want to cover:
|
7
|
+
|
8
|
+
* Ruby version
|
9
|
+
|
10
|
+
* System dependencies
|
11
|
+
|
12
|
+
* Configuration
|
13
|
+
|
14
|
+
* Database creation
|
15
|
+
|
16
|
+
* Database initialization
|
17
|
+
|
18
|
+
* How to run the test suite
|
19
|
+
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
21
|
+
|
22
|
+
* Deployment instructions
|
23
|
+
|
24
|
+
* ...
|
25
|
+
|
26
|
+
|
27
|
+
Please feel free to use a different markup language if you do not plan to run
|
28
|
+
<tt>rake doc:app</tt>.
|
data/spec/dummy/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|