merb_auth_slice_multisite 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.textile +147 -0
  2. data/VERSION.yml +4 -0
  3. data/app/controllers/application.rb +5 -0
  4. data/app/controllers/exceptions.rb +33 -0
  5. data/app/controllers/passwords.rb +29 -0
  6. data/app/controllers/sessions.rb +56 -0
  7. data/app/helpers/application_helper.rb +64 -0
  8. data/app/mailers/send_password_mailer.rb +11 -0
  9. data/app/mailers/views/send_password_mailer/send_password.text.erb +3 -0
  10. data/app/models/site.rb +26 -0
  11. data/app/views/exceptions/unauthenticated.html.erb +61 -0
  12. data/app/views/layout/merb_auth_slice_multisite.html.erb +16 -0
  13. data/config/database.yml +33 -0
  14. data/config/dependencies.rb +33 -0
  15. data/config/init.rb +84 -0
  16. data/config/router.rb +5 -0
  17. data/lib/merb-auth-more/strategies/multisite/multisite_password_form.rb +77 -0
  18. data/lib/merb-auth-remember-me/mixins/authenticated_user.rb +97 -0
  19. data/lib/merb-auth-remember-me/mixins/authenticated_user/dm_authenticated_user.rb +17 -0
  20. data/lib/merb-auth-remember-me/strategies/remember_me.rb +55 -0
  21. data/lib/merb_auth_slice_multisite.rb +107 -0
  22. data/lib/merb_auth_slice_multisite/merbtasks.rb +103 -0
  23. data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site.rb +63 -0
  24. data/lib/merb_auth_slice_multisite/mixins/user_belongs_to_site/dm_user_belongs_to_site.rb +28 -0
  25. data/lib/merb_auth_slice_multisite/slicetasks.rb +18 -0
  26. data/lib/merb_auth_slice_multisite/spectasks.rb +54 -0
  27. data/public/javascripts/master.js +0 -0
  28. data/public/stylesheets/master.css +2 -0
  29. data/spec/mailers/send_password_mailer_spec.rb +47 -0
  30. data/spec/mixins/authenticated_user_spec.rb +33 -0
  31. data/spec/mixins/user_belongs_to_site_spec.rb +56 -0
  32. data/spec/models/site_spec.rb +56 -0
  33. data/spec/spec_helper.rb +101 -0
  34. data/spec/strategies/remember_me_spec.rb +62 -0
  35. data/stubs/app/controllers/sessions.rb +19 -0
  36. data/stubs/app/views/exceptions/unauthenticated.html.erb +61 -0
  37. metadata +91 -0
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5
+ <title>Fresh MerbAuthSliceMultisite Slice</title>
6
+ <link href="<%= public_path_for :stylesheet, 'master.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
7
+ <script src="<%= public_path_for :javascript, 'master.js' %>" type="text/javascript" charset="utf-8"></script>
8
+ </head>
9
+ <!-- you can override this layout at slices/merb_auth_slice_multisite/app/views/layout/merb_auth_slice_multisite.html.erb -->
10
+ <body class="merb_auth_slice_multisite-slice">
11
+ <div id="container">
12
+ <h1>MerbAuthSliceMultisite Slice</h1>
13
+ <div id="main"><%= catch_content :for_layout %></div>
14
+ </div>
15
+ </body>
16
+ </html>
@@ -0,0 +1,33 @@
1
+ ---
2
+ # This is a sample database file for the DataMapper ORM
3
+ development: &defaults
4
+ # These are the settings for repository :default
5
+ adapter: sqlite3
6
+ database: sample_development.db
7
+
8
+ # Add more repositories
9
+ # repositories:
10
+ # repo1:
11
+ # adapter: sqlite3
12
+ # database: sample_1_development.db
13
+ # repo2:
14
+ # ...
15
+
16
+ test:
17
+ <<: *defaults
18
+ database: sample_test.db
19
+
20
+ # repositories:
21
+ # repo1:
22
+ # database: sample_1_test.db
23
+
24
+ production:
25
+ <<: *defaults
26
+ database: production.db
27
+
28
+ # repositories:
29
+ # repo1:
30
+ # database: sample_production.db
31
+
32
+ rake:
33
+ <<: *defaults
@@ -0,0 +1,33 @@
1
+ # dependencies are generated using a strict version, don't forget to edit the dependency versions when upgrading.
2
+ merb_gems_version = "1.0.11"
3
+ dm_gems_version = "0.9.11"
4
+ do_gems_version = "0.9.11"
5
+
6
+ # For more information about each component, please read http://wiki.merbivore.com/faqs/merb_components
7
+ dependency "merb-core", merb_gems_version
8
+ dependency "merb-action-args", merb_gems_version
9
+ dependency "merb-assets", merb_gems_version
10
+ dependency("merb-cache", merb_gems_version) do
11
+ Merb::Cache.setup do
12
+ register(Merb::Cache::FileStore) unless Merb.cache
13
+ end
14
+ end
15
+ dependency "merb-helpers", merb_gems_version
16
+ dependency "merb-mailer", merb_gems_version
17
+ dependency "merb-slices", merb_gems_version
18
+ dependency "merb-auth-core", merb_gems_version
19
+ dependency "merb-auth-more", merb_gems_version
20
+ dependency "merb-auth-slice-password", merb_gems_version
21
+ dependency "merb-param-protection", merb_gems_version
22
+ dependency "merb-exceptions", merb_gems_version
23
+
24
+ dependency "data_objects", do_gems_version
25
+ dependency "dm-core", dm_gems_version
26
+ dependency "dm-aggregates", dm_gems_version
27
+ dependency "dm-migrations", dm_gems_version
28
+ dependency "dm-timestamps", dm_gems_version
29
+ dependency "dm-types", dm_gems_version
30
+ dependency "dm-validations", dm_gems_version
31
+ dependency "dm-serializer", dm_gems_version
32
+
33
+ dependency "merb_datamapper", merb_gems_version
@@ -0,0 +1,84 @@
1
+ #
2
+ # ==== Standalone MerbAuthSliceMultisite configuration
3
+ #
4
+ # This configuration/environment file is only loaded by bin/slice, which can be
5
+ # used during development of the slice. It has no effect on this slice being
6
+ # loaded in a host application. To run your slice in standalone mode, just
7
+ # run 'slice' from its directory. The 'slice' command is very similar to
8
+ # the 'merb' command, and takes all the same options, including -i to drop
9
+ # into an irb session for example.
10
+ #
11
+ # The usual Merb configuration directives and init.rb setup methods apply,
12
+ # including use_orm and before_app_loads/after_app_loads.
13
+ #
14
+ # If you need need different configurations for different environments you can
15
+ # even create the specific environment file in config/environments/ just like
16
+ # in a regular Merb application.
17
+ #
18
+ # In fact, a slice is no different from a normal # Merb application - it only
19
+ # differs by the fact that seamlessly integrates into a so called 'host'
20
+ # application, which in turn can override or finetune the slice implementation
21
+ # code and views.
22
+ #
23
+ require(File.join(File.expand_path(File.dirname(__FILE__)),"..","lib","merb_auth_slice_multisite"))
24
+
25
+ require 'config/dependencies.rb'
26
+ use_orm :datamapper
27
+ use_test :rspec
28
+ use_template_engine :erb
29
+
30
+ # Setup the required configuration for the slice
31
+ Merb::Slices::config[:merb_auth_slice_multisite][:send_password_from_email] = "no-reply@yourapp.com"
32
+ Merb::Slices::config[:merb_auth_slice_multisite][:domain] = "example.com"
33
+
34
+
35
+ Merb::BootLoader.before_app_loads do
36
+ DataMapper.setup(:default, "sqlite3::memory:")
37
+
38
+ class User
39
+ include DataMapper::Resource
40
+ include Merb::Authentication::Mixins::UserBelongsToSite
41
+ include Merb::Authentication::Mixins::AuthenticatedUser
42
+
43
+ property :id, Serial
44
+ property :email, String
45
+ property :login, String
46
+ property :password, String
47
+
48
+ belongs_to :site
49
+ end
50
+
51
+ class Merb::Authentication
52
+ def self.user_class
53
+ ::User
54
+ end
55
+
56
+ def store_user(user)
57
+ return nil if user.nil?
58
+ user.login
59
+ end
60
+ def fetch_user(user_id)
61
+ User.first(:login => login)
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ Merb::Config.use do |c|
68
+ # Sets up a custom session id key which is used for the session persistence
69
+ # cookie name. If not specified, defaults to '_session_id'.
70
+ # c[:session_id_key] = '_session_id'
71
+
72
+ # The session_secret_key is only required for the cookie session store.
73
+ c[:session_secret_key] = 'ef573d4e2d6f9bf3a713cd45eb4f9411dfba94ef'
74
+
75
+ # There are various options here, by default Merb comes with 'cookie',
76
+ # 'memory', 'memcache' or 'container'.
77
+ # You can of course use your favorite ORM instead:
78
+ # 'datamapper', 'sequel' or 'activerecord'.
79
+ c[:session_store] = 'cookie'
80
+
81
+ # When running a slice standalone, you're usually developing it,
82
+ # so enable template reloading by default.
83
+ c[:reload_templates] = true
84
+ end
@@ -0,0 +1,5 @@
1
+ # This file is here so slice can be testing as a stand alone application.
2
+
3
+ #Merb::Router.prepare do
4
+ # ...
5
+ #end
@@ -0,0 +1,77 @@
1
+ require 'merb-auth-more/strategies/abstract_password'
2
+ # This strategy uses a login, password, and site_id parameter.
3
+ #
4
+ # Overwrite the :password_param, :login_param, and :site_id_param
5
+ # to return the name of the field (on the form) that you're using the
6
+ # login with. These can be strings or symbols
7
+ #
8
+ # == Required
9
+ #
10
+ # === Methods
11
+ # <User>.authenticate(login_param, password_param)
12
+ #
13
+ class Merb::Authentication
14
+ module Strategies
15
+ module Multisite
16
+
17
+ # add site_id to base params.
18
+ # http://github.com/wycats/merb/blob/784ac7d71780d1a7cfb9152ba4cb0e
19
+ # 18a990ab7a/merb-auth/merb-auth-more/lib/merb-auth-more/
20
+ # strategies/abstract_password.rb
21
+ class Base < Merb::Authentication::Strategy
22
+ abstract!
23
+
24
+ # Overwrite this method to customize the field
25
+ def self.password_param
26
+ (Merb::Plugins.config[:"merb-auth"][:password_param] || :password).to_s.to_sym
27
+ end
28
+
29
+ # Overwrite this method to customize the field
30
+ def self.login_param
31
+ (Merb::Plugins.config[:"merb-auth"][:login_param] || :login).to_s.to_sym
32
+ end
33
+
34
+ # http://scottmotte.com/archives/194.html
35
+ def self.site_id_param
36
+ (Merb::Plugins.config[:"merb-auth"][:site_id_param] || :site_id).to_s.to_sym
37
+ end
38
+
39
+ def password_param
40
+ @password_param ||= Base.password_param
41
+ end
42
+
43
+ def login_param
44
+ @login_param ||= Base.login_param
45
+ end
46
+
47
+ # http://scottmotte.com/archives/194.html
48
+ def site_id_param
49
+ @site_id_param ||= Base.site_id_param
50
+ end
51
+ end
52
+
53
+ # custom strategy taking into account site_id and authenticating with it
54
+ # the authenticate method is already well defined so for now I made a muck of
55
+ # if/else statements
56
+ class Form < Base
57
+
58
+ def run!
59
+ if (login = request.params[login_param]) && (password = request.params[password_param]) && (site_id = request.params[site_id_param])
60
+ user = user_class.all(site_id_param => site_id).authenticate(login, password)
61
+ if !user
62
+ errors = request.session.authentication.errors
63
+ errors.clear!
64
+ errors.add(login_param, strategy_error_message)
65
+ end
66
+ user
67
+ end
68
+ end # run!
69
+
70
+ def strategy_error_message
71
+ "#{login_param.to_s.capitalize} or #{password_param.to_s.capitalize} were incorrect"
72
+ end
73
+
74
+ end # Form
75
+ end # Multisite Password
76
+ end # Strategies
77
+ end # Authentication
@@ -0,0 +1,97 @@
1
+ require "digest/sha1"
2
+ module Merb
3
+ class Authentication
4
+ module Mixins
5
+ # This mixin provides basic user remember token.
6
+ #
7
+ # Added properties:
8
+ # :remember_token_expires_at, DateTime
9
+ # :remember_token, String
10
+ #
11
+ # To use it simply require it and include it into your user class.
12
+ #
13
+ # class User
14
+ # include Merb::Authentication::Mixins::AuthenticatedUser
15
+ #
16
+ # end
17
+ #
18
+ module AuthenticatedUser
19
+ def self.included(base)
20
+ base.class_eval do
21
+ include Merb::Authentication::Mixins::AuthenticatedUser::InstanceMethods
22
+ extend Merb::Authentication::Mixins::AuthenticatedUser::ClassMethods
23
+
24
+ path = File.expand_path(File.dirname(__FILE__)) / "authenticated_user"
25
+ if defined?(DataMapper) && DataMapper::Resource > self
26
+ require path / "dm_authenticated_user"
27
+ extend(Merb::Authentication::Mixins::AuthenticatedUser::DMClassMethods)
28
+ elsif defined?(ActiveRecord) && ancestors.include?(ActiveRecord::Base)
29
+ require path / "ar_authenticated_user"
30
+ extend(Merb::Authentication::Mixins::AuthenticatedUser::ARClassMethods)
31
+ elsif defined?(Sequel) && ancestors.include?(Sequel::Model)
32
+ require path / "sq_authenticated_user"
33
+ extend(Merb::Authentication::Mixins::AuthenticatedUser::SQClassMethods)
34
+ end
35
+
36
+ end # base.class_eval
37
+ end # self.included
38
+
39
+
40
+ module ClassMethods
41
+ def secure_digest(*args)
42
+ Digest::SHA1.hexdigest(args.flatten.join('--'))
43
+ end
44
+
45
+ # Create random key.
46
+ #
47
+ # ==== Returns
48
+ # String:: The generated key
49
+ def make_token
50
+ secure_digest(Time.now, (1..10).map{ rand.to_s })
51
+ end
52
+ end # ClassMethods
53
+
54
+ module InstanceMethods
55
+ def remember_token?
56
+ (!remember_token.blank?) &&
57
+ remember_token_expires_at && (Time.now.utc < remember_token_expires_at.to_time)
58
+ end
59
+
60
+ # These create and unset the fields required for remembering users between browser closes
61
+ def remember_me(time = 2.weeks)
62
+ remember_me_for time
63
+ end
64
+
65
+ def remember_me_for(time)
66
+ remember_me_until time.from_now.utc
67
+ end
68
+
69
+ def remember_me_until(time)
70
+ self.remember_token_expires_at = time
71
+ self.remember_token = self.class.make_token
72
+ save
73
+ end
74
+
75
+ # refresh token (keeping same expires_at) if it exists
76
+ def refresh_token
77
+ if remember_token?
78
+ self.remember_token = self.class.make_token
79
+ save
80
+ end
81
+ end
82
+
83
+ #
84
+ # Deletes the server-side record of the authentication token. The
85
+ # client-side (browser cookie) and server-side (this remember_token) must
86
+ # always be deleted together.
87
+ #
88
+ def forget_me
89
+ self.remember_token_expires_at = nil
90
+ self.remember_token = nil
91
+ save
92
+ end
93
+ end # InstanceMethods
94
+ end # AuthenticatedUser
95
+ end # Mixins
96
+ end # Authentication
97
+ end # Merb
@@ -0,0 +1,17 @@
1
+ module Merb
2
+ class Authentication
3
+ module Mixins
4
+ module AuthenticatedUser
5
+ module DMClassMethods
6
+ def self.extended(base)
7
+ base.class_eval do
8
+ property :remember_token_expires_at, DateTime
9
+ property :remember_token, String
10
+ end # base.class_eval
11
+
12
+ end # self.extended
13
+ end # DMClassMethods
14
+ end # AuthenticatedUser
15
+ end # Mixins
16
+ end # Authentication
17
+ end #Merb
@@ -0,0 +1,55 @@
1
+ class RememberMe < Merb::Authentication::Strategy
2
+ def run!
3
+ login_from_cookie
4
+ end
5
+
6
+ def current_user
7
+ @current_user
8
+ end
9
+
10
+ def current_user=(new_user)
11
+ @current_user = new_user
12
+ end
13
+
14
+ # Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
15
+ # for the paranoid: we _should_ be storing user_token = hash(cookie_token, request IP)
16
+ def login_from_cookie
17
+ current_user = cookies[:auth_token] && Merb::Authentication.user_class.first(:conditions => ["remember_token = ?", cookies[:auth_token]])
18
+ if current_user && current_user.remember_token?
19
+ handle_remember_cookie! false # freshen cookie token (keeping date)
20
+ current_user
21
+ end
22
+ end
23
+
24
+ #
25
+ # Remember_me Tokens
26
+ #
27
+ # Cookies shouldn't be allowed to persist past their freshness date,
28
+ # and they should be changed at each login
29
+
30
+ # Cookies shouldn't be allowed to persist past their freshness date,
31
+ # and they should be changed at each login
32
+
33
+ def valid_remember_cookie?
34
+ return nil unless current_user
35
+ (current_user.remember_token?) &&
36
+ (cookies[:auth_token] == current_user.remember_token)
37
+ end
38
+
39
+ # Refresh the cookie auth token if it exists, create it otherwise
40
+ def handle_remember_cookie! new_cookie_flag
41
+ return unless current_user
42
+ case
43
+ when valid_remember_cookie? then current_user.refresh_token # keeping same expiry date
44
+ when new_cookie_flag then current_user.remember_me
45
+ else current_user.forget_me
46
+ end
47
+ send_remember_cookie!
48
+ end
49
+
50
+ def send_remember_cookie!
51
+ cookies.set_cookie(:auth_token, current_user.remember_token, :expires => current_user.remember_token_expires_at.to_time)
52
+ end
53
+
54
+
55
+ end
@@ -0,0 +1,107 @@
1
+ if defined?(Merb::Plugins)
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ dependency 'merb-mailer'
6
+ dependency 'merb-slices', :immediate => true
7
+ dependency 'merb-auth-core'
8
+ dependency 'merb-auth-more'
9
+ require(File.expand_path(File.dirname(__FILE__) / "merb_auth_slice_multisite" / "mixins") / "user_belongs_to_site")
10
+ require(File.expand_path(File.dirname(__FILE__) / "merb-auth-remember-me" / "mixins") / "authenticated_user")
11
+
12
+ Merb::Plugins.add_rakefiles "merb_auth_slice_multisite/merbtasks", "merb_auth_slice_multisite/slicetasks", "merb_auth_slice_multisite/spectasks"
13
+
14
+ # Register the Slice for the current host application
15
+ Merb::Slices::register(__FILE__)
16
+
17
+ # Slice configuration - set this in a before_app_loads callback.
18
+ # By default a Slice uses its own layout, so you can swicht to
19
+ # the main application layout or no layout at all if needed.
20
+ #
21
+ # Configuration options:
22
+ # :layout - the layout to use; defaults to :merb_auth_slice_multisite
23
+ # :mirror - which path component types to use on copy operations; defaults to all
24
+ Merb::Slices::config[:merb_auth_slice_multisite][:layout] ||= :application
25
+
26
+ # All Slice code is expected to be namespaced inside a module
27
+ module MerbAuthSliceMultisite
28
+
29
+ # Slice metadata
30
+ self.description = "see gem description"
31
+ self.version = "see gem version"
32
+ self.author = "see gem authors"
33
+
34
+ # Stub classes loaded hook - runs before LoadClasses BootLoader
35
+ # right after a slice's classes have been loaded internally.
36
+ def self.loaded
37
+ end
38
+
39
+ # Initialization hook - runs before AfterAppLoads BootLoader
40
+ def self.init
41
+ require 'merb-auth-more/mixins/redirect_back'
42
+ unless MerbAuthSliceMultisite[:no_default_strategies]
43
+ # Register the custom strategy so that this slice may utilize it
44
+ # from http://github.com/wycats/merb/blob/784ac7d71780d1a7cfb9152ba4cb0
45
+ # e18a990ab7a/merb-auth/merb-auth-more/lib/merb-auth-more.rb
46
+ merb_auth_more_path = File.expand_path(File.dirname(__FILE__)) / "merb-auth-more" / "strategies" / "multisite"
47
+ merb_auth_remember_me_path = File.expand_path(File.dirname(__FILE__)) / "merb-auth-remember-me" / "strategies"
48
+ Merb::Authentication.register(:multisite_password_form, merb_auth_more_path / "multisite_password_form.rb")
49
+ Merb::Authentication.register(:remember_me, merb_auth_remember_me_path / "remember_me.rb")
50
+ # activate the strategies
51
+ ::Merb::Authentication.activate!(:multisite_password_form)
52
+ ::Merb::Authentication.activate!(:remember_me)
53
+
54
+ Merb::Authentication.after_authentication do |user,request,params|
55
+ if params[:remember_me] == "1"
56
+ user.remember_me
57
+ request.cookies.set_cookie(:auth_token, user.remember_token, :expires => user.remember_token_expires_at.to_time)
58
+ end
59
+ user
60
+ end # Merb::Authentication.after_authentication
61
+ end
62
+ end
63
+
64
+ # Activation hook - runs after AfterAppLoads BootLoader
65
+ def self.activate
66
+ end
67
+
68
+ # Deactivation hook - triggered by Merb::Slices.deactivate(MerbAuthSliceMultisite)
69
+ def self.deactivate
70
+ end
71
+
72
+ # Setup routes inside the host application
73
+ #
74
+ # @param scope<Merb::Router::Behaviour>
75
+ # Routes will be added within this scope (namespace). In fact, any
76
+ # router behaviour is a valid namespace, so you can attach
77
+ # routes at any level of your router setup.
78
+ #
79
+ # @note prefix your named routes with :merb_auth_slice_multisite_
80
+ # to avoid potential conflicts with global named routes.
81
+ def self.setup_router(scope)
82
+ scope.match("/send_password", :method => :post).to(:controller => "passwords", :action => "send_password").name(:send_password)
83
+ scope.match("/login", :method => :get ).to(:controller => "/exceptions", :action => "unauthenticated").name(:login)
84
+ scope.match("/login", :method => :put ).to(:controller => "sessions", :action => "update").name(:perform_login)
85
+ scope.match("/logout").to(:controller => "sessions", :action => "destroy").name(:logout)
86
+ end
87
+
88
+ end
89
+
90
+ # Setup the slice layout for MerbAuthSliceMultisite
91
+ #
92
+ # Use MerbAuthSliceMultisite.push_path and MerbAuthSliceMultisite.push_app_path
93
+ # to set paths to merb_auth_slice_multisite-level and app-level paths. Example:
94
+ #
95
+ # MerbAuthSliceMultisite.push_path(:application, MerbAuthSliceMultisite.root)
96
+ # MerbAuthSliceMultisite.push_app_path(:application, Merb.root / 'slices' / 'merb_auth_slice_multisite')
97
+ # ...
98
+ #
99
+ # Any component path that hasn't been set will default to MerbAuthSliceMultisite.root
100
+ #
101
+ # Or just call setup_default_structure! to setup a basic Merb MVC structure.
102
+ MerbAuthSliceMultisite.setup_default_structure!
103
+
104
+ # Add dependencies for other MerbAuthSliceMultisite classes below. Example:
105
+ # dependency "merb_auth_slice_multisite/other"
106
+
107
+ end