Merge branch '6221-write-trash-list'
[arvados.git] / services / api / app / controllers / arvados / v1 / repositories_controller.rb
1 class Arvados::V1::RepositoriesController < ApplicationController
2   skip_before_filter :find_object_by_uuid, :only => :get_all_permissions
3   skip_before_filter :render_404_if_no_object, :only => :get_all_permissions
4   before_filter :admin_required, :only => :get_all_permissions
5
6   def get_all_permissions
7     # users is a map of {user_uuid => User object}
8     users = {}
9     # user_aks is a map of {user_uuid => array of public keys}
10     user_aks = {}
11     # admins is an array of user_uuids
12     admins = []
13     User.eager_load(:authorized_keys).find_each do |u|
14       next unless u.is_active or u.uuid == anonymous_user_uuid
15       users[u.uuid] = u
16       user_aks[u.uuid] = u.authorized_keys.collect do |ak|
17         {
18           public_key: ak.public_key,
19           authorized_key_uuid: ak.uuid
20         }
21       end
22       admins << u.uuid if u.is_admin
23     end
24     @repo_info = {}
25     Repository.eager_load(:permissions).find_each do |repo|
26       @repo_info[repo.uuid] = {
27         uuid: repo.uuid,
28         name: repo.name,
29         push_url: repo.push_url,
30         fetch_url: repo.fetch_url,
31         user_permissions: {},
32       }
33       # evidence is an array of {name: 'can_xxx', user_uuid: 'x-y-z'},
34       # one entry for each piece of evidence we find in the permission
35       # database that establishes that a user can access this
36       # repository. Multiple entries can be added for a given user,
37       # possibly with different access levels; these will be compacted
38       # below.
39       evidence = []
40       repo.permissions.each do |perm|
41         if ArvadosModel::resource_class_for_uuid(perm.tail_uuid) == Group
42           # A group has permission. Each user who has access to this
43           # group also has access to the repository. Access level is
44           # min(group-to-repo permission, user-to-group permission).
45           users.each do |user_uuid, user|
46             perm_mask = user.group_permissions[perm.tail_uuid]
47             if not perm_mask
48               next
49             elsif perm_mask[:manage] and perm.name == 'can_manage'
50               evidence << {name: 'can_manage', user_uuid: user_uuid}
51             elsif perm_mask[:write] and ['can_manage', 'can_write'].index perm.name
52               evidence << {name: 'can_write', user_uuid: user_uuid}
53             elsif perm_mask[:read]
54               evidence << {name: 'can_read', user_uuid: user_uuid}
55             end
56           end
57         elsif users[perm.tail_uuid]
58           # A user has permission; the user exists; and either the
59           # user is active, or it's the special case of the anonymous
60           # user which is never "active" but is allowed to read
61           # content from public repositories.
62           evidence << {name: perm.name, user_uuid: perm.tail_uuid}
63         end
64       end
65       # Owner of the repository, and all admins, can do everything.
66       ([repo.owner_uuid] | admins).each do |user_uuid|
67         # Except: no permissions for inactive users, even if they own
68         # repositories.
69         next unless users[user_uuid]
70         evidence << {name: 'can_manage', user_uuid: user_uuid}
71       end
72       # Distill all the evidence about permissions on this repository
73       # into one hash per user, of the form {'can_xxx' => true, ...}.
74       # The hash is nil for a user who has no permissions at all on
75       # this particular repository.
76       evidence.each do |perm|
77         user_uuid = perm[:user_uuid]
78         user_perms = (@repo_info[repo.uuid][:user_permissions][user_uuid] ||= {})
79         user_perms[perm[:name]] = true
80       end
81     end
82     # Revisit each {'can_xxx' => true, ...} hash for some final
83     # cleanup to make life easier for the requestor.
84     #
85     # Add a 'gitolite_permissions' key alongside the 'can_xxx' keys,
86     # for the convenience of the gitolite config file generator.
87     #
88     # Add all lesser permissions when a greater permission is
89     # present. If the requestor only wants to know who can write, it
90     # only has to test for 'can_write' in the response.
91     @repo_info.values.each do |repo|
92       repo[:user_permissions].each do |user_uuid, user_perms|
93         if user_perms['can_manage']
94           user_perms['gitolite_permissions'] = 'RW'
95           user_perms['can_write'] = true
96           user_perms['can_read'] = true
97         elsif user_perms['can_write']
98           user_perms['gitolite_permissions'] = 'RW'
99           user_perms['can_read'] = true
100         elsif user_perms['can_read']
101           user_perms['gitolite_permissions'] = 'R'
102         end
103       end
104     end
105     # The response looks like
106     #   {"kind":"...",
107     #    "repositories":[r1,r2,r3,...],
108     #    "user_keys":usermap}
109     # where each of r1,r2,r3 looks like
110     #   {"uuid":"repo-uuid-1",
111     #    "name":"username/reponame",
112     #    "push_url":"...",
113     #    "user_permissions":{"user-uuid-a":{"can_read":true,"gitolite_permissions":"R"}}}
114     # and usermap looks like
115     #   {"user-uuid-a":[{"public_key":"ssh-rsa g...","authorized_key_uuid":"ak-uuid-g"},...],
116     #    "user-uuid-b":[{"public_key":"ssh-rsa h...","authorized_key_uuid":"ak-uuid-h"},...],...}
117     send_json(kind: 'arvados#RepositoryPermissionSnapshot',
118               repositories: @repo_info.values,
119               user_keys: user_aks)
120   end
121 end