wristband 0.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.
- data/.DS_Store +0 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +88 -0
- data/README.md +113 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +10 -0
- data/app/controllers/sessions_controller.rb +34 -0
- data/app/controllers/users_controller.rb +34 -0
- data/app/mailers/user_mailer.rb +33 -0
- data/app/models/session_user.rb +59 -0
- data/app/models/user.rb +52 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/app/views/sessions/new.html.haml +15 -0
- data/app/views/user_mailer/email_verification.text.html.rhtml +7 -0
- data/app/views/user_mailer/email_verification.text.plain.rhtml +9 -0
- data/app/views/user_mailer/forgot_password.text.html.rhtml +10 -0
- data/app/views/user_mailer/forgot_password.text.plain.rhtml +10 -0
- data/app/views/users/show.html.haml +6 -0
- data/config/application.rb +15 -0
- data/config/boot.rb +13 -0
- data/config/database.yml +16 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +26 -0
- data/config/environments/production.rb +49 -0
- data/config/environments/test.rb +35 -0
- data/config/initializers/wristband.rb +1 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +11 -0
- data/config.ru +4 -0
- data/db/migrate/01_create_users_table.rb +26 -0
- data/db/schema.rb +27 -0
- data/lib/generators/wristband/.DS_Store +0 -0
- data/lib/generators/wristband/wristband_generator.rb +56 -0
- data/lib/wristband/application_extensions.rb +67 -0
- data/lib/wristband/authority_check.rb +160 -0
- data/lib/wristband/support.rb +28 -0
- data/lib/wristband/user_extensions.rb +89 -0
- data/lib/wristband.rb +86 -0
- data/public/robots.txt +5 -0
- data/script/rails +6 -0
- data/test/dummy/user.rb +13 -0
- data/test/test_helper.rb +78 -0
- data/test/unit/has_authorities_test.rb +86 -0
- data/test/unit/session_user_test.rb +8 -0
- data/test/unit/user_test.rb +105 -0
- data/wristband.gemspec +107 -0
- metadata +209 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
Wristband::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/application.rb
|
3
|
+
|
4
|
+
# The test environment is used exclusively to run your application's
|
5
|
+
# test suite. You never need to work with it otherwise. Remember that
|
6
|
+
# your test database is "scratch space" for the test suite and is wiped
|
7
|
+
# and recreated between test runs. Don't rely on the data there!
|
8
|
+
config.cache_classes = true
|
9
|
+
|
10
|
+
# Log error messages when you accidentally call methods on nil.
|
11
|
+
config.whiny_nils = true
|
12
|
+
|
13
|
+
# Show full error reports and disable caching
|
14
|
+
config.consider_all_requests_local = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
# Raise exceptions instead of rendering exception templates
|
18
|
+
config.action_dispatch.show_exceptions = false
|
19
|
+
|
20
|
+
# Disable request forgery protection in test environment
|
21
|
+
config.action_controller.allow_forgery_protection = false
|
22
|
+
|
23
|
+
# Tell Action Mailer not to deliver emails to the real world.
|
24
|
+
# The :test delivery method accumulates sent emails in the
|
25
|
+
# ActionMailer::Base.deliveries array.
|
26
|
+
config.action_mailer.delivery_method = :test
|
27
|
+
|
28
|
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
29
|
+
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
30
|
+
# like if you have constraints or database-specific column types
|
31
|
+
# config.active_record.schema_format = :sql
|
32
|
+
|
33
|
+
# Print deprecation notices to the stderr
|
34
|
+
config.active_support.deprecation = :stderr
|
35
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'wristband'
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Rails::Application.routes.draw do
|
2
|
+
|
3
|
+
post '/login', :to => 'sessions#create'
|
4
|
+
get '/login', :to => 'sessions#new'
|
5
|
+
get '/logout', :to => 'sessions#destroy'
|
6
|
+
match '/forgot_password', :to => 'sessions#forgot_password'
|
7
|
+
|
8
|
+
resources :users
|
9
|
+
match '/register', :to => 'users#new'
|
10
|
+
|
11
|
+
end
|
data/config.ru
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateUsersTable < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :users do |t|
|
4
|
+
t.string :email
|
5
|
+
t.string :email_validation_key
|
6
|
+
t.datetime :validated_at
|
7
|
+
t.string :password_crypt, :limit => 40
|
8
|
+
t.string :password_salt, :limit => 40
|
9
|
+
t.string :remember_token
|
10
|
+
t.string :role
|
11
|
+
t.timestamps
|
12
|
+
# --- Other useful fields ---
|
13
|
+
# t.string :first_name
|
14
|
+
# t.string :last_name
|
15
|
+
# t.string :address
|
16
|
+
# t.string :city
|
17
|
+
# t.string :state
|
18
|
+
# t.string :zip
|
19
|
+
# t.string :ip
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.down
|
24
|
+
drop_table :users
|
25
|
+
end
|
26
|
+
end
|
data/db/schema.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended to check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(:version => 1) do
|
14
|
+
|
15
|
+
create_table "users", :force => true do |t|
|
16
|
+
t.string "email"
|
17
|
+
t.string "email_validation_key"
|
18
|
+
t.datetime "validated_at"
|
19
|
+
t.string "password_crypt", :limit => 40
|
20
|
+
t.string "password_salt", :limit => 40
|
21
|
+
t.string "remember_token"
|
22
|
+
t.string "role"
|
23
|
+
t.datetime "created_at"
|
24
|
+
t.datetime "updated_at"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
Binary file
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class WristbandGenerator < Rails::Generators::Base
|
2
|
+
include Rails::Generators::Migration
|
3
|
+
|
4
|
+
source_root File.expand_path('../../../..', __FILE__)
|
5
|
+
|
6
|
+
def generate_models
|
7
|
+
copy_file 'app/models/user.rb', 'app/models/user.rb'
|
8
|
+
copy_file 'app/models/session_user.rb', 'app/models/session_user.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_controllers
|
12
|
+
copy_file 'app/controllers/users_controller.rb', 'app/controllers/users_controller.rb'
|
13
|
+
copy_file 'app/controllers/sessions_controller.rb', 'app/controllers/sessions_controller.rb'
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_views
|
17
|
+
copy_file 'app/views/users/show.html.haml', 'app/views/users/show.html.haml'
|
18
|
+
copy_file 'app/views/sessions/new.html.haml', 'app/views/sessions/new.html.haml'
|
19
|
+
|
20
|
+
end
|
21
|
+
def generate_user_mailer
|
22
|
+
copy_file 'app/mailers/user_mailer.rb', 'app/mailers/user_mailer.rb'
|
23
|
+
copy_file 'app/views/user_mailer/forgot_password.text.html.rhtml', 'app/views/user_mailer/forgot_password.text.html.rhtml'
|
24
|
+
copy_file 'app/views/user_mailer/forgot_password.text.plain.rhtml', 'app/views/user_mailer/forgot_password.text.plain.rhtml'
|
25
|
+
copy_file 'app/views/user_mailer/email_verification.text.html.rhtml', 'app/views/user_mailer/email_verification.text.html.rhtml'
|
26
|
+
copy_file 'app/views/user_mailer/email_verification.text.plain.rhtml', 'app/views/user_mailer/email_verification.text.plain.rhtml'
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_migration
|
30
|
+
destination = File.expand_path('db/migrate/01_create_users_table.rb', self.destination_root)
|
31
|
+
migration_dir = File.dirname(destination)
|
32
|
+
destination = self.class.migration_exists?(migration_dir, 'create_users_table')
|
33
|
+
|
34
|
+
if destination
|
35
|
+
puts "\e[0m\e[31mFound existing create_users_table.rb migration. Remove it if you want to regenerate.\e[0m"
|
36
|
+
else
|
37
|
+
migration_template 'db/migrate/01_create_users_table.rb', 'db/migrate/create_users_table.rb'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.next_migration_number(dirname)
|
42
|
+
orm = Rails.configuration.generators.options[:rails][:orm]
|
43
|
+
require "rails/generators/#{orm}"
|
44
|
+
"#{orm.to_s.camelize}::Generators::Base".constantize.next_migration_number(dirname)
|
45
|
+
end
|
46
|
+
|
47
|
+
# def generate_routes
|
48
|
+
# route "get '/login', :to => 'sessions#new'"
|
49
|
+
# route "post '/login', :to => 'sessions#create'"
|
50
|
+
# route "get '/logout', :to => 'sessions#destroy'"
|
51
|
+
# route "match '/forgot_password', :to => 'users#forgot_password'"
|
52
|
+
# route "resources :users"
|
53
|
+
# route "match '/register', :to => 'users#new'"
|
54
|
+
# end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Wristband
|
2
|
+
module ApplicationExtensions
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:extend, Wristband::ApplicationExtensions::ClassMethods)
|
5
|
+
base.send(:include, Wristband::ApplicationExtensions::InstanceMethods)
|
6
|
+
base.send(:helper_method, :logged_in?, :current_user) if base.respond_to?(:helper_method)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# ...
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def login(session_user)
|
15
|
+
login_as_user(session_user.user, session_user.remember_me)
|
16
|
+
end
|
17
|
+
|
18
|
+
def login_as_user(user, remember_me=false)
|
19
|
+
self.current_user = user
|
20
|
+
if remember_me
|
21
|
+
token = Support.encrypt_with_salt(user.id.to_s, Time.now.to_f.to_s)
|
22
|
+
cookies[:login_token] = { :value => token, :expires => 2.weeks.from_now.utc }
|
23
|
+
user.update_attribute(:remember_token, token)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Logs a user out and deletes the remember_token.
|
28
|
+
def logout
|
29
|
+
current_user.update_attribute(:remember_token, nil) if current_user
|
30
|
+
cookies.delete(:login_token)
|
31
|
+
reset_session
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns true if a user is logged in
|
35
|
+
def logged_in?
|
36
|
+
!!current_user
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the current user in session. Use this on your views and controllers.
|
40
|
+
def current_user
|
41
|
+
@current_user ||= (session[:user_id] and ::User.find_by_id(session[:user_id]))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets the current user in session
|
45
|
+
def current_user=(user)
|
46
|
+
@current_user = user
|
47
|
+
session[:user_id] = (user and user.id)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Logs a user automatically from his cookie
|
51
|
+
#
|
52
|
+
# You can use this function as a before filter on your controllers.
|
53
|
+
def login_from_cookie
|
54
|
+
return if (logged_in? or !cookies[:login_token])
|
55
|
+
self.current_user = ::User.find_by_remember_token(cookies[:login_token])
|
56
|
+
end
|
57
|
+
|
58
|
+
# You can use this function as a before filter on your controllers that require autentication.
|
59
|
+
#
|
60
|
+
# If the user is not logged in +respond_not_logged_in+ will be called.
|
61
|
+
def login_required
|
62
|
+
logged_in?
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# = AuthorityCheck
|
2
|
+
# The different user authorities are defined in a separate class so as to reduce
|
3
|
+
# clutter in the User model itself.
|
4
|
+
#
|
5
|
+
# class User < ActiveRecord::Base
|
6
|
+
# wristband :has_authorities => true
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# This will refer to the class UserAuthorityCheck for all authority tests, but the
|
10
|
+
# name of this module can be defined as required:
|
11
|
+
#
|
12
|
+
# class User < ActiveRecord::Base
|
13
|
+
# has_authorities => :permissions
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# That would reference the class UserPermissions instead for all tests.
|
17
|
+
#
|
18
|
+
# A sample authority checking class is defined as:
|
19
|
+
#
|
20
|
+
# class UserAuthorityCheck < AuthorityCheck
|
21
|
+
# def wear_shoes?
|
22
|
+
# unless (@user.name.match(/^a/i))
|
23
|
+
# fail!("Only people with names that start with 'A' can wear shoes.")
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# <b>Note the syntax: </b>All authority checks are defined as ending with a trailing question mark
|
29
|
+
# character.
|
30
|
+
#
|
31
|
+
# A check is considered to have passed if
|
32
|
+
# * a call to <tt>allow!</tt> has been made, or
|
33
|
+
# * no calls to <tt>fail!</tt> have been made.
|
34
|
+
#
|
35
|
+
# Once defined, the user authorities are checked via a call to a User instance:
|
36
|
+
#
|
37
|
+
# user.has_authority_to?(:wear_shoes)
|
38
|
+
#
|
39
|
+
# While the <tt>has_authority_to?</tt> method returns only true or false, a call to
|
40
|
+
# <tt>has_objections_to?</tt> will return nil on success or any error messages if there
|
41
|
+
# is a failure.
|
42
|
+
#
|
43
|
+
#
|
44
|
+
# ==== Passing parameters to the authority methods
|
45
|
+
#
|
46
|
+
# Any call to these tests may include options in the form of a Hash:
|
47
|
+
#
|
48
|
+
# user.has_authority_to?(:send_message, :text => "Foo bar")
|
49
|
+
#
|
50
|
+
# These options can be acted upon within the authority check:
|
51
|
+
#
|
52
|
+
# def send_message?
|
53
|
+
# if (options[:text].match(/foo/i))
|
54
|
+
# fail!("Messages may not contain forbidden words.")
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# ==== Before chains
|
59
|
+
#
|
60
|
+
# In addition to defining straight tests, a chain can be defined to run before
|
61
|
+
# any of the tests themselves. This allows certain calls to be over-ruled. For
|
62
|
+
# example:
|
63
|
+
#
|
64
|
+
# before_check :allow_if_admin!
|
65
|
+
#
|
66
|
+
# def allow_if_admin!
|
67
|
+
# if (@user.is_admin?)
|
68
|
+
# allow!
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# In this case, the <tt>allow_if_admin!</tt> method will be called before any checks are performed. If
|
73
|
+
# the <tt>allow!</tt> method is executed, all subsequent tests are halted and the check
|
74
|
+
# is considered to have passed.
|
75
|
+
|
76
|
+
class AuthorityCheck
|
77
|
+
attr_accessor :user
|
78
|
+
attr_accessor :options
|
79
|
+
|
80
|
+
def initialize(user, test_method, options = { })
|
81
|
+
self.user = user
|
82
|
+
self.options = options
|
83
|
+
|
84
|
+
@test_method = "#{test_method}?".to_sym
|
85
|
+
|
86
|
+
@result = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# Checkes if the user is allowed to do something.
|
90
|
+
# Returns <tt>true</tt> or <tt>false</tt>
|
91
|
+
def allowed_to?
|
92
|
+
execute_tests!
|
93
|
+
|
94
|
+
# Either explicitly allowed (true) or not given any reasons as to why
|
95
|
+
# not (nil, empty)
|
96
|
+
(@result === true or (@result === nil and @reasons.blank?)) ? true : false
|
97
|
+
end
|
98
|
+
|
99
|
+
def denied_for_reasons
|
100
|
+
@reasons = [ ]
|
101
|
+
|
102
|
+
allowed_to? ? nil : @reasons
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
class << self
|
107
|
+
def check_chain
|
108
|
+
@check_chain ||= [ ]
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_chain=(value)
|
112
|
+
@check_chain = value
|
113
|
+
end
|
114
|
+
|
115
|
+
def before_check(method, options = { })
|
116
|
+
self.check_chain += remap_chain_methods([ method ])
|
117
|
+
end
|
118
|
+
|
119
|
+
def check_alias_as(original, *aliases)
|
120
|
+
aliases.flatten.each do |alias_name|
|
121
|
+
alias_method alias_name, original
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
protected
|
129
|
+
def allow!
|
130
|
+
@result = true
|
131
|
+
end
|
132
|
+
|
133
|
+
def fail!(message)
|
134
|
+
if (@reasons and message)
|
135
|
+
@reasons << message
|
136
|
+
end
|
137
|
+
|
138
|
+
@result = false
|
139
|
+
end
|
140
|
+
|
141
|
+
def execute_tests!
|
142
|
+
self.class.check_chain.each do |method|
|
143
|
+
case (method)
|
144
|
+
when Symbol, String
|
145
|
+
send(method)
|
146
|
+
else
|
147
|
+
method.call
|
148
|
+
end
|
149
|
+
|
150
|
+
# Stop evaluating if this test called allow!
|
151
|
+
return if (@result === true)
|
152
|
+
end
|
153
|
+
|
154
|
+
self.send(@test_method)
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.remap_chain_methods(methods)
|
158
|
+
methods
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Wristband
|
2
|
+
module Support
|
3
|
+
CONSONANTS = %w( b c d f g h j k l m n p qu r s t v w x z ch cr fr nd ng nk nt ph pr rd sh sl sp st th tr )
|
4
|
+
VOWELS = %w( a e i o u y )
|
5
|
+
|
6
|
+
def random_string(length = 8)
|
7
|
+
(1 .. length).collect { |n|
|
8
|
+
(n % 2 != 0) ? CONSONANTS[rand(CONSONANTS.size)] : VOWELS[rand(VOWELS.size)]
|
9
|
+
}.to_s[0, length]
|
10
|
+
end
|
11
|
+
module_function :random_string
|
12
|
+
|
13
|
+
def encrypt_with_salt(password, salt)
|
14
|
+
return password unless (salt and !salt.empty?)
|
15
|
+
|
16
|
+
Digest::SHA1.hexdigest([ password, salt ].to_s)
|
17
|
+
end
|
18
|
+
module_function :encrypt_with_salt
|
19
|
+
|
20
|
+
def random_salt(length = nil)
|
21
|
+
salt = Digest::SHA1.hexdigest([ rand, rand, random_string(64), rand, rand ].to_s)
|
22
|
+
|
23
|
+
length ? salt[0, length] : salt
|
24
|
+
end
|
25
|
+
module_function :random_salt
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Wristband
|
2
|
+
module UserExtensions
|
3
|
+
def self.included(base)
|
4
|
+
base.send(:extend, Wristband::UserExtensions::ClassMethods)
|
5
|
+
base.send(:include, Wristband::UserExtensions::InstanceMethods)
|
6
|
+
base.send(:extend, Wristband::Support)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def authenticate(email, password)
|
11
|
+
self.execute_authentication_chain(self, self.wristband[:before_authentication_chain]) == false and return
|
12
|
+
user = nil
|
13
|
+
wristband[:login_with_fields].find do |field|
|
14
|
+
user = send("find_by_#{field}", email)
|
15
|
+
end
|
16
|
+
(user and user.password_match?(password)) || return
|
17
|
+
self.execute_authentication_chain(user, self.wristband[:after_authentication_chain]) == false and return
|
18
|
+
user
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute_authentication_chain(object, list)
|
22
|
+
list.each do |func|
|
23
|
+
case func
|
24
|
+
when Symbol,String
|
25
|
+
object.send(func) == false and return false
|
26
|
+
when Proc
|
27
|
+
func.call(object) == false and return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def verify_email!(email_validation_key)
|
33
|
+
if user = find_by_email_validation_key(email_validation_key)
|
34
|
+
user.update_attribute(:validated_at, Time.now.to_s(:db))
|
35
|
+
user
|
36
|
+
else
|
37
|
+
raise UserVerificationError, 'We were not able to verify your account or it may have been verified already. Please contact us for assistance.'.t
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def roles_for_select
|
42
|
+
self.class.wristband[:roles].collect{ |k| [ k.to_s.titleize, k.to_s] }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
module InstanceMethods
|
48
|
+
|
49
|
+
def has_authority_to?(action, options = { })
|
50
|
+
self.class.wristband[:authority_class].new(self, action, options).allowed_to?
|
51
|
+
end
|
52
|
+
|
53
|
+
def has_objections_to?(action, options = { })
|
54
|
+
self.class.wristband[:authority_class].new(self, action, options).denied_for_reasons
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize_salt
|
58
|
+
self.password_salt = Wristband::Support.random_salt
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize_token
|
62
|
+
self.remember_token = Wristband::Support.random_salt(16)
|
63
|
+
end
|
64
|
+
|
65
|
+
def encrypt_password
|
66
|
+
initialize_salt if new_record?
|
67
|
+
return if self.password.blank?
|
68
|
+
self.send("#{self.class.wristband[:password_column]}=", Wristband::Support.encrypt_with_salt(self.password, self.password_salt))
|
69
|
+
end
|
70
|
+
|
71
|
+
def password_match?(string)
|
72
|
+
self.send(self.class.wristband[:password_column]) == Wristband::Support.encrypt_with_salt(string, self.password_salt)
|
73
|
+
end
|
74
|
+
|
75
|
+
def password_crypted?
|
76
|
+
self.password_salt and !self.password_salt.empty?
|
77
|
+
end
|
78
|
+
|
79
|
+
def password_crypt=(value)
|
80
|
+
if (value != read_attribute(:password_crypt))
|
81
|
+
initialize_token
|
82
|
+
end
|
83
|
+
|
84
|
+
write_attribute(:password_crypt, value)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/wristband.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'wristband/user_extensions'
|
2
|
+
require 'wristband/support'
|
3
|
+
require 'wristband/application_extensions'
|
4
|
+
require 'wristband/authority_check'
|
5
|
+
|
6
|
+
module Wristband
|
7
|
+
|
8
|
+
VERSION = "1.0.0"
|
9
|
+
|
10
|
+
class Engine < ::Rails::Engine; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def included base #:nodoc:
|
14
|
+
base.extend ClassMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def wristband(options={})
|
20
|
+
options[:login_with] ||= [:email]
|
21
|
+
options[:before_authentication] ||= []
|
22
|
+
options[:after_authentication] ||= []
|
23
|
+
options[:has_authorities] ||= false
|
24
|
+
options[:roles] ||= []
|
25
|
+
|
26
|
+
class_eval do
|
27
|
+
include Wristband::UserExtensions
|
28
|
+
|
29
|
+
options[:password_column] ||= :password_crypt
|
30
|
+
|
31
|
+
# These two are used on the login form
|
32
|
+
attr_accessor :password
|
33
|
+
attr_accessor :password_confirmation
|
34
|
+
|
35
|
+
before_save :encrypt_password
|
36
|
+
|
37
|
+
# Add roles
|
38
|
+
unless options[:roles].blank?
|
39
|
+
options[:roles].each do |role|
|
40
|
+
define_method "is_#{role}?" do
|
41
|
+
self.role.to_s == role.to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
attr_accessor :wristband
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
self.wristband = {
|
52
|
+
:login_with_fields => [options[:login_with]].flatten,
|
53
|
+
:before_authentication_chain => [options[:before_authentication]].flatten,
|
54
|
+
:after_authentication_chain => [options[:after_authentication]].flatten,
|
55
|
+
:password_column => options[:password_column],
|
56
|
+
:roles => options[:roles]
|
57
|
+
}
|
58
|
+
|
59
|
+
if options[:has_authorities]
|
60
|
+
self.wristband[:authority_class] = UtilityMethods.interpret_class_specification(self, options[:has_authorities])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module UtilityMethods
|
66
|
+
def self.interpret_class_specification(model_class, with_class)
|
67
|
+
case (with_class)
|
68
|
+
when Symbol
|
69
|
+
"#{model_class.class_name}#{with_class.to_s.camelcase}".constantize
|
70
|
+
when String
|
71
|
+
with_class.constantize
|
72
|
+
when true
|
73
|
+
"#{model_class.name}AuthorityCheck".constantize
|
74
|
+
else
|
75
|
+
with_class
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
ActiveRecord::Base.send(:extend, Wristband::ClassMethods)
|
84
|
+
ActionController::Base.send(:include, Wristband::ApplicationExtensions)
|
85
|
+
|
86
|
+
class UserVerificationError < StandardError; end
|
data/public/robots.txt
ADDED
data/script/rails
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
5
|
+
require File.expand_path('../../config/boot', __FILE__)
|
6
|
+
require 'rails/commands'
|