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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +45 -0
  3. data/Rakefile +9 -15
  4. data/app/controllers/cz_auth/application_controller.rb +1 -22
  5. data/app/controllers/cz_auth/authentication_controller.rb +26 -0
  6. data/app/controllers/cz_auth/concerns/authentication.rb +118 -0
  7. data/app/views/cz_auth/authentication/create.json.erb +4 -0
  8. data/app/views/cz_auth/authentication/invalid.json +1 -0
  9. data/config/environment.rb +1 -0
  10. data/config/routes.rb +3 -11
  11. data/lib/cz_auth.rb +1 -4
  12. data/lib/cz_auth/engine.rb +6 -0
  13. data/lib/cz_auth/requires_authentication.rb +39 -8
  14. data/lib/cz_auth/version.rb +1 -1
  15. data/lib/generators/cz_auth/install_generator.rb +1 -1
  16. data/spec/dummy/README.rdoc +28 -0
  17. data/spec/dummy/Rakefile +6 -0
  18. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  19. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  20. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  21. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  22. data/spec/dummy/app/models/user.rb +3 -0
  23. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/spec/dummy/bin/bundle +3 -0
  25. data/spec/dummy/bin/rails +4 -0
  26. data/spec/dummy/bin/rake +4 -0
  27. data/spec/dummy/config.ru +4 -0
  28. data/spec/dummy/config/application.rb +28 -0
  29. data/spec/dummy/config/boot.rb +5 -0
  30. data/spec/dummy/config/database.yml +13 -0
  31. data/spec/dummy/config/environment.rb +5 -0
  32. data/spec/dummy/config/environments/development.rb +29 -0
  33. data/spec/dummy/config/environments/production.rb +80 -0
  34. data/spec/dummy/config/environments/test.rb +36 -0
  35. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  36. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  37. data/spec/dummy/config/initializers/inflections.rb +16 -0
  38. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  39. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  40. data/spec/dummy/config/initializers/session_store.rb +3 -0
  41. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  42. data/spec/dummy/config/locales/en.yml +23 -0
  43. data/spec/dummy/config/routes.rb +3 -0
  44. data/spec/dummy/db/migrate/20130725213651_create_users.rb +12 -0
  45. data/spec/dummy/db/schema.rb +25 -0
  46. data/spec/dummy/log/test.log +1180 -0
  47. data/spec/dummy/public/404.html +58 -0
  48. data/spec/dummy/public/422.html +58 -0
  49. data/spec/dummy/public/500.html +57 -0
  50. data/spec/dummy/public/favicon.ico +0 -0
  51. data/spec/dummy/spec/factories/user.rb +9 -0
  52. data/spec/dummy/spec/models/user_spec.rb +40 -0
  53. data/spec/dummy/spec/spec_helper.rb +37 -0
  54. data/spec/spec_helper.rb +37 -0
  55. metadata +58 -26
  56. data/README.rdoc +0 -19
  57. data/app/controllers/cz_auth/registrations_controller.rb +0 -19
  58. data/app/controllers/cz_auth/sessions_controller.rb +0 -22
  59. data/app/helpers/cz_auth/application_helper.rb +0 -11
  60. data/app/views/cz_auth/registrations/new.html.erb +0 -1
  61. data/app/views/cz_auth/sessions/new.html.erb +0 -1
  62. 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
- RDoc::Task.new(:rdoc) do |rdoc|
16
- rdoc.rdoc_dir = 'rdoc'
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
- Bundler::GemHelper.install_tasks
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
- before_filter :fetch_resource
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,4 @@
1
+ {
2
+ "email" : "<%= @authenticated.email %>",
3
+ "auth_token" : "<%= @authenticated.auth_token %>"
4
+ }
@@ -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
- Rails.application.routes.draw do
2
-
3
- # Resources
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
@@ -1,5 +1,2 @@
1
1
  require "cz_auth/engine"
2
- require "cz_auth/requires_authentication"
3
-
4
- module CzAuth
5
- end
2
+ require "cz_auth/requires_authentication"
@@ -1,4 +1,10 @@
1
1
  module CzAuth
2
2
  class Engine < ::Rails::Engine
3
+ config.generators do |g|
4
+ g.test_framework :rspec, :fixture => false
5
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
6
+ g.assets false
7
+ g.helper false
8
+ end
3
9
  end
4
10
  end
@@ -2,22 +2,53 @@ module CzAuth
2
2
  module RequiresAuthentication
3
3
  extend ActiveSupport::Concern
4
4
 
5
- included do
6
- def self.requires_authentication
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
- private
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 generate_token(column)
15
- begin
16
- self[column] = SecureRandom.urlsafe_base64
17
- end while User.exists?(column => self[column])
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
@@ -1,3 +1,3 @@
1
1
  module CzAuth
2
- VERSION = "0.0.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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} username email password_digest auth_token")
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>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Dummy::Application.load_tasks
@@ -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 .