ae_users 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/README +47 -0
  2. data/Rakefile +36 -0
  3. data/VERSION +1 -0
  4. data/ae_users.gemspec +117 -0
  5. data/app/controllers/account_controller.rb +167 -0
  6. data/app/controllers/auth_controller.rb +202 -0
  7. data/app/controllers/permission_controller.rb +172 -0
  8. data/app/helpers/account_helper.rb +2 -0
  9. data/app/helpers/auth_helper.rb +5 -0
  10. data/app/helpers/permission_helper.rb +2 -0
  11. data/app/models/account.rb +50 -0
  12. data/app/models/auth_notifier.rb +34 -0
  13. data/app/models/auth_ticket.rb +39 -0
  14. data/app/models/email_address.rb +17 -0
  15. data/app/models/login.rb +23 -0
  16. data/app/models/open_id_identity.rb +5 -0
  17. data/app/models/permission.rb +57 -0
  18. data/app/models/person.rb +156 -0
  19. data/app/models/role.rb +7 -0
  20. data/app/views/account/_personal_info.rhtml +35 -0
  21. data/app/views/account/_procon_profile.rhtml +3 -0
  22. data/app/views/account/_signup_form.html.erb +39 -0
  23. data/app/views/account/activate.rhtml +6 -0
  24. data/app/views/account/activation_error.rhtml +11 -0
  25. data/app/views/account/change_password.rhtml +3 -0
  26. data/app/views/account/edit_profile.rhtml +117 -0
  27. data/app/views/account/signup.rhtml +9 -0
  28. data/app/views/account/signup_noactivation.rhtml +7 -0
  29. data/app/views/account/signup_success.rhtml +8 -0
  30. data/app/views/auth/_auth_form.rhtml +54 -0
  31. data/app/views/auth/_forgot_form.html.erb +12 -0
  32. data/app/views/auth/_mini_auth_form.rhtml +17 -0
  33. data/app/views/auth/_openid_auth_form.html.erb +14 -0
  34. data/app/views/auth/_other_login_options.html.erb +24 -0
  35. data/app/views/auth/auth_form.js.erb +63 -0
  36. data/app/views/auth/forgot.rhtml +3 -0
  37. data/app/views/auth/forgot_form.rhtml +6 -0
  38. data/app/views/auth/index.css.erb +23 -0
  39. data/app/views/auth/login.rhtml +6 -0
  40. data/app/views/auth/needs_activation.rhtml +6 -0
  41. data/app/views/auth/needs_person.html.erb +32 -0
  42. data/app/views/auth/needs_profile.rhtml +14 -0
  43. data/app/views/auth/openid_login.html.erb +6 -0
  44. data/app/views/auth/resend_activation.rhtml +3 -0
  45. data/app/views/auth_notifier/account_activation.rhtml +13 -0
  46. data/app/views/auth_notifier/generated_password.rhtml +10 -0
  47. data/app/views/permission/_add_grantee.rhtml +47 -0
  48. data/app/views/permission/_role_member.rhtml +8 -0
  49. data/app/views/permission/_show.rhtml +81 -0
  50. data/app/views/permission/_userpicker.rhtml +0 -0
  51. data/app/views/permission/add_role_member.rhtml +3 -0
  52. data/app/views/permission/admin.rhtml +45 -0
  53. data/app/views/permission/edit.rhtml +9 -0
  54. data/app/views/permission/edit_role.rhtml +63 -0
  55. data/app/views/permission/grant.rhtml +10 -0
  56. data/db/migrate/002_create_accounts.rb +17 -0
  57. data/db/migrate/003_create_email_addresses.rb +17 -0
  58. data/db/migrate/004_create_people.rb +24 -0
  59. data/db/migrate/013_simplify_signup.rb +15 -0
  60. data/db/migrate/014_create_permissions.rb +16 -0
  61. data/db/migrate/015_create_roles.rb +18 -0
  62. data/db/migrate/016_refactor_people.rb +36 -0
  63. data/db/migrate/017_people_permissions.rb +9 -0
  64. data/generators/ae_users/USAGE +14 -0
  65. data/generators/ae_users/ae_users_generator.rb +12 -0
  66. data/generators/ae_users/templates/add.png +0 -0
  67. data/generators/ae_users/templates/admin.png +0 -0
  68. data/generators/ae_users/templates/group.png +0 -0
  69. data/generators/ae_users/templates/logout.png +0 -0
  70. data/generators/ae_users/templates/migration.rb +25 -0
  71. data/generators/ae_users/templates/openid.gif +0 -0
  72. data/generators/ae_users/templates/remove.png +0 -0
  73. data/generators/ae_users/templates/user.png +0 -0
  74. data/init.rb +1 -0
  75. data/install.rb +1 -0
  76. data/lib/ae_users.rb +781 -0
  77. data/rails/init.rb +20 -0
  78. data/tasks/ae_users_tasks.rake +4 -0
  79. data/test/ae_users_test.rb +8 -0
  80. data/uninstall.rb +1 -0
  81. metadata +134 -0
