Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
canvas-demo-techex17/deps/ldap/slapdconf/slapdadm
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
550 lines (439 sloc)
13.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# | |
# Copyright (c) 2014-2015 Evolveum | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# | |
# Author: Radovan Semancik | |
# | |
# Required packages: | |
# Ubuntu: libnet-ldap-perl libauthen-sasl-perl libuuid-perl | |
use strict; | |
use warnings; | |
use Net::LDAP::LDIF; | |
use Date::Format; | |
use UUID; | |
use Getopt::Long qw(:config bundling no_auto_abbrev pass_through); | |
use Pod::Usage; | |
my @userDbTypes = qw(hdb bdb mdb); | |
my ($verbose,$optHelp,$optYes,$optRc,$optDoNothing); | |
my ($configDir,$dbDir,$serviceName,$ldapUser,$ldapGroup); | |
my $debug = 0; | |
$SIG{__DIE__} = sub { Carp::confess(@_) }; | |
my $command; | |
if ($ARGV[0] !~ /^-/) { | |
$command = shift; | |
} | |
GetOptions ( | |
"rc" => \$optRc, | |
"do-nothing|n" => \$optDoNothing, | |
"yes|y" => \$optYes, | |
"verbose|v" => \$verbose, | |
"debug|d" => \$debug, | |
"help|h" => \$optHelp, | |
) or usage(); | |
usage() if $optHelp; | |
$configDir = "/etc/ldap" unless $configDir; | |
$dbDir = "/var/lib/ldap" unless $dbDir; | |
$serviceName = "slapd" unless $serviceName; | |
$ldapUser = "openldap" unless $ldapUser; | |
$ldapGroup = "openldap" unless $ldapGroup; | |
my $cnConfigDir; | |
if (!defined($command)) { | |
$command = shift; | |
} | |
if (!$command) { usage(); } | |
if ($command eq "--help") { usage(); } | |
if ($command eq "list-suffixes") { listSuffixes() } | |
elsif ($command eq "delete-suffix") { deleteSuffix() } | |
elsif ($command eq "create-suffix") { createSuffix() } | |
elsif ($command eq "delete-schema") { deleteSchema() } | |
elsif ($command eq "delete-all") { deleteAll() } | |
elsif ($command eq "help") { usage() } | |
else { usage() } | |
sub listSuffixes { | |
checkConfigDirs(); | |
# TODO | |
} | |
sub createSuffix { | |
my $suffix; | |
if ($ARGV[0] !~ /^-/) { | |
$suffix = shift @ARGV; | |
} | |
my ($dbDirectory,$rootDn,$rootPassword,$dbType); | |
GetOptions ( | |
"dbDir|B=s" => \$dbDirectory, | |
"rootDn=s" => \$rootDn, | |
"rootPassword=s" => \$rootPassword, | |
) or usage(); | |
if (!defined($suffix)) { | |
$suffix = shift @ARGV; | |
} | |
if (!$suffix) { | |
die("No suffix name specified\n"); | |
} | |
$dbDirectory = "/var/lib/ldap/$suffix" unless $dbDirectory; | |
$rootDn = "cn=admin,".$suffix unless $rootDn; | |
$rootPassword = "secret" unless $rootPassword; | |
$dbType = "hdb" unless $dbType; | |
checkConfigDirs(); | |
stopSlapd(); | |
my $entry = findConfigObjectBySuffix($suffix); | |
if ($entry) { | |
die("Suffix $suffix already exists\n"); | |
} | |
my $index = findLastDbConfigFileIndex(); | |
my $olcDatabaseName = "{".($index+1)."}$dbType"; | |
my $olcDatabaseRdn = "olcDatabase=$olcDatabaseName"; | |
my $dbConfigPath = "$cnConfigDir/$olcDatabaseRdn.ldif"; | |
# print("Max: $index, file: $dbConfigPath\n"); | |
my $nowTimestamp = time2str("%Y%m%d%k%M%SZ",time()); | |
my ($uuid,$uuidStr); | |
UUID::generate($uuid); | |
UUID::unparse($uuid,$uuidStr); | |
$entry = Net::LDAP::Entry->new($olcDatabaseRdn, | |
objectClass => [ 'olcDatabaseConfig', 'olcHdbConfig' ], | |
olcDatabase => $olcDatabaseName, | |
olcDbDirectory => $dbDirectory, | |
olcSuffix => $suffix, | |
olcRootDN => $rootDn, | |
olcRootPW => $rootPassword, | |
olcDbCheckpoint => '512 30', | |
olcDbConfig => [ | |
'{0}set_cachesize 0 2097152 0', | |
'{1}set_lk_max_objects 1500', | |
'{2}set_lk_max_locks 1500', | |
'{3}set_lk_max_lockers 1500' ], | |
structuralObjectClass => 'olcHdbConfig', | |
entryUUID => $uuidStr, | |
creatorsName => 'gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth', | |
createTimestamp => $nowTimestamp, | |
olcAccess => [ | |
'{0}to attrs=userPassword,shadowLastChange by dn="'.$rootDn.'" write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth write by anonymous auth by self write by * none', | |
'{1}to dn.base="" by * read', | |
'{2}to * by dn="'.$rootDn.'" write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth write by anonymous auth by self write by * none', | |
], | |
olcDbIndex => [ 'objectClass eq' ], | |
); | |
my $entryLdif = $entry->ldif; | |
$entryLdif =~ s/^\n*//m; | |
if ($optDoNothing) { | |
print("Would create file $dbConfigPath with the following content:\n"); | |
print($entryLdif."<<\n"); | |
} else { | |
open(LDIF,">$dbConfigPath"); | |
print LDIF $entryLdif; | |
close(LDIF); | |
chownName($ldapUser, $ldapGroup, $dbConfigPath); | |
} | |
if (! -e $dbDirectory) { | |
if ($optDoNothing) { | |
print("Would create directory $dbDirectory\n"); | |
} else { | |
mkdir($dbDirectory); | |
chownName($ldapUser, $ldapGroup, $dbDirectory); | |
} | |
} | |
startSlapd(); | |
} | |
sub chownName { | |
my ($user,$group,@files) = @_; | |
my ($ulogin,$upass,$uid,$ugid) = getpwnam($user); | |
my ($glogin,$gpass,$gid) = getgrnam($group); | |
chown($uid,$gid,@files); | |
} | |
sub findLastDbConfigFileIndex { | |
my (@cnConfigNodes) = listDir($cnConfigDir); | |
my @dbNodes = grep { /^olcDatabase=/ } @cnConfigNodes; | |
my $max = 0; | |
foreach my $dbNode (@dbNodes) { | |
if (my ($num,$name) = ($dbNode =~ /^olcDatabase=\{(-?\d+)\}([^\s]+)\.ldif$/)) { | |
if (-f "$cnConfigDir/$dbNode") { | |
if ($num > $max) { | |
$max = $num; | |
} | |
} else { | |
die("What the heck is $cnConfigDir/$dbNode?\n"); | |
} | |
} | |
} | |
return $max; | |
} | |
sub findConfigObjectBySuffix { | |
my ($suffix) = @_; | |
my (@cnConfigNodes) = listDir($cnConfigDir); | |
my @dbNodes = grep { /^olcDatabase=/ } @cnConfigNodes; | |
OUTER: foreach my $dbNode (@dbNodes) { | |
if (my ($num,$name) = ($dbNode =~ /^olcDatabase=\{(-?\d+)\}([^\s]+)\.ldif$/)) { | |
if (-f "$cnConfigDir/$dbNode") { | |
my $file="$cnConfigDir/$dbNode"; | |
print "Processing file $file\n" if $verbose; | |
my $ldif = Net::LDAP::LDIF->new($file,"r"); | |
while( not $ldif->eof()) { | |
my $entry = $ldif->read_entry(); | |
if ($ldif->error()) { | |
die("LDIF error in $file:".$ldif->error_lines().": ".$ldif->error()."\n"); | |
} | |
my $olcSuffix = $entry->get_value('olcSuffix'); | |
if (!defined($olcSuffix) || $suffix ne $olcSuffix) { | |
print("Skipping DB $name because suffix does not match\n") if $verbose; | |
$ldif->done(); | |
next OUTER; | |
} | |
$ldif->done(); | |
return $entry; | |
} | |
$ldif->done(); | |
} else { | |
die("What the heck is $cnConfigDir/$dbNode?\n"); | |
} | |
} | |
} | |
return undef; | |
} | |
sub deleteSuffix { | |
my $suffix = shift @ARGV; | |
if (!$suffix) { | |
die("No suffix name specified\n"); | |
} | |
deleteDbAndConfigFiles($suffix); | |
} | |
sub deleteAll { | |
deleteDbAndConfigFiles(); | |
} | |
sub deleteDbAndConfigFiles { | |
my ($suffix) = @_; | |
checkConfigDirs(); | |
# if (! -d $dbDir) { | |
# die("$dbDir is not a directory\n"); | |
# } | |
stopSlapd(); | |
#print("C: $cnConfigDir\n"); | |
my (@cnConfigNodes) = listDir($cnConfigDir); | |
#print("N: ".join(", ",@cnConfigNodes)."\n"); | |
my @dbNodes = grep { /^olcDatabase=/ } @cnConfigNodes; | |
#print("D: ".join(", ",@dbNodes)."\n"); | |
my @deletedNodeNames; | |
OUTER: foreach my $dbNode (@dbNodes) { | |
if (my ($num,$name) = ($dbNode =~ /^olcDatabase=\{(-?\d+)\}([^\s\.]+)(\.ldif)?$/)) { | |
#print ("$num - $name\n"); | |
if ($num < 1) { | |
print("Skipping DB $name because num is $num\n") if $verbose; | |
next; | |
} | |
if (!(grep{$_ eq $name} @userDbTypes)) { | |
print("Skipping DB $name because it is not know to be user DB type\n") if $verbose; | |
next; | |
} | |
if (-d "$cnConfigDir/$dbNode") { | |
# skip dirs for now. We will deal with them later | |
} elsif (-f "$cnConfigDir/$dbNode") { | |
my $file="$cnConfigDir/$dbNode"; | |
print "Processing file $file\n" if $verbose; | |
my $ldif = Net::LDAP::LDIF->new($file,"r"); | |
while( not $ldif->eof()) { | |
my $entry = $ldif->read_entry(); | |
if ($ldif->error()) { | |
die("LDIF error in $file:".$ldif->error_lines().": ".$ldif->error()."\n"); | |
} | |
my $olcSuffix = $entry->get_value('olcSuffix'); | |
if ($suffix && ($suffix ne $olcSuffix)) { | |
print("Skipping DB $name because suffix does not match ($olcSuffix vs $suffix)\n") if $verbose; | |
$ldif->done(); | |
next OUTER; | |
} | |
my $dbdir = $entry->get_value('olcDbDirectory'); | |
print("Cleaning up DB directory $dbdir\n") if $verbose; | |
cleanupDbDir($dbdir); | |
} | |
$ldif->done(); | |
deleteFile($file); | |
push @deletedNodeNames,"olcDatabase={$num}$name"; | |
} else { | |
die("What the heck is $cnConfigDir/$dbNode?\n"); | |
} | |
} | |
} | |
foreach my $dbNodeName (@deletedNodeNames) { | |
if (-d "$cnConfigDir/$dbNodeName") { | |
deleteDir("$cnConfigDir/$dbNodeName"); | |
} | |
} | |
startSlapd(); | |
if ($suffix && !@deletedNodeNames) { | |
die("Suffix $suffix not found\n"); | |
} | |
} | |
sub deleteSchema { | |
my $schemaName = shift @ARGV; | |
if (!$schemaName) { | |
die("No schema name specified\n"); | |
} | |
checkConfigDirs(); | |
stopSlapd(); | |
my $schemaIndex = undef; | |
($schemaName, $schemaIndex) = parseIndexedName($schemaName); | |
print "schemaName=$schemaName, schemaIndex=$schemaIndex\n" if $debug; | |
my $file = undef; | |
my (@schemaNodes) = listDir($cnConfigDir."/cn=schema"); | |
foreach my $schemaNode (@schemaNodes) { | |
if (my ($entryIndex, $entryName) = ($schemaNode =~ /^cn=\{(-?\d+)\}([^\s\.]+)(\.ldif)?$/)) { | |
if ($entryName eq $schemaName) { | |
if (defined $schemaIndex && $schemaIndex != $entryIndex) { | |
die("Schema $schemaName present, but it does have index $entryIndex and not $schemaIndex\n"); | |
} | |
$file="$cnConfigDir/cn=schema/$schemaNode"; | |
last; | |
} | |
} | |
} | |
if (!$file) { | |
die("Schema $schemaName does not exist\n"); | |
} | |
print "Deleting $file\n" if $debug; | |
deleteFile($file); | |
startSlapd(); | |
} | |
### RC | |
sub stopSlapd { | |
if ($optRc) { | |
system("service $serviceName stop") == 0 or die("Service stop failed: $! ($?)\n"); | |
} else { | |
my $out = `service $serviceName status`; | |
chomp($out); | |
if ($out !~ /is not running/i) { | |
die("slapd seems to be running ($out)\n"); | |
} | |
} | |
} | |
sub startSlapd { | |
if ($optRc) { | |
system("service $serviceName start") == 0 or die("Service start failed: $! ($?)\n"); | |
} | |
} | |
### Util | |
sub parseIndexedName { | |
my ($val) = @_; | |
if ($val =~ /^{(\d+)}/) { | |
return ($', $1); | |
} else { | |
return ($val, undef); | |
} | |
} | |
sub checkConfigDirs { | |
if (! -d $configDir) { | |
die("$configDir is not a directory\n"); | |
} | |
my $slapdDDir = "$configDir/slapd.d"; | |
if (! -d $slapdDDir) { | |
die("$configDir does not contain slapd.d subdirectory\n"); | |
} | |
$cnConfigDir = "$slapdDDir/cn=config"; | |
if (! -d $cnConfigDir) { | |
die("$configDir does not contain slapd.d/cn=config subdirectory\n"); | |
} | |
} | |
sub deleteDir { | |
my ($dirpath) = @_; | |
my @nodes = listDir($dirpath); | |
foreach my $node (@nodes) { | |
my $nodepath = "$dirpath/$node"; | |
if (-f $nodepath) { | |
deleteFile($nodepath); | |
} else { | |
die("Unexpected thing $nodepath, aborting\n"); | |
} | |
} | |
if ($optDoNothing) { | |
print "Would delete directory $dirpath\n"; | |
} else { | |
print "Deleting directory $dirpath\n" if $verbose; | |
rmdir($dirpath) or die("Error deleting directory $dirpath: $!\n"); | |
} | |
} | |
sub cleanupDbDir { | |
my ($dirpath) = @_; | |
my @nodes = listDir($dirpath); | |
foreach my $node (@nodes) { | |
my $nodepath = "$dirpath/$node"; | |
if (-f $nodepath) { | |
deleteFile($nodepath); | |
} else { | |
die("Unexpected thing $nodepath, aborting\n"); | |
} | |
} | |
# do NOT delete the directory. we need it. | |
} | |
sub deleteFile { | |
my ($path) = @_; | |
if ($optDoNothing) { | |
print "Would delete file $path\n"; | |
} else { | |
print "Deleting file $path\n" if $verbose; | |
unlink($path) or die("Error deleting file $path: $!\n"); | |
} | |
} | |
sub listDir { | |
my ($dirpath) = @_; | |
opendir(my $dh, $dirpath) || die "can't opendir $dirpath: $!"; | |
my @nodes = grep { /^[^\.]/ && "$dirpath/$_" } readdir($dh); | |
closedir $dh; | |
return @nodes; | |
} | |
### USAGE and DOCUMENTATION | |
sub usage { | |
pod2usage(-verbose => 2); | |
exit(1); | |
} | |
__END__ | |
=head1 NAME | |
slapdadm - a command-line tool to configure stopped OpenLDAP instance. | |
=head1 SYNOPSIS | |
slapdadm [global options] command [command options] | |
=head1 OPTIONS | |
=head2 COMMANDS | |
=over 8 | |
=item B<list-suffixes> | |
List directory suffixes configured on the server. | |
=item B<create-suffix> I<suffix> | |
Creates new directory suffix with an associated database. | |
It will create empty database without any entry - even without a root entry. This needs to be populated. | |
But it creates a default configuration for the suffix including root user and default ACLs. | |
=item B<delete-suffix> I<suffix> | |
Deletes an existing directory suffix together with an associated database and the data. | |
=item B<delete-all> | |
Deletes all suffixes and databases. This comes handy if your OpenLDAP comes pre-configured from your | |
distribution and you want it to be configured differently. | |
=item B<help> | |
Displays command usage summary. | |
=back | |
=head2 GLOBAL OPTIONS | |
=over 8 | |
=item [ B<-v> | B<--verbose> ] | |
Increases verbosity. | |
=item B<--help> | |
Displays help message. | |
=back | |
=head1 DESCRIPTION | |
This command-line tool is used to configure a stopped OpenLDAP instance. The configuration | |
is done by direct manipulation of files in C</etc/ldap/slapd.d> directory and the database | |
files. | |
=head1 EXAMPLES | |
slapdadm delete-suffix dc=example,dc=com | |
=head1 NOTES | |
This is still work in progress. Please feel free to contribute. | |
=head1 AUTHOR | |
Radovan Semancik | |
=head1 SEE ALSO | |
C<slapdconf> | |
=cut |