Skip to content

Commit

Permalink
Merge pull request #1220 from Martchus/read_access
Browse files Browse the repository at this point in the history
Allow anonymous read access to operator tables
  • Loading branch information
coolo committed Mar 17, 2017
2 parents 38959c2 + 8a2ccb0 commit 1c196ff
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 93 deletions.
5 changes: 4 additions & 1 deletion assets/javascripts/job_templates.js
@@ -1,5 +1,6 @@
var job_templates_url;
var job_group_id;
var user_is_admin;

function setupJobTemplates(url, id) {
job_templates_url = url;
Expand Down Expand Up @@ -135,7 +136,9 @@ function buildMediumGroup(group, media) {
var table = $('<table class="table table-striped mediagroup" id="' + group + '"/>').appendTo(div);
var thead = $('<thead/>').appendTo(table);
var tr = $('<tr/>').appendTo(thead);
var tname = tr.append($('<th class="name">Test <a href="#" class="plus-sign"><i class="fa fa-plus"></i></a></th>'));
var tname = tr.append($('<th class="name">Test'
+ (user_is_admin ? ' <a href="#" class="plus-sign"><i class="fa fa-plus"></i></a>' : '')
+ '</th>'));
tr.append($('<th class="prio">Prio</th>'));
var archs = {};
var tests = {};
Expand Down
46 changes: 24 additions & 22 deletions lib/OpenQA/WebAPI.pm
Expand Up @@ -243,31 +243,33 @@ sub startup {
#
## Admin area starts here
###
my $admin_auth = $r->under('/admin')->to('session#ensure_admin');
my $admin_r = $admin_auth->route('/')->to(namespace => 'OpenQA::WebAPI::Controller::Admin');
my $op_auth = $r->under('/admin')->to('session#ensure_operator');
my $op_r = $op_auth->route('/')->to(namespace => 'OpenQA::WebAPI::Controller::Admin');
my $admin_auth = $r->under('/admin')->to('session#ensure_admin');
my $admin_r = $admin_auth->route('/')->to(namespace => 'OpenQA::WebAPI::Controller::Admin');
my $op_auth = $r->under('/admin')->to('session#ensure_operator');
my $op_r = $op_auth->route('/')->to(namespace => 'OpenQA::WebAPI::Controller::Admin');
my $pub_admin_r = $r->route('/admin')->to(namespace => 'OpenQA::WebAPI::Controller::Admin');

# operators accessible tables
$op_r->get('/products')->name('admin_products')->to('product#index');
$op_r->get('/machines')->name('admin_machines')->to('machine#index');
$op_r->get('/test_suites')->name('admin_test_suites')->to('test_suite#index');
$pub_admin_r->get('/products')->name('admin_products')->to('product#index');
$pub_admin_r->get('/machines')->name('admin_machines')->to('machine#index');
$pub_admin_r->get('/test_suites')->name('admin_test_suites')->to('test_suite#index');

$op_r->get('/job_templates/:groupid')->name('admin_job_templates')->to('job_template#index');
$pub_admin_r->get('/job_templates/:groupid')->name('admin_job_templates')->to('job_template#index');

$op_r->get('/groups')->name('admin_groups')->to('job_group#index');
$op_r->get('/job_group/:groupid')->name('admin_job_group_row')->to('job_group#job_group_row');
$op_r->get('/parent_group/:groupid')->name('admin_parent_group_row')->to('job_group#parent_group_row');
$op_r->get('/edit_parent_group/:groupid')->name('admin_edit_parent_group')->to('job_group#edit_parent_group');
$op_r->get('/groups/connect/:groupid')->name('job_group_new_media')->to('job_group#connect');
$pub_admin_r->get('/groups')->name('admin_groups')->to('job_group#index');
$pub_admin_r->get('/job_group/:groupid')->name('admin_job_group_row')->to('job_group#job_group_row');
$pub_admin_r->get('/parent_group/:groupid')->name('admin_parent_group_row')->to('job_group#parent_group_row');
$pub_admin_r->get('/edit_parent_group/:groupid')->name('admin_edit_parent_group')
->to('job_group#edit_parent_group');
$pub_admin_r->get('/groups/connect/:groupid')->name('job_group_new_media')->to('job_group#connect');

$op_r->get('/assets')->name('admin_assets')->to('asset#index');

$op_r->get('/workers')->name('admin_workers')->to('workers#index');
$op_r->get('/workers/:worker_id')->name('admin_worker_show')->to('workers#show');
$op_r->get('/workers/:worker_id/ajax')->name('admin_worker_previous_jobs_ajax')->to('workers#previous_jobs_ajax');

$op_r->get('/productlog')->name('admin_product_log')->to('audit_log#productlog');
$pub_admin_r->get('/productlog')->name('admin_product_log')->to('audit_log#productlog');

# admins accessible tables
$admin_r->get('/users')->name('admin_users')->to('user#index');
Expand Down Expand Up @@ -403,33 +405,33 @@ sub startup {
$api_ra->delete('/assets/#type/#name')->name('apiv1_delete_asset_name')->to('asset#delete');

# api/v1/test_suites
$api_ro->get('test_suites')->name('apiv1_test_suites')->to('table#list', table => 'TestSuites');
$api_public_r->get('test_suites')->name('apiv1_test_suites')->to('table#list', table => 'TestSuites');
$api_ra->post('test_suites')->to('table#create', table => 'TestSuites');
$api_ro->get('test_suites/:id')->name('apiv1_test_suite')->to('table#list', table => 'TestSuites');
$api_public_r->get('test_suites/:id')->name('apiv1_test_suite')->to('table#list', table => 'TestSuites');
$api_ra->put('test_suites/:id')->to('table#update', table => 'TestSuites');
$api_ra->post('test_suites/:id')->to('table#update', table => 'TestSuites'); #in case PUT is not supported
$api_ra->delete('test_suites/:id')->to('table#destroy', table => 'TestSuites');

# api/v1/machines
$api_ro->get('machines')->name('apiv1_machines')->to('table#list', table => 'Machines');
$api_public_r->get('machines')->name('apiv1_machines')->to('table#list', table => 'Machines');
$api_ra->post('machines')->to('table#create', table => 'Machines');
$api_ro->get('machines/:id')->name('apiv1_machine')->to('table#list', table => 'Machines');
$api_public_r->get('machines/:id')->name('apiv1_machine')->to('table#list', table => 'Machines');
$api_ra->put('machines/:id')->to('table#update', table => 'Machines');
$api_ra->post('machines/:id')->to('table#update', table => 'Machines'); #in case PUT is not supported
$api_ra->delete('machines/:id')->to('table#destroy', table => 'Machines');

# api/v1/products
$api_ro->get('products')->name('apiv1_products')->to('table#list', table => 'Products');
$api_public_r->get('products')->name('apiv1_products')->to('table#list', table => 'Products');
$api_ra->post('products')->to('table#create', table => 'Products');
$api_ro->get('products/:id')->name('apiv1_product')->to('table#list', table => 'Products');
$api_public_r->get('products/:id')->name('apiv1_product')->to('table#list', table => 'Products');
$api_ra->put('products/:id')->to('table#update', table => 'Products');
$api_ra->post('products/:id')->to('table#update', table => 'Products'); #in case PUT is not supported
$api_ra->delete('products/:id')->to('table#destroy', table => 'Products');

# api/v1/job_templates
$api_ro->get('job_templates')->name('apiv1_job_templates')->to('job_template#list');
$api_public_r->get('job_templates')->name('apiv1_job_templates')->to('job_template#list');
$api_ra->post('job_templates')->to('job_template#create');
$api_ro->get('job_templates/:job_template_id')->name('apiv1_job_template')->to('job_template#list');
$api_public_r->get('job_templates/:job_template_id')->name('apiv1_job_template')->to('job_template#list');
$api_ra->delete('job_templates/:job_template_id')->to('job_template#destroy');

# api/v1/comments
Expand Down
26 changes: 13 additions & 13 deletions t/api/03-auth.t
Expand Up @@ -72,7 +72,7 @@ my $ret;

subtest 'access limiting for non authenticated users' => sub() {
$t->get_ok('/api/v1/jobs')->status_is(200);
$t->get_ok('/api/v1/products')->status_is(403);
$t->get_ok('/api/v1/products')->status_is(200);
my $delete = $t->delete_ok('/api/v1/assets/1')->status_is(403);
is($delete->tx->res->code, 403, 'delete forbidden');
is_deeply(
Expand All @@ -88,32 +88,32 @@ subtest 'access limiting for non authenticated users' => sub() {
subtest 'access limiting for authenticated users but not operators nor admins' => sub() {
$t->ua->apikey('LANCELOTKEY01');
$t->ua->apisecret('MANYPEOPLEKNOW');
$t->get_ok('/api/v1/jobs')->status_is(200);
$t->get_ok('/api/v1/products')->status_is(403);
$t->delete_ok('/api/v1/assets/1')->status_is(403);
$t->get_ok('/api/v1/jobs')->status_is(200, 'accessible (public)');
$t->post_ok('/api/v1/assets')->status_is(403, 'restricted (operator and admin only)');
$t->delete_ok('/api/v1/assets/1')->status_is(403, 'restricted (admin only)');
};

subtest 'access limiting for authenticated operators but not admins' => sub() {
$t->ua->apikey('PERCIVALKEY01');
$t->ua->apisecret('PERCIVALSECRET01');
$t->get_ok('/api/v1/jobs')->status_is(200);
$t->get_ok('/api/v1/products')->status_is(200);
$t->delete_ok('/api/v1/assets/1')->status_is(403);
$t->get_ok('/api/v1/jobs')->status_is(200, 'accessible (public)');
$t->post_ok('/api/v1/jobs/99927/set_done')->status_is(200, 'accessible (operator and admin only)');
$t->delete_ok('/api/v1/assets/1')->status_is(403, 'restricted (admin only)');
};

subtest 'access granted for admins' => sub() {
$t->ua->apikey('ARTHURKEY01');
$t->ua->apisecret('EXCALIBUR');
$t->get_ok('/api/v1/jobs')->status_is(200);
$t->get_ok('/api/v1/products')->status_is(200);
$t->delete_ok('/api/v1/assets/1')->status_is(200);
$t->get_ok('/api/v1/jobs')->status_is(200, 'accessible (public)');
$t->post_ok('/api/v1/jobs/99927/set_done')->status_is(200, 'accessible (operator and admin only)');
$t->delete_ok('/api/v1/assets/1')->status_is(200, 'accessible (admin only)');
};

subtest 'wrong api key - expired' => sub() {
$t->ua->apikey('EXPIREDKEY01');
$t->ua->apisecret('WHOCARESAFTERALL');
$t->get_ok('/api/v1/jobs')->status_is(200);
$ret = $t->get_ok('/api/v1/products')->status_is(403);
$ret = $t->post_ok('/api/v1/products/1')->status_is(403);
is($ret->tx->res->json->{error}, 'api key expired', 'key expired error');
$t->delete_ok('/api/v1/assets/1')->status_is(403);
is($ret->tx->res->json->{error}, 'api key expired', 'key expired error');
Expand All @@ -123,7 +123,7 @@ subtest 'wrong api key - not maching key + secret' => sub() {
$t->ua->apikey('EXPIREDKEY01');
$t->ua->apisecret('INVALIDSECRET');
$t->get_ok('/api/v1/jobs')->status_is(200);
$ret = $t->get_ok('/api/v1/products')->status_is(403);
$ret = $t->post_ok('/api/v1/products/1')->status_is(403);
$t->delete_ok('/api/v1/assets/1')->status_is(403);
};

Expand Down Expand Up @@ -153,7 +153,7 @@ subtest 'wrong api key - replay attack' => sub() {
}
});
$t->get_ok('/api/v1/jobs')->status_is(200);
$ret = $t->get_ok('/api/v1/products')->status_is(403);
$ret = $t->post_ok('/api/v1/products/1')->status_is(403);
is($ret->tx->res->json->{error}, 'timestamp mismatch', 'timestamp mismatch error');
$t->delete_ok('/api/v1/assets/1')->status_is(403);
is($ret->tx->res->json->{error}, 'timestamp mismatch', 'timestamp mismatch error');
Expand Down
5 changes: 2 additions & 3 deletions t/ui/05-auth.t
Expand Up @@ -40,7 +40,7 @@ $test_case->login($t, 'https://openid.camelot.uk/lancelot');
# ...who should see a logout option but no link to API keys
$res = OpenQA::Test::Case::trim_whitespace(
$t->get_ok('/tests')->status_is(200)->tx->res->dom->at('#user-action')->all_text);
is($res, 'Logged in as lance Logout', 'lance is logged in');
like($res, qr/Logged in as lance Operators Menu.*Logout/, 'lance is logged in');
$t->get_ok('/api_keys')->status_is(403);

#
Expand All @@ -56,7 +56,7 @@ $test_case->login($t, 'morgana');
# ...who should see a logout option but no link to API keys
$res = OpenQA::Test::Case::trim_whitespace(
$t->get_ok('/tests')->status_is(200)->tx->res->dom->at('#user-action')->all_text);
is($res, 'Logged in as morgana Logout', 'morgana as no api keys');
like($res, qr/Logged in as morgana Operators Menu.*API help Logout/, 'morgana as no api keys');
$t->get_ok('/api_keys')->status_is(403);

#
Expand All @@ -73,7 +73,6 @@ my $actions = OpenQA::Test::Case::trim_whitespace(
$t->get_ok('/tests')->status_is(200)->tx->res->dom->at('#user-action')->all_text);
like($actions, qr/Logged in as perci Operators Menu.*Manage API keys API help Logout/, 'perci has operator links');
unlike($actions, qr/Administrators Menu/, 'perci has no admin links');

$t->get_ok('/api_keys')->status_is(200);

done_testing();
3 changes: 2 additions & 1 deletion t/ui/06-operator_links.t
Expand Up @@ -95,8 +95,9 @@ $driver->find_element('#user-action a')->click();
for my $item ('Medium types', 'Machines', 'Workers', 'Assets', 'Scheduled products') {
ok($driver->find_element_by_link_text($item), "can see $item");
}

# we shouldn't see users, audit
for my $item ('Users', 'Audit log') {
for my $item ('Users', 'Needles', 'Audit log') {
eval { $driver->find_element($item, 'link_text') };
ok($@, "can not see $item");
}
Expand Down
6 changes: 1 addition & 5 deletions t/ui/17-product-log.t
Expand Up @@ -41,11 +41,7 @@ if (!$driver) {
my $t = Test::Mojo->new('OpenQA::WebAPI');
# we need to talk to the phantom instance or else we're using wrong database
my $url = 'http://localhost:' . t::ui::PhantomTest::get_mojoport;

# Scheduled isos are only available to operators and admins
$t->get_ok($url . '/admin/productlog')->status_is(302);
$t->get_ok($url . '/login')->status_is(302);
$t->get_ok($url . '/admin/productlog')->status_is(200);
$t->get_ok($url . '/admin/productlog')->status_is(200, 'scheduled isos are public');

# Schedule iso - need UA change to add security headers
# XXX: Test::Mojo loses it's app when setting a new ua
Expand Down
17 changes: 15 additions & 2 deletions templates/admin/group/group_property_editor.html.ep
Expand Up @@ -4,11 +4,21 @@
% }
>
<div class="panel-heading">
<h3 class="panel-title">Edit job group properties</h3>
<h3 class="panel-title">
% if (is_admin) {
Edit job group properties
% }
% else {
Job group properties
% }
</h3>
</div>
<div class="panel-body">
<form action="#" class="form-horizontal" onsubmit="return submitProperties(this);"
data-put-url="<%= url_for(($is_parent ? 'apiv1_put_parent_group' : 'apiv1_put_job_group') => (group_id => $group->id)) %>">
data-put-url="<%= url_for(($is_parent ? 'apiv1_put_parent_group' : 'apiv1_put_job_group') => (group_id => $group->id)) %>">
% if (!is_admin) {
<fieldset disabled>
% }
<div class="form-group">
<label for="editor-name" class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
Expand Down Expand Up @@ -130,6 +140,9 @@
<p class="properties-status"></p>
</div>
</div>
% if (!is_admin) {
</fieldset>
% }
</form>
</div>
</div>
58 changes: 39 additions & 19 deletions templates/admin/job_template/index.html.ep
Expand Up @@ -3,29 +3,49 @@
% title 'Jobs for ' . $group->name;

% content_for 'ready_function' => begin
setupJobTemplates("<%= url_for('apiv1_job_templates') %>", <%= $group->id %>, <%= is_admin %>);
user_is_admin =
% if(is_admin) {
true
% }
% else {
false
% }
;
setupJobTemplates("<%= url_for('apiv1_job_templates') %>", <%= $group->id %>);
% end

<div class="row">
<div class="col-sm-12">
% if (is_admin) {
<form action="<%= url_for('admin_groups') %>" class="corner-buttons">
<button type="button" class="btn btn-default" onclick="toggleEdit();">
<span><span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit job group properties</span>
</button>
<button type="submit" class="btn btn-default">
<span><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Manage all job groups</span>
</button>
</form>
% }
<form action="<%= url_for('admin_groups') %>" class="corner-buttons">
<button type="button" class="btn btn-default" onclick="toggleEdit();">
<span>
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
% if (is_admin) {
Edit job group properties
% }
% else {
Show job group properties
% }
</span>
</button>
<button type="submit" class="btn btn-default">
<span>
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
% if (is_admin) {
Manage all job groups
% }
% else {
Show all job groups
% }
</span>
</button>
</form>
<h2>
Jobs for
<span id="job-group-name"><%= $group->name %></span>
</h2>
%= include 'layouts/info'
% if (is_admin) {
%= include 'admin/group/group_property_editor', group => $group, is_parent => 0
% }
%= include 'admin/group/group_property_editor', group => $group, is_parent => 0
<div id="media">
<p id="loading"><i class="fa fa-spinner fa-spin"></i> Loading…</p>

Expand Down Expand Up @@ -58,11 +78,11 @@
</div>

% if (is_admin) {
<p>
%= link_to url_for('job_group_new_media', groupid => $group->id) => begin
<i class="fa fa-plus-square"></i> Test new medium as part of this group
% end
</p>
<p>
%= link_to url_for('job_group_new_media', groupid => $group->id) => begin
<i class="fa fa-plus-square"></i> Test new medium as part of this group
% end
</p>
% }
</div>

Expand Down

0 comments on commit 1c196ff

Please sign in to comment.