sparkly-auth 1.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.
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