ae_users 0.6.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/README +47 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/ae_users.gemspec +117 -0
- data/app/controllers/account_controller.rb +167 -0
- data/app/controllers/auth_controller.rb +202 -0
- data/app/controllers/permission_controller.rb +172 -0
- data/app/helpers/account_helper.rb +2 -0
- data/app/helpers/auth_helper.rb +5 -0
- data/app/helpers/permission_helper.rb +2 -0
- data/app/models/account.rb +50 -0
- data/app/models/auth_notifier.rb +34 -0
- data/app/models/auth_ticket.rb +39 -0
- data/app/models/email_address.rb +17 -0
- data/app/models/login.rb +23 -0
- data/app/models/open_id_identity.rb +5 -0
- data/app/models/permission.rb +57 -0
- data/app/models/person.rb +156 -0
- data/app/models/role.rb +7 -0
- data/app/views/account/_personal_info.rhtml +35 -0
- data/app/views/account/_procon_profile.rhtml +3 -0
- data/app/views/account/_signup_form.html.erb +39 -0
- data/app/views/account/activate.rhtml +6 -0
- data/app/views/account/activation_error.rhtml +11 -0
- data/app/views/account/change_password.rhtml +3 -0
- data/app/views/account/edit_profile.rhtml +117 -0
- data/app/views/account/signup.rhtml +9 -0
- data/app/views/account/signup_noactivation.rhtml +7 -0
- data/app/views/account/signup_success.rhtml +8 -0
- data/app/views/auth/_auth_form.rhtml +54 -0
- data/app/views/auth/_forgot_form.html.erb +12 -0
- data/app/views/auth/_mini_auth_form.rhtml +17 -0
- data/app/views/auth/_openid_auth_form.html.erb +14 -0
- data/app/views/auth/_other_login_options.html.erb +24 -0
- data/app/views/auth/auth_form.js.erb +63 -0
- data/app/views/auth/forgot.rhtml +3 -0
- data/app/views/auth/forgot_form.rhtml +6 -0
- data/app/views/auth/index.css.erb +23 -0
- data/app/views/auth/login.rhtml +6 -0
- data/app/views/auth/needs_activation.rhtml +6 -0
- data/app/views/auth/needs_person.html.erb +32 -0
- data/app/views/auth/needs_profile.rhtml +14 -0
- data/app/views/auth/openid_login.html.erb +6 -0
- data/app/views/auth/resend_activation.rhtml +3 -0
- data/app/views/auth_notifier/account_activation.rhtml +13 -0
- data/app/views/auth_notifier/generated_password.rhtml +10 -0
- data/app/views/permission/_add_grantee.rhtml +47 -0
- data/app/views/permission/_role_member.rhtml +8 -0
- data/app/views/permission/_show.rhtml +81 -0
- data/app/views/permission/_userpicker.rhtml +0 -0
- data/app/views/permission/add_role_member.rhtml +3 -0
- data/app/views/permission/admin.rhtml +45 -0
- data/app/views/permission/edit.rhtml +9 -0
- data/app/views/permission/edit_role.rhtml +63 -0
- data/app/views/permission/grant.rhtml +10 -0
- data/db/migrate/002_create_accounts.rb +17 -0
- data/db/migrate/003_create_email_addresses.rb +17 -0
- data/db/migrate/004_create_people.rb +24 -0
- data/db/migrate/013_simplify_signup.rb +15 -0
- data/db/migrate/014_create_permissions.rb +16 -0
- data/db/migrate/015_create_roles.rb +18 -0
- data/db/migrate/016_refactor_people.rb +36 -0
- data/db/migrate/017_people_permissions.rb +9 -0
- data/generators/ae_users/USAGE +14 -0
- data/generators/ae_users/ae_users_generator.rb +12 -0
- data/generators/ae_users/templates/add.png +0 -0
- data/generators/ae_users/templates/admin.png +0 -0
- data/generators/ae_users/templates/group.png +0 -0
- data/generators/ae_users/templates/logout.png +0 -0
- data/generators/ae_users/templates/migration.rb +25 -0
- data/generators/ae_users/templates/openid.gif +0 -0
- data/generators/ae_users/templates/remove.png +0 -0
- data/generators/ae_users/templates/user.png +0 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/ae_users.rb +781 -0
- data/rails/init.rb +20 -0
- data/tasks/ae_users_tasks.rake +4 -0
- data/test/ae_users_test.rb +8 -0
- data/uninstall.rb +1 -0
- metadata +134 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
class CreatePeople < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
ActiveRecord::Base.establish_connection :users
|
4
|
+
create_table :people do |t|
|
5
|
+
t.column :firstname, :string
|
6
|
+
t.column :lastname, :string
|
7
|
+
t.column :gender, :string
|
8
|
+
t.column :nickname, :string
|
9
|
+
t.column :address, :string
|
10
|
+
t.column :home_phone, :string
|
11
|
+
t.column :work_phone, :string
|
12
|
+
t.column :best_call_time, :string
|
13
|
+
t.column :birthdate, :datetime
|
14
|
+
t.column :account_id, :integer
|
15
|
+
t.column :created_at, :datetime
|
16
|
+
t.column :updated_at, :datetime
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down
|
21
|
+
ActiveRecord::Base.establish_connection :users
|
22
|
+
drop_table :people
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SimplifySignup < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
ActiveRecord::Base.establish_connection :users
|
4
|
+
rename_column "people", "home_phone", "phone"
|
5
|
+
remove_column "people", "work_phone"
|
6
|
+
remove_column "people", "address"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.down
|
10
|
+
ActiveRecord::Base.establish_connection :users
|
11
|
+
rename_column "people", "phone", "home_phone"
|
12
|
+
add_column "people", "work_phone", :string
|
13
|
+
add_column "people", "address", :string
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreatePermissions < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
ActiveRecord::Base.establish_connection :users
|
4
|
+
create_table :permissions do |t|
|
5
|
+
t.column :role_id, :integer, :null => false
|
6
|
+
t.column :permission, :string
|
7
|
+
t.column :permissioned_id, :integer
|
8
|
+
t.column :permissioned_type, :string
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
ActiveRecord::Base.establish_connection :users
|
14
|
+
drop_table :permissions
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateRoles < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
ActiveRecord::Base.establish_connection :users
|
4
|
+
create_table :roles do |t|
|
5
|
+
t.column :name, :string, :null => false
|
6
|
+
end
|
7
|
+
create_table :people_roles, :id => false do |t|
|
8
|
+
t.column :person_id, :integer, :null => false
|
9
|
+
t.column :role_id, :integer, :null => false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
ActiveRecord::Base.establish_connection :users
|
15
|
+
drop_table :roles
|
16
|
+
drop_table :people_roles
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class RefactorPeople < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
ActiveRecord::Base.establish_connection :users
|
4
|
+
create_table :procon_profiles do |t|
|
5
|
+
t.column :person_id, :integer, :null => false
|
6
|
+
t.column :nickname, :string
|
7
|
+
t.column :phone, :string
|
8
|
+
t.column :best_call_time, :string
|
9
|
+
end
|
10
|
+
Person.find(:all).each do |person|
|
11
|
+
prof = ProconProfile.new :person => person
|
12
|
+
prof.nickname = person.nickname
|
13
|
+
prof.phone = person.phone
|
14
|
+
prof.best_call_time = person.best_call_time
|
15
|
+
prof.save
|
16
|
+
end
|
17
|
+
remove_column "people", "nickname"
|
18
|
+
remove_column "people", "phone"
|
19
|
+
remove_column "people", "best_call_time"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.down
|
23
|
+
ActiveRecord::Base.establish_connection :users
|
24
|
+
add_column "people", "nickname", :string
|
25
|
+
add_column "people", "phone", :string
|
26
|
+
add_column "people", "best_call_time", :string
|
27
|
+
ProconProfile.find(:all).each do |prof|
|
28
|
+
person = prof.person
|
29
|
+
person.nickname = prof.nickname
|
30
|
+
person.phone = prof.phone
|
31
|
+
person.best_call_time = prof.best_call_time
|
32
|
+
person.save
|
33
|
+
end
|
34
|
+
drop_table :procon_profiles
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Description:
|
2
|
+
Copies the ae_users resource files into the correct place in your Rails working directory.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
./script/generate ae_users
|
6
|
+
|
7
|
+
This will create:
|
8
|
+
public/images/ae_users/add.png
|
9
|
+
public/images/ae_users/admin.png
|
10
|
+
public/images/ae_users/group.png
|
11
|
+
public/images/ae_users/logout.png
|
12
|
+
public/images/ae_users/openid.gif
|
13
|
+
public/images/ae_users/remove.png
|
14
|
+
public/images/ae_users/user.png
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AeUsersGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.directory "public/images/ae_users"
|
5
|
+
%w{add admin group logout remove user}.each do |img|
|
6
|
+
m.file "#{img}.png", "public/images/ae_users/#{img}.png"
|
7
|
+
end
|
8
|
+
m.file "openid.gif", "public/images/ae_users/openid.gif"
|
9
|
+
m.migration_template 'migration.rb', "db/migrate", :migration_file_name => 'ae_users_local_tables'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AeUsersLocalTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :permissions do |t|
|
4
|
+
t.column :role_id, :integer
|
5
|
+
t.column :person_id, :integer
|
6
|
+
t.column :permission, :string
|
7
|
+
t.column :permissioned_id, :integer
|
8
|
+
t.column :permissioned_type, :string
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :auth_tickets do |t|
|
12
|
+
t.column :secret, :string
|
13
|
+
t.column :person_id, :integer
|
14
|
+
t.timestamps
|
15
|
+
t.column :expires_at, :datetime
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :auth_tickets, :secret, :unique => true
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.down
|
22
|
+
drop_table :auth_tickets
|
23
|
+
drop_table :permissions
|
24
|
+
end
|
25
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rails/init'
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
data/lib/ae_users.rb
ADDED
@@ -0,0 +1,781 @@
|
|
1
|
+
# AeUsers
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
module AeUsers
|
5
|
+
begin
|
6
|
+
@@db_name = Rails::Configuration.new.database_configuration["users"]["database"]
|
7
|
+
def self.db_name
|
8
|
+
@@db_name
|
9
|
+
end
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
|
13
|
+
@@signup_allowed = true
|
14
|
+
def self.signup_allowed?
|
15
|
+
@@signup_allowed
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.disallow_signup
|
19
|
+
@@signup_allowed = false
|
20
|
+
end
|
21
|
+
|
22
|
+
@@permissioned_classes = []
|
23
|
+
def self.add_permissioned_class(klass)
|
24
|
+
if not @@permissioned_classes.include?(klass.name)
|
25
|
+
@@permissioned_classes.push(klass.name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.permissioned_classes
|
30
|
+
return @@permissioned_classes.collect do |name|
|
31
|
+
eval(name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.permissioned_class(name)
|
36
|
+
if @@permissioned_classes.include?(name)
|
37
|
+
return eval(name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@@js_framework = "prototype"
|
42
|
+
def self.js_framework
|
43
|
+
@@js_framework
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.js_framework=(framework)
|
47
|
+
@@js_framework = framework
|
48
|
+
end
|
49
|
+
|
50
|
+
# yeah, the following 2 functions are Incredibly Evil(tm). I couldn't find any other way
|
51
|
+
# to pass around an ActiveRecord class without having it be potentially overwritten on
|
52
|
+
# association access.
|
53
|
+
def self.profile_class
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.profile_class=(klass)
|
58
|
+
module_eval <<-END_FUNC
|
59
|
+
def self.profile_class
|
60
|
+
return #{klass.name}
|
61
|
+
end
|
62
|
+
END_FUNC
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.map_openid(map)
|
66
|
+
map.open_id_complete 'auth', :controller => "auth", :action => "login", :requirements => { :method => :get }
|
67
|
+
end
|
68
|
+
|
69
|
+
class PermissionCache
|
70
|
+
def initialize
|
71
|
+
@cache = {}
|
72
|
+
end
|
73
|
+
|
74
|
+
def permitted?(person, permissioned, permission)
|
75
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache looking up result for #{person}, #{permissioned}, #{permission}"
|
76
|
+
pcache = person_cache(person)
|
77
|
+
key = pcache_key(permissioned, permission)
|
78
|
+
unless pcache.has_key?(key)
|
79
|
+
RAILS_DEFAULT_LOGGER.debug "Cache miss! Loading uncached permission."
|
80
|
+
pcache[key] = person.uncached_permitted?(permissioned, permission)
|
81
|
+
end
|
82
|
+
RAILS_DEFAULT_LOGGER.debug "Result is #{pcache[key]}"
|
83
|
+
return pcache[key]
|
84
|
+
end
|
85
|
+
|
86
|
+
def invalidate(person, permissioned, permission)
|
87
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache invalidating result for #{person}, #{permissioned}, #{permission}"
|
88
|
+
pcache = person_cache(person)
|
89
|
+
pcache.delete(pcache_key(permissioned, permission))
|
90
|
+
end
|
91
|
+
|
92
|
+
def invalidate_all(options={})
|
93
|
+
if options[:person]
|
94
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache invalidating all results for #{options[:person]}"
|
95
|
+
@cache.delete(options[:person])
|
96
|
+
elsif options[:permission] and options[:permissioned]
|
97
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache invalidating all results for #{options[:permissioned]}, #{options[:permission]}"
|
98
|
+
@cache.each_value do |pcache|
|
99
|
+
pcache.delete(pcache_key(options[:permissioned], options[:permission]))
|
100
|
+
end
|
101
|
+
else
|
102
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache invalidating all results!"
|
103
|
+
@cache = {}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def person_cache(person)
|
109
|
+
unless @cache.has_key?(person)
|
110
|
+
RAILS_DEFAULT_LOGGER.debug "Permission cache creating new pcache for #{person}"
|
111
|
+
@cache[person] = {}
|
112
|
+
end
|
113
|
+
@cache[person]
|
114
|
+
end
|
115
|
+
|
116
|
+
def pcache_key(permissioned, permission)
|
117
|
+
if permissioned
|
118
|
+
return "#{permissioned.id}_#{permission}"
|
119
|
+
else
|
120
|
+
return "nil_#{permission}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
@@cache_permissions = true
|
126
|
+
@@permission_cache = AeUsers::PermissionCache.new
|
127
|
+
def self.cache_permissions=(value)
|
128
|
+
@@cache_permissions = value
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.cache_permissions?
|
132
|
+
@@cache_permissions
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.permission_cache
|
136
|
+
@@permission_cache
|
137
|
+
end
|
138
|
+
|
139
|
+
module Acts
|
140
|
+
module Permissioned
|
141
|
+
def self.included(base)
|
142
|
+
base.extend ClassMethods
|
143
|
+
end
|
144
|
+
|
145
|
+
module ClassMethods
|
146
|
+
def acts_as_permissioned(options = {})
|
147
|
+
has_many :permissions, :as => :permissioned, :dependent => :destroy, :include => [:person, :role, :permissioned]
|
148
|
+
|
149
|
+
cattr_accessor :permission_names
|
150
|
+
self.permission_names = options[:permission_names] || [:show, :edit, :destroy]
|
151
|
+
self.permission_names = self.permission_names.collect do |perm|
|
152
|
+
perm.to_s
|
153
|
+
end
|
154
|
+
if not self.permission_names.include? "change_permissions"
|
155
|
+
self.permission_names.push "change_permissions"
|
156
|
+
end
|
157
|
+
|
158
|
+
self.permission_names.each do |perm|
|
159
|
+
define_method("permit_#{perm}?") do |person|
|
160
|
+
self.permitted?(person, perm)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
AeUsers.add_permissioned_class(self)
|
165
|
+
|
166
|
+
extend AeUsers::Acts::Permissioned::SingletonMethods
|
167
|
+
include AeUsers::Acts::Permissioned::InstanceMethods
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
module SingletonMethods
|
172
|
+
end
|
173
|
+
|
174
|
+
module InstanceMethods
|
175
|
+
def permitted?(person, permission=nil)
|
176
|
+
person.permitted? self, permission
|
177
|
+
end
|
178
|
+
|
179
|
+
def permitted_people(permission)
|
180
|
+
grants = permissions.select { |perm| perm.permission == permission }
|
181
|
+
people = []
|
182
|
+
grants.collect {|grant| grant.grantee}.each do |grantee|
|
183
|
+
if grantee.kind_of? Person
|
184
|
+
if not people.include? grantee
|
185
|
+
people << grantee
|
186
|
+
end
|
187
|
+
elsif grantee.kind_of? Role
|
188
|
+
grantee.people.each do |person|
|
189
|
+
if not people.include? person
|
190
|
+
people << person
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
return people
|
196
|
+
end
|
197
|
+
|
198
|
+
def grant(grantees, permissions=nil)
|
199
|
+
if not grantees.kind_of?(Array)
|
200
|
+
grantees = [grantees]
|
201
|
+
end
|
202
|
+
|
203
|
+
if not permissions.kind_of?(Array)
|
204
|
+
if permissions.nil?
|
205
|
+
permissions = self.class.permission_names
|
206
|
+
else
|
207
|
+
permissions = [permissions]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
grantees.each do |grantee|
|
212
|
+
if grantee.kind_of? Role
|
213
|
+
permissions.each do |perm|
|
214
|
+
if AeUsers.cache_permissions?
|
215
|
+
grantee.members.each do |person|
|
216
|
+
AeUsers.permission_cache.invalidate(person, self, perm)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
Permission.create :role => grantee, :permission => perm, :permissioned => self
|
220
|
+
end
|
221
|
+
elsif grantee.kind_of? Person
|
222
|
+
permissions.each do |perm|
|
223
|
+
if AeUsers.cache_permissions?
|
224
|
+
AeUsers.permission_cache.invalidate(grantee, self, perm)
|
225
|
+
end
|
226
|
+
Permission.create :person => grantee, :permission => perm, :permissioned => self
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def revoke(grantees, permissions=nil)
|
233
|
+
if not grantees.kind_of?(Array)
|
234
|
+
grantees = [grantees]
|
235
|
+
end
|
236
|
+
|
237
|
+
if not permissions.kind_of?(Array)
|
238
|
+
if permissions.nil?
|
239
|
+
permissions = self.class.permission_names
|
240
|
+
else
|
241
|
+
permissions = [permissions]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
grantees.each do |grantee|
|
246
|
+
permissions.each do |perm|
|
247
|
+
existing = if grantee.kind_of? Role
|
248
|
+
if AeUsers.cache_permissions?
|
249
|
+
grantee.members.each do |person|
|
250
|
+
AeUsers.permission_cache.invalidate(person, self, perm)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
Permission.find_by_role_and_permission_type(grantee, perm)
|
254
|
+
elsif grantee.kind_of? Person
|
255
|
+
if AeUsers.cache_permissions?
|
256
|
+
AeUsers.permission_cache.invalidate(pesron, self, perm)
|
257
|
+
end
|
258
|
+
Permission.find_by_person_and_permission_type(person, perm)
|
259
|
+
end
|
260
|
+
|
261
|
+
if existing
|
262
|
+
existing.destroy
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
module ControllerExtensions
|
272
|
+
module RequirePermission
|
273
|
+
def self.included(base)
|
274
|
+
base.extend ClassMethods
|
275
|
+
end
|
276
|
+
|
277
|
+
def access_denied(msg=nil, options={})
|
278
|
+
options = {
|
279
|
+
:layout => active_layout
|
280
|
+
}.update(options)
|
281
|
+
msg ||= "Sorry, you don't have access to view that page."
|
282
|
+
if logged_in?
|
283
|
+
body = "If you feel you've been denied access in error, please contact the administrator of this web site."
|
284
|
+
respond_to do |format|
|
285
|
+
format.html { render options.update({:inline => "<h1>#{msg}</h1>\n\n<div id=\"login\"><p><b>#{body}</b></p></div>"}) }
|
286
|
+
format.xml { render :xml => { :error => msg }.to_xml, :status => :forbidden }
|
287
|
+
format.js { render :json => msg, :status => :forbidden }
|
288
|
+
format.json { render :json => msg, :status => :forbidden }
|
289
|
+
end
|
290
|
+
else
|
291
|
+
flash[:error_messages] = msg
|
292
|
+
redirect_to :controller => 'auth', :action => 'login'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def logged_in?
|
297
|
+
if @logged_in_person
|
298
|
+
return @logged_in_person
|
299
|
+
end
|
300
|
+
if session[:person]
|
301
|
+
begin
|
302
|
+
@logged_in_person = Person.find(session[:person])
|
303
|
+
rescue ActiveRecord::RecordNotFound
|
304
|
+
end
|
305
|
+
elsif session[:account]
|
306
|
+
begin
|
307
|
+
acct = Account.find(session[:account])
|
308
|
+
session[:person] = acct.person.id
|
309
|
+
@logged_in_person = acct.person
|
310
|
+
rescue ActiveRecord::RecordNotFound
|
311
|
+
end
|
312
|
+
elsif attempt_login_from_params
|
313
|
+
return logged_in?
|
314
|
+
else
|
315
|
+
return @logged_in_person
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def logged_in_person
|
320
|
+
return logged_in?
|
321
|
+
end
|
322
|
+
|
323
|
+
def attempt_login(login)
|
324
|
+
@account = Account.find_by_email_address(login.email)
|
325
|
+
if not @account.nil? and not @account.active
|
326
|
+
redirect_to :controller => 'auth', :action => :needs_activation, :account => @account, :email => login.email, :return_to => login.return_to
|
327
|
+
return false
|
328
|
+
elsif not @account.nil? and @account.check_password login.password
|
329
|
+
if (not AeUsers.profile_class.nil? and not @account.person.nil? and
|
330
|
+
AeUsers.profile_class.find_by_person_id(@account.person.id).nil?)
|
331
|
+
|
332
|
+
session[:provisional_person] = @account.person.id
|
333
|
+
redirect_to :controller => 'auth', :action => :needs_profile, :return_to => login.return_to
|
334
|
+
return false
|
335
|
+
else
|
336
|
+
session[:person] = @account.person.id
|
337
|
+
return true
|
338
|
+
end
|
339
|
+
else
|
340
|
+
flash[:error_messages] = ['Invalid email address or password.']
|
341
|
+
return false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def attempt_open_id_login(return_to)
|
346
|
+
if return_to
|
347
|
+
session[:return_to] = return_to
|
348
|
+
else
|
349
|
+
return_to = session[:return_to]
|
350
|
+
end
|
351
|
+
|
352
|
+
openid_url = params[:openid_url]
|
353
|
+
params.delete(:openid_url)
|
354
|
+
|
355
|
+
optional_fields = Person.sreg_map.keys
|
356
|
+
if AeUsers.profile_class and AeUsers.profile_class.respond_to?('sreg_map')
|
357
|
+
optional_fields += AeUsers.profile_class.sreg_map.keys
|
358
|
+
end
|
359
|
+
authenticate_with_open_id(openid_url, :optional => optional_fields) do |result, identity_url, registration|
|
360
|
+
if result.successful?
|
361
|
+
id = OpenIdIdentity.find_by_identity_url(identity_url)
|
362
|
+
if not id.nil?
|
363
|
+
@person = id.person
|
364
|
+
end
|
365
|
+
if id.nil? or @person.nil?
|
366
|
+
if AeUsers.signup_allowed?
|
367
|
+
session[:identity_url] = identity_url
|
368
|
+
redirect_to :controller => 'auth', :action => :needs_person, :return_to => return_to, :registration => registration.data
|
369
|
+
return false
|
370
|
+
else
|
371
|
+
flash[:error_messages] = ["Sorry, you are not registered with this site."]
|
372
|
+
return false
|
373
|
+
end
|
374
|
+
else
|
375
|
+
if (not AeUsers.profile_class.nil? and AeUsers.profile_class.find_by_person_id(@person.id).nil?)
|
376
|
+
session[:provisional_person] = @person.id
|
377
|
+
redirect_to :controller => 'auth', :action => :needs_profile, :return_to => return_to
|
378
|
+
return false
|
379
|
+
else
|
380
|
+
session[:person] = @person.id
|
381
|
+
return true
|
382
|
+
end
|
383
|
+
end
|
384
|
+
else
|
385
|
+
flash[:error_messages] = result.message
|
386
|
+
return false
|
387
|
+
end
|
388
|
+
end
|
389
|
+
return session[:person]
|
390
|
+
end
|
391
|
+
|
392
|
+
def attempt_ticket_login(secret)
|
393
|
+
t = AuthTicket.find_ticket(secret)
|
394
|
+
if t.nil?
|
395
|
+
flash[:error_messages] = ["Ticket not found"]
|
396
|
+
return false
|
397
|
+
else
|
398
|
+
session[:person] = t.person
|
399
|
+
t.destroy
|
400
|
+
return session[:person]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def attempt_login_from_params
|
405
|
+
return_to = request.request_uri
|
406
|
+
if not params[:ae_email].blank? and not params[:ae_password].blank?
|
407
|
+
login = Login.new(:email => params[:ae_email], :password => params[:ae_password], :return_to => return_to)
|
408
|
+
attempt_login(login)
|
409
|
+
elsif not params[:openid_url].blank?
|
410
|
+
attempt_open_id_login(return_to)
|
411
|
+
elsif not params[:ae_ticket].blank?
|
412
|
+
attempt_ticket_login(params[:ae_ticket])
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def do_permission_check(obj, perm_name, fail_msg)
|
417
|
+
attempt_login_from_params
|
418
|
+
p = logged_in_person
|
419
|
+
if not (p and p.permitted?(obj, perm_name))
|
420
|
+
access_denied fail_msg
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def create_account_and_person()
|
425
|
+
account = Account.new(:password => params[:password1])
|
426
|
+
person = Person.new(params[:person])
|
427
|
+
addr = EmailAddress.new :address => params[:email], :person => person, :primary => true
|
428
|
+
person.account = account
|
429
|
+
|
430
|
+
if not AeUsers.profile_class.nil?
|
431
|
+
app_profile = AeUsers.profile_class.send(:new, :person => person)
|
432
|
+
app_profile.attributes = params[:app_profile]
|
433
|
+
end
|
434
|
+
|
435
|
+
if request.post?
|
436
|
+
error_fields = []
|
437
|
+
error_messages = []
|
438
|
+
|
439
|
+
if Person.find_by_email_address(params[:email])
|
440
|
+
error_fields.push "email"
|
441
|
+
error_messages.push "An account at that email address already exists!"
|
442
|
+
end
|
443
|
+
|
444
|
+
if params[:password1] != params[:password2]
|
445
|
+
error_fields += ["password1", "password2"]
|
446
|
+
error_messages.push "Passwords do not match."
|
447
|
+
elsif params[:password1].length == 0
|
448
|
+
error_fields += ["password1", "password2"]
|
449
|
+
error_messages.push "You must enter a password."
|
450
|
+
end
|
451
|
+
|
452
|
+
["firstname", "lastname", "email", "gender"].each do |field|
|
453
|
+
if (not params[field] or params[field].length == 0) and (not params[:person][field] or params[:person][field].length == 0)
|
454
|
+
error_fields.push field
|
455
|
+
error_messages.push "You must enter a value for #{field}."
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
if error_fields.size > 0 or error_messages.size > 0
|
460
|
+
flash[:error_fields] = error_fields
|
461
|
+
flash[:error_messages] = error_messages
|
462
|
+
else
|
463
|
+
account.save
|
464
|
+
addr.save
|
465
|
+
person.save
|
466
|
+
if app_profile
|
467
|
+
app_profile.save
|
468
|
+
end
|
469
|
+
|
470
|
+
@account = account
|
471
|
+
@addr = addr
|
472
|
+
@person = person
|
473
|
+
@app_profile = app_profile
|
474
|
+
|
475
|
+
begin
|
476
|
+
ActionMailer::Base.default_url_options[:host] = request.host
|
477
|
+
account.generate_activation
|
478
|
+
rescue
|
479
|
+
account.activation_key = nil
|
480
|
+
account.active = true
|
481
|
+
account.save
|
482
|
+
return :no_activation
|
483
|
+
end
|
484
|
+
|
485
|
+
return :success
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
module ClassMethods
|
491
|
+
def require_login(conditions = {})
|
492
|
+
before_filter conditions do |controller|
|
493
|
+
if not controller.logged_in?
|
494
|
+
controller.attempt_login_from_params
|
495
|
+
if not controller.logged_in?
|
496
|
+
controller.access_denied "Sorry, but you need to be logged in to view that page."
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def require_class_permission(perm_name, conditions = {})
|
503
|
+
delegated = false
|
504
|
+
if conditions[:class_name]
|
505
|
+
cn = conditions[:class_name]
|
506
|
+
delegated = true
|
507
|
+
elsif conditions[:class_param]
|
508
|
+
cpn = conditions[:class_param]
|
509
|
+
end
|
510
|
+
before_filter conditions do |controller|
|
511
|
+
if cn.nil? and cpn
|
512
|
+
cn = controller.params[cpn]
|
513
|
+
delegated = true
|
514
|
+
end
|
515
|
+
controller_cn = controller.class.name.gsub(/Controller$/, "").singularize
|
516
|
+
cn ||= controller_cn
|
517
|
+
full_perm_name = "#{perm_name}_#{cn.tableize}"
|
518
|
+
if delegated
|
519
|
+
msg = "Sorry, but you are not permitted to #{perm_name} #{controller_cn.tableize.humanize.downcase} in this #{cn.tableize.humanize.singularize.downcase}."
|
520
|
+
else
|
521
|
+
msg = "Sorry, but you are not permitted to #{perm_name} #{cn.tableize.humanize.downcase}."
|
522
|
+
end
|
523
|
+
controller.do_permission_check(nil, full_perm_name, msg)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def require_permission(perm_name, conditions = {})
|
528
|
+
if conditions[:class_name]
|
529
|
+
cn = conditions[:class_name]
|
530
|
+
end
|
531
|
+
id_param = conditions[:id_param] || :id
|
532
|
+
before_filter conditions do |controller|
|
533
|
+
cn ||= controller.class.name.gsub(/Controller$/, "").singularize
|
534
|
+
o = eval(cn).find(controller.params[id_param])
|
535
|
+
if not o.nil?
|
536
|
+
controller.do_permission_check(o, perm_name, "Sorry, but you are not permitted to #{perm_name} this #{cn.tableize.singularize.humanize.downcase}.")
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
def rest_edit_permissions(options = {})
|
542
|
+
options = {
|
543
|
+
:restrict_create => false,
|
544
|
+
}.update(options)
|
545
|
+
restrict_create = options[:restrict_create]
|
546
|
+
options.delete(:restrict_create)
|
547
|
+
require_permission("edit", { :only => [:edit, :update] }.update(options))
|
548
|
+
if restrict_create
|
549
|
+
require_class_permission("create", { :only => [:new, :create] }.update(options))
|
550
|
+
end
|
551
|
+
require_permission("destroy", { :only => [:destroy] }.update(options))
|
552
|
+
end
|
553
|
+
|
554
|
+
def rest_view_permissions(options = {})
|
555
|
+
options = {
|
556
|
+
:restrict_list => false,
|
557
|
+
}.update(options)
|
558
|
+
restrict_list = options[:restrict_list]
|
559
|
+
options.delete(:restrict_list)
|
560
|
+
if restrict_list
|
561
|
+
require_class_permission("list", { :only => [:index] }.update(options))
|
562
|
+
elsif options[:class_name]
|
563
|
+
require_permission("show", { :only => [:index], :id_param => "#{options[:class_name].tableize}_id" }.update(options))
|
564
|
+
end
|
565
|
+
require_permission("show", { :only => [:show] }.update(options))
|
566
|
+
end
|
567
|
+
|
568
|
+
def rest_permissions(options = {})
|
569
|
+
rest_view_permissions(options)
|
570
|
+
rest_edit_permissions(options)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
module HelperFunctions
|
577
|
+
def permission_names(item)
|
578
|
+
if item.kind_of? ActiveRecord::Base
|
579
|
+
return item.class.permission_names
|
580
|
+
else
|
581
|
+
return item.permission_names
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
def full_permission_name(item, perm)
|
586
|
+
if item.kind_of? ActiveRecord::Base
|
587
|
+
return perm
|
588
|
+
else
|
589
|
+
return "#{perm}_#{item.class.name.tableize}"
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
def permission_grants(item, perm)
|
594
|
+
if item.kind_of? ActiveRecord::Base
|
595
|
+
grants = item.permissions.select {|p| p.permission == perm }
|
596
|
+
else
|
597
|
+
full_perm_name = full_permission_name(item, perm)
|
598
|
+
grants = Permission.find_all_by_permission(full_perm_name)
|
599
|
+
end
|
600
|
+
return grants
|
601
|
+
end
|
602
|
+
|
603
|
+
def all_permitted?(item, perm)
|
604
|
+
if item
|
605
|
+
# try to short-circuit this with an eager load check
|
606
|
+
if item.permissions.select {|p| (p.permission == perm or p.permission.nil?) and p.role.nil? and p.person.nil? }.size > 0
|
607
|
+
return true
|
608
|
+
end
|
609
|
+
end
|
610
|
+
sql = "permission = ? and (role_id = 0 or role_id is null) and (person_id = 0 or person_id is null)"
|
611
|
+
return Permission.find(:all, :conditions => [sql, full_permission_name(item, perm)]).length > 0
|
612
|
+
end
|
613
|
+
|
614
|
+
def logged_in?
|
615
|
+
return controller.logged_in?
|
616
|
+
end
|
617
|
+
|
618
|
+
def logged_in_person
|
619
|
+
return controller.logged_in_person
|
620
|
+
end
|
621
|
+
|
622
|
+
def app_profile(person = nil)
|
623
|
+
if person.nil?
|
624
|
+
person = logged_in_person
|
625
|
+
end
|
626
|
+
|
627
|
+
AeUsers.profile_class.find_by_person_id(person.id)
|
628
|
+
end
|
629
|
+
|
630
|
+
def user_picker(field_name, options = {})
|
631
|
+
options = {
|
632
|
+
:people => true,
|
633
|
+
:roles => false,
|
634
|
+
:callback => nil,
|
635
|
+
:default => nil,
|
636
|
+
:clear_after => true
|
637
|
+
}.update(options)
|
638
|
+
|
639
|
+
domid = field_name.gsub(/\W/, "_").gsub(/__+/, "_").sub(/_$/, "").sub(/^_/, "")
|
640
|
+
|
641
|
+
default = options[:default]
|
642
|
+
rhtml = text_field_tag("#{field_name}_shim", default ? default.name : "", { :style => "width: 15em; display: inline; float: none;" })
|
643
|
+
rhtml << hidden_field_tag(field_name, default ? default.id : "")
|
644
|
+
auto_complete_url = url_for(:controller => "permission", :action => "auto_complete_for_permission_grantee",
|
645
|
+
:people => options[:people], :roles => options[:roles], :escape => false)
|
646
|
+
|
647
|
+
if AeUsers.js_framework == "prototype"
|
648
|
+
rhtml << <<-ENDRHTML
|
649
|
+
<div id="#{domid}_shim_auto_complete" class="auto_complete"></div>
|
650
|
+
<%= auto_complete_field('#{domid}_shim', :select => "grantee_id", :param_name => "q",
|
651
|
+
:after_update_element => "function (el, selected) {
|
652
|
+
kid = el.value.split(':');
|
653
|
+
klass = kid[0];
|
654
|
+
id = kid[1];
|
655
|
+
cb = function(klass, id) {
|
656
|
+
$('#{domid}').value = el.value;
|
657
|
+
#{options[:clear_after] ? "$('#{domid}_shim').value = '';" : "$('#{domid}_shim').value = selected.getAttribute('granteeName');"}
|
658
|
+
#{options[:callback]}
|
659
|
+
};
|
660
|
+
cb(klass, id);
|
661
|
+
}",
|
662
|
+
:url => "#{auto_complete_url}") %>
|
663
|
+
ENDRHTML
|
664
|
+
elsif AeUsers.js_framework == "jquery"
|
665
|
+
rhtml << <<-ENDRHTML
|
666
|
+
<script type="text/javascript">
|
667
|
+
jQuery(function() {
|
668
|
+
jq_domid = "\##{domid.gsub(/(\W)/, '\\\\\\\\\1')}";
|
669
|
+
jQuery(jq_domid + "_shim").autocomplete('#{auto_complete_url}',
|
670
|
+
{
|
671
|
+
formatItem: function(data, i, n, value) {
|
672
|
+
return value;
|
673
|
+
},
|
674
|
+
}).bind('result', function(e, data) {
|
675
|
+
jQuery(jq_domid).val(data[1]);
|
676
|
+
#{options[:callback]}
|
677
|
+
}
|
678
|
+
);
|
679
|
+
});
|
680
|
+
</script>
|
681
|
+
ENDRHTML
|
682
|
+
end
|
683
|
+
|
684
|
+
render :inline => rhtml
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
module InstanceTagExtensions
|
689
|
+
DEFAULT_USERPICKER_OPTIONS = {
|
690
|
+
"auto_complete_url_params" => {:controller => "permission", :action => "auto_complete_for_permission_grantee"}
|
691
|
+
}
|
692
|
+
|
693
|
+
def to_user_picker_tag(people, roles, options={})
|
694
|
+
options = options.stringify_keys
|
695
|
+
options = DEFAULT_USERPICKER_OPTIONS.merge(options)
|
696
|
+
add_default_name_and_id(options)
|
697
|
+
|
698
|
+
default = options["default"]
|
699
|
+
shim = tag("input", :type => "text", :id => "#{options["id"]}_shim", :value => default ? default.name : "",
|
700
|
+
:style => "width: 15em; display: inline; float: none;")
|
701
|
+
hidden = to_input_field_tag("hidden", options.update("value" => default ? default.id : ""))
|
702
|
+
|
703
|
+
url_params = options["auto_complete_url_params"].update(:people => people, :roles => roles,
|
704
|
+
:escape => false)
|
705
|
+
RAILS_DEFAULT_LOGGER.debug url_params.collect { |k, v| "#{k}: #{v}" }.join(", ")
|
706
|
+
|
707
|
+
options["auto_complete_url"] = @template_object.url_for(url_params)
|
708
|
+
shim + hidden + user_picker_extra_content(options) + user_picker_js(options)
|
709
|
+
end
|
710
|
+
|
711
|
+
private
|
712
|
+
def user_picker_js(options = {})
|
713
|
+
case AeUsers.js_framework
|
714
|
+
when "prototype"
|
715
|
+
user_picker_js_for_prototype(options)
|
716
|
+
when "jquery"
|
717
|
+
user_picker_js_for_jquery(options)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
def user_picker_extra_content(options = {})
|
722
|
+
if AeUsers.js_framework == "prototype"
|
723
|
+
return @template_object.tag("div", :id => "#{options['id']}_shim_auto_complete", :class => "auto_complete")
|
724
|
+
end
|
725
|
+
|
726
|
+
return ""
|
727
|
+
end
|
728
|
+
|
729
|
+
def user_picker_js_for_prototype(options = {})
|
730
|
+
@template_object.auto_complete_field(:select => "grantee_id", :param_name => "q",
|
731
|
+
:after_update_element =>
|
732
|
+
"function (el, selected) {
|
733
|
+
kid = el.value.split(':');
|
734
|
+
klass = kid[0];
|
735
|
+
id = kid[1];
|
736
|
+
cb = function(klass, id) {
|
737
|
+
$('#{options['id']}').value = el.value;
|
738
|
+
#{options['clear_after'] ? "$('#{options['id']}_shim').value = '';" : "$('#{options['id']}_shim').value = selected.getAttribute('granteeName');"}
|
739
|
+
#{options['callback']}
|
740
|
+
};
|
741
|
+
cb(klass, id);
|
742
|
+
}",
|
743
|
+
:url => options['auto_complete_url'])
|
744
|
+
end
|
745
|
+
|
746
|
+
def user_picker_js_for_jquery(options = {})
|
747
|
+
<<-ENDRHTML
|
748
|
+
<script type="text/javascript">
|
749
|
+
jQuery(function() {
|
750
|
+
jQuery('\##{options['id']}_shim').autocomplete('#{options['auto_complete_url']}',
|
751
|
+
{
|
752
|
+
formatItem: function(data, i, n, value) {
|
753
|
+
return value;
|
754
|
+
},
|
755
|
+
}).bind('result', function(e, data) {
|
756
|
+
jQuery(jq_domid).val(data[1]);
|
757
|
+
#{options['callback']}
|
758
|
+
}
|
759
|
+
);
|
760
|
+
});
|
761
|
+
</script>
|
762
|
+
ENDRHTML
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
module FormHelperFunctions
|
767
|
+
|
768
|
+
|
769
|
+
def person_field(object_name, method, options={})
|
770
|
+
it = ActionView::Base::InstanceTag.new(object_name, method, self, options.delete(:object))
|
771
|
+
it.to_user_picker_tag(true, false, options)
|
772
|
+
end
|
773
|
+
|
774
|
+
end
|
775
|
+
|
776
|
+
module FormBuilderFunctions
|
777
|
+
def person_field(method, options = {})
|
778
|
+
@template.send("person_field", @object_name, method, objectify_options(options))
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|