diff --git a/codalab/model/bundle_model.py b/codalab/model/bundle_model.py index 4224ac1d0..c0511e8ed 100644 --- a/codalab/model/bundle_model.py +++ b/codalab/model/bundle_model.py @@ -642,20 +642,29 @@ def add_join(table: Table, condition: Any, left_outer_join: bool = False): if user_id != self.root_user_id: # Restrict to the bundles that we have access to. + aliased_group_bundle_permission = aliased(cl_group_bundle_permission) add_join( - cl_group_bundle_permission, - cl_bundle.c.uuid == cl_group_bundle_permission.c.object_uuid, + aliased_group_bundle_permission, + cl_bundle.c.uuid == aliased_group_bundle_permission.c.object_uuid, + left_outer_join=True, + ) + aliased_cl_user_group = aliased(cl_user_group) + add_join( + aliased_cl_user_group, + aliased_cl_user_group.c.user_id == user_id, + left_outer_join=True, ) - add_join(cl_user_group, cl_user_group.c.user_id == user_id, left_outer_join=True) access_via_owner = cl_bundle.c.owner_id == user_id access_via_group = and_( or_( # Join constraint (group) - cl_group_bundle_permission.c.group_uuid + aliased_group_bundle_permission.c.group_uuid == self.public_group_uuid, # Public group - cl_user_group.c.user_id == user_id, + aliased_group_bundle_permission.c.group_uuid + == aliased_cl_user_group.c.group_uuid, # Private group ), - cl_group_bundle_permission.c.permission + aliased_group_bundle_permission.c.permission >= GROUP_OBJECT_PERMISSION_READ, # Match the uuid of the parent + aliased_group_bundle_permission.c.group_uuid is not None, ) where_clause = and_(where_clause, or_(access_via_owner, access_via_group)) diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index ba381995c..eb12af8ae 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -1872,50 +1872,11 @@ def test_perm(ctx): @TestModule.register('search') def test_search(ctx): - name = random_name() - uuid1 = _run_command([cl, 'upload', test_path('a.txt'), '-n', name]) - uuid2 = _run_command([cl, 'upload', test_path('b.txt'), '-n', name]) - check_equals(uuid1, _run_command([cl, 'search', uuid1, '-u'])) - check_equals( - uuid1[:8], _run_command([cl, 'search', 'uuid=' + uuid1, '-f', 'uuid']).split("\n")[2] - ) - check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1, '-u'])) - check_equals('', _run_command([cl, 'search', 'uuid=' + uuid1[0:8], '-u'])) - check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1[0:8] + '.*', '-u'])) - check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1[0:8] + '%', '-u'])) - check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1, 'name=' + name, '-u'])) - check_equals( - uuid1 + '\n' + uuid2, _run_command([cl, 'search', 'name=' + name, 'id=.sort', '-u']) - ) - check_equals( - uuid1 + '\n' + uuid2, - _run_command([cl, 'search', 'uuid=' + uuid1 + ',' + uuid2, 'id=.sort', '-u']), - ) - check_equals( - uuid2 + '\n' + uuid1, _run_command([cl, 'search', 'name=' + name, 'id=.sort-', '-u']) - ) - check_equals('2', _run_command([cl, 'search', 'name=' + name, '.count'])) - size1 = float(_run_command([cl, 'info', '-f', 'data_size', uuid1])) - size2 = float(_run_command([cl, 'info', '-f', 'data_size', uuid2])) - check_equals( - size1 + size2, float(_run_command([cl, 'search', 'name=' + name, 'data_size=.sum'])) - ) - # Check floating - check_equals('', _run_command([cl, 'search', '.floating', '-u'])) - uuid3 = _run_command([cl, 'upload', test_path('a.txt')]) - _run_command([cl, 'detach', uuid3], 0) - check_equals(uuid3, _run_command([cl, 'search', '.floating', '-u'])) - _run_command([cl, 'rm', uuid3]) # need to remove since not on main worksheet - # Check search when groups empty - check_equals('', _run_command([cl, 'search', '.shared'])) - # Check search with non-root user. - if not os.getenv('CODALAB_PROTECTED_MODE'): - # This test does not work when protected_mode is True. - _, current_user_name = current_user() - user_name = 'non_root_user_' + random_name() - create_user(ctx, user_name, disk_quota='2000') - switch_user(user_name) - check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1, '-u'])) + def test_search_helper(ctx): + # Basic Search and info + name = random_name() + uuid1 = _run_command([cl, 'upload', test_path('a.txt'), '-n', name]) + uuid2 = _run_command([cl, 'upload', test_path('b.txt'), '-n', name]) check_equals(uuid1, _run_command([cl, 'search', uuid1, '-u'])) check_equals( uuid1[:8], _run_command([cl, 'search', 'uuid=' + uuid1, '-f', 'uuid']).split("\n")[2] @@ -1925,22 +1886,103 @@ def test_search(ctx): check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1[0:8] + '.*', '-u'])) check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1[0:8] + '%', '-u'])) check_equals(uuid1, _run_command([cl, 'search', 'uuid=' + uuid1, 'name=' + name, '-u'])) + check_equals( + uuid1 + '\n' + uuid2, _run_command([cl, 'search', 'name=' + name, 'id=.sort', '-u']) + ) + check_equals( + uuid1 + '\n' + uuid2, + _run_command([cl, 'search', 'uuid=' + uuid1 + ',' + uuid2, 'id=.sort', '-u']), + ) + check_equals( + uuid2 + '\n' + uuid1, _run_command([cl, 'search', 'name=' + name, 'id=.sort-', '-u']) + ) + check_equals('2', _run_command([cl, 'search', 'name=' + name, '.count'])) + size1 = float(_run_command([cl, 'info', '-f', 'data_size', uuid1])) + size2 = float(_run_command([cl, 'info', '-f', 'data_size', uuid2])) + check_equals( + size1 + size2, float(_run_command([cl, 'search', 'name=' + name, 'data_size=.sum'])) + ) + + # Search for floating bundles + check_equals('', _run_command([cl, 'search', '.floating', '-u'])) + uuid3 = _run_command([cl, 'upload', test_path('a.txt')]) + _run_command([cl, 'detach', uuid3], 0) + check_equals(uuid3, _run_command([cl, 'search', '.floating', '-u'])) + _run_command([cl, 'rm', uuid3]) # need to remove since not on main worksheet + + # Search bundles on private worksheets + wuuid = _run_command([cl, 'work', '-u']) + new_wuuid = _run_command([cl, 'new', random_name()]) + _run_command([cl, 'wperm', new_wuuid, 'public', 'n']) # make worksheet private + _run_command([cl, 'work', new_wuuid]) # switch to worksheet + uuid = _run_command([cl, 'upload', test_path('a.txt')]) + check_equals( + uuid, _run_command([cl, 'search', uuid, '-u']) + ) # Make sure this user can access bundle + + # Make sure other users can't access that bundle on the private worksheet. + if not os.getenv('CODALAB_PROTECTED_MODE'): + # Make sure other users can't access it. + _, current_user_name = current_user() + user_name = random_name() + create_user(ctx, user_name, disk_quota='1000000') + switch_user(user_name) + check_equals('', _run_command([cl, 'search', uuid, '-u'])) + switch_user(current_user_name) + + # Switch back to public worksheet. + _run_command([cl, 'work', wuuid]) + + # Make sure bundle that has no permissions on the public worksheet cannot be searched. + if not os.getenv('CODALAB_PROTECTED_MODE'): + uuid = _run_command([cl, 'upload', test_path('a.txt')]) + _run_command([cl, 'perm', uuid, 'public', 'none']) + _, current_user_name = current_user() + user_name = 'non_root_user_' + random_name() + create_user(ctx, user_name, disk_quota='1000000') + switch_user(user_name) + check_equals('', _run_command([cl, 'search', uuid])) + switch_user(current_user_name) + + # Search with groups + # Empty group + check_equals('', _run_command([cl, 'search', '.shared'])) + group_bundle_uuid = _run_command([cl, 'upload', test_path('a.txt')]) + ctx.collect_bundle(group_bundle_uuid) + user_id, user_name = current_user() + # Create new group + group_name = random_name() + group_uuid = create_group(ctx, group_name) + # Make bundle unavailable to public but available to the group + _run_command([cl, 'perm', group_bundle_uuid, 'public', 'n']) + _run_command([cl, 'perm', group_bundle_uuid, group_name, 'r']) + check_contains(group_bundle_uuid[:8], _run_command([cl, 'search', '.shared'])) + check_contains( + group_bundle_uuid[:8], _run_command([cl, 'search', 'group={}'.format(group_uuid)]) + ) + check_contains( + group_bundle_uuid[:8], _run_command([cl, 'search', 'group={}'.format(group_name)]) + ) + # Check other groups do not have access to it + other_group_name = random_name() + other_group_uuid = create_group(ctx, other_group_name) + check_equals('', _run_command([cl, 'search', 'group={}'.format(other_group_uuid)])) + + # Test with root user. + test_search_helper(ctx) + + # Test with non-root user. + # When protected_mode is True, only root can run many of these commands, so we + # can't run the test with another user. + if not os.getenv('CODALAB_PROTECTED_MODE'): + _, current_user_name = current_user() + user_name = 'non_root_user_' + random_name() + create_user(ctx, user_name, disk_quota='1000000') + switch_user(user_name) + new_wuuid = _run_command([cl, 'new', random_name()]) + _run_command([cl, 'work', new_wuuid]) + test_search_helper(ctx) switch_user(current_user_name) - # Check search by group - group_bname = random_name() - group_buuid = _run_command([cl, 'run', 'echo hello', '-n', group_bname]) - wait(group_buuid) - ctx.collect_bundle(group_buuid) - user_id, user_name = current_user() - # Create new group - group_name = random_name() - group_uuid = create_group(ctx, group_name) - # Make bundle unavailable to public but available to the group - _run_command([cl, 'perm', group_buuid, 'public', 'n']) - _run_command([cl, 'perm', group_buuid, group_name, 'r']) - check_contains(group_buuid[:8], _run_command([cl, 'search', '.shared'])) - check_contains(group_buuid[:8], _run_command([cl, 'search', 'group={}'.format(group_uuid)])) - check_contains(group_buuid[:8], _run_command([cl, 'search', 'group={}'.format(group_name)])) @TestModule.register('search_time')