Commit 1dc8f890 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

wip(auth): group access restrictions

issue #1488
parent c3996edc
......@@ -661,7 +661,7 @@ sub _update_isos {
,min_disk_size => '0'
}
);
$self->_scheduled_fedora_releases(\%data);
$self->_scheduled_fedora_releases(\%data) if $0 !~ /\.t$/;
$self->_update_table($table, $field, \%data);
}
......@@ -1730,6 +1730,13 @@ sub _sql_create_tables($self) {
,extra => 'TEXT'
}
]
,[
group_access => {
id => 'integer NOT NULL PRIMARY KEY AUTO_INCREMENT'
,id_domain => 'integer NOT NULL references `domains` (`id`) ON DELETE CASCADE'
,name => 'char(80)'
}
]
,
[
settings => {
......
......@@ -460,7 +460,7 @@ sub search_group {
return $entries[0];
}
sub search_group_member($cn) {
sub search_group_member($cn, $retry = 0) {
my $base = "ou=groups,"._dc_base();
my $ldap = _init_ldap_admin();
my $mesg = $ldap ->search (
......@@ -468,6 +468,10 @@ sub search_group_member($cn) {
,base => $base
,sizelimit => 100
);
if ( ($mesg->code == 1 || $mesg->code == 81) && $retry <3 ) {
$LDAP_ADMIN = undef;
return search_group_member($cn, $retry+1);
}
warn $mesg->code." ".$mesg->error." [base: $base]" if $mesg->code;
my @entries = map { $_->get_value('cn') } $mesg->entries();
......
......@@ -362,6 +362,7 @@ sub _load_allowed {
my $ldap_entry;
$ldap_entry = $self->ldap_entry if $self->external_auth && $self->external_auth eq 'ldap';
my @domains = $self->_list_domains_access();
for my $id_domain ( @domains ) {
......@@ -403,6 +404,26 @@ sub _load_allowed {
$self->{_allowed}->{$id_domain} = 1;
}
}
$self->_load_allowed_groups();
}
sub _load_allowed_groups($self) {
my $sth = $$CONNECTOR->dbh->prepare("SELECT id_domain,name from group_access");
my ($id_domain, $name);
$sth->execute();
$sth->bind_columns(\($id_domain, $name));
while ( $sth->fetch ) {
next if $self->{_allowed}->{$id_domain};
$self->{_allowed}->{$id_domain} = 0;
next unless $self->is_external && $self->external_auth eq 'ldap';
$self->{_allowed}->{$id_domain} = 1
if Ravada::Auth::LDAP::is_member($self->name, $name);
}
}
1;
......@@ -5623,9 +5623,12 @@ Arguments is a named list
=cut
sub grant_access($self, %args) {
my $type = delete $args{type} or confess "Error: Missing type";
return $self->_allow_group_access(%args) if $type eq 'group';
my $attribute = delete $args{attribute} or confess "Error: Missing attribute";
my $value = delete $args{value} or confess "Error: Missing value";
my $type = delete $args{type} or confess "Error: Missing type";
my $allowed = delete $args{allowed};
$allowed = 1 if !defined $allowed;
my $last = ( delete $args{last} or 0 );
......@@ -5659,6 +5662,17 @@ sub grant_access($self, %args) {
$self->_fix_default_access($type) unless $value eq '*';
}
sub _allow_group_access($self, %args) {
my $group = delete $args{group} or confess "Error: group required";
confess "Error: unknown args ".Dumper(\%args) if keys %args;
my $sth = $$CONNECTOR->dbh->prepare(
"INSERT INTO group_access "
."( id_domain,name)"
." VALUES(?,? )"
);
$sth->execute($self->id, $group);
}
sub _fix_default_access($self, $type) {
my @list = $self->list_access($type);
my $id_default;
......
......@@ -394,7 +394,7 @@
$scope.init = function(id, url,is_admin) {
url_ws = url;
$scope.showmachineId=id;
$scope.tab_access=['client']
$scope.tab_access=['group']
$scope.client_attributes = [ 'User-Agent'
, 'Accept', 'Connection', 'Accept-Language', 'DNT', 'Host'
, 'Accept-Encoding', 'Cache-Control', 'X-Forwarded-For'
......@@ -891,6 +891,18 @@
});
};
$scope.list_access_groups = function() {
/* $http.get("/domain/access_groups/"+showmachine.id).then(function(response) {
$scope.access_groups=response.data;
});*/
};
$scope.add_group_access = function(group) {
$http.get("/machine/add_access_group/"+$scope.showmachine.id+"/"+group)
.then(function(response) {
$scope.list_access_groups();
});
};
$scope.message = [];
$scope.disk_remove = [];
$scope.pending_before = 10;
......@@ -900,9 +912,11 @@
$scope.access_value = [ ];
$scope.access_allowed = [ ];
$scope.access_last = [ ];
$scope.access_groups = [ ];
$scope.new_base = undefined;
$scope.list_ldap_attributes();
$scope.list_access_groups();
};
function swListMach() {
......
......@@ -917,6 +917,18 @@ get '/machine/move_access/(#id_domain)/(#id_access)/(#position)' => sub {
};
get '/machine/add_access_group/(#id_domain)/(#group)' => sub($c) {
my $id_domain = $c->stash('id_domain');
my $group = $c->stash('group');
my $ok = 0;
eval {
my $domain = Ravada::Front::Domain->open($id_domain);
$domain->add_access_group($group);
$ok =1;
};
return $c->render( json => { ok => $ok, error => $@ });
};
get '/machine/compact/(#id_domain)' => sub($c) {
my $req = Ravada::Request->compact(
id_domain => $c->stash('id_domain')
......
......@@ -93,6 +93,7 @@ sub test_user($name, $with_posix_group=0, $password='jameson', $storage=undef, $
eval { $mcnulty->allowed_access(1) };
is($@,'');
# try to login
diag($name,$password);
my $mcnulty_login = Ravada::Auth::login($name,$password);
ok($mcnulty_login,"No login");
ok(ref $mcnulty_login && ref($mcnulty_login) eq 'Ravada::Auth::LDAP',
......
......@@ -34,7 +34,7 @@ sub _create_group($oc=['top','groupOfNames']) {
return $group;
}
init('t/etc/ravada_ldap.conf');
init('t/etc/ravada_ldap_basic.conf');
for my $oc (
['top','groupOfUniqueNames','nsMemberOf','posixGroup']
......
---
ldap:
admin_user:
dn: cn=Directory Manager
password: 12345678
base: 'dc=example,dc=com'
......@@ -11,7 +11,7 @@ use feature qw(signatures);
use lib 't/lib';
use Test::Ravada;
init();
init('t/etc/ravada_ldap_basic.conf');
clean();
sub _remove_bases(@bases) {
......@@ -36,6 +36,44 @@ sub _remove_bases(@bases) {
}
}
sub test_access_by_group($vm) {
my $base = create_domain($vm->type);
$base->prepare_base(user_admin);
$base->is_public(1);
my $g_name = new_domain_name();
my $group = Ravada::Auth::LDAP::search_group(name => $g_name);
if (!$group) {
Ravada::Auth::LDAP::add_group($g_name);
}
$base->grant_access(
type => 'group'
,group => $g_name
);
my $user = create_user(new_domain_name(),$$);
is($user->is_admin, 0 );
my $list_bases = rvd_front->list_machines_user($user);
is(scalar(@$list_bases),0) or exit;
my $user_ldap0 = create_ldap_user(new_domain_name(), $$);
my $user_ldap = Ravada::Auth::SQL->new(name => $user_ldap0->get_value('cn'));
$list_bases = rvd_front->list_machines_user($user_ldap);
is(scalar(@$list_bases),0) or exit;
Ravada::Auth::LDAP::add_to_group($user_ldap->ldap_entry->dn, $g_name);
$user_ldap->_load_allowed(1);
$list_bases = rvd_front->list_machines_user($user_ldap);
is(scalar(@$list_bases),1) or exit;
$list_bases = rvd_front->list_machines_user(user_admin);
is(scalar(@$list_bases),1) or exit;
remove_domain($base);
}
sub test_access_by_agent($vm, $do_clones=0) {
my $base = create_domain($vm->type);
......@@ -324,8 +362,7 @@ sub test_maintenance() {
###########################################################################
test_maintenance();
for my $vm_name (vm_names()) {
for my $vm_name (reverse vm_names()) {
my $vm = rvd_back->search_vm($vm_name);
SKIP: {
......@@ -338,6 +375,8 @@ for my $vm_name (vm_names()) {
skip($msg,10) if !$vm;
diag("Testing access restrictions in domain for $vm_name");
test_access_by_group($vm);
test_access_by_agent($vm);
test_access_by_lang($vm);
......@@ -355,6 +394,7 @@ for my $vm_name (vm_names()) {
}
}
test_maintenance();
end();
done_testing();
......@@ -369,7 +369,9 @@ sub init($config=undef, $sqlite = 1 , $flush=0) {
}
if ( $RVD_BACK && ref($RVD_BACK) ) {
warn ''.localtime();
clean();
warn ''.localtime();
# clean removes the temporary config file, so we dump it again
if ( $config && ref($config) ) {
DumpFile($FILE_CONFIG_TMP, $config);
......@@ -907,7 +909,7 @@ sub create_ldap_user($name, $password, $keep=0) {
if ( Ravada::Auth::LDAP::search_user($name) ) {
return if $keep;
# diag("Removing $name");
diag("Removing $name");
Ravada::Auth::LDAP::remove_user($name)
}
......@@ -929,9 +931,17 @@ sub create_ldap_user($name, $password, $keep=0) {
push @USERS_LDAP,($name);
my @user = Ravada::Auth::LDAP::search_user($name);
my @user = Ravada::Auth::LDAP::search_user(name => $name, filter => '');
# diag("Adding $name to ldap");
return $user[0];
my $user_ldap = $user[0];
my $user_sql
= Ravada::Auth::SQL::add_user(name => $name, is_external => 1, external_auth => 'ldap');
for my $group ( $user_sql->groups ) {
Ravada::Auth::LDAP::remove_from_group($user_ldap->dn, $group);
}
return $user_ldap;
}
sub _list_requests {
......
<div>
<button
class="btn"
ng-class="{
'btn btn-primary': tab_access == 'group'
,'btn': tab_access !== 'group'
}"
ng-model="tab_access_group"
ng-click="tab_access='group'">Group</button>
<button
class="btn"
ng-class="{
......@@ -18,6 +28,10 @@
ng-click="tab_access='ldap'">LDAP</button>
</div>
<div ng-show="tab_access=='group'">
%= include "main/machine_access_group"
</div>
<div ng-show="tab_access=='ldap'">
%= include "main/machine_access_ldap"
</div>
......
<i ng-show="!ldap_groups" class="fas fa-sync-alt fa-spin"></i>
<select ng-model="allow_group"
ng-show="ldap_groups"
ng-options="group for group in ldap_groups | orderBy:group"
>
</select>
<button ng-show="allow_group"
ng-click="add_group_access(allow_group)">Add</button>
<div class="alert alert-warning" ng-show="access_groups.length == 0">
<%=l 'This virtual machine has no group restrictions.' %>
</div>
<div ng-show="access_groups.length > 0">
<b><%=l 'Only users from these groups will be allowed to execute this machine' %></b>
</div>
<table>
</thead>
<tbody>
<tr ng-repeat="group in access_groups">
<td>
<button ng-click="remove_group_access(group)"
class="badge badge-light text-blue">x</button>
{{group}}
</td>
</tr>
</tbody>
</table>
......@@ -31,13 +31,18 @@
<a class="nav-link" href="#password" role="tab" data-toggle="tab" aria-controls="password" aria-selected="true">Password</a>
</li>
% }
% if ( $_user->is_admin && $user->is_external && $user->external_auth eq 'ldap' ) {
% if ( $_user->is_admin && $user->is_external && $user->external_auth eq 'ldap' && $user->ldap_entry ) {
<li class="nav-item">
<a class="nav-link" href="#groups" role="tab" data-toggle="tab" aria-controls="groups" aria-selected="true">Groups</a>
</li>
% }
</ul>
% if ( $_user->is_admin && $user->is_external && $user->external_auth eq 'ldap' && !$user->ldap_entry ) {
<div class="alert alert-danger">
<%=l 'Error: the LDAP entry for this user has been removed.' %>
</div>
% }
<div class="tab-content" id="myTabContent">
% if ( $_user->is_admin ) {
<div class="tab-pane fade show active" id="admin" role="tabpanel" aria-labelledby="admin-tab">
......@@ -54,7 +59,7 @@
%= include '/main/manage_user_password'
</div>
% }
% if ( $_user->is_admin && $user->is_external && $user->external_auth eq 'ldap' ) {
% if ( $_user->is_admin && $user->is_external && $user->external_auth eq 'ldap' && $user->ldap_entry ) {
<div class="tab-pane fade" id="groups" role="tabpanel" aria-labelledby="group-tab">
%= include '/main/manage_user_groups'
</div>
......
......@@ -4,7 +4,7 @@
<%=l 'No LDAP groups created.' %>
<a href="/admin/groups"><%=l 'Add groups' %></a>
</span>
<select
<select class="selectpicker" data-live-search="true"
ng-show="groups && groups.length"
ng-model="new_group"
ng-options="group for group in groups"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment