Fetch all user information with Net::LDAP - perl

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().

Related

perl LDAP entry not recognised

We are writing a Perl code (to be run from Unix) which will reset the password of a Windows AD User. (We are not using powershell as we have been asked not to use Windows scripts).
With the following Perl code, we are able to connect to the AD User directory and query the correct user.
#!/usr/bin/perl -w
#########################
#This script resets the password in active user directory
#########################
use strict;
use warnings;
use DBI;
use Net::LDAP;
use Net::LDAPS;
use Authen::SASL qw(Perl);
use Net::LDAP::Control::Paged;
use Time::Local;
my $CERTDIR = "<cert path>";
my $AD_PASS = "$CERTDIR/.VDIAD_pass";
my $sAN = "vahmed";
### Generate Random Password ###
my $randompass = askPasswd();
my $uninewpass;
my $mail;
my $fullname;
my $name;
my $distName;
my $finalresult;
my #AD_passwords = get_domain_pass();
my $result = reset_AD_Password();
#Reset AD user password
sub reset_AD_Password {
my $ad = Net::LDAP->new($AD_passwords[0]);
my $msg = $ad->bind(dn => "cn=$AD_passwords[2],$AD_passwords[1]",
password => $AD_passwords[3],
version => 3);
if ($msg->code)
{
print "Error :" . $msg->error() . "\n";
exit 2;
}
my $acc_name = 'sAMAccountName';
my $acc_fullname = 'displayName';
my $acc_base = 'manager';
my $acc_distName = 'distinguishedName';
my $acc_mail = 'mail';
my $act = $ad->search(
base => "$AD_passwords[1]",
filter => "(&(objectCategory=person)(sAMAccountName=$sAN))",
attrs => [$acc_name, $acc_fullname, $acc_distName, $acc_mail]);
die 1 if ($act->count() !=1 );
my $samdn = $act->entry(0)->dn;
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
}
}
However we get an error on the line:
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
The error states "Can't locate object method "get_value" via package (distinguished Name) (perhaps you forgot to load (distinguished Name))"
However the code works correctly when we replace $samdn with the following code:
foreach my $entry ($act->entries){
$name = $entry->get_value($acc_name);
$fullname = $entry->get_value($acc_fullname);
$distName = $entry->get_value($acc_distName);
$mail = $entry->get_value($acc_mail);
}
It would appear that the code is unable to identify $samdn as a Net::LDAP::Entry record.
We have tried typecasting $samdn but got the same error.
Could someone help in resolving this issue as we would not prefer to use the for loop just in case more that one record is returned by the search? Thanks in advance.
You are not assigning a Net::LDAP::Entry to $samdn. You are assigning the dn of the first entry.
# VVVV
my $samdn = $act->entry(0)->dn;
Get rid of that ->dn and it should work, if $act->entry(0) returns a Net::LDAP::Entry.

Perl LDAP search for user being a CN attribute

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 {
}
}
}

How can I get a specific tag from array returned from Net::Amazon::EC2 decribe_tags?

I'm using Net::Amazon::EC2 to get some information about my instances.
I get all of the tags associated with an instance with:
my $tags = $ec2->describe_tags("Filter.Name" => "resource-id", "Filter.Value" => $instance_id);
According to the docs, this returns an array ref of DescribeTag objects.
I can iterate through the results:
foreach my $tag (#$tags) {
print $tag->key . " = " . $tag->value . "\n";
}
Is there a way I can get a tag with a specific key?
You could probably grep through them. Not very elegant, but I don't know the module you are using.
my #filtered_tags = grep { $_->key eq 'specific' } #$tags;

Mojolicious wildcard placeholders and question mark

The wildcard placeholder (*) is said to match absolutely everything.
But I'm afraid that it doesn't...
I have a webservice with the following method:
get '/*param' => sub {
my $self = shift;
my $param = $self->stash('param');
$self->app->log->debug($param);
}
When i query my service with: http://localhost:3000/search
then the method logs "search" which is ok
but
when i query my service with: http://localhost:3000/search?page=1
then the method also logs "search" which is not ok IMO
I also tried replacing
get '/*param' => sub {
with
get '/:param' => [param => qr/.*/] => sub {
but the result is the same.
Does anybody know of a way around this?
Or should I file this as a bug?
Regards,
Lorenzo
UPDATE
for people with the same problem, I've worked around this issue like this:
get '/*path' => sub {
my $self = shift;
my $path = $self->stash('path');
my #params = $self->param;
if (scalar #params > 0) {
$path .= '?';
foreach my $param (#params) {
$path .= $param . '=' . $self->param($param) . '&';
}
$path = substr($path, 0, length($path) - 1);
}
$self->app->log->debug($path);
}
?page= its not url.
Its param.
So no any bugs here.
you have 'search' in $param.
And $page=1 in stash.
I think Korjavin is right, that's expected behavior. Looks like "page=1" as a parameter and should be in $stash->param('page'). See GET-POST-parameters in ::Lite
If it does not work, maybe renaming the "param" placeholder to something else helps? Maybe it's a name-clash.
The request parameters wouldn't be in the stash.
They're in
$self->req->params
So
my $params = $self->req->params->to_hash;
$self->app->log->debug(Dumper $params);
Should allow you to see the information you're after

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.