auth-slice 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 PragmaQuest Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,65 @@
1
+ AuthSlice
2
+ =========
3
+
4
+ An user authentication slice for the Merb framework. See http://github.com/ctran/merb-skel for an example application that uses this slice
5
+
6
+ == Installation:
7
+
8
+ > gem install merb
9
+ > gem install merb-slices
10
+
11
+ To use activerecord adapter
12
+ > gem install activerecord
13
+ > gem merb_activerecord
14
+
15
+ To use datamapper adapter
16
+ > gem install datamapper
17
+ > gem install merb_datamapper
18
+
19
+ === config/init.rb
20
+
21
+ # add the slice as a regular dependency
22
+ dependency 'auth-slice'
23
+
24
+ === config/router.rb
25
+
26
+ # This will add the following routes /login, /logout and /signup
27
+ r.slice(:AuthSlice)
28
+
29
+ # This will add the following routes /auth-slice/login, /auth-slice/logout and /auth-slice/signup
30
+ r.add_slice(:AuthSlice)
31
+
32
+ # This will add the following routes /user/login, /user/logout and /user/signup
33
+ r.add_slice(:AuthSlice, 'user') # same as :path => 'user'
34
+
35
+ === Normally you should also run the following rake task:
36
+ > rake slices:auth_slice:install
37
+
38
+ == Customization/Overrides
39
+
40
+ By default, the user model class is in AuthSlice::User. To use shorter name, add this to your init.rb
41
+
42
+ Merb::Slices.config[:auth_slice] = { :user_model_class => "User" }
43
+
44
+ You can also put your application-level overrides in:
45
+
46
+ host-app/slices/auth-slice/app - controllers, models, views ...
47
+
48
+ Templates are located in this order:
49
+
50
+ 1. host-app/slices/auth-slice/app/views/*
51
+ 2. gems/auth-slice/app/views/*
52
+ 3. host-app/app/views/*
53
+
54
+ You can use the host application's layout by configuring the
55
+ auth-slice slice in a before_app_loads block:
56
+
57
+ Merb::Slices.config[:auth_slice] = { :layout => :application }
58
+
59
+ By default :auth_slice is used. If you need to override
60
+ stylesheets or javascripts, just specify your own files in your layout
61
+ instead/in addition to the ones supplied (if any) in
62
+ host-app/public/slices/auth-slice.
63
+
64
+ In any case don't edit those files directly as they may be clobbered any time
65
+ rake auth_slice:install is run.
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+ require 'merb-core/version'
5
+ require 'merb-core/test/tasks/spectasks'
6
+
7
+ PLUGIN = "auth-slice"
8
+ NAME = "auth-slice"
9
+ AUTHOR = "Cuong Tran"
10
+ EMAIL = "ctran@pragmaquest.com"
11
+ HOMEPAGE = "http://merb-slices.rubyforge.org/auth-slice/"
12
+ SUMMARY = "Merb Slice that provides user authentication"
13
+ SLICE_VERSION = "0.1.0"
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = NAME
17
+ s.version = SLICE_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README", "LICENSE"]
21
+ s.summary = SUMMARY
22
+ s.description = s.summary
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+ s.add_dependency('merb-slices', '>= 0.9.4')
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE README Rakefile) + Dir.glob("{lib,spec,app,public}/**/*")
29
+ end
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.gem_spec = spec
33
+ end
34
+
35
+ desc "Install Foo as a gem"
36
+ task :install => [:package] do
37
+ sh %{sudo gem install pkg/#{NAME}-#{SLICE_VERSION} --no-update-sources --local}
38
+ end
39
+
40
+ namespace :jruby do
41
+
42
+ desc "Run :package and install the resulting .gem with jruby"
43
+ task :install => :package do
44
+ sh %{#{SUDO} jruby -S gem install pkg/#{NAME}-#{VERSION}.gem --no-rdoc --no-ri}
45
+ end
46
+
47
+ end
@@ -0,0 +1,4 @@
1
+ class AuthSlice::Application < Merb::Controller
2
+ controller_for_slice
3
+ include AuthSlice::ControllerMixin
4
+ end
@@ -0,0 +1,150 @@
1
+ module AuthSlice
2
+ module ControllerMixin
3
+ protected
4
+ # Returns true or false if the user is logged in.
5
+ # Preloads @current_user with the user model if they're logged in.
6
+ def logged_in?
7
+ current_user != :false
8
+ end
9
+
10
+ # Accesses the current user from the session. Set it to :false if login fails
11
+ # so that future calls do not hit the database.
12
+ def current_user
13
+ @current_user ||= (login_from_session || login_from_basic_auth || login_from_cookie || :false)
14
+ end
15
+
16
+ # Store the given user in the session.
17
+ def current_user=(new_user)
18
+ session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
19
+ @current_user = new_user
20
+ end
21
+
22
+ # Check if the user is authorized
23
+ #
24
+ # Override this method in your controllers if you want to restrict access
25
+ # to only a few actions or if you want to check if the user
26
+ # has the correct rights.
27
+ #
28
+ # Example:
29
+ #
30
+ # # only allow nonbobs
31
+ # def authorized?
32
+ # current_user.username != "bob"
33
+ # end
34
+ def authorized?
35
+ logged_in?
36
+ end
37
+
38
+ # Filter method to enforce a login requirement.
39
+ #
40
+ # To require logins for all actions, use this in your controllers:
41
+ #
42
+ # before_filter :login_required
43
+ #
44
+ # To require logins for specific actions, use this in your controllers:
45
+ #
46
+ # before_filter :login_required, :only => [ :edit, :update ]
47
+ #
48
+ # To skip this in a subclassed controller:
49
+ #
50
+ # skip_before_filter :login_required
51
+ #
52
+ def login_required
53
+ authorized? || throw(:halt, :access_denied)
54
+ end
55
+
56
+ # Redirect as appropriate when an access request fails.
57
+ #
58
+ # The default HTML action is to redirect to the login screen.
59
+ #
60
+ # The default XML action is to render the text Couldn't authenticate you.
61
+ # To provide this response wrapped in XML, make sure to specify an
62
+ # XML layout, such as /app/views/layouts/application.xml.builder.
63
+ #
64
+ # Override this method in your controllers if you want to have special
65
+ # behavior in case the user is not authorized
66
+ # to access the requested action. For example, a popup window might
67
+ # simply close itself.
68
+ def access_denied
69
+ case content_type
70
+ when :html
71
+ store_location
72
+ redirect url(:login)
73
+ when :xml
74
+ headers["Status"] = "Unauthorized"
75
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
76
+ self.status = 401
77
+ render "Couldn't authenticate you"
78
+ end
79
+ end
80
+
81
+ # Store the URI of the current request in the session.
82
+ #
83
+ # We can return to this location by calling #redirect_back_or_default.
84
+ def store_location
85
+ session[:return_to] = request.uri
86
+ end
87
+
88
+ # Redirect to the URI stored by the most recent store_location call or
89
+ # to the passed default.
90
+ def redirect_back_or_default(default)
91
+ loc = session[:return_to] || default
92
+ session[:return_to] = nil
93
+ redirect loc
94
+ end
95
+
96
+ # Inclusion hook to make #current_user and #logged_in?
97
+ # available as ActionView helper methods.
98
+ # def self.included(base)
99
+ # base.send :helper_method, :current_user, :logged_in?
100
+ # end
101
+
102
+ # Called from #current_user. First attempt to login by the user id stored in the session.
103
+ def login_from_session
104
+ self.current_user = find_user_by_id(session[:user]) if session[:user]
105
+ end
106
+
107
+ # Called from #current_user. Now, attempt to login by basic authentication information.
108
+ def login_from_basic_auth
109
+ username, passwd = get_auth_data
110
+ self.current_user = verify_login(username, passwd) if username && passwd
111
+ end
112
+
113
+ # Called from #current_user. Finaly, attempt to login by an expiring token in the cookie.
114
+ def login_from_cookie
115
+ user = cookies[:auth_token] && find_user_by_remember_token(cookies[:auth_token])
116
+ if user && user.remember_token?
117
+ user.remember_me
118
+ cookies[:auth_token] = { :value => user.remember_token, :expires => Time.parse(user.remember_token_expires_at.strftime) }
119
+ self.current_user = user
120
+ end
121
+ end
122
+
123
+ def reset_session
124
+ session.data.each{|k,v| session.data.delete(k)}
125
+ end
126
+
127
+ protected
128
+ def verify_login(username, password)
129
+ AuthSlice::User.authenticate(username, password)
130
+ end
131
+
132
+ def find_user_by_id(user_id)
133
+ AuthSlice::User.find_by_id(user_id)
134
+ end
135
+
136
+ def find_user_by_remember_token(token)
137
+ AuthSlice::User.find_by_remember_token(token)
138
+ end
139
+
140
+ private
141
+ @@http_auth_headers = %w(Authorization HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION)
142
+
143
+ # gets BASIC auth info
144
+ def get_auth_data
145
+ auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
146
+ auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
147
+ return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,41 @@
1
+ class AuthSlice::Users < AuthSlice::Application
2
+ provides :html
3
+
4
+ skip_before :login_required
5
+
6
+ def login
7
+ if request.post?
8
+ self.current_user = verify_login(params[:username], params[:password])
9
+ if logged_in?
10
+ if params[:remember_me] == "1"
11
+ self.current_user.remember_me
12
+ cookies[:auth_token] = {
13
+ :value => self.current_user.remember_token,
14
+ :expires => Time.parse(current_user.remember_token_expires_at.strftime)
15
+ }
16
+ end
17
+ return redirect_back_or_default('/')
18
+ end
19
+ end
20
+
21
+ render
22
+ end
23
+
24
+ def logout
25
+ self.current_user.forget_me if logged_in?
26
+ cookies.delete :auth_token
27
+ reset_session
28
+ redirect_back_or_default('/')
29
+ end
30
+
31
+ def signup
32
+ cookies.delete :auth_token
33
+ @user = AuthSlice::User.new(params['auth_slice::user'] || {})
34
+
35
+ if request.post? && @user.save
36
+ return redirect_back_or_default('/')
37
+ end
38
+
39
+ render
40
+ end
41
+ end
@@ -0,0 +1,64 @@
1
+ module Merb
2
+ module AuthSlice
3
+ module ApplicationHelper
4
+
5
+ # @param *segments<Array[#to_s]> Path segments to append.
6
+ #
7
+ # @return <String>
8
+ # A path relative to the public directory, with added segments.
9
+ def image_path(*segments)
10
+ public_path_for(:image, *segments)
11
+ end
12
+
13
+ # @param *segments<Array[#to_s]> Path segments to append.
14
+ #
15
+ # @return <String>
16
+ # A path relative to the public directory, with added segments.
17
+ def javascript_path(*segments)
18
+ public_path_for(:javascript, *segments)
19
+ end
20
+
21
+ # @param *segments<Array[#to_s]> Path segments to append.
22
+ #
23
+ # @return <String>
24
+ # A path relative to the public directory, with added segments.
25
+ def stylesheet_path(*segments)
26
+ public_path_for(:stylesheet, *segments)
27
+ end
28
+
29
+ # Construct a path relative to the public directory
30
+ #
31
+ # @param <Symbol> The type of component.
32
+ # @param *segments<Array[#to_s]> Path segments to append.
33
+ #
34
+ # @return <String>
35
+ # A path relative to the public directory, with added segments.
36
+ def public_path_for(type, *segments)
37
+ File.join(::AuthSlice.public_dir_for(type), *segments)
38
+ end
39
+
40
+ # Construct an app-level path.
41
+ #
42
+ # @param <Symbol> The type of component.
43
+ # @param *segments<Array[#to_s]> Path segments to append.
44
+ #
45
+ # @return <String>
46
+ # A path within the host application, with added segments.
47
+ def app_path_for(type, *segments)
48
+ File.join(::AuthSlice.app_dir_for(type), *segments)
49
+ end
50
+
51
+ # Construct a slice-level path.
52
+ #
53
+ # @param <Symbol> The type of component.
54
+ # @param *segments<Array[#to_s]> Path segments to append.
55
+ #
56
+ # @return <String>
57
+ # A path within the slice source (Gem), with added segments.
58
+ def slice_path_for(type, *segments)
59
+ File.join(::AuthSlice.dir_for(type), *segments)
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ module AuthSlice
2
+ module Adapter
3
+ module Activerecord
4
+ def self.create_user_model
5
+ Object.class_eval <<-end_eval
6
+ class ::AuthSlice::User < ActiveRecord::Base
7
+ include AuthSlice::Adapter::Activerecord
8
+ end
9
+ end_eval
10
+ end
11
+
12
+ def self.included(base)
13
+ base.class_eval do
14
+ include AuthSlice::BaseModel
15
+ extend ClassMethods
16
+
17
+ validates_presence_of :name
18
+ validates_presence_of :username
19
+ validates_presence_of :email
20
+ validates_length_of :username, :within => 3..40
21
+ validates_length_of :password, :within => 4..40, :if => :password_required?
22
+ validates_presence_of :password_confirmation, :if => :password_required?
23
+ validates_confirmation_of :password, :if => :password_required?
24
+ validates_uniqueness_of :username, :case_sensitive => false, :if => lambda { |u| !u.username.blank? }
25
+ validates_uniqueness_of :email, :case_sensitive => false, :if => lambda { |u| !u.email.blank? }
26
+
27
+ before_save :encrypt_password
28
+
29
+ def username=(value)
30
+ self[:username] = value.downcase if value
31
+ end
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ def drop_db_table
37
+ self.connection.drop_table("users")
38
+ end
39
+
40
+ def create_db_table
41
+ self.connection.create_table("users") do |t|
42
+ t.string :name, :limit => 40, :null => false
43
+ t.string :username, :limit => 40, :null => false
44
+ t.string :email, :null => false
45
+ t.string :crypted_password, :limit => 40
46
+ t.string :salt, :limit => 40, :null => false
47
+ t.string :remember_token
48
+ t.date :remember_token_expires_at
49
+ t.timestamps
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end