Unverified Commit 7ed0bdbc authored by Francesc Guasch's avatar Francesc Guasch Committed by GitHub
Browse files

start #797 (#799)

* feat(frontend): redirect to logout after timeout

issue #753

* test(frontend): list machines

returns only machines available to the user

issue #698

* wip(grants): fixed list and settings

list own clones and machines
change settings to own clones and machines

issue #698

* fix(frontend): list bases only show public to non-admins

issue #698

* fix(backend): check iso file wasn't picking the right arg

this is because we cherry-picked this code

issue #780

* wip(grants): can list own machines if can create

issue #698

* wip(frontend): properly show and hide action buttons

issue #698

* test(backend): check deep recurson from frontend

issue #698

* wip(backend): fix deep recurson from frontend

issue #698

* Grants revision

* fix(backend): removed domains return inactive

issue #698

* wip(frontend): allow manage machine

issue #698

* wip(fronted): hide clones if listing large for user

issue #698

* wip(grants): fixed list machines when can remove all

issue #698

* fix(frontend): settings is now manage

issue #698

* fix clone permisions

* Fix remove_clones , remove_clones_all & remove

* Fix can_change_settings_clones

* fix show grants when user has grant permissions

* fix show users when user has manage_users permission

* fix jump into /admin/users without permissions

* wip(backend): run prepare and remove base as huge task

this way other tasks will be executed in another process

issue #797

* fix(frontend): machine screenshot failed on first load

issue #797

* fix(upgrade): add grant screenshot for old releases

issue #797

* fix(upgrade): add grant screenshot for old releases

issue #797

* backend(grants): enable screenshot to all users

