Perl LDAP search for user being a CN attribute - perl

How to do a Perl LDAP search/authentication for user being an attribute in access group or it's subgroups?
All users (UIDs) are in:
ou=Users,o=company,c=com
Access group is:
cn=Site Full Access,ou=Access,o=company,c=com
Access group has users and subgroups as attributes like:
uniquemember | uid=usernameA,ou=Users,o=company,c=com
uniquemember | uid=usernameB,ou=Users,o=company,c=com
uniquemember | uid=usernameC,ou=Users,o=company,c=com
uniquemember | cn=Site Full Access Employees,ou=Access,o=company,c=com
(subgroup has its own uniquemember attributes)
Authentication script idea:
Bind user with his login/password (done).
If successful, create user's DN like uid=$username,ou=Users,o=company,c=com (done)
Iterate through attributes of cn=Site Full Access,ou=Access,o=company,c=com and compare them to user's DN
If encountered a group, search for user's DN inside this group too
I am using Net::LDAP, but there is not much code to show as what works is quite standard. This is the listing of uniquemember attributes:
my $mesg = $ldap->bind ($user_dn, password=>"$pass");
$mesg->code && return undef;
$mesg = $ldap->search(base=>$ldap_access_full, filter=>"(objectclass=*)");
$mesg->code && return undef;
my #entries = $mesg->entries;
my $entry;
foreach $entry ( #entries )
{
if ($entry->exists('uniquemember'))
{
my $ref = $entr->get_value('uniquemember', asref=>1);
for my $uid (#$ref)
{
print $uid . "<br/>";
}
}
}
This gives me an array of user DNs but also groups that will have to be listed and compared in some recursive function.
How do I approach this? Perhaps there is another way to check both password and access group, with user still being an attribute of access group or it's subgroups? I can't do any LDAP modifications.

This called nested groups. For AD here is one solution: How to retrieve all Groups from LDAP with Perl
One solution is to ignore groups, only allow direct user objects.
If you need to process sub groups you should retrieve the entry and check it is a group or people. In case you need to check this in many levels (sub groups of sub groups of..) then you needs to do the check recursivly.
For one level of nested groups. It is half complete and not tested but you got the idea.
Of course use subroutines, but I did not used for easier understanding (and lack of time)
$mesg = $ldap->search(base=>$ldap_access_full, filter=>"(objectclass=*)",attrs => [qw(uniquemember)]);
$mesg->code && die $mesg->code;
my #entries = $mesg->entries;
my $entry;
foreach $entry ( #entries ) {
my #uniquemembers = $entr->get_value('uniquemember');
foreach my $uniquemember (#uniquemembers){
#get entry
$mesg = $ldap->search(base=>$uniquemember, filter=>"(objectclass=*)", attrs => [qw(objectclass uid uniquemember)], scope => 'base');
$mesg->code && die $mesg->code;
#if is a group or user?
my $uniquemember_entry = ($mesg->entries)[0];
my #objectclasses = $uniquemember_entry->get_value('objectclass');
my $uid = $uniquemember_entry->get_value('uid');
if (grep {/user/i} #objectclasses || defined $uid){
print "$uniquemember has uid: $uid\n";
} elsif (grep {/group/i} #objectclasses){
print "it is a group: $uniquemember\n";
#get its members
my #nested_group_uniquemembers = $uniquemember_entry->get_value('uniquemember');
#get entries
foreach my $nested_group_uniquemember (#nested_group_uniquemembers){
$mesg = $ldap->search(base=>$nested_group_uniquemember, filter=>"(objectclass=*)", attrs => [qw(objectclass uid uniquemember)], scope => 'base');
$mesg->code && die $mesg->code;
}
#check it is an user or group...
} else {
}
}
}

Related

Mojolicious, redirects, session and trying to create an authentication system

I'm trying to get away from Basic Auth in my Mojolicious application. I am able to detect the absence of a session key and redirect to a login page. The login page then posts to my application and I authenticate to a back end process. That back end process is returning success and then my mojo app sets the session like thus:
$self->session( user => $name, groups => $groups );
in debugging this, $name and $group are both defined and valid. I then wish to redirect into the "protected" space of my app. The redirect lands in the right place but then fails to detect the $self->session('user') (is undef when debugging) I end up redirecting back to login repeatedly.
I'll include snippets of the setup below. What am I missing?
MyApp.pm
my $r = $self->routes;
$r->route('/verify')->via('post')->to('util-auth#verify')->name('verify');
$r->route('/login')->via('get')->to('util-auth#login')->name('login');
my $app = $r->under('/myapp')->to('util-auth#check');
$app->route('/foo')->via('get')->to('controller-api#foo')->name('foo');
MyApp::Util::Auth
sub verify {
my $self = shift;
my $name = $self->param('username');
my $pass = $self->param('password');
my $dest = "/myapp/foo"; # in the protected area
if ( $self->authenticate($name, $pass) ) {
my $groups = $self->get_groups($name);
$self->session(
user => $name,
groups => $groups,
);
}
else {
$self->flash( message => "invalid login..." );
}
$self->redirect_to($dest);
}
sub login {
my $self = shift;
$self->render(); # renders the login form
}
sub check {
my $self = shift;
my $user = $self->session('user');
return 1 if defined $user;
$self->redirect_to('/login');
return 0;
}
I was having a similar problem and I ended up putting these in stash. I think session is string based, mainly because a cookie is set with session info.
Why your verify function accept name, pass via #_ variable?
May be need to use $self->param('name') and $self->param('pass')?
See working example here:
https://gist.github.com/Logioniz/bdf6f22c00fc51798c43

Fetch all user information with Net::LDAP

Currently have an small perl script what for the given username fetch his email address from the ActiveDirectory using Net::LDAP.
The search part is the following:
my $user = "myuser";
my $mesg = $ldap->search(
base => "dc=some,dc=example,dc=com",
filter => '(&(sAMAccountName=' . $user . ')(mail=*))', #?!?
);
for my $entry ($mesg->entries) {
my $val = $entry->get_value('mail');
say "==$val==";
}
Working ok.
How i should modify the above statement to fetch all available information for the given user myuser? I'm looking to get an perl-ish data structure, such something like next:
my $alldata = search(... all info for the given $user ... );
say Dumper $alldata; #hashref with all stored informations for the $user
It is probably dead simple - but i'm an total AD & LDAP-dumb person...
Edit: When I dump out the $msg->entries (what is an LADP::Entry object) got something, but i'm not sure than it contains everything or only the part of the stored data...
I've done something similar, and I use this to query LDAP:
my $ldapResponse = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
And then this to parse it:
if ($ldapResponse && $ldapResponse->count()) {
$ldapResponse->code && die $ldapResponse->error;
my %domainNames = %{$ldapResponse->as_struct};
foreach my $domainName (keys %domainNames) {
my %ldapResponse;
my %dnHash = %{$domainNames{$domainName}};
foreach my $attr (sort(keys %dnHash)) {
# Note that the value for each key of %dnHash is an array,
# so join it together into a string.
my $value = join(" ", #{$dnHash{$attr}});
$ldapResponse{$attr} = $value;
}
// Dump/use %ldapResponse
}
}
I've never tried to use the ldap->entries in your code, but the above works for me!
I explicitly specify a(long) list of attributes ($attr), but perhaps that's optional as your example shows, and you can get ALL LDAP fields by just skipping that arg to search().

using perl to get users of AD group

I've been trying to get this to print out all the members in "domain users". Problem is, it only prints out a small portion of them, then it just sorta stops. Not sure why. Can someone shed some light on the problem?
#!/usr/bin/perl
use Net::LDAP;
my $uid = "cn=account,cn=users,dc=domain,dc=local";
my $bindPass = "password";
my $ldapServer = "ldap://server.domain.local";
# connect to ldap server
$ldap = Net::LDAP -> new ($ldapServer) || die "Could not connect to server\n";
# bind to ldap server
$ldap -> bind($uid, password => $bindPass);
# search for group
$mesg = $ldap -> search(filter => "(&(cn=Domain Users))", base => "dc=domain,dc=local");
$entry = $mesg -> entry;
# #members = $entry -> get_value ('member;Range=0-*');
#the above entry when uncommented doesn't work either.
#members = $entry -> get_value ('member');
foreach $thing (#members) {
print "$thing\n";
}
From the Net::LDAP docs:
sizelimit => N
A sizelimit that restricts the maximum number of entries to be
returned as a result of the search. A value of 0, and the default,
means that no restriction is requested. Servers may enforce a maximum
number of entries to return.
It might very well be your AD server has a restriction configured. Try checking $mesg->error() after the search.
You might have more success if you use ldap://server.domain.local:3268/ as your URL. AD uses a "mini" ldap server on that port to talk to replicated servers (google "global catalog"); you won't see all attributes on that server, but maybe it's less restrictive as to the maximum number of entries.

How to retrieve all Groups from LDAP with Perl

I have a Perl script wich binds to an LDAP server and retrieves all users. So far it works good but I want to filter that search in order to gather all groups. Once I have all groups the user can select one of these groups and I'll show him only users that are member of that group. How can I do those queries? I tryed this one:
my $mesg = $ldap->search(
base => $base,
filter => '(objectclass=user)',
attrs => ['memberOf']
);
But then some groups are repeated and I will have to manually filter the result (and I'd like to avoid that). And what about the second query?
cnThe filter to get all groups is "(objectclass=group)" you can retreive groups in only one organizationalUnit (scope => 'one') or in all suborganization (scope => 'sub')
$mesg = $ldap->search( filter=>"(&(objectclass=group)(cn=the group choosen by the user)",
base=>"ou=Monou,dc=societe,dc=fr"
scope=>"sub"
attrs=> ['cn', 'member']);
#entries = $mesg->entries;
foreach $entry (#entries)
{
$entry->dump;
#member = $entry->get_value("member"); # returns all members
}
For more help see An Introduction to perl-ldap
Edited
So the filter you were looking for is :
(&(objectClass=user)(memberof=CN=Mongroupe,OU=MonOU,DC=societe,DC=fr))
Use objectclass=* to get all.
my $msg = $ldap->search(base => $dn,
scope => 'one',
filter => "(objectclass=*)");
$msg->all_entries;

How can I get the date of an email using Perl's Mail::MboxParser::Mail?

This is a simple question. I have a little program here that reads
a list of emails in a specific inbox of a user account specified by the program.
I can access an account using its username, password and host. The only problem is I don't know how to get the date on each of these mails.
Here's some part of my code:
my $pop = new Mail::POP3Client(
USER => $user, #some user,password & host assigned
PASSWORD => $pass,
HOST => $host );
for( $i = 1; $i <= $pop->Count(); $i++ ) {
#header = $pop->Head($i);
#body = $pop->Body($i);
$mail = new Mail::MboxParser::Mail(\#header, \#body);
$user_email = $mail->from()->{email
print "Email:".$user_email; #this prints out right
foreach( $pop->Head( $i ) ) {
/^(Date):\s+/i && print $_, "\n";
$date = $_;
}
}
Now what i need is to get the only one date for each email,
but that loop gives me all.. but when remove the loop, it
returns an error. I'm using Perl.
Kindly help me? :)
According to MboxParser::Email doc, you should be able to do:
$date = $mail->header->{'date'}; #Keys are all lowercase
If you have more than one date returned, $date will be an array ref and you can access the first occurence of the Date with:
$date->[0];
So you shouldn't need to loop through the header and use a regular expression.