[ARVADOS] created: 5ff17b27bd039f3b219e18dfc2e1d67d8043e11d
git at public.curoverse.com
git at public.curoverse.com
Tue Jul 15 16:23:33 EDT 2014
at 5ff17b27bd039f3b219e18dfc2e1d67d8043e11d (commit)
commit 5ff17b27bd039f3b219e18dfc2e1d67d8043e11d
Author: Brett Smith <brett at curoverse.com>
Date: Tue Jul 15 16:16:38 2014 -0400
2044: Add Workbench interface to manage project sharing.
This empowers administrators to add, modify, and remove permission
links on projects.
diff --git a/apps/workbench/app/assets/javascripts/select_modal.js b/apps/workbench/app/assets/javascripts/select_modal.js
index 0a58213..fbdf15f 100644
--- a/apps/workbench/app/assets/javascripts/select_modal.js
+++ b/apps/workbench/app/assets/javascripts/select_modal.js
@@ -68,6 +68,11 @@ $(document).on('click', '.selectable', function() {
});
$(document).on('page-refresh', function(event, data, status, jqxhr, action_data) {
window.location.reload();
+}).on('tab-refresh', function(event, data, status, jqxhr, action_data) {
+ tab_name = $('.tab-pane.active')[0].id;
+ tab_pane_valid_state[tab_name] = false;
+ ajaxRefreshTabPane(tab_name);
+ $('body > .modal-container .modal').modal('hide');
}).on('redirect-to-created-object', function(event, data, status, jqxhr, action_data) {
window.location.href = data.href.replace(/^[^\/]*\/\/[^\/]*/, '');
});
diff --git a/apps/workbench/app/controllers/projects_controller.rb b/apps/workbench/app/controllers/projects_controller.rb
index 352b0ae..e45e840 100644
--- a/apps/workbench/app/controllers/projects_controller.rb
+++ b/apps/workbench/app/controllers/projects_controller.rb
@@ -8,7 +8,7 @@ class ProjectsController < ApplicationController
end
def show_pane_list
- %w(Contents Permissions Advanced)
+ %w(Contents Sharing Advanced)
end
def remove_item
@@ -83,6 +83,11 @@ class ProjectsController < ApplicationController
@share_links = Link.filter([['head_uuid', '=', @object.uuid],
['link_class', '=', 'permission']])
@logs = Log.limit(10).filter([['object_uuid', '=', @object.uuid]])
+ @users = User.limit(10000).
+ select(["uuid", "is_active", "first_name", "last_name"]).
+ filter([['is_active', '=', 'true']])
+ @groups = Group.limit(10000).
+ select(["uuid", "name", "description"])
@objects_and_names = []
@objects.each do |object|
@@ -121,6 +126,20 @@ class ProjectsController < ApplicationController
end
end
+ helper_method :managed_by_user?
+ def managed_by_user?(user=nil)
+ user ||= current_user
+ if user.nil?
+ false
+ else
+ user_uuid = user.uuid
+ user.is_admin or (user_uuid == @object.owner_uuid) or
+ @share_links.any? { |link|
+ (link.tail_uuid == user_uuid) and (link.name == "can_manage")
+ }
+ end
+ end
+
def create
@new_resource_attrs = (params['project'] || {}).merge(group_class: 'project')
@new_resource_attrs[:name] ||= 'New project'
diff --git a/apps/workbench/app/views/application/_choose.html.erb b/apps/workbench/app/views/application/_choose.html.erb
index fdd3d3c..18df513 100644
--- a/apps/workbench/app/views/application/_choose.html.erb
+++ b/apps/workbench/app/views/application/_choose.html.erb
@@ -30,16 +30,20 @@
</div>
</nav>
+ <% preview_pane = (params[:preview_pane] != "false")
+ pane_col_class = preview_pane ? "col-sm-6" : "" %>
<div class="row" style="height: 20em">
- <div class="col-sm-6 container-fluid arv-filterable-list selectable-container"
+ <div class="<%= pane_col_class %> container-fluid arv-filterable-list selectable-container"
style="height: 100%; overflow-y: scroll"
data-infinite-scroller="#choose-scroll"
id="choose-scroll"
data-infinite-content-href="<%= @next_page_href %>">
<%= render partial: 'choose_rows', locals: {multiple: multiple} %>
</div>
- <div class="col-sm-6 modal-dialog-preview-pane" style="height: 100%; overflow-y: scroll">
+ <% if preview_pane %>
+ <div class="<%= pane_col_class %> modal-dialog-preview-pane" style="height: 100%; overflow-y: scroll">
</div>
+ <% end %>
</div>
<div class="modal-footer">
diff --git a/apps/workbench/app/views/groups/_choose_rows.html.erb b/apps/workbench/app/views/groups/_choose_rows.html.erb
new file mode 100644
index 0000000..772ef19
--- /dev/null
+++ b/apps/workbench/app/views/groups/_choose_rows.html.erb
@@ -0,0 +1,9 @@
+<% icon_class = fa_icon_class_for_class(Group) %>
+<% @objects.each do |object| %>
+ <div class="row filterable selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= object.uuid %>">
+ <div class="col-sm-12" style="overflow-x:hidden">
+ <i class="fa fa-fw <%= icon_class %>"></i>
+ <%= object.name %>
+ </div>
+ </div>
+<% end %>
diff --git a/apps/workbench/app/views/projects/_show_permissions.html.erb b/apps/workbench/app/views/projects/_show_permissions.html.erb
deleted file mode 100644
index 0817062..0000000
--- a/apps/workbench/app/views/projects/_show_permissions.html.erb
+++ /dev/null
@@ -1,44 +0,0 @@
-<div>
- <div class="row">
- <div class="col-md-6 col-md-push-6">
- <div class="panel panel-default">
- <div class="panel-heading">
- Additional permissions
- </div>
- <div class="panel-body">
- <p>
- <% if not @share_links.any? %>
- <span class="deemphasize">(No additional permissions)</span>
- <% else %>
- Also shared with:
- <% @share_links.andand.each do |link| %>
- <br /><%= link_to_if_arvados_object link.tail_uuid, friendly_name: true %>
- <% end %>
- <% end %>
- </p>
- </div>
- </div>
- </div>
- <div class="col-md-6 col-md-pull-6">
- <% if @object.owner %>
- <div class="panel panel-default">
- <div class="panel-heading">
- Inherited permissions
- </div>
- <div class="panel-body">
- <p>Permissions for this project are inherited by the owner or parent project:
- </p>
- <p style="margin-left: 4em">
- <% if User == resource_class_for_uuid(@object.owner_uuid) %>
- <i class="fa fa-fw fa-user"></i>
- <% else %>
- <i class="fa fa-fw fa-folder"></i>
- <% end %>
- <%= link_to_if_arvados_object @object.owner_uuid, friendly_name: true %>
- </p>
- </div>
- </div>
- <% end %>
- </div>
- </div>
-</div>
diff --git a/apps/workbench/app/views/projects/_show_sharing.html.erb b/apps/workbench/app/views/projects/_show_sharing.html.erb
new file mode 100644
index 0000000..0185d77
--- /dev/null
+++ b/apps/workbench/app/views/projects/_show_sharing.html.erb
@@ -0,0 +1,104 @@
+<%
+ uuid_map = {}
+ [@users, @groups].each do |obj_list|
+ obj_list.each { |o| uuid_map[o.uuid] = o }
+ end
+ perm_name_desc_map = {}
+ perm_desc_name_map = {}
+ perms_json = []
+ ['Read', 'Write', 'Manage'].each do |link_desc|
+ link_name = "can_#{link_desc.downcase}"
+ perm_name_desc_map[link_name] = link_desc
+ perm_desc_name_map[link_desc] = link_name
+ perms_json << {value: link_name, text: link_desc}
+ end
+ perms_json = perms_json.to_json
+ manager = managed_by_user?
+ owner_icon = fa_icon_class_for_uuid(@object.owner_uuid)
+ if owner_icon == "fa-users"
+ owner_icon = "fa-folder"
+ owner_type = "parent project"
+ else
+ owner_type = "owning user"
+ end
+%>
+
+<% if manager %>
+<div class="pull-right">
+ <% ["users", "groups"].each do |share_class| %>
+
+ <%= link_to(send("choose_#{share_class}_path",
+ title: "Share with #{share_class}",
+ preview_pane: false,
+ multiple: true,
+ limit: 10000,
+ action_method: 'post',
+ action_href: share_with_project_path,
+ action_name: 'Add',
+ action_data: {selection_param: 'uuids[]', success: 'tab-refresh'}.to_json),
+ class: "btn btn-primary btn-sm", remote: true, method: 'get') do %>
+ <i class="fa fa-fw fa-plus"></i> Share with <%= share_class %>…
+ <% end %>
+
+ <% end %>
+</div>
+<% end %>
+
+<p>Permissions for this project are inherited from the <%= owner_type %>
+ <i class="fa fa-fw <%= owner_icon %>"></i>
+ <%= link_to_if_arvados_object @object.owner_uuid, friendly_name: true %>.
+</p>
+
+<table id="project_sharing" class="topalign table" style="clear: both; margin-top: 1em;">
+ <tr>
+ <th>User/Group Name</th>
+ <th<%= raw(' colspan="2"') if manager %>>Project Access</th>
+ </tr>
+
+ <% @share_links.andand.each do |link|
+ shared_with = uuid_map[link.tail_uuid]
+ if shared_with.nil?
+ link_name = link.tail_uuid
+ elsif shared_with.respond_to?(:full_name)
+ link_name = shared_with.full_name
+ else
+ link_name = shared_with.name
+ end %>
+ <tr data-object-uuid="<%= link.uuid %>">
+ <td>
+ <i class="fa fa-fw <%= fa_icon_class_for_uuid(link.tail_uuid) %>"></i>
+ <%= link_to_if_arvados_object(link.tail_uuid, link_text: link_name) %>
+ </td>
+ <% if manager %>
+ <td><%= link_to perm_name_desc_map[link.name], '#', {
+ "data-emptytext" => "Read",
+ "data-placement" => "bottom",
+ "data-type" => "select",
+ "data-url" => url_for(action: "update", id: link.uuid, controller: "links", merge: true),
+ "data-title" => "Set #{link_name}'s access level",
+ "data-name" => "[name]",
+ "data-pk" => {id: link.tail_uuid, key: "link"}.to_json,
+ "data-value" => link.name,
+ "data-clear" => false,
+ "data-source" => perms_json,
+ "data-tpl" => "<select id=\"share_change_level\"></select>",
+ "class" => "editable form-control",
+ } %>
+ </td>
+ <td>
+ <%= link_to(
+ {action: 'destroy', id: link.uuid, controller: "links"},
+ {title: 'Revoke', class: 'btn btn-default btn-nodecorate', method: :delete,
+ data: {confirm: "Revoke #{link_name}'s access to this project?",
+ remote: true}}) do %>
+ <i class="fa fa-fw fa-trash-o"></i>
+ <% end %>
+ </td>
+ <% else %>
+ <td>
+ <%= perm_name_desc_map[link.name] %>
+ </td>
+ <% end %>
+ </tr>
+ <% end %>
+</table>
diff --git a/apps/workbench/app/views/users/_choose_rows.html.erb b/apps/workbench/app/views/users/_choose_rows.html.erb
new file mode 100644
index 0000000..a893e5e
--- /dev/null
+++ b/apps/workbench/app/views/users/_choose_rows.html.erb
@@ -0,0 +1,9 @@
+<% icon_class = fa_icon_class_for_class(User) %>
+<% @objects.each do |object| %>
+ <div class="row filterable selectable <%= 'multiple' if multiple %>" data-object-uuid="<%= object.uuid %>">
+ <div class="col-sm-12" style="overflow-x:hidden">
+ <i class="fa fa-fw <%= icon_class %>"></i>
+ <%= object.full_name %>
+ </div>
+ </div>
+<% end %>
diff --git a/apps/workbench/config/routes.rb b/apps/workbench/config/routes.rb
index 9701f41..55e2fda 100644
--- a/apps/workbench/config/routes.rb
+++ b/apps/workbench/config/routes.rb
@@ -24,6 +24,7 @@ ArvadosWorkbench::Application.routes.draw do
match '/logout' => 'sessions#destroy', via: [:get, :post]
get '/logged_out' => 'sessions#index'
resources :users do
+ get 'choose', :on => :collection
get 'home', :on => :member
get 'welcome', :on => :collection
get 'activity', :on => :collection
@@ -35,7 +36,9 @@ ArvadosWorkbench::Application.routes.draw do
resources :logs
resources :factory_jobs
resources :uploaded_datasets
- resources :groups
+ resources :groups do
+ get 'choose', on: :collection
+ end
resources :specimens
resources :pipeline_templates do
get 'choose', on: :collection
diff --git a/apps/workbench/test/integration/projects_test.rb b/apps/workbench/test/integration/projects_test.rb
index 5758fc7..846c57a 100644
--- a/apps/workbench/test/integration/projects_test.rb
+++ b/apps/workbench/test/integration/projects_test.rb
@@ -75,13 +75,83 @@ class ProjectsTest < ActionDispatch::IntegrationTest
find('.modal-footer a,button', text: 'Move').click
wait_for_ajax
- # Wait for the page to refresh and show the new parent in Permissions panel
- click_link 'Permissions'
- find('.panel', text: 'Project 1234')
-
- assert(find('.panel', text: 'Permissions for this project are inherited by the owner or parent project').
- all('*', text: 'Project 1234').any?,
+ # Wait for the page to refresh and show the new parent in Sharing panel
+ click_link 'Sharing'
+ assert(page.has_link?("Project 1234"),
"Project 5678 should now be inside project 1234")
end
+ def show_project_using(auth_key, proj_key='aproject')
+ project_uuid = api_fixture('groups')[proj_key]['uuid']
+ visit(page_with_token(auth_key, "/projects/#{project_uuid}"))
+ assert(page.has_text?("A Project"), "not on expected project page")
+ end
+
+ def count_shares
+ page.all("#project_sharing tr").size
+ end
+
+ def add_share_and_check(share_type, name)
+ start_share_count = count_shares
+ click_on("Share with #{share_type}")
+ find(".selectable", text: name).click
+ find(".modal-footer a,button", text: "Add").click
+ # click_on "Sharing"; puts "FIXME: Manually re-clicking the Sharing tab shouldn't be necessary"
+ assert(page.has_link?(name),
+ "new share was not added to sharing table")
+ assert_equal(start_share_count + 1, count_shares,
+ "new share did not add row to sharing table")
+ end
+
+ def modify_share_and_check(name)
+ start_share_count = count_shares
+ link_row = all("#project_sharing tr").select { |row| row.has_text?(name) }
+ assert_equal(1, link_row.size, "row with new permission not found")
+ within(link_row.first) do
+ click_on("Read")
+ select("Write", from: "share_change_level")
+ click_on("editable-submit")
+ assert(has_link?("Write"),
+ "failed to change access level on new share")
+ click_on "Revoke"
+ end
+ assert(page.has_no_text?(name),
+ "new share row still exists after being revoked")
+ assert_equal(start_share_count - 1, count_shares,
+ "revoking share did not remove row from sharing table")
+ end
+
+ test "project viewer can see project sharing, but not change it" do
+ show_project_using("project_viewer")
+ click_on "Sharing"
+ assert(page.has_text?("Project Viewer"), "did not find self on sharing tab")
+ assert(page.has_no_link?(/add a (user|group)/i),
+ "read-only project user given option to add permissions")
+ assert_empty(all("#project_sharing a").
+ reject { |a| a[:href] =~ %r{/(users|groups)/[-0-9a-z]+$} },
+ "read-only project user given option to modify permissions")
+ end
+
+ test "project owner can manage sharing for another user" do
+ add_user = api_fixture('users')['future_project_user']
+ new_name = ["first_name", "last_name"].map { |k| add_user[k] }.join(" ")
+
+ show_project_using("active")
+ click_on "Sharing"
+ assert(page.has_no_text?(new_name),
+ "project is already shared with user to add")
+ add_share_and_check("users", new_name)
+ modify_share_and_check(new_name)
+ end
+
+ test "project owner can manage sharing for another group" do
+ new_name = api_fixture('groups')['future_project_viewing_group']['name']
+
+ show_project_using("active")
+ click_on "Sharing"
+ assert(page.has_no_text?(new_name),
+ "project is already shared with group to add")
+ add_share_and_check("groups", new_name)
+ modify_share_and_check(new_name)
+ end
end
commit c319de3e3107dbd9820f49e1a5f8d019accb8783
Author: Brett Smith <brett at curoverse.com>
Date: Tue Jul 15 15:39:10 2014 -0400
2044: Workbench route to share a project with others.
This route is intended to support AJAX requests from the interface to
share a project with one or more users/groups.
diff --git a/apps/workbench/app/controllers/projects_controller.rb b/apps/workbench/app/controllers/projects_controller.rb
index 91d6e8a..352b0ae 100644
--- a/apps/workbench/app/controllers/projects_controller.rb
+++ b/apps/workbench/app/controllers/projects_controller.rb
@@ -131,4 +131,26 @@ class ProjectsController < ApplicationController
@updates = params['project']
super
end
+
+ def share_with
+ if not params[:uuids].andand.any?
+ @errors = ["No user/group UUIDs specified to share with."]
+ return render_error(status: 422)
+ end
+ results = {"success" => [], "failure" => []}
+ params[:uuids].each do |shared_uuid|
+ begin
+ Link.create(tail_uuid: shared_uuid, link_class: "permission",
+ name: "can_read", head_uuid: @object.uuid)
+ rescue ArvadosApiClient::ApiError => error
+ results["failure"] << shared_uuid
+ else
+ results["success"] << shared_uuid
+ end
+ end
+ status = (results["failure"].empty?) ? 200 : 422
+ respond_to do |f|
+ f.json { render(json: results, status: status) }
+ end
+ end
end
diff --git a/apps/workbench/config/routes.rb b/apps/workbench/config/routes.rb
index a4f69b3..9701f41 100644
--- a/apps/workbench/config/routes.rb
+++ b/apps/workbench/config/routes.rb
@@ -61,6 +61,7 @@ ArvadosWorkbench::Application.routes.draw do
match 'remove/:item_uuid', on: :member, via: :delete, action: :remove_item
match 'remove_items', on: :member, via: :delete, action: :remove_items
get 'choose', on: :collection
+ post 'share_with', on: :member
end
post 'actions' => 'actions#post'
diff --git a/apps/workbench/test/functional/projects_controller_test.rb b/apps/workbench/test/functional/projects_controller_test.rb
index b338aa8..86f525d 100644
--- a/apps/workbench/test/functional/projects_controller_test.rb
+++ b/apps/workbench/test/functional/projects_controller_test.rb
@@ -9,4 +9,17 @@ class ProjectsControllerTest < ActionController::TestCase
assert_template 'user_agreements/index',
"Inactive user was not presented with a user agreement at the front page"
end
+
+ test "sharing a project with a user and group" do
+ uuid_list = [api_fixture("groups")["future_project_viewing_group"]["uuid"],
+ api_fixture("users")["future_project_user"]["uuid"]]
+ post(:share_with, {
+ id: api_fixture("groups")["asubproject"]["uuid"],
+ format: "json",
+ uuids: uuid_list},
+ session_for(:active))
+ assert_response :success
+ json_response = Oj.load(@response.body)
+ assert_equal(uuid_list, json_response["success"])
+ end
end
diff --git a/services/api/test/fixtures/groups.yml b/services/api/test/fixtures/groups.yml
index cd6157b..62b9958 100644
--- a/services/api/test/fixtures/groups.yml
+++ b/services/api/test/fixtures/groups.yml
@@ -68,6 +68,17 @@ asubproject:
description: "Test project belonging to active user's first test project"
group_class: folder
+future_project_viewing_group:
+ uuid: zzzzz-j7d0g-futrprojviewgrp
+ owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+ created_at: 2014-04-21 15:37:48 -0400
+ modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+ modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+ modified_at: 2014-04-21 15:37:48 -0400
+ updated_at: 2014-04-21 15:37:48 -0400
+ name: Future Project Viewing Group
+ description: "Group used to test granting Group Project viewing"
+
bad_group_has_ownership_cycle_a:
uuid: zzzzz-j7d0g-cx2al9cqkmsf1hs
owner_uuid: zzzzz-j7d0g-0077nzts8c178lw
diff --git a/services/api/test/fixtures/users.yml b/services/api/test/fixtures/users.yml
index f6d5b21..08976b8 100644
--- a/services/api/test/fixtures/users.yml
+++ b/services/api/test/fixtures/users.yml
@@ -50,6 +50,17 @@ project_viewer:
is_admin: false
prefs: {}
+future_project_user:
+ # Workbench tests give this user permission on aproject.
+ uuid: zzzzz-tpzed-futureprojview2
+ email: future-project-user at arvados.local
+ first_name: Future Project
+ last_name: User
+ identity_url: https://future-project-user.openid.local
+ is_active: true
+ is_admin: false
+ prefs: {}
+
spectator:
uuid: zzzzz-tpzed-l1s2piq4t4mps8r
email: spectator at arvados.local
commit 6ea6e2523b7c9fdd5e8816a3c9d20512d5779ac1
Author: Brett Smith <brett at curoverse.com>
Date: Tue Jul 15 15:35:12 2014 -0400
2044: Add more helpers to get FA icons for objects.
diff --git a/apps/workbench/app/helpers/application_helper.rb b/apps/workbench/app/helpers/application_helper.rb
index d1bac0c..c3856c2 100644
--- a/apps/workbench/app/helpers/application_helper.rb
+++ b/apps/workbench/app/helpers/application_helper.rb
@@ -393,38 +393,39 @@ module ApplicationHelper
end
end
- def fa_icon_class_for_object object
- case object.class.to_s.to_sym
- when :User
- 'fa-user'
- when :Group
+ RESOURCE_CLASS_ICONS = {
+ "Collection" => "fa-archive",
+ "Group" => "fa-users",
+ "Human" => "fa-male", # FIXME: Use a more inclusive icon.
+ "Job" => "fa-gears",
+ "KeepDisk" => "fa-hdd-o",
+ "KeepService" => "fa-exchange",
+ "Link" => "fa-arrows-h",
+ "Node" => "fa-cloud",
+ "PipelineInstance" => "fa-gears",
+ "PipelineTemplate" => "fa-gears",
+ "Repository" => "fa-code-fork",
+ "Specimen" => "fa-flask",
+ "Trait" => "fa-clipboard",
+ "User" => "fa-user",
+ "VirtualMachine" => "fa-terminal",
+ }
+ DEFAULT_ICON_CLASS = "fa-cube"
+
+ def fa_icon_class_for_class(resource_class, default=DEFAULT_ICON_CLASS)
+ RESOURCE_CLASS_ICONS.fetch(resource_class.to_s, default)
+ end
+
+ def fa_icon_class_for_uuid(uuid, default=DEFAULT_ICON_CLASS)
+ fa_icon_class_for_class(resource_class_for_uuid(uuid), default)
+ end
+
+ def fa_icon_class_for_object(object, default=DEFAULT_ICON_CLASS)
+ case class_name = object.class.to_s
+ when "Group"
object.group_class ? 'fa-folder' : 'fa-users'
- when :Job, :PipelineInstance, :PipelineTemplate
- 'fa-gears'
- when :Collection
- 'fa-archive'
- when :Specimen
- 'fa-flask'
- when :Trait
- 'fa-clipboard'
- when :Human
- 'fa-male'
- when :VirtualMachine
- 'fa-terminal'
- when :Repository
- 'fa-code-fork'
- when :Link
- 'fa-arrows-h'
- when :User
- 'fa-user'
- when :Node
- 'fa-cloud'
- when :KeepService
- 'fa-exchange'
- when :KeepDisk
- 'fa-hdd-o'
else
- 'fa-cube'
+ RESOURCE_CLASS_ICONS.fetch(class_name, default)
end
end
end
commit ff9323418d6a13da701be80aee9e3134e8aa6bab
Author: Brett Smith <brett at curoverse.com>
Date: Tue Jul 15 14:30:12 2014 -0400
2044: Let users read permission links for groups they're in.
This lets everyone in a project see basic information about who has
access to a project.
diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb
index 1ea9332..9a806d3 100644
--- a/services/api/app/models/arvados_model.rb
+++ b/services/api/app/models/arvados_model.rb
@@ -130,11 +130,11 @@ class ArvadosModel < ActiveRecord::Base
sql_params += [uuid_list, user_uuids]
if sql_table == "links" and users_list.any?
- # This row is a 'permission' or 'resources' link class
- # The uuid for a member of users_list is referenced in either the head
- # or tail of the link
+ # This row is a 'permission' or 'resources' link class that
+ # references a member of users_list or a group readable by
+ # those users.
sql_conds += ["(#{sql_table}.link_class in (#{sanitize 'permission'}, #{sanitize 'resources'}) AND (#{sql_table}.head_uuid IN (?) OR #{sql_table}.tail_uuid IN (?)))"]
- sql_params += [user_uuids, user_uuids]
+ sql_params += [uuid_list, uuid_list]
end
if sql_table == "logs" and users_list.any?
diff --git a/services/api/test/fixtures/links.yml b/services/api/test/fixtures/links.yml
index 313db7f..1227618 100644
--- a/services/api/test/fixtures/links.yml
+++ b/services/api/test/fixtures/links.yml
@@ -318,6 +318,23 @@ test_timestamps:
name: test
properties: {}
+admin_can_write_aproject:
+ # Yes, this permission is effectively redundant.
+ # We use it to test that other project admins can see
+ # all the project's sharing.
+ uuid: zzzzz-o0j2j-adminmgsproject
+ owner_uuid: zzzzz-tpzed-000000000000000
+ created_at: 2014-01-24 20:42:26 -0800
+ modified_by_client_uuid: zzzzz-ozdt8-brczlopd8u8d0jr
+ modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz
+ modified_at: 2014-01-24 20:42:26 -0800
+ updated_at: 2014-01-24 20:42:26 -0800
+ tail_uuid: zzzzz-tpzed-d9tiejq69daie8f
+ link_class: permission
+ name: can_write
+ head_uuid: zzzzz-j7d0g-v955i6s2oi1cbso
+ properties: {}
+
project_viewer_can_read_project:
uuid: zzzzz-o0j2j-projviewerreadp
owner_uuid: zzzzz-tpzed-000000000000000
diff --git a/services/api/test/functional/arvados/v1/links_controller_test.rb b/services/api/test/functional/arvados/v1/links_controller_test.rb
index d5b4266..9dfc350 100644
--- a/services/api/test/functional/arvados/v1/links_controller_test.rb
+++ b/services/api/test/functional/arvados/v1/links_controller_test.rb
@@ -283,4 +283,15 @@ class Arvados::V1::LinksControllerTest < ActionController::TestCase
}
assert_response 422
end
+
+ test "project owner sees project's permission links" do
+ authorize_with :active
+ get :index, filters: [['head_uuid', '=', groups(:aproject).uuid]]
+ uuid_list = assigns(:objects).andand.map(&:uuid)
+ assert_not_nil(uuid_list, "no index objects assigned")
+ [:admin_can_write_aproject, :project_viewer_can_read_project].each do |lsym|
+ assert_includes(uuid_list, links(lsym).uuid,
+ "#{lsym} missing from project permission index")
+ end
+ end
end
commit 36ed0d796ef167c0c795a1d6ba9d8d25f5c446ff
Author: Brett Smith <brett at curoverse.com>
Date: Mon Jul 14 17:07:39 2014 -0400
2044: All users can give all other users folder permissions.
This supports a Workbench sharing button, the same way that the
changes to users#index does.
diff --git a/services/api/app/models/link.rb b/services/api/app/models/link.rb
index bb069ee..4852461 100644
--- a/services/api/app/models/link.rb
+++ b/services/api/app/models/link.rb
@@ -104,4 +104,14 @@ class Link < ArvadosModel
super
end
end
+
+ # A user can give all other users permissions on folders.
+ def skip_uuid_read_permission_check
+ skipped_attrs = super
+ if (ArvadosModel.resource_class_for_uuid(head_uuid) == Group) and
+ (ArvadosModel.resource_class_for_uuid(tail_uuid) == User)
+ skipped_attrs << "tail_uuid"
+ end
+ skipped_attrs
+ end
end
diff --git a/services/api/test/unit/link_test.rb b/services/api/test/unit/link_test.rb
index e403265..5bd8038 100644
--- a/services/api/test/unit/link_test.rb
+++ b/services/api/test/unit/link_test.rb
@@ -61,4 +61,29 @@ class LinkTest < ActiveSupport::TestCase
ob.destroy
end
end
+
+ def make_active_perm(perm_attrs)
+ set_user_from_auth :active
+ Link.create({link_class: "permission",
+ name: "can_read",
+ head_uuid: groups(:aproject).uuid,
+ }.merge(perm_attrs))
+ end
+
+ test "link granting permission to nonexistent user is invalid" do
+ link = make_active_perm(tail_uuid:
+ users(:active).uuid.sub(/-\w+$/, "-#{'z' * 15}"))
+ refute link.valid?
+ end
+
+ test "link granting non-project permission to unreadable user is invalid" do
+ link = make_active_perm(tail_uuid: users(:admin).uuid,
+ head_uuid: collections(:bar_file).uuid)
+ refute link.valid?
+ end
+
+ test "link granting project permissions to unreadable user is valid" do
+ link = make_active_perm(tail_uuid: users(:admin).uuid)
+ assert link.valid?
+ end
end
commit 1c7011593c295718e03eaf540b3a90074e3d47c8
Author: Brett Smith <brett at curoverse.com>
Date: Thu Jul 3 14:10:31 2014 -0400
2044: Workbench supports API select parameter.
This can help ensure symmetry between admin and non-admin requests for
the user index.
diff --git a/apps/workbench/app/models/arvados_base.rb b/apps/workbench/app/models/arvados_base.rb
index 6d427fd..1dac43f 100644
--- a/apps/workbench/app/models/arvados_base.rb
+++ b/apps/workbench/app/models/arvados_base.rb
@@ -131,6 +131,10 @@ class ArvadosBase < ActiveRecord::Base
ArvadosResourceList.new(self).limit(*args)
end
+ def self.select(*args)
+ ArvadosResourceList.new(self).select(*args)
+ end
+
def self.eager(*args)
ArvadosResourceList.new(self).eager(*args)
end
diff --git a/apps/workbench/app/models/arvados_resource_list.rb b/apps/workbench/app/models/arvados_resource_list.rb
index dedd18c..3164c79 100644
--- a/apps/workbench/app/models/arvados_resource_list.rb
+++ b/apps/workbench/app/models/arvados_resource_list.rb
@@ -26,6 +26,12 @@ class ArvadosResourceList
self
end
+ def select(columns)
+ @select ||= []
+ @select += columns
+ self
+ end
+
def filter _filters
@filters ||= []
@filters += _filters
@@ -64,6 +70,7 @@ class ArvadosResourceList
api_params[:eager] = '1' if @eager
api_params[:limit] = @limit if @limit
api_params[:offset] = @offset if @offset
+ api_params[:select] = @select if @select
api_params[:order] = @orderby_spec if @orderby_spec
api_params[:filters] = @filters if @filters
res = arvados_api_client.api @resource_class, '', api_params
diff --git a/apps/workbench/test/unit/group_test.rb b/apps/workbench/test/unit/group_test.rb
index 435463a..c555c4a 100644
--- a/apps/workbench/test/unit/group_test.rb
+++ b/apps/workbench/test/unit/group_test.rb
@@ -25,4 +25,13 @@ class GroupTest < ActiveSupport::TestCase
assert_equal(expect_name, oi.name_for(expect_uuid),
"Expected name_for '#{expect_uuid}' to be '#{expect_name}'")
end
+
+ test "can select specific group columns" do
+ use_token :admin
+ Group.select(["uuid", "name"]).limit(5).each do |user|
+ assert_not_nil user.uuid
+ assert_not_nil user.name
+ assert_nil user.owner_uuid
+ end
+ end
end
diff --git a/apps/workbench/test/unit/user_test.rb b/apps/workbench/test/unit/user_test.rb
index 20cde7e..89e95df 100644
--- a/apps/workbench/test/unit/user_test.rb
+++ b/apps/workbench/test/unit/user_test.rb
@@ -1,4 +1,12 @@
require 'test_helper'
class UserTest < ActiveSupport::TestCase
+ test "can select specific user columns" do
+ use_token :admin
+ User.select(["uuid", "is_active"]).limit(5).each do |user|
+ assert_not_nil user.uuid
+ assert_not_nil user.is_active
+ assert_nil user.first_name
+ end
+ end
end
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list