issue #797
parent f15446ad
......@@ -79,8 +79,8 @@ our $DIR_SQL = "sql/mysql";
$DIR_SQL = "/usr/share/doc/ravada/sql/mysql" if ! -e $DIR_SQL;
# LONG commands take long
our %HUGE_COMMAND = map { $_ => 1 } qw(download);
our %LONG_COMMAND = map { $_ => 1 } (qw(prepare_base remove_base screenshot shutdown force_shutdown ), keys %HUGE_COMMAND);
our %HUGE_COMMAND = map { $_ => 1 } qw(download prepare_base remove_base);
our %LONG_COMMAND = map { $_ => 1 } (qw(screenshot shutdown force_shutdown ), keys %HUGE_COMMAND);
our $USER_DAEMON;
our $USER_DAEMON_NAME = 'daemon';
......@@ -801,6 +801,7 @@ sub _alias_grants($self) {
sub _add_grants($self) {
$self->_add_grant('shutdown', 1);
$self->_add_grant('screenshot', 1);
}
sub _add_grant($self, $grant, $allowed) {
......@@ -864,6 +865,7 @@ sub _enable_grants($self) {
,'manage_users'
,'remove', 'remove_all', 'remove_clone', 'remove_clone_all'
,'shutdown', 'shutdown_all', 'shutdown_clone'
,'screenshot'
);
$sth = $CONNECTOR->dbh->prepare("SELECT id,name FROM grant_types");
......
......@@ -368,6 +368,7 @@ sub can_list_own_machines {
if $self->can_create_base()
|| $self->can_create_machine()
|| $self->can_remove_clone_all()
|| $self->is_operator()
;
return 0;
}
......@@ -405,7 +406,9 @@ sub can_list_machines {
return 1 if $self->is_admin()
|| $self->can_remove_all || $self->can_remove_clone_all
|| $self->can_shutdown_all
|| $self->can_change_settings_all();
|| $self->can_change_settings_all()
|| $self->can_change_settings_clones()
|| $self->can_clone_all();
return 0;
}
......@@ -582,7 +585,7 @@ sub can_do_domain($self, $grant, $domain) {
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown);
confess "Invalid grant here '$grant'" if !$valid_grant{$grant};
return 0 if !$self->can_do($grant);
return 0 if !$self->can_do($grant) && !$domain->id_base;
return 1 if $self->can_do("${grant}_all");
return 1 if $domain->id_owner == $self->id;
......@@ -794,7 +797,7 @@ Returns a list of all the available permissions
=cut
sub list_all_permissions($self) {
return if !$self->is_admin;
return if !$self->is_admin && !$self->can_grant();
$self->_load_grants();
my $sth = $$CON->dbh->prepare(
......@@ -846,6 +849,15 @@ sub can_change_settings($self, $id_domain=undef) {
=cut
=head2 can_manage_machine
The user can change settings, remove or change other things yet to be defined.
Some changes require special permissions granted.
Unlinke change_settings that any user is granted to his own machines by default.
=cut
sub can_manage_machine($self, $domain) {
return 1 if $self->is_admin;
......@@ -853,10 +865,16 @@ sub can_manage_machine($self, $domain) {
return 1 if $self->can_change_settings($domain);
return 1 if $self->can_remove_all;
return 1 if $self->can_remove_clone_all
&& $domain->id_base;
return 1 if $self->can_clone_all;
return 1 if $self->can_remove && $domain->id_owner == $self->id;
if ( $self->can_remove_clones && $domain->id_base ) {
if ( ($self->can_remove_clones || $self->can_change_settings_clones) && $domain->id_base ) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
}
......@@ -881,7 +899,7 @@ sub can_remove_clones($self, $id_domain=undef) {
sub can_remove_machine($self, $domain) {
return 1 if $self->can_remove_all();
return 0 if !$self->can_remove();
#return 0 if !$self->can_remove();
$domain = Ravada::Front::Domain->open($domain) if !ref $domain;
......
......@@ -569,8 +569,17 @@ sub _data($self, $field, $value=undef, $table='domains') {
}
return $self->{$data}->{$field} if exists $self->{$data}->{$field};
my @field_select = ( name => $self->name );
@field_select = ( id_domain => $self->id ) if $table ne 'domains';
my @field_select;
if ($table eq 'domains' ) {
if (exists $self->{_data}->{id} ) {
@field_select = ( id => $self->{_data}->{id});
} else {
confess "ERROR: Unknown domain" if ref($self) =~ /^Ravada::Front::Domain/;
@field_select = ( name => $self->name );
}
} else {
@field_select = ( id_domain => $self->id );
}
$self->{$data} = $self->_select_domain_db( _table => $table, @field_select );
confess "No DB info for domain @field_select in $table ".$self->name
......@@ -1370,6 +1379,7 @@ sub _post_shutdown {
$self->_remove_iptables(%arg);
$self->_data(status => 'shutdown')
if $self->is_known && !$self->is_volatile && !$self->is_active;
if ($self->is_known && $self->id_base) {
for ( 1 .. 5 ) {
last if !$self->is_active;
......@@ -1395,6 +1405,7 @@ sub _post_shutdown {
}
sub _around_is_active($orig, $self) {
return 0 if $self->is_removed;
my $is_active = $self->$orig();
return $is_active if $self->readonly
|| !$self->is_known
......
......@@ -137,6 +137,7 @@ sub list_machines_user {
my @list;
while ( $sth->fetch ) {
next if !$is_public && !$user->is_admin;
my $is_active = 0;
my $clone = $self->search_clone(
id_owner =>$user->id
......@@ -176,24 +177,44 @@ sub list_machines_user {
sub list_machines($self, $user) {
return $self->list_domains() if $user->can_list_machines;
my @list = ();
if ($user->can_remove_clones() || $user->can_shutdown_clones() ) {
my $machines = $self->list_bases( id_owner => $user->id );
for my $base (@$machines) {
confess "ERROR: BAse without id ".Dumper($base) if !$base->{id};
push @$machines,@{$self->list_domains( id_base => $base->{id} )};
}
return $machines;
push @list,(@$machines);
}
if ($user->can_remove_clone_all()) {
=pod
if ($user->can_remove_clone_all()) {
my $machines = $self->list_bases( );
for my $base (@$machines) {
push @$machines,@{$self->list_domains( id_base => $base->{id} )};
my $clones = $self->list_domains( id_base => $base->{id} );
next if !scalar @$clones;
push @list, ($base);
push @list, @{$clones};
}
return $machines;
}
return $self->list_clones() if $user->can_list_clones;
return [];
=cut
push @list,(@{$self->list_clones()}) if $user->can_list_clones;
if ($user->can_create_base || $user->can_create_machine || $user->is_operator) {
my $machines = $self->list_domains(id_owner => $user->id);
for my $clone (@$machines) {
next if !$clone->{id_base};
push @$machines,@{$self->list_domains( id => $clone->{id_base} )};
}
push @list,(@$machines);
}
return [@list] if scalar @list < 2;
my %uniq = map { $_->{name} => $_ } @list;
return [sort { $a->{name} cmp $b->{name} } values %uniq];
}
sub _around_list_machines($orig, $self, $user) {
......@@ -206,6 +227,9 @@ sub _around_list_machines($orig, $self, $user) {
$m->{can_view} = 0;
$m->{can_view} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_manage} = ( $user->can_manage_machine($m->{id}) or 0);
$m->{can_change_settings} = ( $user->can_change_settings($m->{id}) or 0);
}
return $machines;
}
......
......@@ -82,7 +82,7 @@ sub is_paused($self) {
return 0;
}
sub is_removed { confess "TODO" }
sub is_removed { return 0 }
sub list_volumes { confess "TODO" }
sub name($self) {
......
......@@ -608,7 +608,7 @@ sub _domain_create_from_iso {
my $device_cdrom;
confess "Template ".$iso->{name}." has no URL, iso_file argument required."
if !$iso->{url} && !$args{iso_file} && !$iso->{device};
if !$iso->{url} && !$iso_file && !$iso->{device};
if ($iso_file) {
if ( $iso_file ne "<NONE>") {
......
......@@ -51,3 +51,7 @@
background: #4CAF50; /* Green background */
cursor: pointer; /* Cursor on hover */
}
.disabled {
color: darkgray;
cursor: not-allowed;
......@@ -94,7 +94,7 @@
$http.get(toGet);
};
$scope.action = function(machineId, action) {
$scope.refresh = true;
$scope.refresh = 2;
if ( action == 'restore' ) {
$scope.host_restore = machineId;
$scope.host_shutdown = 0;
......@@ -109,14 +109,16 @@
};
$scope.list_machines_user = function() {
var seconds = 5000;
if ($scope.refresh) {
var seconds = 1000;
if ($scope.refresh <= 0) {
$http.get('/list_machines_user.json').then(function(response) {
$scope.machines = response.data;
}, function error(response) {
console.log(response.status);
});
$scope.refresh = 5;
} else {
seconds = 60000;
$scope.refresh = true;
$scope.refresh--;
}
$timeout(function() {
$scope.list_machines_user();
......@@ -141,7 +143,7 @@
};
$scope.startIntro = startIntro;
$scope.host_action = 0;
$scope.refresh = true;
$scope.refresh = 0;
$scope.list_machines_user();
};
......
......@@ -347,21 +347,9 @@ get '/machine/info/(:id).(:type)' => sub {
$c->render(json => $domain->info($USER) );
};
any '/machine/settings/(:id).(:type)' => sub {
my $c = shift;
return settings_machine($c);
};
any '/machine/manage/(:id).(:type)' => sub {
my $c = shift;
my ($domain) = _search_requested_machine($c);
return access_denied($c) if !$domain;
return access_denied($c) unless $USER->is_admin
|| $domain->id_owner == $USER->id;
return manage_machine($c);
my $c = shift;
return manage_machine($c);
};
get '/machine/view/(:id).(:type)' => sub {
......@@ -537,7 +525,7 @@ any '/users/register' => sub {
any '/admin/user/(:id).(:type)' => sub {
my $c = shift;
return access_denied($c) if !$USER->can_manage_users();
return access_denied($c) if !$USER->can_manage_users() && !$USER->can_grant();
my $user = Ravada::Auth::SQL->search_by_id($c->stash('id'));
......@@ -1010,6 +998,7 @@ sub admin {
push @{$c->stash->{js}}, '/js/admin.js';
if ($page eq 'users') {
return access_denied($c) if !$USER->is_admin && !$USER->can_manage_users && !$USER->can_grant;
$c->stash(list_users => []);
$c->stash(name => $c->param('name' or ''));
if ( $c->param('name') ) {
......@@ -1018,7 +1007,7 @@ sub admin {
}
if ($page eq 'machines') {
$c->stash(hide_clones => 0 );
my $list_domains = $RAVADA->list_domains();
my $list_domains = $RAVADA->list_machines($USER);
$c->stash(hide_clones => 1 )
if defined $CONFIG_FRONT->{admin}->{hide_clones}
......@@ -1308,38 +1297,12 @@ sub register {
}
sub manage_machine {
my $c = shift;
return login($c) if !_logged_in($c);
my ($domain) = _search_requested_machine($c);
if (!$domain) {
return $c->render(text => "Domain no found");
}
return access_denied($c) if $domain->id_owner != $USER->id
&& !$USER->is_admin;
Ravada::Request->shutdown_domain(id_domain => $domain->id, uid => $USER->id) if $c->param('shutdown');
Ravada::Request->start_domain( uid => $USER->id
,name => $domain->name
, remote_ip => _remote_ip($c)
) if $c->param('start');
Ravada::Request->pause_domain(name => $domain->name, uid => $USER->id)
if $c->param('pause');
Ravada::Request->resume_domain(name => $domain->name, uid => $USER->id) if $c->param('resume');
$c->stash(domain => $domain);
_enable_buttons($c, $domain);
$c->render( template => 'main/manage_machine');
}
sub settings_machine {
my $c = shift;
my ($domain) = _search_requested_machine($c);
return access_denied($c) if !$domain;
return access_denied($c) if !($USER->can_manage_machine($domain->id) || $USER->is_admin);
return access_denied($c) if !($USER->can_manage_machine($domain->id)
|| $USER->is_admin
);
$c->stash(domain => $domain);
$c->stash(USER => $USER);
......
......@@ -78,6 +78,23 @@ sub test_list_bases {
ok(scalar @$bases == $expected,"Expecting '$expected' bases, got ".scalar @$bases);
}
sub test_domain_name {
my $vm_name = shift;
my $domain = create_domain($vm_name);
my $sth = $test->connector->dbh->prepare("DELETE FROM domains WHERE id=?");
$sth->execute($domain->id);
my $id = $domain->id;
$domain = Ravada::Front::Domain->open($id);
eval {
$domain->name();
};
like($@,qr'Unknown domain');
}
####################################################################
#
......@@ -156,6 +173,7 @@ for my $vm_name ('Void','KVM','LXC') {
test_remove_domain($name);
test_domain_name($vm_name);
}
}
......
......@@ -96,6 +96,28 @@ sub test_list_domains {
is($list_domains->[0]->{status}, 'hibernated');
}
sub test_list_bases {
my $vm_name = shift;
my $vm = rvd_back->search_vm($vm_name);
my $user2 = create_user('malcolm.reynolds','serenity');
my $base = create_domain($vm_name);
my $list = rvd_front->list_machines_user($user2);
is(scalar @$list, 0);
$base->prepare_base(user_admin);
$list = rvd_front->list_machines_user($user2);
is(scalar @$list, 0);
$list = rvd_front->list_machines_user(user_admin);
is(scalar @$list, 1);
$base->remove(user_admin);
$user2->remove();
}
#########################################################
remove_old_domains();
......@@ -129,6 +151,8 @@ for my $vm_name (reverse sort @VMS) {
my $domain = test_create_domain($vm_name);
test_list_domains($vm_name, $domain);
test_list_bases($vm_name);
$domain->remove($USER);
}
......
......@@ -370,6 +370,16 @@ sub test_remove_clone_all {
$clone_name = new_domain_name();
$clone = $domain->clone(user => $usera, name => $clone_name);
my $other_domain = create_domain($vm_name);
ok($other_domain);
is($user->is_admin, 0);
is($user->can_list_machines, 1);
my $list = rvd_front->list_machines($user);
is(scalar@$list,4);
ok( grep { $_->{name} eq $other_domain->name } @$list);
$usera->revoke($user,'remove_clone_all');
eval { $clone->remove($user); };
......@@ -448,9 +458,17 @@ sub test_frontend {
is($user->can_list_machines, 0);
is($user->can_list_own_machines, 1);
my $list_machines = rvd_front->list_domains( id_owner => $user->id );
is (scalar @$list_machines, 1 );
ok($list_machines->[0]->{name} eq $clone->name);
my $list_domains = rvd_front->list_domains( id_owner => $user->id );
is (scalar @$list_domains, 1 );
ok($list_domains->[0]->{name} eq $clone->name);
my $list_machines = rvd_front->list_machines( $user );
is (scalar @$list_machines, 2 );
if (defined $list_machines->[1]) {
ok($list_machines->[1]->{name} eq $clone->name);
is($list_machines->[1]->{can_manage}, 1);
is($list_machines->[0]->{can_manage}, 0);
}
$usera->revoke($user, 'create_base');
is($user->can_list_machines, 0);
......@@ -497,6 +515,7 @@ sub test_create_domain {
,name => $domain_name
);
my $domain;
eval { $domain = $vm->create_domain(%create_args)};
like($@,qr'not allowed'i);
......@@ -526,6 +545,10 @@ sub test_create_domain {
my $domain3 = $vm->search_domain($domain_name);
ok($domain3);
is($user->can_change_settings($domain3),1);
my $list_machines = rvd_front->list_machines($user);
is (scalar @$list_machines, 1 );
eval { $domain3->remove($usera) if $domain3 };
is($@,'');
......
......@@ -169,6 +169,8 @@ sub test_list_all{
user_admin->grant($user, 'remove_all');
is($user->can_list_machines, 1);
is($user->can_manage_machine($base),1);
$list = rvd_front->list_machines($user);
is(scalar @$list , 2);
......@@ -183,6 +185,7 @@ sub test_list_clones_from_own_base {
user_admin->grant($user,'create_machine');
my $base = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base->prepare_base( user_admin );
$base->is_public(1);
......@@ -215,6 +218,7 @@ sub test_list_clones_from_own_base_2 {
user_admin->grant($user,'create_machine');
my $base = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base->prepare_base( user_admin );
$base->is_public(1);
......@@ -245,7 +249,9 @@ sub test_list_clones_from_own_base_2 {
#####################################################################3
#
# another base
user_admin->grant($user,'create_machine');
my $base2 = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base2->prepare_base(user_admin);
$base2->is_public(1);
......@@ -256,10 +262,10 @@ sub test_list_clones_from_own_base_2 {
$list = rvd_front->list_machines($user);
is(scalar @$list , 5) and do {
is($list->[0]->{name}, $base->name);
is($list->[1]->{name}, $base2->name);
is($list->[2]->{name}, $clone->name, Dumper($list->[2]));
is($list->[3]->{name}, $clone2->name, Dumper($list->[3]));
is($list->[4]->{name}, $clone3->name, Dumper($list->[4]));
is($list->[1]->{name}, $clone->name);
is($list->[2]->{name}, $clone2->name);
is($list->[3]->{name}, $base2->name);
is($list->[4]->{name}, $clone3->name);
};
for my $m (@$list) {
......
......@@ -194,6 +194,7 @@ sub test_list_clones_from_own_base {
user_admin->grant($user,'create_machine');
my $base = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base->prepare_base( user_admin );
$base->is_public(1);
......@@ -228,6 +229,7 @@ sub test_list_clones_from_own_base_2 {
user_admin->grant($user,'create_machine');
my $base = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base->prepare_base( user_admin );
$base->is_public(1);
......@@ -260,7 +262,9 @@ sub test_list_clones_from_own_base_2 {
#####################################################################3
#
# another base
user_admin->grant($user,'create_machine');
my $base2 = create_domain($vm->type, $user);
user_admin->revoke($user,'create_machine');
$base2->prepare_base(user_admin);
$base2->is_public(1);
......@@ -271,10 +275,10 @@ sub test_list_clones_from_own_base_2 {
$list = rvd_front->list_machines($user);
is(scalar @$list , 5) and do {
is($list->[0]->{name}, $base->name);
is($list->[1]->{name}, $base2->name);
is($list->[2]->{name}, $clone->name, Dumper($list->[2]));
is($list->[3]->{name}, $clone2->name, Dumper($list->[3]));
is($list->[4]->{name}, $clone3->name, Dumper($list->[4]));
is($list->[1]->{name}, $clone->name);
is($list->[2]->{name}, $clone2->name);
is($list->[3]->{name}, $base2->name);
is($list->[4]->{name}, $clone3->name);
};
for my $m (@$list) {
......
......@@ -25,7 +25,7 @@ navbar-inverse navbar-fixed-top" role="navigation">
<li><a href="/admin/machines">
<i class="fa fa-desktop" aria-hidden="true"></i>&nbsp<%=l 'machines' %></a>
</li>
% if ($_user->is_admin) {
% if ($_user->is_admin || $_user->can_grant || $_user->can_manage_users) {
<li><a href="/admin/users">
<i class="fa fa-user" aria-hidden="true"></i>&nbsp<%=l 'users' %></a>
</li>
......
......@@ -158,8 +158,9 @@
<tbody>
<tr ng-repeat-start="machine in list_machines | orderBy : orderParam">
<td class="lgMachName">
<a align="right" href="/machine/settings/{{machine.id}}.html"
title ="<%=l 'Machine settings' %>"><b
<a align="right" href="/machine/manage/{{machine.id}}.html"
ng-class="{disabled: !machine.can_manage}"
title ="<%=l 'Manage machine' %>"><b
ng-cloak>{{machine.name}}</b></a>