Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat share machine #2015

Merged
merged 13 commits into from
Jan 22, 2024
19 changes: 18 additions & 1 deletion lib/Ravada.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,10 @@ sub _add_indexes_generic($self) {
,"UNIQUE (name)"

]
,domain_share => [
"index(id_domain)"
,"unique(id_user, id_domain)"
]
,virtual_networks => [
"unique(id_vm,internal_id)"
,"unique(id_vm,name)"
Expand Down Expand Up @@ -2293,6 +2297,12 @@ sub _sql_create_tables($self) {
}
]
,
[ domain_share => {
id => 'INTEGER PRIMARY KEY AUTO_INCREMENT'
,id_domain => 'integer NOT NULL references `domains` (`id`) ON DELETE CASCADE',
,id_user => 'int not null references `users` (`id`) ON DELETE CASCADE'
}
],
[
bookings => {
id => 'INTEGER PRIMARY KEY AUTO_INCREMENT'
Expand Down Expand Up @@ -4995,9 +5005,14 @@ sub _cmd_remove_base {
my $user = Ravada::Auth::SQL->search_by_id( $uid);

my $domain = $self->search_domain_by_id($id_domain);

die "Unknown domain id '$id_domain'\n" if !$domain;

die "User ".$user->name." [".$user->id."] not allowed to remove base "
.$domain->name."\n"
unless $user->is_admin || (
$domain->id_owner == $user->id && $user->can_create_base());


$domain->remove_base($user);

}
Expand Down Expand Up @@ -5671,6 +5686,8 @@ sub _cmd_list_cpu_models($self, $request) {
my $id_domain = $request->args('id_domain');

my $domain = Ravada::Domain->open($id_domain);
return [] if !$domain->_vm->can_list_cpu_models();

my $info = $domain->get_info();
my $vm = $domain->_vm->vm;

Expand Down
57 changes: 57 additions & 0 deletions lib/Ravada/Auth/SQL.pm
Original file line number Diff line number Diff line change
Expand Up @@ -699,11 +699,17 @@ sub can_do_domain($self, $grant, $domain) {
my %valid_grant = map { $_ => 1 } qw(change_settings shutdown reboot rename expose_ports);
confess "Invalid grant here '$grant'" if !$valid_grant{$grant};

return 1 if ( $grant eq 'shutdown' || $grant eq 'reboot' )
&& $self->can_shutdown_machine($domain);

return 0 if !$self->can_do($grant) && !$self->_domain_id_base($domain);

return 1 if $self->can_do("${grant}_all");
return 1 if $self->_domain_id_owner($domain) == $self->id && $self->can_do($grant);

return 1 if $grant eq 'change_settings'
&& $self->_machine_shared($domain);

if ($self->can_do("${grant}_clones") && $self->_domain_id_base($domain)) {
my $base;
my $id_base = $self->_domain_id_base($domain);
Expand Down Expand Up @@ -1067,6 +1073,7 @@ sub can_manage_machine($self, $domain) {

return 1 if $self->can_clone_all
|| $self->can_change_settings($domain)
|| $self->_machine_shared($domain)
|| $self->can_rename_all
|| $self->can_remove_all
|| ($self->can_remove_clone_all && $domain->id_base)
Expand Down Expand Up @@ -1160,6 +1167,8 @@ sub can_shutdown_machine($self, $domain) {

return 1 if $self->id == $domain->id_owner;

return 1 if $self->_machine_shared($domain->id);

if ($domain->id_base && $self->can_shutdown_clone()) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
Expand All @@ -1168,6 +1177,54 @@ sub can_shutdown_machine($self, $domain) {
return 0;
}

=head2 can_start_machine

Return true if the user can shutdown this machine

Arguments:

=over

=item * domain

=back

=cut

sub can_start_machine($self, $domain) {

return 1 if $self->can_view_all();

$domain = Ravada::Front::Domain->open($domain) if !ref $domain;

return 1 if $self->id == $domain->id_owner;

=pod
#TODO missing can_start_clones
if ($domain->id_base && $self->can_start_clone()) {
my $base = Ravada::Front::Domain->open($domain->id_base);
return 1 if $base->id_owner == $self->id;
}
=cut

return 1 if $self->_machine_shared($domain->id);

return 0;
}

sub _machine_shared($self, $id_domain) {
$id_domain = $id_domain->id if ref($id_domain);
my $sth = $$CON->dbh->prepare(
"SELECT id FROM domain_share "
." WHERE id_domain=? AND id_user=?"
);
$sth->execute($id_domain, $self->id);
my ($id) = $sth->fetchrow;
return 1 if $id;
return 0;
}


=head2 grants

Returns a list of permissions granted to the user in a hash
Expand Down
35 changes: 34 additions & 1 deletion lib/Ravada/Domain.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,8 @@ sub _access_denied_error($self,$user) {

confess "User ".$user->name." [".$user->id."] not allowed to access ".$self->name
." owned by ".($owner_name or '<UNDEF>')." [".($id_owner or '<UNDEF>')."]"
if (defined $id_owner && $id_owner != $user->id );
unless (defined $id_owner && $id_owner == $user->id )
|| $user->can_start_machine($self);

confess $err if $err;

Expand Down Expand Up @@ -7679,4 +7680,36 @@ sub remove_backup($self, $backup, $remove_file=0) {
$sth->execute($backup->{id});
}

sub share($self, $user) {
my $sth = $$CONNECTOR->dbh->prepare(
"INSERT INTO domain_share "
."(id_domain, id_user)"
." VALUES(?,?)"
);
$sth->execute($self->id, $user->id);
}

sub remove_share($self, $user) {
my $sth = $$CONNECTOR->dbh->prepare(
"DELETE FROM domain_share "
." WHERE id_domain=? AND id_user=?"
);
$sth->execute($self->id, $user->id);
}


sub list_shares($self) {
my $sth = $$CONNECTOR->dbh->prepare(
"SELECT u.name FROM users u,domain_share ds "
." WHERE u.id=ds.id_user "
." AND ds.id_domain=?"
);
$sth->execute($self->id);
my @shares;
while (my ($name) = $sth->fetchrow) {
push @shares,($name);
}
return @shares;
}

1;
55 changes: 52 additions & 3 deletions lib/Ravada/Front.pm
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ sub list_machines_user($self, $user, $access_data={}) {
id_owner =>$user->id
,id_base => $id
);
push @clones,$self->_search_shared($id, $user->id);

my ($clone) = ($clones[0] or undef);
next unless $clone || $user->is_admin || ($is_public && $user->allowed_access($id)) || ($id_owner == $user->id);
$name = $alias if defined $alias;
Expand Down Expand Up @@ -211,9 +213,9 @@ sub _get_clone_info($user, $base, $clone = Ravada::Front::Domain->open($base->{i
$c->{is_locked} = $clone->is_locked;
$c->{description} = ( $clone->_data('description')
or $base->{description});
$c->{can_remove} = 0;

$c->{can_remove} = ( $user->can_remove() && $user->id == $clone->_data('id_owner'));
$c->{can_remove} = 0 if !$c->{can_remove};

if ($clone->is_active && !$clone->is_locked
&& $user->can_screenshot) {
Expand Down Expand Up @@ -256,12 +258,16 @@ sub _init_available_actions($user, $m) {
eval { $m->{can_shutdown} = $user->can_shutdown($m->{id}) };

$m->{can_start} = 0;
$m->{can_start} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_start} = 1 if $m->{id_owner} == $user->id || $user->is_admin
|| $user->_machine_shared($m->{id})
;

$m->{can_reboot} = $m->{can_shutdown} && $m->{can_start};

$m->{can_view} = 0;
$m->{can_view} = 1 if $m->{id_owner} == $user->id || $user->is_admin;
$m->{can_view} = 1 if $m->{id_owner} == $user->id || $user->is_admin
|| $user->_machine_shared($m->{id})
;

$m->{can_manage} = ( $user->can_manage_machine($m->{id}) or 0);
eval {
Expand Down Expand Up @@ -1000,6 +1006,25 @@ sub search_clone($self, %args) {

}

sub _search_shared($self, $id_base, $id_user) {
my $sth = $CONNECTOR->dbh->prepare(
"SELECT d.id, d.name FROM domains d, domain_share ds"
." WHERE id_base=? "
." AND ds.id_user=? "
." AND ds.id_domain=d.id "
);
$sth->execute($id_base, $id_user);

my @clones;
while ( my ($id_domain, $name) = $sth->fetchrow ) {
push @clones,($self->search_domain($name));
}
$sth->finish;

return @clones;

}

=head2 search_domain

Searches a domain by name
Expand Down Expand Up @@ -1766,6 +1791,30 @@ sub _filter_active($pools, $active) {

}

=head2 list_users_share

Returns a list of users to share

=cut

sub list_users_share($self, $name=undef,@skip) {
my $users = $self->list_users();
my @found = @$users;
if ($name) {
@found = grep { $_->{name} =~ /$name/ } @$users;
}
if (@skip) {
my %skip = map { $_->id => 1} @skip;
my @pre=@found;
@found = ();
for my $user (@pre) {
next if $skip{$user->{id}};
push @found,($user);
}
}
return \@found;
}

=head2 upload_users

Upload a list of users to the database
Expand Down
10 changes: 10 additions & 0 deletions lib/Ravada/VM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2835,6 +2835,16 @@ sub list_unused_volumes($self) {
return @vols;
}

=head2 can_list_cpu_models

Default for Virtual Managers that can list cpu models is 0

=cut

sub can_list_cpu_models($self) {
return 0;
}

sub _around_copy_file_storage($orig, $self, $file, $storage) {
my $sth = $self->_dbh->prepare("SELECT id,info FROM volumes"
." WHERE file=? "
Expand Down
4 changes: 4 additions & 0 deletions lib/Ravada/VM/KVM.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,10 @@ sub get_library_version($self) {
return $self->vm->get_library_version();
}

sub can_list_cpu_models($self) {
return 1;
}

sub list_virtual_networks($self) {
my @networks;
for my $net ($self->vm->list_all_networks()) {
Expand Down
40 changes: 40 additions & 0 deletions public/js/ravada.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@
$scope.lock_info = false;
$scope.topology = false;
$scope.searching_ldap_attributes = true;
$scope.shared_user_found=false;
$scope.storage_pools=['default'];

$scope.getUnixTimeFromDate = function(date) {
Expand Down Expand Up @@ -595,6 +596,8 @@
$scope.storage_pools[i]=response.data[i].name;
}
});

$scope.list_shares();
}
list_interfaces();
if (is_admin) {
Expand Down Expand Up @@ -1208,6 +1211,43 @@
});
};

$scope.search_shared_user = function() {
$scope.searching_shared_user = true;
$scope.shared_user_found = '';
$http.get("/search_user/"+$scope.user_share)
.then(function(response) {
$scope.shared_user_found = response.data.found;
$scope.shared_user_count = response.data.count;
$scope.searching_shared_user=false;
if ($scope.shared_user_count == 1) {
$scope.user_share = response.data.found;
}
});
};

$scope.share_machine = function() {
$http.get("/machine/share/"+$scope.showmachine.id+"/"
+$scope.shared_user_found)
.then(function(response) {
$scope.list_shares();
});
};

$scope.remove_share_machine = function(user) {
$http.get("/machine/remove_share/"+$scope.showmachine.id+"/"
+user)
.then(function(response) {
$scope.list_shares();
});
};

$scope.list_shares = function() {
$http.get("/machine/list_shares/"+$scope.showmachine.id)
.then(function(response) {
$scope.shares = response.data;
});
};

$scope.message = [];
$scope.disk_remove = [];
$scope.pending_before = 10;
Expand Down
Loading
Loading