database shell
search /var/pg2ldap/backend.pl
####
create view ldap as select agency as objectclass,
(agency || '-' || folderid) as dnqualifier,
(fname || ' ' || lname) as cn,
lname as sn,
fname as givenname,
org as o,
orgunit as ou,
city as l,
cp as postalcode,
adr as postaladdress,
country as c,
email as mail,
tel as telephonenumber,
fax as facsimiletelephonenumber,
photo as photoid,
cert as certid,
comment as description
from userspool
where state = 'ACTIVE'
##
##
use strict;
use Parse::RecDescent;
use DBI;
use MIME::Base64;
my $host = "localhost";
my $port = 5432;
my $dbname = "my_db";
my $username = "username";
my $password = "password";
# <...>
# In a search request, the server feeds us the scope and dereference fields
# in the numeric form used by the protocol. ldapsearch(1) takes these
# fields as arguments in symbolic form. These arrays convert between
# the two representations.
my @scopes = ("base", "onelevel", "subtree");
my @derefs = ("never", "search", "find", "always");
my $dataSource = "dbi:Pg:dbname=$dbname;host=$host;port=$port";
my $operation = <>;
chop($operation);
if ($operation eq "SEARCH") {
my ($suffix, $base, $scope, $deref, $sizelimit, $timelimit, $filter);
my ($attrsonly, @attrs);
while (<>) {
if (/^suffix: (.*)$/) { $suffix = $1; }
elsif (/^base: (.*)$/) { $base = $1; }
elsif (/^scope: (.*)$/) { $scope = $scopes[$1]; }
elsif (/^deref: (.*)$/) { $deref = $derefs[$1]; }
elsif (/^sizelimit: (.*)$/) { $sizelimit = $1; }
elsif (/^timelimit: (.*)$/) { $timelimit = $1; }
elsif (/^filter: (.*)$/) { $filter = $1; }
elsif (/^attrsonly: (.*)$/) { $attrsonly = $1; }
elsif (/^attrs: (.*)$/) { if ($1 eq "all") {
@attrs = ();
} else {
@attrs = split / /, $1;
}
}
# <...>
LdapUserDatasDNAttrs($base, $filter);
# <...>
}
}
# <...>
sub LdapUserDatasDNAttrs {
my ($suffix, $filter) = @_;
my $sqlCond = TranslateLdapFilter($filter);
my ($dnQualifier) = ($suffix =~ /^dnQualifier=([^,]+),/);
print "dn: $suffix\n";
print "objectClass: top\n";
print "objectClass: person\n";
print "objectClass: organizationalPerson\n";
print "objectClass: inetOrgPerson\n";
print "dnQualifier: $dnQualifier\n";
my $dbh = DBI->connect($dataSource, $username, $password,
{AutoCommit => 0, RaiseError => 1})
|| die "Can't connect: $DBI::errstr";
$dbh->commit;
my $statement = "
SELECT cn,
sn,
givenname,
o,
ou,
c,
l,
postalcode,
postaladdress,
mail,
telephonenumber,
facsimiletelephonenumber,
photoid,
certid,
description
FROM ldap
WHERE (dnqualifier = '$dnQualifier') AND ($sqlCond);
";
my $sth = $dbh->prepare($statement)
|| die "Can't prepare: $DBI::errstr";
$sth->execute
|| die "Can't execute statement: $DBI::errstr";
while(my @row = $sth->fetchrow_array) {
my ($cn,
$sn,
$givenName,
$o,
$ou,
$c,
$l,
$postalCode,
$postalAddress,
$mail,
$telephoneNumber,
$facsimileTelephoneNumber,
$photoId,
$certId,
$description) = (@row);
print "cn: $cn\n" if ($cn ne '');
print "givenName: $givenName\n" if ($givenName ne '');
print "sn: $sn\n" if ($sn ne '');
print "o: $o\n" if ($o ne '');
print "ou: $ou\n" if ($ou ne '');
print "c: $c\n" if ($c ne '');
print "l: $l\n" if ($l ne '');
print "postalCode: $postalCode\n" if ($postalCode ne '');
print "postalAddress: $postalAddress\n"
if ($postalAddress ne '');
print "mail: $mail\n" if ($mail ne '');
print "telephoneNumber: $telephoneNumber\n"
if ($telephoneNumber ne '');
print "facsimileTelephoneNumber: $facsimileTelephoneNumber\n"
if ($facsimileTelephoneNumber ne '');
my $photo = '';
my $photoFd = $dbh->func($photoId,
$dbh->{pg_INV_READ},
'lo_open');
my $buff = '';
while($dbh->func($photoFd, $buff, 57 * 1000, 'lo_read')) {
$photo .= $buff;
}
$dbh->func($photoFd, 'lo_close');
my $photoB64 = MIME::Base64::encode($photo, "\n ");
print "jpegPhoto:: $photoB64\n";
my $cert = '';
my $certFd = $dbh->func($certId,
$dbh->{pg_INV_READ},
'lo_open');
$buff = '';
while($dbh->func($certFd, $buff, 57 * 1000, 'lo_read')) {
$cert .= $buff;
}
my $certB64 = MIME::Base64::encode($cert, "\n ");
print "userCertificate:: $certB64\n";
print "description: $description\n" if ($description ne '');
}
print "\n";
$dbh->commit;
$sth->finish;
$dbh->disconnect;
}
sub TranslateLdapFilter {
my ($filter) = @_;
my $grammar = q{
{ my $oper;
sub decode {
my ($str) = @_;
$str =~ s/\\\\([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $str;
}
}
translate: request
request: request_and
| request_or
| request_not
| request_data
request_and: '(&' request request ')' # & operator
{ "($item[2]) AND ($item[3])"; }
request_or: '(|' request request ')' # | operator
{ "($item[2]) OR ($item[3])"; }
request_not: '(!' request ')' # ! operator
{ "NOT ($item[2])"; }
request_data: '(' attr '=~' query ')' # sounds like
{ "soundex($item{attr}) = soundex($item{query_value})"; }
| '(' attr '=' query ')' # other forms
{ "lower($item{attr}) $oper lower($item{query})"; }
attr: /[A-Za-z0-9]+/i
{ "$item[1]"; }
query: /[^\)]*/
{ my ($str) = decode($item[1]);
if(($str =~ tr/*/%/) > 0) {
$oper = 'LIKE';
} else {
$oper = '=';
}
"'$str'";
}
};
$::RD_HINT = 1;
my $parser = new Parse::RecDescent($grammar) or die "Bad grammar!\n";
return $parser->translate($filter);
}