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