shelldap
changeset 33 057fefab56b0
parent 32 95dbffcc757b
child 34 40c3719c87d4
equal deleted inserted replaced
32:95dbffcc757b 33:057fefab56b0
   193     cat uid=mahlon,ou=People,dc=example,o=company
   193     cat uid=mahlon,ou=People,dc=example,o=company
   194     cat uid=mahlon + userPassword
   194     cat uid=mahlon + userPassword
   195 
   195 
   196 =item B<  cd>
   196 =item B<  cd>
   197 
   197 
   198 Change DN.  Translated to LDAP, this changes the current basedn.
   198 Change directory.  Translated to LDAP, this changes the current basedn.
   199 All commands after a 'cd' operate within the new basedn.
   199 All commands after a 'cd' operate within the new basedn.
   200 
   200 
   201     cd                change to 'home' basedn
   201     cd                  change to 'home' basedn
   202     cd ~              same thing
   202     cd ~                change to the binddn, or basedn if anonymously bound
   203     cd -              change to previous node
   203     cd -                change to previous node
   204     cd ou=People      change to explicit path below current node
   204     cd ou=People        change to explicit path below current node
   205     cd ..             change to parent node
   205     cd ..               change to parent node
   206     cd ..,..,ou=Groups  change to node ou=Groups, which is a sibling
   206     cd ../../ou=Groups  change to node ou=Groups, which is a sibling
   207                       to the current node's parent node
   207                         to the current node's grandparent
   208 
   208 
   209 Since LDAP doesn't actually limit what can be a container object, you
   209 Since LDAP doesn't actually limit what can be a container object, you
   210 can actually cd into any entry. Many commands then work on '.', meaning
   210 can actually cd into any entry. Many commands then work on '.', meaning
   211 "wherever I currently am."
   211 "wherever I currently am."
   212 
   212 
   370 use strict;
   370 use strict;
   371 use warnings;
   371 use warnings;
   372 use Term::ReadKey;
   372 use Term::ReadKey;
   373 use Term::Shell;
   373 use Term::Shell;
   374 use Digest::MD5;
   374 use Digest::MD5;
   375 use Net::LDAP qw(LDAP_SUCCESS LDAP_SERVER_DOWN);
   375 use Net::LDAP qw/ LDAP_SUCCESS LDAP_SERVER_DOWN /;
   376 use Net::LDAP::Util qw(canonical_dn ldap_explode_dn);
   376 use Net::LDAP::Util qw/ canonical_dn ldap_explode_dn /;
   377 use Net::LDAP::LDIF;
   377 use Net::LDAP::LDIF;
   378 use Data::Dumper;
   378 use Data::Dumper;
   379 use File::Temp;
   379 use File::Temp;
   380 use Algorithm::Diff;
   380 use Algorithm::Diff;
   381 use Carp 'confess';
   381 use Carp 'confess';
   731 
   731 
   732 	$self->{'cwd_entries'} = $cache->{'entries'};
   732 	$self->{'cwd_entries'} = $cache->{'entries'};
   733 	return;
   733 	return;
   734 }
   734 }
   735 
   735 
   736 # convert a given path to a DN: deal with '..', '.', '~'
   736 # convert a given path to a DN: deal with '..', '.'
   737 # Synopsis: $dn = $self->path_to_dn($path, [relative => N]);
   737 # Synopsis: $dn = $self->path_to_dn( $path );
   738 sub path_to_dn
   738 sub path_to_dn
   739 {
   739 {
   740 	my $self  = shift;
   740 	my $self    = shift;
   741 	my $path = shift;
   741 	my $path    = shift;
   742 	my %flags = @_;
   742 	my %flags   = @_;
   743 	my $base = $self->base();
   743 	my $curbase = $self->base();
   744 
       
   745 	# fail on wrong / missing parameter
       
   746 	return undef  if (!defined($path));
       
   747 
       
   748 	# return configured base DN
       
   749 	return($conf->{basedn})  if ($path eq '~');
       
   750 
   744 
   751 	# return current base DN
   745 	# return current base DN
   752 	return($base)  if ($path eq '.');
   746 	return $curbase if $path eq '.';
   753 
   747 
   754 	if ($path =~ /^\.\./o) {	# relative path
   748 	# support 'cd -'
       
   749 	return $self->{'previous_base'} if $path eq '-';
       
   750 
       
   751 	# support empty 'cd' or 'cd ~' going to root
       
   752 	return $conf->{'basedn'} if $path eq '~' || ! $path;
       
   753 
       
   754 	# relative path, upwards
       
   755 	#
       
   756 	if ( $path =~ /^\.\./o ) {
   755 		# support '..' (possibly iterated and as prefix to a DN)
   757 		# support '..' (possibly iterated and as prefix to a DN)
   756 		my @base = @{ldap_explode_dn($base, casefold => 'none')};
   758 		my @base = @{ ldap_explode_dn($curbase, casefold => 'none') };
   757 
   759 
   758 		# deal with leading ..,
   760 		# deal with leading ..,
   759 		while ($path =~ /^\.\./) {
   761 		while ( $path =~ /^\.\./ ) {
   760 			shift(@base)  if (@base);
   762 			shift( @base ) if @base;
   761 			$path =~ s/^\.\.//;
   763 			$path =~ s/^\.\.//;
   762 			last  if ($path !~ /,\s*/);
   764 			last if $path !~ /[,\/]\s*/;
   763 			$path =~ s/,\s*//;
   765 			$path =~ s/[,\/]\s*//;
   764 		}
   766 		}
   765 
   767 
   766 		# build a new absolute DN
   768 		# append the new dn to the node if one was specified:
   767 		$path .= ',' . canonical_dn(\@base, casefold => 'none')
   769 		#    cd ../../cn=somewhere  vs
   768 			if (@base);
   770 		#    cd ../../
   769 	}
   771 		#
   770 	elsif ($path =~ /,\s*~$/o) {	# absolute path
   772 		my $newbase_root = canonical_dn( \@base, casefold => 'none' );
   771 		$path =~ s/,\s*~$//;
   773 		$path = $path ? $path . ',' . $newbase_root : $newbase_root;
   772 		$path.= ','.$conf->{basedn}
   774 	}
   773 			if ($conf->{basedn});
   775 
   774 	}
   776 	# attach the base if it isn't already there (this takes care of
   775 	else {				# relative or absolute path
   777 	# deeper relative nodes and absolutes)
   776 		$path.= ','.$conf->{basedn}
   778 	#
   777 			if ($conf->{basedn} && $flags{relative});
   779 	else {
   778 	}
   780 		$path = "$path," . $curbase unless $path =~ /$curbase/;
   779 
   781 	}
   780 	return($path);
   782 
       
   783 	return $path;
   781 }
   784 }
   782 
   785 
   783 # given an array ref of shell-like globs, 
   786 # given an array ref of shell-like globs, 
   784 # make and return an LDAP filter object.
   787 # make and return an LDAP filter object.
   785 #
   788 #
   983 			attrs  => \@attrs
   986 			attrs  => \@attrs
   984 		});
   987 		});
   985 	}
   988 	}
   986 	else {
   989 	else {
   987 		# convert given path to DN
   990 		# convert given path to DN
   988 		$dn = $self->path_to_dn($dn, relative => 1);
   991 		$dn = $self->path_to_dn( $dn );
   989 		$s = $self->search({
   992 		$s = $self->search({
   990 			base   => $dn,
   993 			base   => $dn,
   991 			vals   => 1,
   994 			vals   => 1,
   992 			attrs  => \@attrs
   995 			attrs  => \@attrs
   993 		});
   996 		});
  1007 
  1010 
  1008 sub run_cd 
  1011 sub run_cd 
  1009 {
  1012 {
  1010 	my $self	= shift;
  1013 	my $self	= shift;
  1011 	my $newbase = join ' ', @_;
  1014 	my $newbase = join ' ', @_;
  1012 	
       
  1013 	# support 'cd' going to root
       
  1014 	$newbase ||= $conf->{'basedn'};
       
  1015 
       
  1016 	# support 'cd -'
       
  1017 	if ( $newbase eq '-' ) {
       
  1018 		$newbase = $self->{'previous_base'} || return;
       
  1019 	}
       
  1020 
  1015 
  1021 	# convert given path to a DN
  1016 	# convert given path to a DN
  1022 	$newbase = $self->path_to_dn($newbase, relative => 1);
  1017 	$newbase = $self->path_to_dn( $newbase );
  1023 	
  1018 
  1024 	unless ( $self->is_valid_dn( $newbase ) ) {
  1019 	unless ( $self->is_valid_dn( $newbase ) ) {
  1025 		print "No such object\n";
  1020 		print "No such object\n";
  1026 		return;
  1021 		return;
  1027 	}
  1022 	}
  1028 
  1023 
  1065 		print "No destination dn provided.\n";
  1060 		print "No destination dn provided.\n";
  1066 		return;
  1061 		return;
  1067 	}
  1062 	}
  1068 
  1063 
  1069 	# convert given source path to DN
  1064 	# convert given source path to DN
  1070 	$s_dn = $self->path_to_dn($s_dn, relative => 1);
  1065 	$s_dn = $self->path_to_dn( $s_dn );
  1071 
  1066 
  1072 	my $s = $self->search({ base => $s_dn, vals => 1 });
  1067 	my $s = $self->search({ base => $s_dn, vals => 1 });
  1073 	unless ( $s->{'code'} == LDAP_SUCCESS ) {
  1068 	unless ( $s->{'code'} == LDAP_SUCCESS ) {
  1074 		print "No such object\n";
  1069 		print "No such object\n";
  1075 		return;
  1070 		return;
  1234 		print "No dn provided.\n";
  1229 		print "No dn provided.\n";
  1235 		return;
  1230 		return;
  1236 	}
  1231 	}
  1237 
  1232 
  1238 	# convert given path to DN
  1233 	# convert given path to DN
  1239 	$dn = $self->path_to_dn($dn, relative => 1);
  1234 	$dn = $self->path_to_dn( $dn );
  1240 
  1235 
  1241 	my $s = $self->search({ base => $dn, vals => 1 });
  1236 	my $s = $self->search({ base => $dn, vals => 1 });
  1242 
  1237 
  1243 	if ( $s->{'code'} ) {
  1238 	if ( $s->{'code'} ) {
  1244 		print $s->{'message'} . "\n";
  1239 		print $s->{'message'} . "\n";
  1396 
  1391 
  1397 	unless ( $base ) {
  1392 	unless ( $base ) {
  1398 		print "No search base specified.\n";
  1393 		print "No search base specified.\n";
  1399 		return;
  1394 		return;
  1400 	}
  1395 	}
       
  1396 
  1401 	# convert base path to DN
  1397 	# convert base path to DN
  1402 	$base = $self->path_to_dn($base, relative => 1);
  1398 	$base = $self->path_to_dn( $base );
  1403 
  1399 
  1404 	$self->debug("Filter parsed as: " . $filter->as_string() . "\n");
  1400 	$self->debug("Filter parsed as: " . $filter->as_string() . "\n");
  1405 
  1401 
  1406 	my $s = $self->search(
  1402 	my $s = $self->search(
  1407 		{
  1403 		{
  1572 		print "No 'directory' provided.\n";
  1568 		print "No 'directory' provided.\n";
  1573 		return;
  1569 		return;
  1574 	}
  1570 	}
  1575 
  1571 
  1576 	# convert given path to DN
  1572 	# convert given path to DN
  1577 	$dir = $self->path_to_dn($dir, relative => 1);
  1573 	$dir = $self->path_to_dn( $dir );
  1578 
  1574 
  1579 	# normalize name, if it is not yet a legal DN
  1575 	# normalize name, if it is not yet a legal DN
  1580 	$dir = 'ou='.$dir  if (!canonical_dn($dir));
  1576 	$dir = 'ou='.$dir  if (!canonical_dn($dir));
  1581 
  1577 
  1582 	# get RDN: naming attributes (lower-case) and their values
  1578 	# get RDN: naming attributes (lower-case) and their values
  1623 		print "No destination dn provided.\n";
  1619 		print "No destination dn provided.\n";
  1624 		return;
  1620 		return;
  1625 	}
  1621 	}
  1626 
  1622 
  1627 	# convert given source path to DN
  1623 	# convert given source path to DN
  1628 	$s_dn = $self->path_to_dn($s_dn, relative => 1);
  1624 	$s_dn = $self->path_to_dn( $s_dn );
  1629 
  1625 
  1630 	unless ( $self->is_valid_dn( $s_dn ) ) {
  1626 	unless ( $self->is_valid_dn( $s_dn ) ) {
  1631 		print "No such object\n";
  1627 		print "No such object\n";
  1632 		return;
  1628 		return;
  1633 	}
  1629 	}
  1664 		print "Sorry, password changes not supported by LDAP server.\n";
  1660 		print "Sorry, password changes not supported by LDAP server.\n";
  1665 		return;
  1661 		return;
  1666 	}
  1662 	}
  1667 
  1663 
  1668 	# convert given path to DN
  1664 	# convert given path to DN
  1669 	$self->path_to_dn($dn, relative => 1);
  1665 	$self->path_to_dn( $dn );
  1670 
  1666 
  1671 	my $s = $self->search( { base => $dn, scope => 'base' } );
  1667 	my $s = $self->search( { base => $dn, scope => 'base' } );
  1672 	if ( $s->{'code'} ) {
  1668 	if ( $s->{'code'} ) {
  1673 		print $s->{'message'}, "\n";
  1669 		print $s->{'message'}, "\n";
  1674 		return;
  1670 		return;