sparkly-auth 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +17 -0
  3. data/Rakefile +50 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/sparkly_accounts_controller.rb +59 -0
  6. data/app/controllers/sparkly_controller.rb +47 -0
  7. data/app/controllers/sparkly_sessions_controller.rb +52 -0
  8. data/app/models/password.rb +3 -0
  9. data/app/models/remembrance_token.rb +50 -0
  10. data/app/views/sparkly_accounts/edit.html.erb +24 -0
  11. data/app/views/sparkly_accounts/new.html.erb +24 -0
  12. data/app/views/sparkly_accounts/show.html.erb +0 -0
  13. data/app/views/sparkly_sessions/new.html.erb +22 -0
  14. data/dependencies.rb +1 -0
  15. data/generators/sparkly/USAGE +27 -0
  16. data/generators/sparkly/sparkly_generator.rb +76 -0
  17. data/generators/sparkly/templates/accounts_controller.rb +65 -0
  18. data/generators/sparkly/templates/accounts_helper.rb +2 -0
  19. data/generators/sparkly/templates/help_file.txt +56 -0
  20. data/generators/sparkly/templates/initializer.rb +30 -0
  21. data/generators/sparkly/templates/migrations/add_confirmed_to_sparkly_passwords.rb +9 -0
  22. data/generators/sparkly/templates/migrations/create_sparkly_passwords.rb +19 -0
  23. data/generators/sparkly/templates/migrations/create_sparkly_remembered_tokens.rb +15 -0
  24. data/generators/sparkly/templates/sessions_controller.rb +45 -0
  25. data/generators/sparkly/templates/sessions_helper.rb +2 -0
  26. data/generators/sparkly/templates/tasks/migrations.rb +1 -0
  27. data/generators/sparkly/templates/views/sparkly_accounts/edit.html.erb +24 -0
  28. data/generators/sparkly/templates/views/sparkly_accounts/new.html.erb +24 -0
  29. data/generators/sparkly/templates/views/sparkly_accounts/show.html.erb +0 -0
  30. data/generators/sparkly/templates/views/sparkly_sessions/new.html.erb +22 -0
  31. data/init.rb +44 -0
  32. data/lib/auth.rb +52 -0
  33. data/lib/auth/behavior/base.rb +64 -0
  34. data/lib/auth/behavior/core.rb +87 -0
  35. data/lib/auth/behavior/core/authenticated_model_methods.rb +52 -0
  36. data/lib/auth/behavior/core/controller_extensions.rb +52 -0
  37. data/lib/auth/behavior/core/controller_extensions/class_methods.rb +24 -0
  38. data/lib/auth/behavior/core/controller_extensions/current_user.rb +54 -0
  39. data/lib/auth/behavior/core/password_methods.rb +65 -0
  40. data/lib/auth/behavior/remember_me.rb +17 -0
  41. data/lib/auth/behavior/remember_me/configuration.rb +21 -0
  42. data/lib/auth/behavior/remember_me/controller_extensions.rb +66 -0
  43. data/lib/auth/behavior_lookup.rb +10 -0
  44. data/lib/auth/configuration.rb +328 -0
  45. data/lib/auth/encryptors/sha512.rb +20 -0
  46. data/lib/auth/generators/configuration_generator.rb +20 -0
  47. data/lib/auth/generators/controllers_generator.rb +34 -0
  48. data/lib/auth/generators/migration_generator.rb +32 -0
  49. data/lib/auth/generators/route_generator.rb +19 -0
  50. data/lib/auth/generators/views_generator.rb +26 -0
  51. data/lib/auth/model.rb +94 -0
  52. data/lib/auth/observer.rb +21 -0
  53. data/lib/auth/target_list.rb +5 -0
  54. data/lib/auth/tasks/migrations.rb +71 -0
  55. data/lib/auth/token.rb +10 -0
  56. data/lib/sparkly-auth.rb +1 -0
  57. data/rails/init.rb +17 -0
  58. data/rails/routes.rb +19 -0
  59. data/sparkly-auth.gemspec +143 -0
  60. data/spec/controllers/application_controller_spec.rb +13 -0
  61. data/spec/generators/sparkly_spec.rb +64 -0
  62. data/spec/lib/auth/behavior/core_spec.rb +184 -0
  63. data/spec/lib/auth/behavior/remember_me_spec.rb +127 -0
  64. data/spec/lib/auth/extensions/controller_spec.rb +32 -0
  65. data/spec/lib/auth/model_spec.rb +57 -0
  66. data/spec/lib/auth_spec.rb +32 -0
  67. data/spec/mocks/models/user.rb +3 -0
  68. data/spec/routes_spec.rb +24 -0
  69. data/spec/spec_helper.rb +61 -0
  70. data/spec/views_spec.rb +18 -0
  71. metadata +210 -0
