Commit 8206cede authored by Francesc Guasch's avatar Francesc Guasch
Browse files

feature(frontend): manage LDAP groups

parent b39a3e96
...@@ -1181,6 +1181,9 @@ sub _add_indexes_generic($self) { ...@@ -1181,6 +1181,9 @@ sub _add_indexes_generic($self) {
,"unique (id_domain,name):name" ,"unique (id_domain,name):name"
,"unique(id_vm,public_port)" ,"unique(id_vm,public_port)"
] ]
,group_access => [
"unique (id_domain,name)"
]
,requests => [ ,requests => [
"index(status,at_time)" "index(status,at_time)"
,"index(id,date_changed,status,at_time)" ,"index(id,date_changed,status,at_time)"
...@@ -1757,6 +1760,7 @@ sub _sql_create_tables($self) { ...@@ -1757,6 +1760,7 @@ sub _sql_create_tables($self) {
,id_owner => 'int not null' ,id_owner => 'int not null'
,background_color => 'varchar(20)' ,background_color => 'varchar(20)'
,date_created => 'datetime DEFAULT CURRENT_TIMESTAMP' ,date_created => 'datetime DEFAULT CURRENT_TIMESTAMP'
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
} }
] ]
, ,
...@@ -1770,6 +1774,7 @@ sub _sql_create_tables($self) { ...@@ -1770,6 +1774,7 @@ sub _sql_create_tables($self) {
,time_end => 'time not null' ,time_end => 'time not null'
,date_booking => 'date' ,date_booking => 'date'
,visibility => "enum ('private','public') default 'public'" ,visibility => "enum ('private','public') default 'public'"
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
} }
] ]
, ,
...@@ -1779,6 +1784,7 @@ sub _sql_create_tables($self) { ...@@ -1779,6 +1784,7 @@ sub _sql_create_tables($self) {
,id_booking_entry ,id_booking_entry
=> 'int not null references `booking_entries` (`id`) ON DELETE CASCADE' => 'int not null references `booking_entries` (`id`) ON DELETE CASCADE'
,ldap_group => 'varchar(255) not null' ,ldap_group => 'varchar(255) not null'
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
} }
] ]
, ,
...@@ -1788,6 +1794,7 @@ sub _sql_create_tables($self) { ...@@ -1788,6 +1794,7 @@ sub _sql_create_tables($self) {
,id_booking_entry ,id_booking_entry
=> 'int not null references `booking_entries` (`id`) ON DELETE CASCADE' => 'int not null references `booking_entries` (`id`) ON DELETE CASCADE'
,id_user => 'int not null references `users` (`id`) ON DELETE CASCADE' ,id_user => 'int not null references `users` (`id`) ON DELETE CASCADE'
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
} }
] ]
, ,
...@@ -1797,6 +1804,7 @@ sub _sql_create_tables($self) { ...@@ -1797,6 +1804,7 @@ sub _sql_create_tables($self) {
,id_booking_entry ,id_booking_entry
=> 'int not null references `booking_entries` (`id`) ON DELETE CASCADE' => 'int not null references `booking_entries` (`id`) ON DELETE CASCADE'
,id_base => 'int not null references `domains` (`id`) ON DELETE CASCADE' ,id_base => 'int not null references `domains` (`id`) ON DELETE CASCADE'
,date_changed => 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
} }
] ]
, ,
...@@ -2088,6 +2096,9 @@ sub _port_definition($driver, $definition0){ ...@@ -2088,6 +2096,9 @@ sub _port_definition($driver, $definition0){
my ($size) = sort map { length($_) } @found; my ($size) = sort map { length($_) } @found;
return " varchar($size) $default"; return " varchar($size) $default";
} }
elsif ($definition0 =~ /^timestamp /) {
$definition0 = 'timestamp';
}
} }
return $definition0; return $definition0;
} }
...@@ -4141,6 +4152,9 @@ sub _cmd_remove_hardware { ...@@ -4141,6 +4152,9 @@ sub _cmd_remove_hardware {
my $domain = $self->search_domain_by_id($id_domain); my $domain = $self->search_domain_by_id($id_domain);
my $user = Ravada::Auth::SQL->search_by_id($uid); my $user = Ravada::Auth::SQL->search_by_id($uid);
die "Error: User ".$user->name." not allowed to remove hardware from machine "
.$domain->name
if !$user->is_admin;
$domain->remove_controller($hardware, $index); $domain->remove_controller($hardware, $index);
} }
...@@ -4158,7 +4172,7 @@ sub _cmd_change_hardware { ...@@ -4158,7 +4172,7 @@ sub _cmd_change_hardware {
my $user = Ravada::Auth::SQL->search_by_id($uid); my $user = Ravada::Auth::SQL->search_by_id($uid);
die "Error: User ".$user->name." not allowed\n" die "Error: User ".$user->name." not allowed\n"
if !$user->is_admin; if $hardware ne 'memory' && !$user->is_admin;
$domain->change_hardware( $domain->change_hardware(
$request->args('hardware') $request->args('hardware')
......
...@@ -121,11 +121,11 @@ sub enable_LDAP { ...@@ -121,11 +121,11 @@ sub enable_LDAP {
return $value; return $value;
} }
=head2 enable_CAS =head2 enable_SSO
Sets or get CAS support. Sets or get SSO support.
Ravada::Auth::enable_CAS(0); Ravada::Auth::enable_SSO(0);
print "SSO is supported" if Ravada::Auth::enable_SSO(); print "SSO is supported" if Ravada::Auth::enable_SSO();
......
...@@ -181,10 +181,6 @@ sub _new_uid($ldap=_init_ldap_admin(), $base=_dc_base()) { ...@@ -181,10 +181,6 @@ sub _new_uid($ldap=_init_ldap_admin(), $base=_dc_base()) {
} }
} }
sub default_object_class() {
return @OBJECT_CLASS;
}
sub _password_store($password, $storage, $algorithm=undef) { sub _password_store($password, $storage, $algorithm=undef) {
return _password_rfc2307($password, $algorithm) if lc($storage) eq 'rfc2307'; return _password_rfc2307($password, $algorithm) if lc($storage) eq 'rfc2307';
return _password_pbkdf2($password, $algorithm) if lc($storage) eq 'pbkdf2'; return _password_pbkdf2($password, $algorithm) if lc($storage) eq 'pbkdf2';
...@@ -460,7 +456,11 @@ sub search_group { ...@@ -460,7 +456,11 @@ sub search_group {
return $entries[0]; return $entries[0];
} }
sub search_group_member($cn, $retry = 0) { =head2 search_group_members
=cut
sub search_group_members($cn, $retry = 0) {
my $base = "ou=groups,"._dc_base(); my $base = "ou=groups,"._dc_base();
my $ldap = _init_ldap_admin(); my $ldap = _init_ldap_admin();
my $mesg = $ldap ->search ( my $mesg = $ldap ->search (
...@@ -470,7 +470,7 @@ sub search_group_member($cn, $retry = 0) { ...@@ -470,7 +470,7 @@ sub search_group_member($cn, $retry = 0) {
); );
if ( ($mesg->code == 1 || $mesg->code == 81) && $retry <3 ) { if ( ($mesg->code == 1 || $mesg->code == 81) && $retry <3 ) {
$LDAP_ADMIN = undef; $LDAP_ADMIN = undef;
return search_group_member($cn, $retry+1); return search_group_members($cn, $retry+1);
} }
warn $mesg->code." ".$mesg->error." [base: $base]" if $mesg->code; warn $mesg->code." ".$mesg->error." [base: $base]" if $mesg->code;
...@@ -497,7 +497,10 @@ Adds user to group ...@@ -497,7 +497,10 @@ Adds user to group
sub add_to_group { sub add_to_group {
my ($dn, $group_name) = @_; my ($dn, $group_name) = @_;
if ( $dn !~ /=.*,/ ) { if ( $dn !~ /=.*,/ ) {
my $user = search_user(name => $dn) or confess "Error: user '$dn' not found"; my $user = search_user(name => $dn, field => 'uid');
$user = search_user(name => $dn, field => 'cn') if !$user;
confess "Error: user '$dn' not found" if !$user;
$dn = $user->dn; $dn = $user->dn;
} }
...@@ -590,6 +593,12 @@ sub _search_posix_group($self, $name) { ...@@ -590,6 +593,12 @@ sub _search_posix_group($self, $name) {
return $posix_group[0]; return $posix_group[0];
} }
=head2 group_members
Returns a list of the group members
=cut
sub group_members { sub group_members {
return _group_members(@_); return _group_members(@_);
} }
...@@ -956,9 +965,16 @@ sub is_member($cn, $group) { ...@@ -956,9 +965,16 @@ sub is_member($cn, $group) {
my @members = _group_members($group); my @members = _group_members($group);
return 1 if grep /^$cn$/, @members; return 1 if grep /^$cn$/, @members;
$user = search_user($cn) or confess "Error: unknown user '$cn'" if (!$dn) {
if !$user; if (!$user) {
$dn = $user->dn if !$dn; for my $field ( 'uid','cn') {
$user = search_user(name => $cn, field => $field);
last if $user;
}
confess "Error: unknown user '$cn'" if !$user;
}
$dn = $user->dn if !$dn;
}
return 1 if grep /^$dn$/, @members; return 1 if grep /^$dn$/, @members;
......
...@@ -1120,15 +1120,25 @@ sub ldap_entry($self) { ...@@ -1120,15 +1120,25 @@ sub ldap_entry($self) {
return $self->{_ldap_entry} if $self->{_ldap_entry}; return $self->{_ldap_entry} if $self->{_ldap_entry};
my @entries = Ravada::Auth::LDAP::search_user( name => $self->name ); for my $field ( qw(uid cn)) {
$self->{_ldap_entry} = $entries[0]; my ($entry) = Ravada::Auth::LDAP::search_user( name => $self->name,field => $field );
next if !$entry;
$self->{_ldap_entry} = $entry;
return $entry;
}
return $self->{_ldap_entry}; return;
} }
=head2 groups
Returns a list of the groups this user belogs to
=cut
sub groups($self) { sub groups($self) {
return () if !$self->external_auth || $self->external_auth ne 'ldap'; return () if !$self->external_auth || $self->external_auth ne 'ldap';
my @groups = Ravada::Auth::LDAP::search_group_member($self->name); my @groups = Ravada::Auth::LDAP::search_group_members($self->name);
return @groups; return @groups;
} }
......
...@@ -422,7 +422,7 @@ sub _load_allowed_groups($self) { ...@@ -422,7 +422,7 @@ sub _load_allowed_groups($self) {
next unless $self->is_external && $self->external_auth eq 'ldap'; next unless $self->is_external && $self->external_auth eq 'ldap';
$self->{_allowed}->{$id_domain} = 1 $self->{_allowed}->{$id_domain} = 1
if Ravada::Auth::LDAP::is_member($self->name, $name); if Ravada::Auth::LDAP::is_member($self->ldap_entry, $name);
} }
} }
......
...@@ -5673,6 +5673,24 @@ sub _allow_group_access($self, %args) { ...@@ -5673,6 +5673,24 @@ sub _allow_group_access($self, %args) {
$sth->execute($self->id, $group); $sth->execute($self->id, $group);
} }
=head2 list_access_groups
Returns the list of groups who can access this virtual machine
=cut
sub list_access_groups($self) {
my $sth = $$CONNECTOR->dbh->prepare("SELECT name from group_access "
." WHERE id_domain=?"
);
$sth->execute($self->id);
my @groups;
while ( my ($name) = $sth->fetchrow ) {
push @groups,($name);
}
return @groups;
}
sub _fix_default_access($self, $type) { sub _fix_default_access($self, $type) {
my @list = $self->list_access($type); my @list = $self->list_access($type);
my $id_default; my $id_default;
...@@ -6353,6 +6371,12 @@ sub _set_displays_down($self) { ...@@ -6353,6 +6371,12 @@ sub _set_displays_down($self) {
$sth->execute($self->id); $sth->execute($self->id);
} }
=head2 refresh_ports
Refresh the status of the exposed ports
=cut
sub refresh_ports($self, $request=undef) { sub refresh_ports($self, $request=undef) {
my $sth_update = $$CONNECTOR->dbh->prepare("UPDATE domain_ports " my $sth_update = $$CONNECTOR->dbh->prepare("UPDATE domain_ports "
." SET is_active=? " ." SET is_active=? "
......
...@@ -48,6 +48,8 @@ our %TABLE_CHANNEL = ( ...@@ -48,6 +48,8 @@ our %TABLE_CHANNEL = (
list_alerts => 'messages' list_alerts => 'messages'
,list_machines => 'domains' ,list_machines => 'domains'
,list_machines_tree => 'domains' ,list_machines_tree => 'domains'
,list_machines_user_including_privates => ['domains','bookings','booking_entries'
,'booking_entry_ldap_groups', 'booking_entry_users','booking_entry_bases']
,list_requests => 'requests' ,list_requests => 'requests'
); );
...@@ -390,7 +392,7 @@ sub _date_changed_table($self, $table) { ...@@ -390,7 +392,7 @@ sub _date_changed_table($self, $table) {
my $sth = $rvd->_dbh->prepare("SELECT MAX(date_changed) FROM $table"); my $sth = $rvd->_dbh->prepare("SELECT MAX(date_changed) FROM $table");
$sth->execute; $sth->execute;
my ($date) = $sth->fetchrow; my ($date) = $sth->fetchrow;
return $date; return ($date or '');
} }
sub _count_table($self, $table) { sub _count_table($self, $table) {
...@@ -406,9 +408,22 @@ sub _new_info($self, $key) { ...@@ -406,9 +408,22 @@ sub _new_info($self, $key) {
my $channel = $self->clients->{$key}->{channel}; my $channel = $self->clients->{$key}->{channel};
$channel =~ s{/.*}{}; $channel =~ s{/.*}{};
my $table = $TABLE_CHANNEL{$channel} or return; my $table0 = $TABLE_CHANNEL{$channel} or return;
if (!ref($table0)) {
$table0 = [$table0];
}
my $count = '';
my $date = '';
return ($self->_count_table($table),$self->_date_changed_table($table)); for my $table (@$table0) {
$count .= ":" if $count;
$count .= $self->_count_table($table);
$date .= ":" if $date;
$date .= $self->_date_changed_table($table)
}
return ($count, $date);
} }
......
...@@ -848,7 +848,6 @@ ravadaApp.directive("solShowMachine", swMach) ...@@ -848,7 +848,6 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.loading_users = false; $scope.loading_users = false;
$scope.error = response.data.error; $scope.error = response.data.error;
$scope.users = response.data.entries; $scope.users = response.data.entries;
console.log(response.data.error);
}); });
}; };
$scope.add_member = function(cn) { $scope.add_member = function(cn) {
...@@ -873,6 +872,12 @@ ravadaApp.directive("solShowMachine", swMach) ...@@ -873,6 +872,12 @@ ravadaApp.directive("solShowMachine", swMach)
$scope.error = response.data.error; $scope.error = response.data.error;
}); });
}; };
$scope.remove_group = function() {
$scope.confirm_remove=false;
$http.post("/ldap/group/remove/"+group).then(function(response) {
$scope.error=response.data.error;
});
};
}; };
......
...@@ -425,6 +425,7 @@ ...@@ -425,6 +425,7 @@
$scope.list_ldap_attributes(); $scope.list_ldap_attributes();
list_interfaces(); list_interfaces();
list_users(); list_users();
list_access_groups();
} }
$scope.hardware_types = Object.keys(response.data.hardware); $scope.hardware_types = Object.keys(response.data.hardware);
$scope.copy_ram = $scope.showmachine.max_mem / 1024 / 1024; $scope.copy_ram = $scope.showmachine.max_mem / 1024 / 1024;
...@@ -891,18 +892,24 @@ ...@@ -891,18 +892,24 @@
}); });
}; };
$scope.list_access_groups = function() { var list_access_groups = function() {
/* $http.get("/domain/access_groups/"+showmachine.id).then(function(response) { $http.get("/machine/list_access_groups/"+$scope.showmachine.id).then(function(response) {
$scope.access_groups=response.data; $scope.access_groups=response.data;
});*/ });
}; };
$scope.add_group_access = function(group) { $scope.add_group_access = function(group) {
$http.get("/machine/add_access_group/"+$scope.showmachine.id+"/"+group) $http.get("/machine/add_access_group/"+$scope.showmachine.id+"/"+group)
.then(function(response) { .then(function(response) {
$scope.list_access_groups(); list_access_groups();
}); });
}; };
$scope.remove_group_access = function(group) {
$http.get("/machine/remove_access_group/"+$scope.showmachine.id+"/"+group)
.then(function(response) {
list_access_groups();
});
};
$scope.message = []; $scope.message = [];
$scope.disk_remove = []; $scope.disk_remove = [];
$scope.pending_before = 10; $scope.pending_before = 10;
...@@ -912,11 +919,9 @@ ...@@ -912,11 +919,9 @@
$scope.access_value = [ ]; $scope.access_value = [ ];
$scope.access_allowed = [ ]; $scope.access_allowed = [ ];
$scope.access_last = [ ]; $scope.access_last = [ ];
$scope.access_groups = [ ];
$scope.new_base = undefined; $scope.new_base = undefined;
$scope.list_ldap_attributes(); $scope.list_ldap_attributes();
$scope.list_access_groups();
}; };
function swListMach() { function swListMach() {
......
...@@ -916,6 +916,15 @@ get '/machine/move_access/(#id_domain)/(#id_access)/(#position)' => sub { ...@@ -916,6 +916,15 @@ get '/machine/move_access/(#id_domain)/(#id_access)/(#position)' => sub {
return $c->render(json => { ok => 1 }); return $c->render(json => { ok => 1 });
}; };
get '/machine/list_access_groups/(#id_domain)' => sub {
my $c = shift;
return _access_denied($c) if !$USER->is_admin;
my $domain_id = $c->stash('id_domain');
my $domain = Ravada::Front::Domain->open($domain_id);
return $c->render( json => [ $domain->list_access_groups ] );
};
get '/machine/add_access_group/(#id_domain)/(#group)' => sub($c) { get '/machine/add_access_group/(#id_domain)/(#group)' => sub($c) {
my $id_domain = $c->stash('id_domain'); my $id_domain = $c->stash('id_domain');
...@@ -923,12 +932,21 @@ get '/machine/add_access_group/(#id_domain)/(#group)' => sub($c) { ...@@ -923,12 +932,21 @@ get '/machine/add_access_group/(#id_domain)/(#group)' => sub($c) {
my $ok = 0; my $ok = 0;
eval { eval {
my $domain = Ravada::Front::Domain->open($id_domain); my $domain = Ravada::Front::Domain->open($id_domain);
$domain->add_access_group($group); $domain->grant_access(type => 'group' , group => $group);
$ok =1; $ok =1;
}; };
return $c->render( json => { ok => $ok, error => $@ }); return $c->render( json => { ok => $ok, error => $@ });
}; };
get '/machine/remove_access_group/(#id_domain)/(#group)' => sub($c) {
my $sth = $RAVADA->_dbh->prepare("DELETE FROM group_access WHERE id_domain=? "
." AND name=?"
);
$sth->execute($c->stash('id_domain'), $c->stash('group'));
return $c->render(json => { ok => 1 } );
};
get '/machine/compact/(#id_domain)' => sub($c) { get '/machine/compact/(#id_domain)' => sub($c) {
my $req = Ravada::Request->compact( my $req = Ravada::Request->compact(
id_domain => $c->stash('id_domain') id_domain => $c->stash('id_domain')
...@@ -973,7 +991,7 @@ get '/machine/public/#id/#value' => sub { ...@@ -973,7 +991,7 @@ get '/machine/public/#id/#value' => sub {
}; };
get '/machine/set/#id/#field/#value' => sub { get '/machine/set/#id/#field/#value' => sub {
my %privileged = map { $_ => 1 } qw(timeout id_owner); my %privileged = map { $_ => 1 } qw(timeout id_owner shutdown_disconnected shutdown_timeout autostart);
my $c = shift; my $c = shift;
my $id = $c->stash('id'); my $id = $c->stash('id');
...@@ -1086,15 +1104,36 @@ any '/group/new' => sub { ...@@ -1086,15 +1104,36 @@ any '/group/new' => sub {
return new_group($c); return new_group($c);
}; };
any '/admin/user/(:id).(:type)' => sub { any '/admin/user/(#id).(:type)' => sub {
my $c = shift; my $c = shift;
return access_denied($c) if !$USER->can_manage_users() && !$USER->can_grant(); return access_denied($c) if !$USER->can_manage_users() && !$USER->can_grant();
push @{$c->stash->{js}}, '/js/admin.js'; push @{$c->stash->{js}}, '/js/admin.js';
my $user = Ravada::Auth::SQL->search_by_id($c->stash('id')); my $id = $c->stash('id');
my $user;
if ($id =~ /cn=(.*?),/) {
$user = Ravada::Auth::SQL->new(name => $1);
} elsif ($id =~ /^\d+$/) {
$user = Ravada::Auth::SQL->search_by_id($id);
} else {
$user = Ravada::Auth::SQL->new(name => $id);
}
return $c->render(text => "Unknown user id: ".$c->stash('id')) my $origin = $c->param('origin');
if !$user; $c->stash(origin => $origin, warning => '');
return $c->render(text => "Unknown user ".$c->stash('id'))
if !$user || (!$user->id && !$c->stash('origin'));
if ($c->param('import') ) {
return $c->render( text => "Error: missing origin") if !$origin;
if ($user->id) {
$c->stash( warning => "Warning: user already imported");
} else {
Ravada::Auth::SQL::add_user(name => $user->name, is_external => 1, is_temporary => 0
, external_auth => $origin);
$user = Ravada::Auth::SQL->new(name => $user->name);
}
}
if ($c->param('make_admin')) { if ($c->param('make_admin')) {
$USER->make_admin($c->stash('id')) if $c->param('is_admin'); $USER->make_admin($c->stash('id')) if $c->param('is_admin');
...@@ -1132,6 +1171,7 @@ get '/user/list_groups/(#id_user)' => sub($c) { ...@@ -1132,6 +1171,7 @@ get '/user/list_groups/(#id_user)' => sub($c) {
return _access_denied($c) unless $USER->is_admin || $id_user == $USER->id; return _access_denied($c) unless $USER->is_admin || $id_user == $USER->id;