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,65 @@
1
+ class <%=model.accounts_controller.camelize%>Controller < SparklyController
2
+ require_login_for :show, :edit, :update, :destroy
3
+
4
+ # GET new_model_url
5
+ def new
6
+ end
7
+
8
+ # POST model_url
9
+ def create
10
+ if model.save
11
+ login!(model)
12
+ redirect_back_or_default Auth.default_destination, Auth.account_created_message
13
+ else
14
+ render :action => 'new'
15
+ end
16
+ end
17
+
18
+ # GET model_url
19
+ def show
20
+ end
21
+
22
+ # GET edit_model_url
23
+ def edit
24
+ end
25
+
26
+ # PUT model_url
27
+ def update
28
+ if !model_params[:password].blank? || !model_params[:password_confirmation].blank?
29
+ model.password = model_params[:password]
30
+ model.password_confirmation = model_params[:password_confirmation]
31
+ end
32
+
33
+ if model.save
34
+ redirect_back_or_default user_path, Auth.account_updated_message
35
+ else
36
+ render :action => 'edit'
37
+ end
38
+ end
39
+
40
+ # DELETE model_url
41
+ def destroy
42
+ current_user && current_user.destroy
43
+ logout!
44
+ @current_user = nil
45
+ flash[:notice] = Auth.account_deleted_message
46
+ redirect_back_or_default Auth.default_destination
47
+ end
48
+
49
+ protected
50
+ def find_user_model
51
+ # password fields are protected attrs, so we need to exclude them then add them explicitly.
52
+ self.model_instance = current_user ||
53
+ returning(model_class.new(model_params.without(:password, :password_confirmation))) { |model|
54
+ model.password = model_params[:password]
55
+ model.password_confirmation = model_params[:password_confirmation]
56
+ }
57
+ end
58
+
59
+ # Uncomment if you don't trust the params[:model] set up by Sparkly routing, or if you've
60
+ # disabled them.
61
+ #
62
+ #def model_name
63
+ # <%=model.name.inspect%>
64
+ #end
65
+ end
@@ -0,0 +1,2 @@
1
+ module <%=model.accounts_controller.camelize%>Helper
2
+ end
@@ -0,0 +1,56 @@
1
+ I'll assume you know what Sparkly Authentication is since it seems
2
+ to be installed. If that assumption is incorrect, you should check
3
+ out the Sparkly Authentication readme instead.
4
+
5
+ So let's get right into usage. This text was generated by the Sparkly
6
+ command-line generator, invoked via:
7
+
8
+ script/generate sparkly help
9
+
10
+ Depending on what arguments are attached, this generator is capable
11
+ of producing various different results. So let's go through them one
12
+ at a time, in the most common order...
13
+
14
+ 0. Usually the first thing you'll want to do is generate the models
15
+ which will actually be authenticated, such as a User model. See
16
+ the Rails Guides for more details on that. You don't need to
17
+ actually run the migrations yet, however.
18
+
19
+ 1. After you know which models will be authenticated, you're ready
20
+ to invoke the Sparkly Config generator:
21
+
22
+ script/generate sparkly config
23
+
24
+ This will generate a Rails Initializer in config/initializers that
25
+ will be used to set up Sparkly during runtime. This tells it what
26
+ encryption type to use, which models to authenticate, and so on.
27
+ You should take a look at this file to make sure the configuration
28
+ is what you are expecting. Do that. Now.
29
+
30
+ 2. You also need to generate the database table which stores the
31
+ password information -- I'm talking about migrations!
32
+
33
+ script/generate sparkly migrations
34
+
35
+ 4. Run the server and try it out. See how things feel. If you want
36
+ more control over the views (and you should), you can generate
37
+ them like so:
38
+
39
+ script/generate sparkly views
40
+
41
+ Their final resting place basically depends on what your Sparkly
42
+ config from Step 1 looks like. So I hope you double checked it.
43
+
44
+ 5. Finally, if you need control over the, er, controllers, you can go
45
+ ahead and generate them like so:
46
+
47
+ script/generate sparkly controllers
48
+
49
+ Note that exactly which controllers and how many of them will be
50
+ generated depends, once again, on your Sparkly config. Note also
51
+ that this will generate the corresponding views for you, so you
52
+ can skip step 4 if you already know you need to customize the
53
+ controllers.
54
+
55
+ This file has been saved to doc/sparkly_authentication.txt for your
56
+ reference.
@@ -0,0 +1,30 @@
1
+ # This file sets up Sparkly Auth to work properly with Rails. It was generated
2
+ # by "script/generate sparkly config" and can be regenerated with that command, though
3
+ # you may not want to actually do that if you've made changes to this file.
4
+ #
5
+ # You are also HIGHLY encouraged to check out the Auth::Configuration class documentation
6
+ # for a list of all the options you can set here. There are a LOT of them.
7
+ #
8
+ Auth.configure do |config|
9
+ config.authenticate :user
10
+ # Adds a model to be authenticated. See the Auth::Model class for information on
11
+ # what options you can pass. Here are some common examples:
12
+ #
13
+ # config.authenticate :user, :accounts_controller => "users", :sessions_controller => "user_sessions"
14
+ # config.authenticate :user, :key => "login"
15
+ #
16
+ # By default, :key is "email" and the controllers are Sparkly's internal controllers.
17
+ # (Don't forget you can also script/generate controllers or script/generate views to
18
+ # remove the overhead of setting up your own.)
19
+ #
20
+
21
+ # You can also configure the various behaviors (as long as they support configurations):
22
+ # config.remember_me.token_theft_message =
23
+ # "Your account may have been hijacked recently! Verify that all settings are correct."
24
+ #
25
+ # config.remember_me.duration = 6.months
26
+ #
27
+ # See the class documentation for the behaviors' configurations themselves for details
28
+ # about these options. (For example, see Auth::Behaviors::RememberMe::Configuration for
29
+ # the Remember Me configuration options.)
30
+ end
@@ -0,0 +1,9 @@
1
+ class AddConfirmedToSparklyPasswords < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :passwords, :confirmed, :boolean
4
+ end
5
+
6
+ def self.down
7
+ remove_column :passwords, :confirmed
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class CreateSparklyPasswords < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :passwords do |t|
4
+ t.string :secret
5
+ t.string :salt
6
+
7
+ t.string :persistence_token # the token stored in cookies to persist the user's session
8
+ t.string :single_access_token # used to authenticate a user for a single request. This is not persisted.
9
+ t.string :perishable_token # used in confirming an account, usually via email
10
+
11
+ t.references :authenticatable, :polymorphic => true
12
+ t.timestamps
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table :passwords
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSparklyRememberedTokens < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :remembrance_tokens do |t|
4
+ t.string :series_token
5
+ t.string :remembrance_token
6
+
7
+ t.references :authenticatable, :polymorphic => true
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :remembrance_tokens
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ class <%=model.sessions_controller.camelize%>Controller < SparklyController
2
+ # GET new_model_session_url
3
+ def new
4
+ end
5
+
6
+ # POST model_session_url
7
+ def create
8
+ if session[:locked_out_at] && session[:locked_out_at] > Auth.account_lock_duration.ago
9
+ flash[:error] = Auth.account_locked_message
10
+ render :action => 'new'
11
+ return
12
+ end
13
+
14
+ model = model_class.find(:first, :conditions => { model_config.key => model_params[model_config.key] },
15
+ :include => :passwords)
16
+
17
+ if model && model.password_matches?(model_params[:password])
18
+ login! model
19
+ redirect_back_or_default Auth.default_destination, Auth.login_successful_message
20
+ else
21
+ session[:login_failures] = session[:login_failures].to_i + 1
22
+ if Auth.max_login_failures && session[:login_failures] >= Auth.max_login_failures
23
+ session[:locked_out_at] = Time.now
24
+ flash[:error] = Auth.account_locked_message
25
+ else
26
+ flash[:error] = Auth.invalid_credentials_message
27
+ end
28
+ render :action => "new"
29
+ end
30
+ end
31
+
32
+ # DELETE model_session_url
33
+ def destroy
34
+ logout!
35
+ redirect_back_or_default Auth.default_destination, Auth.logout_message
36
+ end
37
+
38
+ protected
39
+ # Uncomment if you don't trust the params[:model] set up by Sparkly routing, or if you've
40
+ # disabled them.
41
+ #
42
+ #def model_name
43
+ # <%=model.name.inspect%>
44
+ #end
45
+ end
@@ -0,0 +1,2 @@
1
+ module <%=model.sessions_controller.camelize%>Helper
2
+ end
@@ -0,0 +1 @@
1
+ require 'auth/tasks/migrations'
@@ -0,0 +1,24 @@
1
+ <%form_for model, :url => model_path do |f|%>
2
+ <p>
3
+ <%=f.error_messages%>
4
+ </p>
5
+
6
+ <p>
7
+ <%=f.label model_config.key%><br/>
8
+ <%=f.text_field model_config.key%>
9
+ </p>
10
+
11
+ <p>
12
+ <%=f.label :password%><br/>
13
+ <%=f.password_field :password, :value => ''%>
14
+ </p>
15
+
16
+ <p>
17
+ <%=f.label :password_confirmation%><br/>
18
+ <%=f.password_field :password_confirmation, :value => ''%>
19
+ </p>
20
+
21
+ <p>
22
+ <%=f.submit "Update Profile"%>
23
+ </p>
24
+ <%end%>
@@ -0,0 +1,24 @@
1
+ <%form_for model, :url => model_path do |f|%>
2
+ <p>
3
+ <%=f.error_messages%>
4
+ </p>
5
+
6
+ <p>
7
+ <%=f.label model_config.key%><br/>
8
+ <%=f.text_field model_config.key%>
9
+ </p>
10
+
11
+ <p>
12
+ <%=f.label :password%><br/>
13
+ <%=f.password_field :password, :value => ''%>
14
+ </p>
15
+
16
+ <p>
17
+ <%=f.label :password_confirmation%><br/>
18
+ <%=f.password_field :password_confirmation, :value => ''%>
19
+ </p>
20
+
21
+ <p>
22
+ <%=f.submit "Sign up"%>
23
+ </p>
24
+ <%end%>
@@ -0,0 +1,22 @@
1
+ <%form_for model, :url => model_session_path do |f|%>
2
+ <p>
3
+ <%=f.label model_config.key%><br/>
4
+ <%=f.text_field model_config.key%>
5
+ </p>
6
+
7
+ <p>
8
+ <%=f.label :password%><br/>
9
+ <%=f.password_field :password, :value => ''%>
10
+ </p>
11
+
12
+ <%if Auth.remember_me.enabled?%>
13
+ <p>
14
+ <%=f.check_box :remember_me, :checked => false%>
15
+ <%=f.label :remember_me%>
16
+ </p>
17
+ <%end%>
18
+
19
+ <p>
20
+ <%=f.submit "Sign in"%>
21
+ </p>
22
+ <%end%>
data/init.rb ADDED
@@ -0,0 +1,44 @@
1
+ require File.expand_path("../dependencies", __FILE__)
2
+ require File.expand_path("../lib/auth", __FILE__)
3
+
4
+ $LOAD_PATH << Auth.path
5
+ ActiveSupport::Dependencies.load_paths << Auth.path
6
+ ActiveSupport::Dependencies.load_once_paths << Auth.path
7
+ #ActiveSupport::Dependencies.load_once_paths.delete(Auth.path)
8
+
9
+ # Kick auth after initialize and do it again before every request in development
10
+ Rails.configuration.to_prepare do
11
+ Auth.kick!
12
+ end
13
+
14
+ # FIXME HACK extension to ActiveRecord::Errors to allow error attributes to be renamed. This is
15
+ # because otherwise we'll end up producing very confusing error messages complaining about :secret,
16
+ # :encrypted_secret, and so forth when all the user really cares about is :password.
17
+ class ActiveRecord::Errors
18
+ def rename_attribute(original_name, new_name)
19
+ original_name, new_name = original_name.to_s, new_name.to_s
20
+ return if original_name == new_name
21
+
22
+ @errors.each do |attribute, old_errors|
23
+ if attribute.to_s == original_name
24
+ original_name = attribute # because some are strings, some are symbols.
25
+
26
+ old_errors.each do |error|
27
+ new_error = error.dup
28
+ new_error.attribute = new_name
29
+ unless @errors[new_name] && @errors[new_name].include?(new_error)
30
+ @errors[new_name] ||= []
31
+ @errors[new_name] << new_error
32
+ end
33
+ end
34
+ @errors.delete(original_name)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ class ActiveRecord::Error
41
+ def ==(other)
42
+ other.kind_of?(ActiveRecord::Error) && attribute.to_s == other.attribute.to_s && message == other.message
43
+ end
44
+ end
data/lib/auth.rb ADDED
@@ -0,0 +1,52 @@
1
+ module Auth
2
+ class << self
3
+ public :delegate
4
+ delegate :path, :encryptor, :default_accounts_controller_name, :default_sessions_controller_name,
5
+ :password_update_frequency, :base_controller, :login_required_message, :logout_required_message,
6
+ :default_destination, :session_duration, :invalid_credentials_message, :login_successful_message,
7
+ :logout_message, :session_timeout_message, :default_login_path, :account_deleted_message,
8
+ :account_created_message, :account_updated_message, :account_locked_message, :max_login_failures,
9
+ :generate_routes?, :disable_route_generation!, :password_uniqueness_message,
10
+ :password_history_length, :base_controller_name, :account_lock_duration,
11
+ :password_format, :password_format_message, :minimum_password_length, :behaviors, :behavior_classes,
12
+ :to => :configuration
13
+
14
+ def configuration
15
+ @configuration ||= Auth::Configuration.new
16
+ end
17
+
18
+ def configure
19
+ yield configuration
20
+ end
21
+
22
+ # Applies all configuration settings. This is done by the Auth system after it has been configured but before
23
+ # it processes any requests.
24
+ def configure!
25
+ begin
26
+ configuration.apply!
27
+ rescue NameError
28
+ puts
29
+ puts "WARNING: #{$!.message}"
30
+ puts
31
+ puts "This happened while trying to configure Sparkly Authentication."
32
+ puts "You should verify that /config/initializers/sparkly_authentication.rb"
33
+ puts "is set up properly. It could be that you just haven't created the"
34
+ puts "model yet. If so, this error will disappear when the model exists."
35
+ puts
36
+ if ENV['AUTH_BACKTRACE']
37
+ puts $!.backtrace
38
+ else
39
+ puts "(Run with AUTH_BACKTRACE=true to see a full bactrace.)"
40
+ end
41
+ puts
42
+ end
43
+ end
44
+
45
+ # Useful for cleaning up after tests, but probably not much else.
46
+ def reset_configuration!
47
+ @configuration = Auth::Configuration.new
48
+ end
49
+
50
+ alias_method :kick!, :configure!
51
+ end
52
+ end
@@ -0,0 +1,64 @@
1
+ class Auth::Behavior::Base
2
+ class_inheritable_array :migrations
3
+ read_inheritable_attribute(:migrations) || write_inheritable_attribute(:migrations, [])
4
+
5
+ class << self
6
+ def apply_to_controllers
7
+ # Add additional methods to ApplicationController (or whatever controller Sparkly is told to use)
8
+ Auth.base_controller.send(:include, "#{name}::ControllerExtensions".constantize)
9
+ # why this doesn't work in cuke?
10
+ # if (container = self.class).const_defined?(:ControllerExtensions)
11
+ # Auth.base_controller.send(:include, container.const_get(:ControllerExtensions))
12
+ # end
13
+ #rescue NameError
14
+ end
15
+ end
16
+
17
+ def apply_to(model)
18
+ track_behavior(model.target) do
19
+ apply_to_accounts(model)
20
+ apply_to_passwords(Password)
21
+ end
22
+ end
23
+
24
+ def apply_to_passwords(password_model)
25
+ raise NotImplementedError, "Be sure to override #apply_to_passwords(passwords_model) in your Auth Behavior"
26
+ end
27
+
28
+ def apply_to_accounts(model_config)
29
+ raise NotImplementedError, "Be sure to override #apply_to_accounts(model_config) in your Auth Behavior"
30
+ end
31
+
32
+ private
33
+ def track_behavior(model)
34
+ if !behavior_tracked?(model)
35
+ track_behavior!(model)
36
+ yield
37
+ end
38
+ end
39
+
40
+ def behavior_tracked?(model)
41
+ behavior_tracker(model).include? behavior_name
42
+ end
43
+
44
+ def track_behavior!(model)
45
+ behavior_tracker(model) << behavior_name
46
+ end
47
+
48
+ def behavior_tracker(model)
49
+ model.instance_variable_get("@__behavior_tracker") || model.instance_variable_set("@__behavior_tracker", [])
50
+ end
51
+
52
+ def behavior_name
53
+ self.class.name
54
+ end
55
+
56
+ public
57
+ class << self
58
+ # Declares a migration template for a behavior. If sourcedir is given, it will be used as the location
59
+ # in which to find the template.
60
+ def migration(filename)
61
+ migrations << filename unless migrations.include?(filename)
62
+ end
63
+ end
64
+ end