@@ -0,0 +1,19 @@
1
+ class Auth::Generators::RouteGenerator < Rails::Generator::NamedBase
2
+ attr_reader :model
3
+
4
+ def initialize(model, options = {})
5
+ @model = model
6
+ args = [ model.name ]
7
+ super(args, options)
8
+ end
9
+
10
+ def manifest
11
+ record do |m|
12
+ m.route_resources plural_name
13
+ end
14
+ end
15
+
16
+ def spec
17
+ @spec ||= Rails::Generator::Spec.new("route", File.join(Auth.path, "auth/generators"), nil)
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ class Auth::Generators::ViewsGenerator < Rails::Generator::NamedBase
2
+ attr_reader :model
3
+
4
+ def initialize(model, options = {})
5
+ @model = model
6
+ args = [ model.name ]
7
+ super(args, options)
8
+ end
9
+
10
+ def manifest
11
+ record do |m|
12
+ m.directory resource_directory = File.join("app/views", model.accounts_controller.underscore)
13
+ m.directory sessions_directory = File.join("app/views", model.sessions_controller.underscore)
14
+
15
+ %w(edit new show).each do |f|
16
+ m.file "views/sparkly_accounts/#{f}.html.erb", File.join(resource_directory, "#{f}.html.erb")
17
+ end
18
+
19
+ m.file "views/sparkly_sessions/new.html.erb", File.join(sessions_directory, "new.html.erb")
20
+ end
21
+ end
22
+
23
+ def spec
24
+ @spec ||= Rails::Generator::Spec.new("views", File.join(Auth.path, "auth/generators"), nil)
25
+ end
26
+ end
data/lib/auth/model.rb ADDED
@@ -0,0 +1,94 @@
1
+ class Auth::Model
2
+ module Authenticated
3
+ def self.included(base)
4
+ base.send(:cattr_accessor, :sparkly_config)
5
+ end
6
+ end
7
+
8
+
9
+ def self.option(name, default = name)
10
+ define_method(name) do
11
+ self.options[name] ||= Auth.configuration.send(default)
12
+ end
13
+ end
14
+
15
+ include Auth::BehaviorLookup
16
+ attr_reader :options
17
+ option :behaviors
18
+ option :password_update_frequency
19
+ option :accounts_controller, :default_accounts_controller_name
20
+ option :sessions_controller, :default_sessions_controller_name
21
+ delegate :name, :table_name, :to => :target
22
+
23
+ # Options include:
24
+ # :behaviors => an array of behaviors to override Auth.configuration.behaviors
25
+ # :password_update_frequency => a time (ie 30.days) to override Auth.configuration.password_update_frequency
26
+ # :skip_routes => true if you do not want to generate routes (useful if you need to map them yourself).
27
+ # :key => the key upon which to authenticate (the username, basically)
28
+ # :with => a regular expression used to validate the password
29
+ # :accounts_controller => the name of the controller to route to for creating accounts, deleting them, etc.
30
+ # :sessions_controller => the name of the controller to route to for logging in, logging out, etc.
31
+ #
32
+ def initialize(name_or_constant, options = {})
33
+ begin
34
+ @target = resolve(name_or_constant).name
35
+ rescue NameError
36
+ # we fail silently because the user might not have generated the model yet. That would mean they're trying
37
+ # to do it now.
38
+ @target = name_or_constant.to_s.camelize
39
+ end
40
+
41
+ @options = options.reverse_merge(default_options)
42
+ end
43
+
44
+ def target
45
+ returning @target.constantize do |klass|
46
+ unless klass.include?(Auth::Model::Authenticated)
47
+ klass.send(:include, Auth::Model::Authenticated)
48
+ klass.sparkly_config = self
49
+ end
50
+ end
51
+ end
52
+
53
+ # Returns true if the specified String, Symbol or constant resolves to the same constant affected by this Model.
54
+ def matches?(name_or_constant)
55
+ target == resolve(name_or_constant)
56
+ end
57
+
58
+ # The key upon which this model is to be authenticated. (The username.) Defaults to :email
59
+ def key
60
+ options[:key]
61
+ end
62
+
63
+ # Merges the specified hash of options with this Model's hash of options. Same as model.options.merge!(options)
64
+ def merge_options!(options)
65
+ self.options.merge! options
66
+ end
67
+
68
+ def apply_options!
69
+ apply_behaviors!
70
+ end
71
+
72
+ private
73
+ def apply_behaviors!
74
+ behaviors.each do |behavior|
75
+ behavior = lookup_behavior(behavior)
76
+ behavior.new.apply_to(self)
77
+ end
78
+ end
79
+
80
+ def resolve(name_or_constant)
81
+ case name_or_constant
82
+ when Symbol then name_or_constant.to_s.camelize.constantize
83
+ when String then name_or_constant.camelize.constantize
84
+ else name_or_constant
85
+ end
86
+ end
87
+
88
+ def default_options
89
+ {
90
+ :key => :email,
91
+ :with => :password
92
+ }
93
+ end
94
+ end
@@ -0,0 +1,21 @@
1
+ class Auth::Observer < ActiveRecord::Observer
2
+ public :add_observer!
3
+
4
+ class << self
5
+ # Attaches the observer to the supplied model classes.
6
+ def observe(*models)
7
+ models.flatten!
8
+ models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
9
+ observed_classes.concat models
10
+ observed_classes.uniq!
11
+ end
12
+
13
+ def observed_classes
14
+ @observed_classes ||= []
15
+ end
16
+ end
17
+
18
+ def observed_classes
19
+ Set.new(self.class.observed_classes)
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ class Auth::TargetList < Array
2
+ def find(which)
3
+ select { |o| o.matches?(which) }.shift
4
+ end
5
+ end
@@ -0,0 +1,71 @@
1
+ namespace :auth do
2
+ namespace :migrate do
3
+ desc "Migrate from Authlogic to Sparkly Auth"
4
+ task :authlogic => :environment do
5
+ require 'console_app'
6
+
7
+ models = Auth.authenticated_models.collect { |config| config.target }
8
+ # So we need to undo the existing Auth config so that there's no validation being done on the authenticated
9
+ # models. So we first reset the config...
10
+ Auth.reset_configuration!
11
+ # Then remove all behaviors (including :core)...
12
+ Auth.configuration.behaviors = []
13
+ # Then reload the models...
14
+ reload!
15
+ # Then apply the new config.
16
+ Auth.kick!
17
+
18
+ # And finally, we use a TOTALLY different Password model. Since the core behavior hasn't been applied to this
19
+ # version of Password, it's unenforced -- meaning we can assign encrypted passwords to it without having to
20
+ # worry about them being re-encrypted.
21
+ class UnenforcedPasswordModel < ActiveRecord::Base
22
+ set_table_name Password.table_name
23
+ belongs_to :authenticatable, :polymorphic => true
24
+ end
25
+
26
+
27
+ for model in models
28
+ model.instance_eval do
29
+ has_many :passwords, :as => :authenticatable
30
+ end
31
+
32
+ model.all.each do |record|
33
+ unless record.attributes['crypted_password'].blank? # skip those users registered AFTER migration
34
+ record.passwords.destroy_all
35
+
36
+ password = UnenforcedPasswordModel.new(:authenticatable => record)
37
+ password.save(false)
38
+ password.update_attribute(:secret, record.attributes['crypted_password'])
39
+ password.update_attribute(:salt, record.attributes['password_salt'])
40
+
41
+ if password.secret != record.attributes['crypted_password']
42
+ raise "Passwords don't match"
43
+ end
44
+
45
+ if record.attributes.keys.include?('persistence_token')
46
+ password.update_attribute(:persistence_token, record.attributes['persistence_token'])
47
+ else
48
+ password.update_attribute(:persistence_token, Auth::Token.new.to_s)
49
+ end
50
+
51
+ if record.attributes.keys.include?('single_access_token')
52
+ password.update_attribute(:single_access_token, record.attributes['single_access_token'])
53
+ else
54
+ password.update_attribute(:single_access_token, Auth::Token.new.to_s)
55
+ end
56
+
57
+ if record.attributes.keys.include?('perishable_token')
58
+ password.update_attribute(:perishable_token, record.attributes['perishable_token'])
59
+ else
60
+ password.update_attribute(:perishable_token, Auth::Token.new.to_s)
61
+ end
62
+
63
+ password.save!
64
+ end
65
+ end
66
+ end
67
+
68
+ puts "Tables have been migrated."
69
+ end
70
+ end
71
+ end
data/lib/auth/token.rb ADDED
@@ -0,0 +1,10 @@
1
+ class Auth::Token
2
+ attr_reader :token
3
+ alias_method :to_s, :token
4
+
5
+ def initialize
6
+ @hex = ActiveSupport::SecureRandom.hex(64)
7
+ # base64 url, see RFC4648
8
+ @token = SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path("../auth.rb", __FILE__)
data/rails/init.rb ADDED
@@ -0,0 +1,17 @@
1
+ # This file sets up the various include paths and whatnot that would have been set up
2
+ # for us by Rails if this were a Rails plugin.
3
+
4
+ base_path = File.expand_path("../..", __FILE__)
5
+
6
+ Rails.configuration.controller_paths << File.join(base_path, "app/controllers")
7
+ Rails.configuration.load_paths << File.join(base_path, 'lib')
8
+ Rails.configuration.load_paths << File.join(base_path, 'app/models')
9
+
10
+ ActionController::Base.view_paths << File.join(base_path, 'app/views')
11
+
12
+ # Routes
13
+ ActionController::Routing::Routes.add_configuration_file(File.join(base_path, "rails/routes.rb"))
14
+
15
+
16
+ # Finally, we're ready to load the app as if it were a plugin.
17
+ require File.expand_path("../../init", __FILE__)
data/rails/routes.rb ADDED
@@ -0,0 +1,19 @@
1
+ if Auth.generate_routes?
2
+ ActionController::Routing::Routes.draw do |map|
3
+ Auth.configuration.authenticated_models.each do |model|
4
+ catch :missing do
5
+ begin
6
+ model.name # if an error is going to occur due to missing model, it'll happen here.
7
+ rescue NameError
8
+ # we rescue silently because the user's already been warned.
9
+ throw :missing
10
+ end
11
+
12
+ map.resource model.name.underscore, :controller => model.accounts_controller,
13
+ :requirements => { :model => model.name } do |model_res|
14
+ model_res.resource :session, :controller => model.sessions_controller, :requirements => { :model => model.name }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,143 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sparkly-auth}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Colin MacKenzie IV"]
12
+ s.date = %q{2010-08-09}
13
+ s.description = %q{As fate would have it, I found other authentication solutions unable to suit my needs. So I rolled my own.}
14
+ s.email = %q{sinisterchipmunk@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "app/controllers/sparkly_accounts_controller.rb",
24
+ "app/controllers/sparkly_controller.rb",
25
+ "app/controllers/sparkly_sessions_controller.rb",
26
+ "app/models/password.rb",
27
+ "app/models/remembrance_token.rb",
28
+ "app/views/sparkly_accounts/edit.html.erb",
29
+ "app/views/sparkly_accounts/new.html.erb",
30
+ "app/views/sparkly_accounts/show.html.erb",
31
+ "app/views/sparkly_sessions/new.html.erb",
32
+ "dependencies.rb",
33
+ "generators/sparkly/USAGE",
34
+ "generators/sparkly/sparkly_generator.rb",
35
+ "generators/sparkly/templates/accounts_controller.rb",
36
+ "generators/sparkly/templates/accounts_helper.rb",
37
+ "generators/sparkly/templates/help_file.txt",
38
+ "generators/sparkly/templates/initializer.rb",
39
+ "generators/sparkly/templates/migrations/add_confirmed_to_sparkly_passwords.rb",
40
+ "generators/sparkly/templates/migrations/create_sparkly_passwords.rb",
41
+ "generators/sparkly/templates/migrations/create_sparkly_remembered_tokens.rb",
42
+ "generators/sparkly/templates/sessions_controller.rb",
43
+ "generators/sparkly/templates/sessions_helper.rb",
44
+ "generators/sparkly/templates/tasks/migrations.rb",
45
+ "generators/sparkly/templates/views/sparkly_accounts/edit.html.erb",
46
+ "generators/sparkly/templates/views/sparkly_accounts/new.html.erb",
47
+ "generators/sparkly/templates/views/sparkly_accounts/show.html.erb",
48
+ "generators/sparkly/templates/views/sparkly_sessions/new.html.erb",
49
+ "init.rb",
50
+ "lib/auth.rb",
51
+ "lib/auth/behavior/base.rb",
52
+ "lib/auth/behavior/core.rb",
53
+ "lib/auth/behavior/core/authenticated_model_methods.rb",
54
+ "lib/auth/behavior/core/controller_extensions.rb",
55
+ "lib/auth/behavior/core/controller_extensions/class_methods.rb",
56
+ "lib/auth/behavior/core/controller_extensions/current_user.rb",
57
+ "lib/auth/behavior/core/password_methods.rb",
58
+ "lib/auth/behavior/remember_me.rb",
59
+ "lib/auth/behavior/remember_me/configuration.rb",
60
+ "lib/auth/behavior/remember_me/controller_extensions.rb",
61
+ "lib/auth/behavior_lookup.rb",
62
+ "lib/auth/configuration.rb",
63
+ "lib/auth/encryptors/sha512.rb",
64
+ "lib/auth/generators/configuration_generator.rb",
65
+ "lib/auth/generators/controllers_generator.rb",
66
+ "lib/auth/generators/migration_generator.rb",
67
+ "lib/auth/generators/route_generator.rb",
68
+ "lib/auth/generators/views_generator.rb",
69
+ "lib/auth/model.rb",
70
+ "lib/auth/observer.rb",
71
+ "lib/auth/target_list.rb",
72
+ "lib/auth/tasks/migrations.rb",
73
+ "lib/auth/token.rb",
74
+ "lib/sparkly-auth.rb",
75
+ "rails/init.rb",
76
+ "rails/routes.rb",
77
+ "sparkly-auth.gemspec",
78
+ "spec/controllers/application_controller_spec.rb",
79
+ "spec/generators/sparkly_spec.rb",
80
+ "spec/lib/auth/behavior/core_spec.rb",
81
+ "spec/lib/auth/behavior/remember_me_spec.rb",
82
+ "spec/lib/auth/extensions/controller_spec.rb",
83
+ "spec/lib/auth/model_spec.rb",
84
+ "spec/lib/auth_spec.rb",
85
+ "spec/mocks/models/user.rb",
86
+ "spec/routes_spec.rb",
87
+ "spec/spec_helper.rb",
88
+ "spec/views_spec.rb"
89
+ ]
90
+ s.homepage = %q{http://www.thoughtsincomputation.com}
91
+ s.rdoc_options = ["--charset=UTF-8"]
92
+ s.require_paths = ["lib"]
93
+ s.rubygems_version = %q{1.3.6}
94
+ s.summary = %q{User authentication with Sparkles!}
95
+ s.test_files = [
96
+ "spec/controllers",
97
+ "spec/controllers/application_controller_spec.rb",
98
+ "spec/generators",
99
+ "spec/generators/sparkly_spec.rb",
100
+ "spec/lib",
101
+ "spec/lib/auth",
102
+ "spec/lib/auth/behavior",
103
+ "spec/lib/auth/behavior/core_spec.rb",
104
+ "spec/lib/auth/behavior/remember_me_spec.rb",
105
+ "spec/lib/auth/extensions",
106
+ "spec/lib/auth/extensions/controller_spec.rb",
107
+ "spec/lib/auth/model_spec.rb",
108
+ "spec/lib/auth_spec.rb",
109
+ "spec/mocks",
110
+ "spec/mocks/models",
111
+ "spec/mocks/models/user.rb",
112
+ "spec/routes_spec.rb",
113
+ "spec/spec_helper.rb",
114
+ "spec/support",
115
+ "spec/views_spec.rb"
116
+ ]
117
+
118
+ if s.respond_to? :specification_version then
119
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
120
+ s.specification_version = 3
121
+
122
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
123
+ s.add_runtime_dependency(%q<sc-core-ext>, [">= 1.2.0"])
124
+ s.add_development_dependency(%q<rspec-rails>, [">= 1.3.2"])
125
+ s.add_development_dependency(%q<webrat>, [">= 0.7.1"])
126
+ s.add_development_dependency(%q<genspec>, [">= 0.1.1"])
127
+ s.add_development_dependency(%q<email_spec>, [">= 0.6.2"])
128
+ else
129
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
130
+ s.add_dependency(%q<rspec-rails>, [">= 1.3.2"])
131
+ s.add_dependency(%q<webrat>, [">= 0.7.1"])
132
+ s.add_dependency(%q<genspec>, [">= 0.1.1"])
133
+ s.add_dependency(%q<email_spec>, [">= 0.6.2"])
134
+ end
135
+ else
136
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
137
+ s.add_dependency(%q<rspec-rails>, [">= 1.3.2"])
138
+ s.add_dependency(%q<webrat>, [">= 0.7.1"])
139
+ s.add_dependency(%q<genspec>, [">= 0.1.1"])
140
+ s.add_dependency(%q<email_spec>, [">= 0.6.2"])
141
+ end
142
+ end
143
+
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationController do
4
+ # sanity check to make sure I haven't added methods and forgot to tell Rails they're not actions
5
+ it "should have only 'not_found' action" do
6
+ ApplicationController.action_methods.to_a.should == ["not_found"]
7
+ end
8
+
9
+ # it "should respond_to current_user" do
10
+ # Auth.kick!
11
+ # ApplicationController.new.should respond_to(:current_user)
12
+ # end
13
+ end