Commit c74d9534 authored by Francesc Guasch's avatar Francesc Guasch
Browse files

Feature #916 configure access (#925)

* doc(test): LDAP local test server start

issue #922

* wip(ldap): grants can be ordered by this field

issue #922

* feature(auth): deny by LDAP attribute

issue #922

* wip(test): remove tes ldap users after tests

issue #922

* test(auth): check deny access

issue #922

* test(auth): check deny & multiple allow/deny

issue #922

* fix(auth): remove auth on remove domain

issue #922

* wip(auth): finish it match auth

issue #922

* wip(frontend): show user LDAP attributes

It is used for access debugging pusrposes we may remove it later.

issue #916

* feature(frontend): show machine access restrictions

issue #916

* test(auth): test LDAP access attributes edges

issue #922

* wip(auth): methods to manage LDAP access entries

issue #916

* wip(auth): check authorization

- if denied returns false
- if allowed continues until en or false
- if no matches returns default

issue #916

* feature(frontend): manage LDAP access restrictions

issue #916

* wip(backend): remove access restrictions if known domain

issue #916

* wip(auth): fixed edge case with no access entries

issue #916

* feature(auth): check restrictions may be end with last

when the last field is enabled, no more restrictions are checked

issue #916

* wip(frontend): small fixes suggested by @gloriarodriguez

- sort attributes
- remove default after delete the last restriction
- set last to true by default on new