@@ -0,0 +1,172 @@
1
+ class PermissionController < ApplicationController
2
+ unloadable
3
+ require_login
4
+
5
+ def admin
6
+ @pclasses = logged_in_person.administrator_classes
7
+ @roles = Role.find :all
8
+ end
9
+
10
+ require_class_permission "change_permissions", :class_param => "klass", :only => [:edit]
11
+ def edit
12
+ pclass = nil
13
+ AeUsers.permissioned_classes.each do |pc|
14
+ if pc.name == params[:klass]
15
+ pclass = pc
16
+ break
17
+ end
18
+ end
19
+
20
+ if pclass.nil?
21
+ render :inline => "<h1>Invalid class name <%= h AeUsers.permissioned_classes %></h1>"
22
+ else
23
+ if params[:id]
24
+ @item = pclass.find(params[:id])
25
+ else
26
+ @item = pclass
27
+ end
28
+ end
29
+ end
30
+
31
+ def auto_complete_for_permission_grantee
32
+ if params[:q]
33
+ query = params[:q].strip.downcase
34
+ liketerm = "%#{query}%"
35
+ terms = query.split
36
+
37
+ if params[:people] == "true"
38
+ sql = terms.collect do |t|
39
+ "((LOWER(firstname) like ?) OR (LOWER(lastname) like ?))"
40
+ end.join(" AND ")
41
+ doubleterms = []
42
+ terms.each do |t|
43
+ doubleterms.push("%#{t}%")
44
+ doubleterms.push("%#{t}%")
45
+ end
46
+ @grantees = Person.find(:all, :conditions => ([sql] + doubleterms))
47
+ @grantees += EmailAddress.find(:all,
48
+ :conditions => ["LOWER(address) like ?", liketerm]).collect do |ea|
49
+ ea.person
50
+ end
51
+ else
52
+ @grantees = []
53
+ end
54
+
55
+ if params[:roles] == "true"
56
+ @grantees += Role.find(:all,
57
+ :conditions => ["LOWER(name) like ?", liketerm])
58
+ end
59
+
60
+ @grantees.uniq!
61
+ else
62
+ @grantees = []
63
+ end
64
+
65
+ render :partial => "add_grantee"
66
+ end
67
+
68
+ before_filter :check_grant_perms, :only => [:grant]
69
+ layout nil, :only => [:grant]
70
+ def grant
71
+ perm_params = {}
72
+ if params[:klass] == 'Person'
73
+ @grantee = Person.find(params[:id])
74
+ perm_params[:person_id] = @grantee.id
75
+ else
76
+ @grantee = Role.find(params[:id])
77
+ perm_params[:role_id] = @grantee.id
78
+ end
79
+
80
+ @perm = Permission.create(perm_params.update(:permission => params[:perm], :permissioned => @permissioned))
81
+ @perm.destroy_caches
82
+ end
83
+
84
+ before_filter :check_revoke_perms, :only => [:revoke]
85
+ def revoke
86
+ @perm.destroy_caches
87
+ @perm.destroy
88
+ render :nothing => true
89
+ end
90
+
91
+ def create_role
92
+ @role = Role.create(params[:role])
93
+ redirect_to :action => 'edit_role', :id => @role.id
94
+ @role.grant(logged_in_person)
95
+ end
96
+
97
+ before_filter :check_edit_role_perms, :only => [:edit_role, :delete_role]
98
+ before_filter :check_edit_role_member_perms, :only => [:add_role_member, :remove_role_member]
99
+ def edit_role
100
+ end
101
+
102
+ def add_role_member
103
+ @person = Person.find(params[:id])
104
+
105
+ @role.people.push @person
106
+ @role.save
107
+ if AeUsers.cache_permissions?
108
+ AeUsers.permission_cache.invalidate_all(@person)
109
+ end
110
+
111
+ render :partial => "role_member", :locals => {:person => @person}
112
+ end
113
+
114
+ def remove_role_member
115
+ @role.people.delete(@role.people.find(params[:id]))
116
+ @role.save
117
+ if AeUsers.cache_permissions?
118
+ AeUsers.permission_cache.invalidate_all(@person)
119
+ end
120
+
121
+ render :nothing => true
122
+ end
123
+
124
+ def delete_role
125
+ if AeUsers.cache_permissions?
126
+ @role.people.each do |person|
127
+ AeUsers.permission_cache.invalidate_all(person)
128
+ end
129
+ end
130
+ @role.destroy
131
+ render :nothing => true
132
+ end
133
+
134
+ private
135
+ def check_grant_perms
136
+ @permissioned = nil
137
+ if params[:item_klass] != 'Class'
138
+ pc = AeUsers.permissioned_class(params[:item_klass])
139
+ @permissioned = pc.find(params[:item_id])
140
+ end
141
+ check_metaperms
142
+ end
143
+
144
+ def check_revoke_perms
145
+ @perm = Permission.find(params[:id])
146
+ @permissioned = @perm.permissioned
147
+ check_metaperms
148
+ if @person == @perm.grantee and @perm.permission == "change_permissions"
149
+ access_denied "Sorry, you can't revoke your own right to change permissions. (You'd probably regret it anyway!)" +
150
+ " If you're trying to transfer ownership to someone else, just give them all the permissions, and have them revoke yours."
151
+ end
152
+ end
153
+
154
+ def check_metaperms
155
+ @person = logged_in_person
156
+ if not @person.permitted?(@permissioned, "change_permissions")
157
+ access_denied "Sorry, you are not allowed to change the permissions of that object."
158
+ end
159
+ end
160
+
161
+ def check_edit_role_perms
162
+ @role ||= Role.find(params[:id])
163
+ if not @role.permitted?(logged_in_person, "edit")
164
+ access_denied "Sorry, you are not allowed to edit this role."
165
+ end
166
+ end
167
+
168
+ def check_edit_role_member_perms
169
+ @role = Role.find(params[:role])
170
+ check_edit_role_perms
171
+ end
172
+ end
@@ -0,0 +1,2 @@
1
+ module AccountHelper
2
+ end
@@ -0,0 +1,5 @@
1
+ module AuthHelper
2
+ def auth_stylesheet
3
+ "<link rel=\"stylesheet\" href=\"#{url_for :controller => 'auth', :action => 'index', :format => 'css'}\" />"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ module PermissionHelper
2
+ end
@@ -0,0 +1,50 @@
1
+ require 'digest/md5'
2
+
3
+ class Account < ActiveRecord::Base
4
+ establish_connection :users
5
+ belongs_to :person
6
+
7
+ def self.find_by_email_address(address)
8
+ p = Person.find_by_email_address(address)
9
+ if not p.nil?
10
+ return p.account
11
+ end
12
+ end
13
+
14
+ def password=(p)
15
+ if not p.nil?
16
+ write_attribute("password", Account.hash_password(p))
17
+ else
18
+ write_attribute("password", nil)
19
+ end
20
+ end
21
+
22
+ def self.hash_password(p)
23
+ if p.nil?
24
+ return nil
25
+ else
26
+ return Digest::MD5.hexdigest(p)
27
+ end
28
+ end
29
+
30
+ def check_password(p)
31
+ return self.password == Account.hash_password(p)
32
+ end
33
+
34
+ def generate_password(address = nil, length = 6)
35
+ chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('1'..'9').to_a - ['o', 'O', 'i', 'I']
36
+ genpwd = Array.new(length) { chars[rand(chars.size)] }.join
37
+ self.password= genpwd
38
+ save
39
+ AuthNotifier::deliver_generated_password(self, genpwd, address)
40
+ return genpwd
41
+ end
42
+
43
+ def generate_activation(address=nil)
44
+ self.active = false
45
+ self.activation_key = Digest::MD5.hexdigest("#{password} #{Time.now.to_s}")
46
+ self.save
47
+
48
+ AuthNotifier::deliver_account_activation(self, address)
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ class AuthNotifier < ActionMailer::Base
2
+ def account_activation(account, address=nil)
3
+ if address.nil?
4
+ address = account.person.primary_email_address
5
+ elsif address.kind_of? EmailAddress
6
+ address = address.address
7
+ end
8
+
9
+ @recipients = address
10
+ @from = "noreply@#{default_url_options[:host]}"
11
+ @subject = "Your account on #{default_url_options[:host]}"
12
+
13
+ @body["name"] = account.person.name || "New User"
14
+ @body["account"] = account
15
+ @body["server_name"] = default_url_options[:host]
16
+ end
17
+
18
+ def generated_password(account, password, address=nil)
19
+ if address.nil?
20
+ address = account.person.primary_email_address
21
+ elsif address.kind_of? EmailAddress
22
+ address = address.address
23
+ end
24
+
25
+ @recipients = address
26
+ @from = "noreply@#{default_url_options[:host]}"
27
+ @subject = "Your password has been reset on #{default_url_options[:host]}"
28
+
29
+ @body["name"] = account.person.name
30
+ @body["account"] = account
31
+ @body["server_name"] = default_url_options[:host]
32
+ @body["password"] = password
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ require 'digest/sha1'
2
+
3
+ class AuthTicket < ActiveRecord::Base
4
+ belongs_to :person
5
+ validates_uniqueness_of :secret
6
+
7
+ before_save do |record|
8
+ record.secret
9
+ end
10
+
11
+ def self.find_ticket(secret)
12
+ AuthTicket.find(:first, :conditions => ["secret = ? and (expires_at is null or expires_at > current_timestamp())", secret])
13
+ end
14
+
15
+ def generate_secret
16
+ secret = nil
17
+ while secret.nil?
18
+ hashseed = "#{id}_#{Time.new.to_i}_#{rand}"
19
+ secret = Digest::SHA1.hexdigest(hashseed)
20
+ if AuthTicket.find_by_secret(secret)
21
+ secret = nil
22
+ end
23
+ end
24
+ self.secret = secret
25
+ return secret
26
+ end
27
+
28
+ def secret
29
+ if read_attribute(:secret).nil?
30
+ generate_secret
31
+ else
32
+ read_attribute(:secret)
33
+ end
34
+ end
35
+
36
+ def expired?
37
+ expires_at and expires_at <= Time.new
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ class EmailAddress < ActiveRecord::Base
2
+ establish_connection :users
3
+ belongs_to :person
4
+ validates_uniqueness_of :address
5
+
6
+ def primary=(value)
7
+ if value and not person.nil?
8
+ person.email_addresses.each do |addr|
9
+ if addr != self
10
+ addr.primary = false
11
+ addr.save
12
+ end
13
+ end
14
+ end
15
+ write_attribute(:primary, value)
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ class Login
2
+ attr_accessor :email, :password, :remember, :return_to, :have_password
3
+
4
+ def initialize(args)
5
+ if not args.nil?
6
+ if args[:email]
7
+ self.email = args[:email]
8
+ end
9
+ if args[:password]
10
+ self.password = args[:password]
11
+ end
12
+ if args[:remember]
13
+ self.remember = args[:remember]
14
+ end
15
+ if args[:return_to]
16
+ self.return_to = args[:return_to]
17
+ end
18
+ if args[:have_password]
19
+ self.have_password = args[:have_password] == "true"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ class OpenIdIdentity < ActiveRecord::Base
2
+ establish_connection :users
3
+ belongs_to :person
4
+ validates_uniqueness_of :identity_url
5
+ end
@@ -0,0 +1,57 @@
1
+ class Permission < ActiveRecord::Base
2
+ belongs_to :role
3
+ belongs_to :person
4
+ belongs_to :permissioned, :polymorphic => true
5
+
6
+ def object
7
+ return permissioned
8
+ end
9
+
10
+ def grantee
11
+ if not role.nil?
12
+ return role
13
+ else
14
+ return person
15
+ end
16
+ end
17
+
18
+ def cache_conds
19
+ cond_sql = "permission_name = ?"
20
+ cond_objs = [permission]
21
+ if person
22
+ cond_sql += " and person_id = ?"
23
+ cond_objs += [person.id]
24
+ end
25
+ if permissioned
26
+ cond_sql += " and permissioned_type = ? and permissioned_id = ?"
27
+ cond_objs += [permissioned.class.name, permissioned.id]
28
+ end
29
+ return [cond_sql] + cond_objs
30
+ end
31
+
32
+ def destroy_caches_for_person(p)
33
+ if AeUsers.cache_permissions?
34
+ if permissioned and permission
35
+ AeUsers.permission_cache.invalidate(p, permissioned, permission)
36
+ else
37
+ AeUsers.permission_cache.invalidate_all(:person => p)
38
+ end
39
+ end
40
+ end
41
+
42
+ def destroy_caches
43
+ if AeUsers.cache_permissions?
44
+ if person
45
+ destroy_caches_for_person(person)
46
+ elsif role
47
+ role.members.each do |person|
48
+ AeUsers.permission_cache.invalidate(person, permissioned, permission)
49
+ end
50
+ elsif permissioned and permission
51
+ AeUsers.permission_cache.invalidate_all(:permissioned => permissioned, :permission => permission)
52
+ else
53
+ AeUsers.permission_cache.invalidate_all
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,156 @@
1
+ class Person < ActiveRecord::Base
2
+ establish_connection :users
3
+ has_one :account
4
+ has_many :open_id_identities
5
+ has_and_belongs_to_many :roles
6
+ has_many :permissions, :dependent => :destroy, :include => :permissioned
7
+ has_many :email_addresses, :dependent => :destroy
8
+
9
+ def self.sreg_map
10
+ {:fullname => Proc.new do |fullname|
11
+ if fullname =~ /^([^ ]+) +(.*)$/
12
+ {:firstname => $1, :lastname => $2}
13
+ else
14
+ {:firstname => fullname}
15
+ end
16
+ end,
17
+ :dob => Proc.new do |dob|
18
+ if dob =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/
19
+ {:birthdate => Time.local($1, $2, $3)}
20
+ else
21
+ {}
22
+ end
23
+ end,
24
+ :gender => Proc.new do |gender|
25
+ if gender == 'M'
26
+ {:gender => 'male'}
27
+ elsif gender == 'F'
28
+ {:gender => 'female'}
29
+ else
30
+ {}
31
+ end
32
+ end,
33
+ :email => Proc.new do |email|
34
+ {:primary_email_address => email}
35
+ end
36
+ }
37
+ end
38
+
39
+ def self.find_by_email_address(address)
40
+ ea = EmailAddress.find_by_address(address)
41
+ if not ea.nil?
42
+ return ea.person
43
+ end
44
+ end
45
+
46
+ def primary_email_address
47
+ primary = email_addresses.find_by_primary true
48
+ if not primary
49
+ primary = email_addresses.find :first
50
+ end
51
+ if primary.nil?
52
+ return nil
53
+ else
54
+ return primary.address
55
+ end
56
+ end
57
+
58
+ def primary_email_address=(address)
59
+ if primary_email_address != address
60
+ ea = email_addresses.find_or_create_by_address(address)
61
+ ea.primary = true
62
+ ea.save
63
+ end
64
+ end
65
+
66
+ def all_permissions
67
+ allperms = permissions
68
+ roles.each do |role|
69
+ allperms += role.permissions
70
+ end
71
+ return allperms
72
+ end
73
+
74
+ def permitted?(obj, perm_name)
75
+ if AeUsers.cache_permissions?
76
+ if obj and obj.kind_of? ActiveRecord::Base
77
+ return AeUsers.permission_cache.permitted?(self, obj, perm_name)
78
+ else
79
+ return AeUsers.permission_cache.permitted?(self, nil, perm_name)
80
+ end
81
+ else
82
+ return uncached_permitted?(obj, perm_name)
83
+ end
84
+ end
85
+
86
+ def uncached_permitted?(obj, perm_name)
87
+ result = false
88
+ all_permissions.each do |permission|
89
+ po = permission.permissioned
90
+
91
+ if po.kind_of? ActiveRecord::Base
92
+ objmatch = (po.class.name == obj.class.name and po.id == obj.id)
93
+ else
94
+ objmatch = (po == obj)
95
+ end
96
+
97
+ permmatch = (permission.permission == perm_name)
98
+
99
+ result = ((po.nil? or objmatch) and
100
+ (permission.permission.nil? or permmatch))
101
+
102
+ if result
103
+ break
104
+ end
105
+ end
106
+ logger.debug "Permission check result: #{result}"
107
+ return result
108
+ end
109
+
110
+ def administrator_classes
111
+ AeUsers.permissioned_classes.select do |c|
112
+ permitted?(c, "change_permissions_#{c.name.tableize}")
113
+ end
114
+ end
115
+
116
+ def administrator?
117
+ administrator_classes.length > 0
118
+ end
119
+
120
+ def current_age
121
+ age_as_of Date.today
122
+ end
123
+
124
+ def age_as_of(base = Date.today)
125
+ if not birthdate.nil?
126
+ base.year - birthdate.year - ((base.month * 100 + base.day >= birthdate.month * 100 + birthdate.day) ? 0 : 1)
127
+ end
128
+ end
129
+
130
+ def app_profile
131
+ @app_profile ||= AeUsers.profile_class.find_by_person_id(id)
132
+ @app_profile
133
+ end
134
+
135
+ def profile
136
+ app_profile
137
+ end
138
+
139
+ def name
140
+ return "#{firstname} #{lastname}"
141
+ # n = firstname
142
+ # if nickname and nickname.length > 0
143
+ # n += " \"#{nickname}\""
144
+ # end
145
+ # n += " #{lastname}"
146
+ # return n
147
+ end
148
+
149
+ if not AeUsers.profile_class.nil?
150
+ class_eval <<-END_CODE
151
+ def #{AeUsers.profile_class.name.tableize.singularize}
152
+ app_profile
153
+ end
154
+ END_CODE
155
+ end
156
+ end