kenn-userify 0.1.1

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.
data/CHANGELOG.textile ADDED
@@ -0,0 +1,3 @@
1
+ h2. 0.1.0 (5/8/2009)
2
+
3
+ * First cut of userify
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Kenn Ejima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,118 @@
1
+ h1. Userify
2
+
3
+ Super simple authentication system for Rails, using username, email and password.
4
+
5
+ Userify focuses on the following 6 conventional actions, and that's all.
6
+
7
+ ( signup / signin / signout / activate / forgot / reset )
8
+
9
+ Userify is inspired by "Clearance":http://github.com/thoughtbot/clearance.
10
+
11
+ h2. Limitations
12
+
13
+ Currently, Userify is available only when you create a new project. Don't expect it to work in existing projects.
14
+
15
+ h2. Requirements
16
+
17
+ Userify requires Haml. However, when you override the default template, you don't have to use Haml.
18
+
19
+ h2. How to use it?
20
+
21
+ Here's the setup method.
22
+
23
+ Install Userify gem, as well as Haml.
24
+
25
+ sudo gem sources -a http://gems.github.com # if you haven't do it already
26
+ sudo gem install haml
27
+ sudo gem install kenn-userify
28
+
29
+ In config/environment.rb:
30
+
31
+ config.gem "haml"
32
+ config.gem "kenn-userify", :lib => "userify", :source => "http://gems.github.com"
33
+ DO_NOT_REPLY = "donotreply@example.com"
34
+
35
+ Run the generator:
36
+
37
+ script/generate userify
38
+
39
+ In config/environments/development.rb and test.rb:
40
+
41
+ HOST = "localhost:3000"
42
+
43
+ Define root_url to *something* in your config/routes.rb. Assuming home controller for root:
44
+
45
+ map.root :controller => 'home'
46
+
47
+ Create user_controller.rb and home_controller.rb:
48
+
49
+ script/generate controller user
50
+ script/generate controller home
51
+
52
+ Rewrite user_controller.rb as follows:
53
+
54
+ class UserController < Userify::UsersController
55
+ end
56
+
57
+ Edit home_controller.rb:
58
+
59
+ class HomeController < ApplicationController
60
+ def index
61
+ end
62
+ end
63
+
64
+ Create app/views/home/index.html.haml:
65
+
66
+ - if signed_in?
67
+ %p= "Hello #{current_user.name}!"
68
+ %h1 Home
69
+ %p My app home
70
+ - if signed_in?
71
+ %p= link_to 'sign out', signout_url
72
+ - else
73
+ %p= link_to 'sign up', signup_url
74
+ %p= link_to 'sign in', signin_url
75
+
76
+ Rename public/index.html:
77
+
78
+ mv public/index.html public/_index.html
79
+
80
+ Run migration and start server:
81
+
82
+ rake db:migrate
83
+ script/server
84
+
85
+ Now, go to http://localhost:3000/ and check how everything works.
86
+
87
+ h2. Customize
88
+
89
+ There are a couple of ways to customize Userify:
90
+
91
+ 1. Override methods in the user_controller subclass.
92
+
93
+ You might want to override the following methods in your user_controller subclass.
94
+
95
+ UserController methods:
96
+
97
+ url_after_signup
98
+ url_after_signin
99
+ url_after_signout
100
+ url_after_activate
101
+ url_after_forgot
102
+ url_after_reset
103
+
104
+ View templates:
105
+
106
+ app/views/layouts/application.html.haml
107
+ app/views/layouts/_error_messages.html.haml
108
+ app/views/user/forgot.html.haml
109
+ app/views/user/reset.html.haml
110
+ app/views/user/signin.html.haml
111
+ app/views/user/signup.html.haml
112
+ app/views/userify_mailer/confirmation.html.erb
113
+ app/views/userify_mailer/reset_password.html.erb
114
+
115
+
116
+ 2. Unpack Userify gem into vendor/gems and directly edit source.
117
+
118
+ rake gems:unpack
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ generators = %w(userify)
5
+
6
+ namespace :generator do
7
+ desc "Cleans up the test app before running the generator"
8
+ task :cleanup do
9
+ generators.each do |generator|
10
+ FileList["generators/#{generator}/templates/**/*.*"].each do |each|
11
+ file = "test/rails_root/#{each.gsub("generators/#{generator}/templates/",'')}"
12
+ File.delete(file) if File.exists?(file)
13
+ end
14
+ end
15
+
16
+ FileList["test/rails_root/db/**/*"].each do |each|
17
+ FileUtils.rm_rf(each)
18
+ end
19
+ FileUtils.rm_rf("test/rails_root/vendor/plugins/userify")
20
+ FileUtils.mkdir_p("test/rails_root/vendor/plugins")
21
+ userify_root = File.expand_path(File.dirname(__FILE__))
22
+ system("ln -s #{userify_root} test/rails_root/vendor/plugins/userify")
23
+ end
24
+
25
+ desc "Run the generator on the tests"
26
+ task :generate do
27
+ generators.each do |generator|
28
+ system "cd test/rails_root && ./script/generate #{generator} && rake db:migrate db:test:prepare"
29
+ end
30
+ end
31
+ end
32
+
33
+ desc "Run the test suite"
34
+ task :default => ['test:all', 'test:features']
35
+
36
+ gem_spec = Gem::Specification.new do |gem_spec|
37
+ gem_spec.name = "userify"
38
+ gem_spec.version = "0.1.1"
39
+ gem_spec.summary = "Super simple authentication system for Rails, using username, email and password."
40
+ gem_spec.email = "kenn.ejima <at> gmail.com"
41
+ gem_spec.homepage = "http://github.com/kenn/userify"
42
+ gem_spec.description = "Super simple authentication system for Rails, using username, email and password."
43
+ gem_spec.authors = ["Kenn Ejima"]
44
+ gem_spec.files = FileList["[A-Z]*", "{app,config,generators,lib,rails}/**/*"]
45
+ end
46
+
47
+ desc "Generate a gemspec file"
48
+ task :gemspec do
49
+ File.open("#{gem_spec.name}.gemspec", 'w') do |f|
50
+ f.write gem_spec.to_yaml
51
+ end
52
+ end
data/TODO.textile ADDED
@@ -0,0 +1,5 @@
1
+ h1. To-do
2
+
3
+ * Add tests
4
+ * Clean up dependencies
5
+ * Rework uid.rb
@@ -0,0 +1,143 @@
1
+ class Userify::UserController < ApplicationController
2
+ unloadable
3
+
4
+ before_filter :redirect_to_root, :except => :signout, :if => :signed_in?
5
+ filter_parameter_logging :password
6
+
7
+ before_filter :assign_user_from_token, :only => [ :activate, :reset ]
8
+ before_filter :forbid_non_existent_token, :only => [ :activate, :reset ]
9
+ before_filter :forbid_confirmed_user, :only => :activate
10
+
11
+ def signup
12
+ case request.method
13
+
14
+ when :get
15
+ @user = ::User.new(params[:signup])
16
+ render :template => 'user/signup'
17
+
18
+ when :post
19
+ @user = ::User.new(params[:signup])
20
+ if @user.save
21
+ ::UserifyMailer.deliver_confirmation @user
22
+ flash[:notice] = "You will receive an email within the next few minutes. " <<
23
+ "It contains instructions for confirming your account."
24
+ redirect_to url_after_signup
25
+ else
26
+ flash[:error] = generate_error_messages_for(@user)
27
+ redirect_to :back
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ def signin
34
+ case request.method
35
+
36
+ when :get
37
+ render :template => 'user/signin'
38
+
39
+ when :post
40
+ @user = ::User.authenticate(params[:signin][:email], params[:signin][:password])
41
+ if @user.nil?
42
+ flash[:error] = "Bad email or password."
43
+ redirect_to :back
44
+ else
45
+ if @user.email_confirmed?
46
+ if params[:signin] and params[:signin][:remember_me] == "1"
47
+ @user.remember_me!
48
+ cookies[:remember_token] = { :value => @user.token, :expires => @user.token_expires_at }
49
+ end
50
+ sign_in(@user)
51
+ flash[:notice] = "Signed in successfully."
52
+ redirect_back_or url_after_signin
53
+ else
54
+ ::UserifyMailer.deliver_confirmation(@user)
55
+ deny_access("User has not confirmed email. Confirmation email will be resent.")
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ def signout
63
+ current_user.forget_me! if current_user
64
+ cookies.delete :remember_token
65
+ reset_session
66
+ flash[:notice] = "You have been signed out."
67
+ redirect_to url_after_signout
68
+ end
69
+
70
+ def activate
71
+ @user.confirm_email!
72
+
73
+ sign_in(@user)
74
+ flash[:notice] = "Confirmed email and signed in."
75
+ redirect_to url_after_activate
76
+ end
77
+
78
+ def forgot
79
+ case request.method
80
+
81
+ when :get
82
+ render :template => 'user/forgot'
83
+
84
+ when :post
85
+ if user = ::User.find_by_email(params[:forgot][:email])
86
+ user.forgot_password!
87
+ ::UserifyMailer.deliver_reset_password user
88
+ flash[:notice] = "You will receive an email within the next few minutes. " <<
89
+ "It contains instructions for changing your password."
90
+ redirect_to url_after_forgot
91
+ else
92
+ flash[:error] = "Unknown email"
93
+ redirect_to :back
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ def reset
100
+ case request.method
101
+
102
+ when :get
103
+ render :template => 'user/reset'
104
+
105
+ when :post
106
+ if @user.update_password(params[:user][:password])
107
+ @user.confirm_email! unless @user.email_confirmed?
108
+ sign_in(@user)
109
+ flash[:notice] = "You have successfully reset your password."
110
+ redirect_to url_after_reset
111
+ else
112
+ redirect_to :back
113
+ end
114
+ end
115
+
116
+ end
117
+
118
+ protected
119
+ def url_after_signup; root_url; end
120
+ def url_after_signin; root_url; end
121
+ def url_after_signout; root_url; end
122
+ def url_after_activate; root_url; end
123
+ def url_after_forgot; root_url; end
124
+ def url_after_reset; root_url; end
125
+
126
+ def assign_user_from_token
127
+ raise ActionController::Forbidden, "missing token" if params[:token].blank?
128
+
129
+ @user = ::User.find_by_token(params[:token])
130
+ end
131
+
132
+ def forbid_non_existent_token
133
+ raise ActionController::Forbidden, "non-existent token" unless @user
134
+ end
135
+
136
+ def forbid_confirmed_user
137
+ raise ActionController::Forbidden, "confirmed user" if @user and @user.email_confirmed?
138
+ end
139
+
140
+ def generate_error_messages_for(obj)
141
+ render_to_string :partial => 'layouts/error_messages', :object => obj.errors.full_messages
142
+ end
143
+ end
@@ -0,0 +1,19 @@
1
+ class UserifyMailer < ActionMailer::Base
2
+
3
+ default_url_options[:host] = HOST
4
+
5
+ def reset_password(user)
6
+ from DO_NOT_REPLY
7
+ recipients user.email
8
+ subject "Reset your password"
9
+ body :user => user
10
+ end
11
+
12
+ def confirmation(user)
13
+ from DO_NOT_REPLY
14
+ recipients user.email
15
+ subject "Account confirmation"
16
+ body :user => user
17
+ end
18
+
19
+ end
@@ -0,0 +1,4 @@
1
+ %div= "#{pluralize error_messages.size, 'error'} occurred."
2
+ %ul
3
+ - error_messages.each do |error_message|
4
+ %li= error_message
@@ -0,0 +1,9 @@
1
+ !!!
2
+ %html{:xmlns => 'http://www.w3.org/1999/xhtml'}
3
+ %head
4
+ %title [Layout generated by userify]
5
+ %body
6
+ #flash
7
+ - flash.each do |key, value|
8
+ %div{ :class => "flash_#{key}" }= value
9
+ = yield
@@ -0,0 +1,10 @@
1
+ %h2 Forgot your password?
2
+
3
+ %p We will email you a link to change your password.
4
+
5
+ - form_for :forgot, :url => forgot_path do |form|
6
+ .text_field
7
+ = form.label :email, "Email address"
8
+ = form.text_field :email
9
+ .submit_field
10
+ = form.submit "Reset password", :disable_with => "Please wait..."
@@ -0,0 +1,12 @@
1
+ %h2 Reset your password
2
+
3
+ %p Your password has been reset. Choose a new password below.
4
+
5
+ = error_messages_for :user
6
+
7
+ - form_for :user, :url => reset_path(:token => @user.token) do |form|
8
+ .password_field
9
+ = form.label :password, "Choose password"
10
+ = form.password_field :password
11
+ .submit_field
12
+ = form.submit "Save this password", :disable_with => "Please wait..."
@@ -0,0 +1,20 @@
1
+ %h2 Sign in
2
+
3
+ - form_for :signin, :url => signin_path do |form|
4
+ .text_field
5
+ = form.label :email, "Email or username"
6
+ = form.text_field :email
7
+ .text_field
8
+ = form.label :password
9
+ = form.password_field :password
10
+ .text_field
11
+ = form.check_box :remember_me
12
+ = form.label :remember_me
13
+ .submit_field
14
+ = form.submit "Sign in", :disable_with => "Please wait..."
15
+
16
+ %ul
17
+ %li
18
+ = link_to "Sign up", signup_path
19
+ %li
20
+ = link_to "Forgot password?", forgot_path
@@ -0,0 +1,14 @@
1
+ %h2 Sign up
2
+
3
+ - form_for :signup, :url => signup_path do |form|
4
+ = form.error_messages
5
+ .text_field
6
+ = form.label :username
7
+ = form.text_field :username
8
+ .text_field
9
+ = form.label :email
10
+ = form.text_field :email
11
+ .password_field
12
+ = form.label :password
13
+ = form.password_field :password
14
+ = form.submit 'Sign up', :disable_with => 'Please wait...'
@@ -0,0 +1,4 @@
1
+ Welcome <%= @user.name %>!
2
+
3
+ To activate your account, you must click the link below:
4
+ <%= activate_url :token => @user.token %>
@@ -0,0 +1,6 @@
1
+ Someone, hopefully you, has requested that we send you a link to change your password.
2
+
3
+ Here's the link:
4
+ <%= reset_url :token => @user.token %>
5
+
6
+ If you didn't request this, ignore this email. Don't worry. Your password hasn't been changed.
@@ -0,0 +1,8 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.signup 'signup', :controller => 'userify/user', :action => 'signup'
3
+ map.signin 'signin', :controller => 'userify/user', :action => 'signin'
4
+ map.signout 'signout', :controller => 'userify/user', :action => 'signout'
5
+ map.activate 'activate/:token', :controller => 'userify/user', :action => 'activate'
6
+ map.forgot 'password/forgot', :controller => 'userify/user', :action => 'forgot'
7
+ map.reset 'password/reset/:token', :controller => 'userify/user', :action => 'reset'
8
+ end
@@ -0,0 +1 @@
1
+ script/generate userify
@@ -0,0 +1,56 @@
1
+ # Mostly pinched from http://github.com/ryanb/nifty-generators/tree/master
2
+
3
+ Rails::Generator::Commands::Base.class_eval do
4
+ def file_contains?(relative_destination, line)
5
+ File.read(destination_path(relative_destination)).include?(line)
6
+ end
7
+ end
8
+
9
+ Rails::Generator::Commands::Create.class_eval do
10
+ def route_name(name, path, route_options = {})
11
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
12
+
13
+ logger.route "map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
14
+ unless options[:pretend]
15
+ gsub_file_once 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
16
+ "#{match}\n map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
17
+ end
18
+ end
19
+ end
20
+
21
+ def insert_into(file, line)
22
+ logger.insert "#{line} into #{file}"
23
+ unless options[:pretend] || file_contains?(file, line)
24
+ gsub_file file, /^(class|module) .+$/ do |match|
25
+ "#{match}\n #{line}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ Rails::Generator::Commands::Destroy.class_eval do
32
+ def route_name(name, path, route_options = {})
33
+ look_for = "\n map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
34
+ logger.route "map.#{name} '#{path}', :controller => '#{route_options[:controller]}', :action => '#{route_options[:action]}'"
35
+ unless options[:pretend]
36
+ gsub_file 'config/routes.rb', /(#{look_for})/mi, ''
37
+ end
38
+ end
39
+
40
+ def insert_into(file, line)
41
+ logger.remove "#{line} from #{file}"
42
+ unless options[:pretend]
43
+ gsub_file file, "\n #{line}", ''
44
+ end
45
+ end
46
+ end
47
+
48
+ Rails::Generator::Commands::List.class_eval do
49
+ def route_name(name, path, options = {})
50
+ logger.route "map.#{name} '#{path}', :controller => '#{options[:controller]}', :action => '#{options[:action]}'"
51
+ end
52
+
53
+ def insert_into(file, line)
54
+ logger.insert "#{line} into #{file}"
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ Rails::Generator::Commands::Create.class_eval do
2
+ def rake_db_migrate
3
+ logger.rake "db:migrate"
4
+ unless system("rake db:migrate")
5
+ logger.rake "db:migrate failed. Rolling back"
6
+ command(:destroy).invoke!
7
+ end
8
+ end
9
+ end
10
+
11
+ Rails::Generator::Commands::Destroy.class_eval do
12
+ def rake_db_migrate
13
+ logger.rake "db:rollback"
14
+ system "rake db:rollback"
15
+ end
16
+ end
17
+
18
+ Rails::Generator::Commands::List.class_eval do
19
+ def rake_db_migrate
20
+ logger.rake "db:migrate"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+
2
+ *******************************************************************************
3
+
4
+ Ok, enough fancy automatic stuff. Time for some old school monkey copy-pasting.
5
+
6
+ 1. Define a HOST constant in your environments files.
7
+ In config/environments/test.rb and config/environments/development.rb it can be:
8
+
9
+ HOST = "localhost:3000"
10
+
11
+ In production.rb it must be the actual host your application is deployed to.
12
+ The constant is used by mailers to generate URLs in emails.
13
+
14
+ 2. In config/environment.rb:
15
+
16
+ DO_NOT_REPLY = "donotreply@example.com"
17
+
18
+ 3. Define root_url to *something* in your config/routes.rb:
19
+
20
+ map.root :controller => 'home'
21
+
22
+ *******************************************************************************
@@ -0,0 +1,36 @@
1
+ class UserifyCreateUsers < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ case connection.adapter_name
5
+ when 'MySQL'
6
+ execute "ALTER DATABASE #{connection.current_database} CHARACTER SET utf8 COLLATE utf8_bin"
7
+ no_case_collation = 'utf8_general_ci'
8
+ mysql = true
9
+ when 'SQLite'
10
+ # COLLATE BINARY by default
11
+ no_case_collation = 'NOCASE'
12
+ mysql = false
13
+ end
14
+
15
+ create_table :users do |t|
16
+ t.string :email, :limit => 60, :null => false
17
+ t.string :fullname, :limit => 60
18
+ t.string :encrypted_password, :limit => 40, :null => false
19
+ t.string :salt, :limit => 40, :null => false
20
+ t.string :token, :limit => 27
21
+ t.datetime :token_expires_at
22
+ t.boolean :email_confirmed, :default => false, :null => false
23
+ t.timestamps
24
+ end
25
+
26
+ execute "ALTER TABLE users ADD username VARCHAR(30) #{mysql ? 'CHARACTER SET utf8' : ''} COLLATE #{no_case_collation}"
27
+
28
+ add_index :users, :username, :unique => true
29
+ add_index :users, :email, :unique => true
30
+ add_index :users, :token, :unique => true
31
+ end
32
+
33
+ def self.down
34
+ drop_table :users
35
+ end
36
+ end
@@ -0,0 +1,67 @@
1
+ require 'securerandom'
2
+
3
+ # Usage:
4
+ #
5
+ # UID.new.to_s => "aWgEPTl1tmebfsQzFP4bxwgy80V"
6
+ # UID.new(5).to_s => "8FsD5"
7
+ # UID.new.to_i => 838068072552051698674007079806269848286804777409
8
+ # 1000000000.base62 => "15ftgG"
9
+ # "123abcABC".base62 => 225587272106046
10
+ #
11
+ # By default, UIDs generates random BASE62 string of 27 characters, which is safer than 160bit SHA-1.
12
+ #
13
+ # >> ('f'*40).hex
14
+ # => 1461501637330902918203684832716283019655932542975
15
+ # >> ('z'*27).base62
16
+ # => 2480707824361381849652170924082266893544595652607
17
+
18
+ class UID
19
+ def initialize(n=27)
20
+ max = ("Z"*n).base62
21
+ @value = SecureRandom.random_number(max)
22
+ end
23
+
24
+ def to_s
25
+ @value.base62
26
+ end
27
+
28
+ def to_i
29
+ @value
30
+ end
31
+
32
+ def to_hex
33
+ @value.to_s(16)
34
+ end
35
+ end
36
+
37
+ class String
38
+ BASE62_PRIMITIVES = {}.tap do |h|
39
+ (('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a).each_with_index {|e, i| h[e] = i }
40
+ end
41
+
42
+ def base62
43
+ i = 0
44
+ i_out = 0
45
+ self.split(//).reverse.each do |c|
46
+ place = BASE62_PRIMITIVES.size ** i
47
+ i_out += BASE62_PRIMITIVES[c] * place
48
+ i += 1
49
+ end
50
+ i_out
51
+ end
52
+ end
53
+
54
+
55
+ class Integer
56
+ BASE62_PRIMITIVES = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
57
+
58
+ def base62
59
+ number = self
60
+ result = ''
61
+ while(number != 0)
62
+ result = BASE62_PRIMITIVES[number % BASE62_PRIMITIVES.size ].to_s + result
63
+ number /= BASE62_PRIMITIVES.size
64
+ end
65
+ result
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ include Userify::User
3
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb")
2
+ require File.expand_path(File.dirname(__FILE__) + "/lib/rake_commands.rb")
3
+
4
+ class UserifyGenerator < Rails::Generator::Base
5
+
6
+ def manifest
7
+ record do |m|
8
+ m.insert_into "app/controllers/application_controller.rb",
9
+ "include Userify::Authentication"
10
+
11
+ user_model = "app/models/user.rb"
12
+ if File.exists?(user_model)
13
+ m.insert_into user_model, "include Userify::User"
14
+ else
15
+ m.directory File.join("app", "models")
16
+ m.file "user.rb", user_model
17
+ end
18
+
19
+ m.directory File.join("lib", "userify")
20
+ m.file "uid.rb", "lib/userify/uid.rb", :collision => :ask
21
+
22
+ m.migration_template "migrations/create_users.rb",
23
+ 'db/migrate',
24
+ :migration_file_name => "userify_create_users"
25
+
26
+ m.readme "README"
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,76 @@
1
+ module Userify
2
+ module Authentication
3
+
4
+ def self.included(controller)
5
+ controller.send(:include, InstanceMethods)
6
+
7
+ controller.class_eval do
8
+ helper_method :current_user
9
+ helper_method :signed_in?
10
+
11
+ hide_action :current_user, :signed_in?
12
+ end
13
+ end
14
+
15
+ module InstanceMethods
16
+ def current_user
17
+ @_current_user ||= (user_from_cookie or user_from_session)
18
+ end
19
+
20
+ def signed_in?
21
+ ! current_user.nil?
22
+ end
23
+
24
+ protected
25
+
26
+ def authenticate
27
+ deny_access unless signed_in?
28
+ end
29
+
30
+ def user_from_session
31
+ if session[:user_id]
32
+ return nil unless user = ::User.find_by_id(session[:user_id])
33
+ return user if user.email_confirmed?
34
+ end
35
+ end
36
+
37
+ def user_from_cookie
38
+ if token = cookies[:remember_token]
39
+ return nil unless user = ::User.find_by_token(token)
40
+ return user if user.remember?
41
+ end
42
+ end
43
+
44
+ def sign_in(user)
45
+ if user
46
+ session[:user_id] = user.id
47
+ end
48
+ end
49
+
50
+ def redirect_back_or(default)
51
+ session[:return_to] ||= params[:return_to]
52
+ if session[:return_to]
53
+ redirect_to(session[:return_to])
54
+ else
55
+ redirect_to(default)
56
+ end
57
+ session[:return_to] = nil
58
+ end
59
+
60
+ def redirect_to_root
61
+ redirect_to root_url
62
+ end
63
+
64
+ def store_location
65
+ session[:return_to] = request.request_uri if request.get?
66
+ end
67
+
68
+ def deny_access(flash_message = nil, opts = {})
69
+ store_location
70
+ flash[:failure] = flash_message if flash_message
71
+ redirect_to signin_url
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,4 @@
1
+ module ActionController
2
+ class Forbidden < StandardError
3
+ end
4
+ end
@@ -0,0 +1 @@
1
+ ActionController::Base.rescue_responses.update('ActionController::Forbidden' => :forbidden)
@@ -0,0 +1,129 @@
1
+ require 'digest/sha1'
2
+
3
+ module Userify
4
+ module User
5
+
6
+ def self.included(model)
7
+ require 'userify/uid'
8
+
9
+ model.extend ClassMethods
10
+ model.send(:include, InstanceMethods)
11
+
12
+ model.class_eval do
13
+ attr_accessible :username, :email, :password
14
+ attr_accessor :password
15
+
16
+ before_validation :normalize_email
17
+
18
+ validates_presence_of :username
19
+ validates_uniqueness_of :username
20
+ validates_presence_of :email
21
+ validates_uniqueness_of :email, :case_sensitive => false
22
+ validates_format_of :email, :with => /.+@.+\..+/
23
+ validates_presence_of :password, :if => :password_required?
24
+
25
+ before_save :initialize_salt, :encrypt_password, :initialize_token
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+ def name
31
+ self.fullname or self.username
32
+ end
33
+
34
+ def authenticated?(password)
35
+ encrypted_password == encrypt(password)
36
+ end
37
+
38
+ def encrypt(string)
39
+ generate_hash("--#{salt}--#{string}--")
40
+ end
41
+
42
+ def remember?
43
+ token_expires_at and Time.now.utc < token_expires_at
44
+ end
45
+
46
+ def remember_me!
47
+ remember_me_until! 6.months.from_now.utc
48
+ end
49
+
50
+ def forget_me!
51
+ clear_token
52
+ save(false)
53
+ end
54
+
55
+ def confirm_email!
56
+ self.email_confirmed = true
57
+ self.token = nil
58
+ save(false)
59
+ end
60
+
61
+ def forgot_password!
62
+ generate_token
63
+ save(false)
64
+ end
65
+
66
+ def update_password(new_password)
67
+ self.password = new_password
68
+ clear_token if valid?
69
+ save
70
+ end
71
+
72
+ protected
73
+
74
+ def generate_hash(string)
75
+ Digest::SHA1.hexdigest(string)
76
+ end
77
+
78
+ def generate_random_base62(n=27)
79
+ UID.new(n).to_s
80
+ end
81
+
82
+ def normalize_email
83
+ self.email.downcase!
84
+ return true
85
+ end
86
+
87
+ def initialize_salt
88
+ self.salt = generate_hash("--#{Time.now.utc.to_s}--#{password}--") if new_record?
89
+ end
90
+
91
+ def encrypt_password
92
+ return if password.blank?
93
+ self.encrypted_password = encrypt(password)
94
+ end
95
+
96
+ def generate_token
97
+ self.token = generate_random_base62
98
+ self.token_expires_at = nil
99
+ end
100
+
101
+ def clear_token
102
+ self.token = nil
103
+ self.token_expires_at = nil
104
+ end
105
+
106
+ def initialize_token
107
+ generate_token if new_record?
108
+ end
109
+
110
+ def password_required?
111
+ encrypted_password.blank? or !password.blank?
112
+ end
113
+
114
+ def remember_me_until!(time)
115
+ self.token_expires_at = time
116
+ self.token = generate_random_base62
117
+ save(false)
118
+ end
119
+ end
120
+
121
+ module ClassMethods
122
+ def authenticate(email_or_username, password)
123
+ user = find(:first, :conditions => ['username = ? OR email = ?', email_or_username, email_or_username])
124
+ user && user.authenticated?(password) ? user : nil
125
+ end
126
+ end
127
+
128
+ end
129
+ end
data/lib/userify.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'userify/extensions/errors'
2
+ require 'userify/extensions/rescue'
3
+ require 'userify/authentication'
4
+ require 'userify/user'
5
+
6
+ class ActionController::Routing::RouteSet
7
+ def load_routes_with_userify!
8
+ userify_routes = File.join(File.dirname(__FILE__), *%w[.. config userify_routes.rb])
9
+ add_configuration_file(userify_routes) unless configuration_files.include? userify_routes
10
+ load_routes_without_userify!
11
+ end
12
+
13
+ alias_method_chain :load_routes!, :userify
14
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'userify'
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kenn-userify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Kenn Ejima
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-12 08:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Super simple authentication system for Rails, using username, email and password.
17
+ email: kenn.ejima <at> gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - CHANGELOG.textile
26
+ - LICENSE
27
+ - Rakefile
28
+ - README.textile
29
+ - TODO.textile
30
+ - app/controllers
31
+ - app/controllers/userify
32
+ - app/controllers/userify/user_controller.rb
33
+ - app/models
34
+ - app/models/userify_mailer.rb
35
+ - app/views
36
+ - app/views/layouts
37
+ - app/views/layouts/_error_messages.html.haml
38
+ - app/views/layouts/application.html.haml
39
+ - app/views/user
40
+ - app/views/user/forgot.html.haml
41
+ - app/views/user/reset.html.haml
42
+ - app/views/user/signin.html.haml
43
+ - app/views/user/signup.html.haml
44
+ - app/views/userify_mailer
45
+ - app/views/userify_mailer/confirmation.html.erb
46
+ - app/views/userify_mailer/reset_password.html.erb
47
+ - config/userify_routes.rb
48
+ - generators/userify
49
+ - generators/userify/lib
50
+ - generators/userify/lib/insert_commands.rb
51
+ - generators/userify/lib/rake_commands.rb
52
+ - generators/userify/templates
53
+ - generators/userify/templates/migrations
54
+ - generators/userify/templates/migrations/create_users.rb
55
+ - generators/userify/templates/README
56
+ - generators/userify/templates/uid.rb
57
+ - generators/userify/templates/user.rb
58
+ - generators/userify/USAGE
59
+ - generators/userify/userify_generator.rb
60
+ - lib/userify
61
+ - lib/userify/authentication.rb
62
+ - lib/userify/extensions
63
+ - lib/userify/extensions/errors.rb
64
+ - lib/userify/extensions/rescue.rb
65
+ - lib/userify/user.rb
66
+ - lib/userify.rb
67
+ - rails/init.rb
68
+ has_rdoc: true
69
+ homepage: http://github.com/kenn/userify
70
+ post_install_message:
71
+ rdoc_options: []
72
+
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.2.0
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Super simple authentication system for Rails, using username, email and password.
94
+ test_files: []
95
+