issue #916
parent 88bcbe88
......@@ -344,7 +344,7 @@ sub _load_allowed {
for my $id_domain ( @domains ) {
my $sth = $$CONNECTOR->dbh->prepare(
"SELECT attribute, value, allowed "
"SELECT attribute, value, allowed, last "
." FROM access_ldap_attribute"
." WHERE id_domain=?"
." ORDER BY n_order "
......@@ -352,18 +352,21 @@ sub _load_allowed {
$sth->execute($id_domain);
my ($n_allowed, $n_denied) = ( 0,0 );
while ( my ($attribute, $value, $allowed) = $sth->fetchrow) {
while ( my ($attribute, $value, $allowed, $last) = $sth->fetchrow) {
$n_allowed++ if $allowed;
$n_denied++ if !$allowed;
if ( $value eq '*'
|| ( $ldap_entry && defined $ldap_entry->get_value($attribute)
&& $ldap_entry->get_value($attribute) eq $value )) {
if ( $value eq '*' ) {
$self->{_allowed}->{$id_domain} = $allowed
if !exists $self->{_allowed}->{$id_domain};
last;
} elsif ( $ldap_entry && defined $ldap_entry->get_value($attribute)
&& $ldap_entry->get_value($attribute) eq $value ) {
$self->{_allowed}->{$id_domain} = $allowed;
last;
last if !$allowed || $last;
}
}
$sth->finish;
......
......@@ -1180,6 +1180,7 @@ sub _remove_domain_cascade($self,$user, $cascade = 1) {
sub _remove_access_attributes_db($self) {
return if !$self->{_data}->{id};
my $sth = $$CONNECTOR->dbh->prepare("DELETE FROM access_ldap_attribute"
." WHERE id_domain=?");
$sth->execute($self->id);
......@@ -3030,7 +3031,7 @@ Example:
=cut
sub allow_ldap_attribute($self, $attribute, $value, $allowed=1 ) {
sub allow_ldap_access($self, $attribute, $value, $allowed=1, $last=0 ) {
my $sth = $$CONNECTOR->dbh->prepare(
"SELECT max(n_order) FROM access_ldap_attribute "
." WHERE id_domain=?"
......@@ -3041,12 +3042,36 @@ sub allow_ldap_attribute($self, $attribute, $value, $allowed=1 ) {
$sth = $$CONNECTOR->dbh->prepare(
"INSERT INTO access_ldap_attribute "
."(id_domain, attribute, value, allowed, n_order) "
."VALUES(?,?,?,?,?)");
$sth->execute($self->id, $attribute, $value, $allowed, $n_order+1);
."(id_domain, attribute, value, allowed, n_order, last) "
."VALUES(?,?,?,?,?,?)");
$sth->execute($self->id, $attribute, $value, $allowed, $n_order+1, $last);
}
#TODO: check something has been deleted
sub delete_ldap_access($self, $id_access) {
my $sth = $$CONNECTOR->dbh->prepare(
"DELETE FROM access_ldap_attribute "
."WHERE id_domain=? AND id=? ");
$sth->execute($self->id, $id_access);
}
sub list_ldap_access($self) {
my $sth = $$CONNECTOR->dbh->prepare(
"SELECT * from access_ldap_attribute"
." WHERE id_domain = ? "
." ORDER BY n_order"
);
$sth->execute($self->id);
my @list;
while (my $row = $sth->fetchrow_hashref) {
$row->{last} = 1 if !$row->{allowed} && !$row->{last};
push @list,($row) if keys %$row;
}
return @list;
}
=head2 deny_ldap_attribute
=head2 deny_ldap_access
If specified, only the LDAP users with that attribute value can clone these
virtual machines.
......@@ -3059,8 +3084,62 @@ Example:
=cut
sub deny_ldap_attribute($self, $attribute, $value) {
$self->allow_ldap_attribute($attribute, $value, 0);
sub deny_ldap_access($self, $attribute, $value) {
$self->allow_ldap_access($attribute, $value, 0);
}
sub _set_access_order($self, $id_access, $n_order) {
my $sth = $$CONNECTOR->dbh->prepare("UPDATE access_ldap_attribute "
." SET n_order=? WHERE id=? AND id_domain=?");
$sth->execute($n_order, $id_access, $self->id);
}
sub move_ldap_access($self, $id_access, $position) {
confess "Error: You can only move position +1 or -1"
if ($position != -1 && $position != 1);
my @list = $self->list_ldap_access();
my $index;
for my $n (0 .. $#list) {
if (defined $list[$n] && $list[$n]->{id} == $id_access ) {
$index = $n;
last;
}
}
confess "Error: access id: $id_access not found for domain ".$self->id
."\n".Dumper(\@list)
if !defined $index;
my ($n_order) = $list[$index]->{n_order};
die "Error: position $index has no n_order for domain ".$self->id
."\n".Dumper(\@list)
if !defined $n_order;
my $index2 = $index + $position;
die "Error: position $index2 has no id for domain ".$self->id
."\n".Dumper(\@list)
if !defined $list[$index2] || !defined$list[$index2]->{id};
my ($id_access2, $n_order2) = ($list[$index2]->{id}, $list[$index2]->{n_order});
die "Error: position ".$index2." not found for domain ".$self->id
."\n".Dumper(\@list)
if !defined $id_access2;
die "Error: n_orders are the same for index $index and ".($index+$position)
."in \n".Dumper(\@list)
if $n_order == $n_order2;
$self->_set_access_order($id_access, $n_order2);
$self->_set_access_order($id_access2, $n_order);
}
sub set_ldap_access($self, $id_access, $allowed, $last) {
my $sth = $$CONNECTOR->dbh->prepare("UPDATE access_ldap_attribute "
." SET allowed=?, last=?"
." WHERE id=?");
$sth->execute($allowed, $last, $id_access);
}
1;
......@@ -331,9 +331,6 @@
console.log($scope.cn);
$http.get('/list_ldap_attributes/'+$scope.cn).then(function(response) {
$scope.ldap_attributes = response.data.attributes;
if ($scope.ldap_attributes && $scope.ldap_attributes.length) {
$scope.change_cn = 0;
}
});
};
$scope.count_ldap_entries = function() {
......
......@@ -5,7 +5,7 @@ CREATE TABLE `access_ldap_attribute` (
`value` varchar(64),
`allowed` int not null default 1,
`n_order` int not null default 1,
PRIMARY KEY (`id`),
UNIQUE KEY `id_base` (`id_domain`,`attribute`,`value`)
`last` int not null default 1,
PRIMARY KEY (`id`)
);
......@@ -109,7 +109,7 @@ sub test_access_by_attribute_deny($vm, $do_clones=0) {
_do_clones($data, $base, $do_clones);
$base->deny_ldap_attribute( givenName => $data->{student}->{user}->{name});
$base->deny_ldap_access( givenName => $data->{student}->{user}->{name});
_refresh_users($data);
is($data->{student}->{user}->allowed_access( $base->id ), 0);
is($data->{teacher}->{user}->allowed_access( $base->id ), 1);
......@@ -144,9 +144,9 @@ sub test_access_by_attribute_several($vm, $do_clones=0) {
_do_clones($data, $base, $do_clones);
$base->deny_ldap_attribute( givenName => $data->{student}->{user}->{name});
$base->allow_ldap_attribute( givenName => $data->{teacher}->{user}->{name});
$base->deny_ldap_attribute( givenName => '*'); #default policy
$base->deny_ldap_access( givenName => $data->{student}->{user}->{name});
$base->allow_ldap_access( givenName => $data->{teacher}->{user}->{name});
$base->deny_ldap_access( givenName => '*'); #default policy
_refresh_users($data);
is($data->{student}->{user}->allowed_access( $base->id ), 0);
is($data->{teacher}->{user}->allowed_access( $base->id ), 1)
......@@ -169,6 +169,99 @@ sub test_access_by_attribute_several($vm, $do_clones=0) {
_remove_bases($base);
_remove_users($data);
}
sub test_access_by_attribute_several2($vm) {
my $base = create_domain($vm->type);
$base->prepare_base(user_admin);
$base->is_public(1);
my $data = _create_users();
is($data->{student}->{user}->allowed_access( $base->id ), 1);
is($data->{teacher}->{user}->allowed_access( $base->id ), 1);
is($data->{other}->{user}->allowed_access( $base->id ), 1);
$base->allow_ldap_access( givenName => $data->{student}->{user}->{name});
$base->deny_ldap_access( sn => $data->{student}->{user}->{name});
$base->allow_ldap_access( givenName => $data->{teacher}->{user}->{name});
$base->deny_ldap_access( givenName => '*'); #default policy
_refresh_users($data);
is($data->{student}->{user}->allowed_access( $base->id ), 0);
is($data->{teacher}->{user}->allowed_access( $base->id ), 1)
or die Dumper($data->{teacher}->{user}->{_allowed});
is($data->{other}->{user}->allowed_access( $base->id ), 0);
_remove_bases($base);
_remove_users($data);
}
sub test_access_by_attribute_move($vm, $do_clones=0) {
my $base = create_domain($vm->type);
$base->prepare_base(user_admin);
$base->is_public(1);
my $data = _create_users();
is($data->{student}->{user}->allowed_access( $base->id ), 1);
is($data->{teacher}->{user}->allowed_access( $base->id ), 1);
is($data->{other}->{user}->allowed_access( $base->id ), 1);
_do_clones($data, $base, $do_clones);
$base->allow_ldap_access( givenName => $data->{teacher}->{user}->{name});
$base->deny_ldap_access( givenName => '*'); #default policy
my @list_ldap_attribute = $base->list_ldap_access();
$base->move_ldap_access($list_ldap_attribute[1]->{id}, -1);
my @list_ldap_attribute2 = $base->list_ldap_access();
is($list_ldap_attribute[0]->{id}, $list_ldap_attribute2[1]->{id}) or exit;
_refresh_users($data);
is($data->{teacher}->{user}->allowed_access( $base->id ), 0)
or die Dumper($data->{teacher}->{user}->{_allowed});
is($data->{other}->{user}->allowed_access( $base->id ), 0);
my $list_bases = rvd_front->list_machines_user($data->{student}->{user});
is(scalar (@$list_bases), 0);
$list_bases = rvd_front->list_machines_user($data->{teacher}->{user});
is(scalar (@$list_bases), 0);
# other has no external_auth, access denied
$list_bases = rvd_front->list_machines_user($data->{other}->{user});
is(scalar (@$list_bases), 0);
$list_bases = rvd_front->list_machines_user(user_admin);
is(scalar (@$list_bases), 1);
_remove_bases($base);
_remove_users($data);
}
sub test_access_by_attribute_move_removed($vm) {
my $base = create_domain($vm->type);
$base->prepare_base(user_admin);
$base->is_public(1);
my $data = _create_users();
$base->allow_ldap_access( givenName => $data->{teacher}->{user}->{name});
$base->allow_ldap_access( givenName => $data->{student}->{user}->{name});
$base->deny_ldap_access( givenName => '*'); #default policy
my @list_ldap_attribute = $base->list_ldap_access();
# remove the access #1
$base->delete_ldap_access($list_ldap_attribute[1]->{id});
$base->move_ldap_access($list_ldap_attribute[2]->{id}, -1);
my @list_ldap_attribute2 = $base->list_ldap_access();
is($list_ldap_attribute[2]->{id}, $list_ldap_attribute2[0]->{id}) or exit;
_remove_bases($base);
_remove_users($data);
}
sub test_2_checks($vm) {
......@@ -178,8 +271,8 @@ sub test_2_checks($vm) {
$base->prepare_base(user_admin);
$base->is_public(1);
$base->allow_ldap_attribute( givenName => $data->{student}->{name});
$base->deny_ldap_attribute( givenName => $data->{teacher}->{name});
$base->allow_ldap_access( givenName => $data->{student}->{name});
$base->deny_ldap_access( givenName => $data->{teacher}->{name});
my $sth = connector->dbh->prepare(
"SELECT id,n_order from access_ldap_attribute "
......@@ -220,7 +313,7 @@ sub test_access_by_attribute($vm, $do_clones=0) {
is($data->{other}->{user}->allowed_access( $base->id ), 1);
is(user_admin->allowed_access( $base->id ), 1);
$base->allow_ldap_attribute( givenName => $data->{student}->{name});
$base->allow_ldap_access( givenName => $data->{student}->{name});
_refresh_users($data);
#################################################################
......@@ -385,8 +478,12 @@ for my $vm_name ('KVM', 'Void') {
test_access_by_attribute_deny($vm);
test_access_by_attribute_deny($vm,1); # with clones
test_access_by_attribute_several2($vm);
test_access_by_attribute_several($vm);
test_access_by_attribute_move($vm);
test_access_by_attribute_move_removed($vm);
}
}
......
<div class="panel panel-default" ng-show="ldap_attributes
|| !cn_change
|| ( ldap_attributes_domain && ldap_attributes_domain.length) ">
<div class="panel panel-default">
<div class="panel-body">
Type a typical LDAP user name to fetch the attribute list
<input type="text" ng-model="cn" ng-change="list_ldap_attributes()"
ng-init="cn='<%= $ldap_attributes_cn %>';list_ldap_attributes()">
<span ng-hide="ldap_attributes || !cn">
User name <b>{{cn}}</b> not found in LDAP server
</span>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
......@@ -27,20 +36,3 @@
%= include "/main/machine_access_new"
</div>
</div>
<div ng-hide="change_cn || !ldap_attributes">
Using LDAP attributes from user <i>{{cn}}</i>
<button ng-click="change_cn=1">change</button>
</div>
<div class="panel panel-default"
ng-show="change_cn || !ldap_attributes">
<div class="panel-body">
Type a typical LDAP user name to fetch the attribute list
<input type="text" ng-model="cn" ng-change="list_ldap_attributes()"
ng-init="cn='<%= $ldap_attributes_cn %>';list_ldap_attributes()">
<br/>
<div class="alert alert-danger" ng-hide="ldap_attributes || !cn">
User name <b>{{cn}}</b> not found in LDAP server
</div>
</div>
</div>
......@@ -7,13 +7,11 @@
</div>
<div class="col-md-1">
<input type="checkbox" ng-checked="attribute.allowed"
title="Last is mandatory when not allowed"
ng-click="set_ldap_access(attribute.id, !attribute.allowed, attribute.last)"
>
</div>
<div class="col-md-1">
<input type="checkbox" ng-checked="attribute.last"
title="Last is mandatory when not allowed"
ng-disabled="!attribute.allowed"
ng-click="set_ldap_access(attribute.id, attribute.allowed, !attribute.last)"
>
......@@ -28,7 +26,7 @@
><i class="fa fa-arrow-up" ></i></button>
</div>
<div class="col-md-1">
<button class="btn"
<button class="btn"
ng-click="delete_ldap_access(attribute.id)"><i class="fas fa-times"></i></button>
</div>
</div>
......@@ -50,4 +48,3 @@
access is denied.</span>
</div>
</div>
<div class="row" ng-show="ldap_attributes || change_cn">
<div class="row">
<div class="col-md-2">
<select ng-model="ldap_attribute" ng-change="ldap_entries=0;ldap_verified=false"
ng-disabled="ldap_verifying"
>
<select ng-model="ldap_attribute" ng-change="ldap_entries=0;ldap_verified=false">
<option ng-repeat="attribute in ldap_attributes" value="{{attribute}}">
{{attribute}}
</option>
......@@ -11,7 +9,6 @@
<div class="col-md-3">
<input ng-model="ldap_attribute_value" type="text"
ng-disabled="ldap_verifying"
ng-change="ldap_entries=0;ldap_verified=false">
</div>
......@@ -30,27 +27,17 @@
ng-disabled="ldap_verifying"
ng-click="count_ldap_entries()"
>verify</button>
<button
ng-disabled="!ldap_attribute || !ldap_attribute_value || ldap_verifying
|| (!ldap_attribute_allowed && !ldap_attribute_last)"
<button ng-show="ldap_attribute && ldap_attribute_value"
ng-disabled="ldap_verifying || (!ldap_attribute_allowed && ! ldap_attribute_last)"
ng-click="add_ldap_access()"
>add</button>
>save</button>
</div>
<div class="col-md-3">
<span ng-show="ldap_verifying">Verifying {{ldap_attribute}} ...</span>
<span ng-show="ldap_verified && !ldap_entries">No entries found</span>
<span ng-show="ldap_verified && ldap_entries">{{ldap_attribute}} = {{ldap_attribute_value}} has at least {{ldap_entries}} entries. </span>
<span ng-show="!ldap_attribute_allowed && !ldap_attribute_last">
Enable last for not allowed restrictions.
</span>
</div>
</div>
<div class="row" ng-show="ldap_attributes">
<div class="alert alert-danger"
ng-show="!ldap_attribute_allowed && !ldap_attribute_last">
Last is mandatory when not allowed.
</div>
<div class="alert alert-info" ng-show="ldap_verifying">
Verifying <i>{{ldap_attribute}} = {{ldap_attribute_value}}</i>
in the LDAP server. It may take long ...
</div>
<div class="alert alert-warning" ng-show="ldap_verified && !ldap_entries">
No entries found.
</div>
<div class="alert alert-success"
ng-show="ldap_verified && ldap_entries">
{{ldap_attribute}} = {{ldap_attribute_value}} has at least {{ldap_entries}} entries.
</div>
</div>
Supports Markdown
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