add /authorized_keys/get_all_logins
[arvados.git] / services / api / app / models / user.rb
1 class User < ArvadosModel
2   include AssignUuid
3   include KindAndEtag
4   include CommonApiTemplate
5   serialize :prefs, Hash
6   has_many :api_client_authorizations
7   before_update :prevent_privilege_escalation
8
9   has_many :authorized_keys, :foreign_key => :authorized_user, :primary_key => :uuid
10
11   api_accessible :superuser, :extend => :common do |t|
12     t.add :email
13     t.add :full_name
14     t.add :first_name
15     t.add :last_name
16     t.add :identity_url
17     t.add :is_admin
18     t.add :prefs
19   end
20
21   ALL_PERMISSIONS = {read: true, write: true, manage: true}
22
23   def full_name
24     "#{first_name} #{last_name}"
25   end
26
27   def groups_i_can(verb)
28     self.group_permissions.select { |uuid, mask| mask[verb] }.keys
29   end
30
31   def can?(actions)
32     actions.each do |action, target|
33       target_uuid = target
34       if target.respond_to? :uuid
35         target_uuid = target.uuid
36       end
37       next if target_uuid == self.uuid
38       next if (group_permissions[target_uuid] and
39                group_permissions[target_uuid][action])
40       if target.respond_to? :owner
41         next if target.owner == self.uuid
42         next if (group_permissions[target.owner] and
43                  group_permissions[target.owner][action])
44       end
45       return false
46     end
47     true
48   end
49
50   def self.invalidate_permissions_cache
51     Rails.cache.delete_matched(/^groups_for_user_/)
52   end
53
54   protected
55
56   def permission_to_create
57     Thread.current[:user] == self or
58       (Thread.current[:user] and Thread.current[:user].is_admin)
59   end
60
61   def prevent_privilege_escalation
62     if self.is_admin_changed? and !current_user.is_admin
63       if current_user.uuid == self.uuid
64         if self.is_admin != self.is_admin_was
65           logger.warn "User #{self.uuid} tried to change is_admin from #{self.is_admin_was} to #{self.is_admin}"
66           self.is_admin = self.is_admin_was
67         end
68       end
69     end
70     true
71   end
72
73   def group_permissions
74     Rails.cache.fetch "groups_for_user_#{self.uuid}" do
75       permissions_from = {}
76       todo = {self.uuid => true}
77       done = {}
78       while !todo.empty?
79         lookup_uuids = todo.keys
80         lookup_uuids.each do |uuid| done[uuid] = true end
81         todo = {}
82         Link.where('tail_uuid in (?) and link_class = ? and head_kind = ?',
83                    lookup_uuids,
84                    'permission',
85                    'arvados#group').each do |link|
86           unless done.has_key? link.head_uuid
87             todo[link.head_uuid] = true
88           end
89           link_permissions = {}
90           case link.name
91           when 'can_read'
92             link_permissions = {read:true}
93           when 'can_write'
94             link_permissions = {read:true,write:true}
95           when 'can_manage'
96             link_permissions = ALL_PERMISSIONS
97           end
98           permissions_from[link.tail_uuid] ||= {}
99           permissions_from[link.tail_uuid][link.head_uuid] ||= {}
100           link_permissions.each do |k,v|
101             permissions_from[link.tail_uuid][link.head_uuid][k] ||= v
102           end
103         end
104       end
105       search_permissions(self.uuid, permissions_from)
106     end
107   end
108
109   def search_permissions(start, graph, merged={}, upstream_mask=nil, upstream_path={})
110     nextpaths = graph[start]
111     return merged if !nextpaths
112     return merged if upstream_path.has_key? start
113     upstream_path[start] = true
114     upstream_mask ||= ALL_PERMISSIONS
115     nextpaths.each do |head, mask|
116       merged[head] ||= {}
117       mask.each do |k,v|
118         merged[head][k] ||= v if upstream_mask[k]
119       end
120       search_permissions(head, graph, merged, upstream_mask.select { |k,v| v && merged[head][k] }, upstream_path)
121     end
122     upstream_path.delete start
123     merged
124   end
125 end