Is there a way to successfully bind while leaving out one of the files in the bind search base string? I don't always know what $site is for a user and if I leave it out the binding fails. Can I have something like OU=*,
$ldapSearchBase = "OU=$site,OU=xxxx,OU=xxxx,DC=$globalLocation,DC=companyName,DC=com";
If I leave the site out I get. it works if I put in my correct site
The wrong password was supplied or the SASL credentials could not be processed
LDAP binds require you to have a single unique distinguished name plus the appropriate credentials (user/pass, SSL, etc.). Your bind will always fail if your DN is not unique.
You might want to try splitting your base DN and varying whatever $site is:
my $ldapSearchBase = "OU=user,OU=accounts,DC=$globalLocation,DC=companyName,DC=com";
my $ldapSite = "OU=$site";
my $bindString = $ldapSearch . "," . $ldapSearchBase;
Use the first container as search base:
$ldapSearchBase = "DC=$globalLocation,DC=companyName,DC=com";
Related
So any one who has used perl dancer knows that to authenticate a user on login you can call authenticate_user
authenticate_user(
params->{username}, params->{password}
);
This is part of the Auth::Extensible plugin.
To me it looks like it encourages the use of storing passwords in plain text! Sure you can hash the password first then make sure the stored password is the same hash but this seems to be more of a work around and i found isn't guaranteed to work. I have only got this to work using sha1 which shouldn't be used. I want to use Bcrypt but the passphrase simply wont match. Possibly odd characters not matching i'm not sure.
The thing is using the dancer Passphrase plugin i can already validate the username and password without even needing to rely on authenticate_user to verify them. But for the dancer framework to consider the user logged in you still have to call authenticate_user which must be passed the password.
I'm completely stuck. I'm curious how other people have managed to use proper password management in dancer2?
Firstly, I'll echo the "you almost certainly don't need to be using authenticate_user()" comments. The plugin can handle all that for you.
However, "it doesn't hash it" is wrong; here's how it works. The
authenticate_user keyword loops through all auth realms configured, and for
each one, asks that provider's authenticate_user() method to see if it accepts
the username and password. The Database provider (and the others) fetch the
record from the DB, and use $self->match_password() (which comes from the
Provider role) to validate it; that code checks if the stored password from
the database starts with {scheme} and if so, uses
Crypt::SaltedHash->validate to validate that the user-supplied password (in
plain text, as it's just come in over the wire) matches the stored, hashed
passsword ($correct in the code below is the stored password):
if ( $correct =~ /^{.+}/ ) {
# Looks like a crypted password starting with the scheme, so try to
# validate it with Crypt::SaltedHash:
return Crypt::SaltedHash->validate( $correct, $given );
}
So, yes, if your stored password in the database is hashed, then it will match
it if the password supplied matches that hash.
For an example of what a stored hashed password should look like, here's
the output of the bundled generate-crypted-password utility:
[davidp#supernova:~]$ generate-crypted-password
Enter plain-text password ?> hunter2
Result: {SSHA}z9llSLkkAXENw8FerEchzRxABeuJ6OPs
See the Crypt::SaltedHash doco for details on which algorhythms are
supported by it, and the format it uses (which "comes from RFC-3112 and
is extended by the use of different digital algorithms").
Do bear in mind that the code behind authenticate_user is exactly what's used
under the hood for you.
For an example of just letting the plugin do the work for you, consider:
get '/secret' => require_login sub {
my $user = logged_in_user();
return "Hi, $user->{username}, let me tell you a secret";
};
... that's it. The require_login means that the plugin will check
if the user is logged in, and if not, redirect them to the login page
to log in. You don't need to call authenticate_user yourself, you
don't need to set any session variables or anything. logged_in_user()
will return a hashref of information about the logged in user (and because
the route code has require_login, there's guaranteed to be one at this
point, so you don't need to check).
If you need to check they have a suitable role, instead of just that they
are logged in, then look at require_role in the documentation instead.
In the documentation for Dancer2::Plugin::Auth::Extensible, the description for authenticate_user() says:
Usually you'll want to let the built-in login handling code deal with authenticating users, but in case you need to do it yourself, this keyword accepts a username and password ...
Which strongly implies to me that you shouldn't be calling this function at all unless you're doing something particularly clever.
I haven't used this module myself, but it seems to me that all the hashing and encryption stuff should be handled by one of the authentication providers and if there's not one that covers the case you use, then you can write one yourself.
Whenever I need to store secure passwords for a Dancer app, I reach for Dancer2::Plugin::Passphrase. I wonder if I should consider writing an Auth::Extensible style authentication provider for it.
I will retrieve user informations from a LDAP server. Customer sent me following 3 data:
LDAP Server IP
Username to authenticate
Password to authenticate
This is the 1st time I am working with LDAP so I studied on it. I see that I must use some parameters like cn, dc, ou. Should not customer give this info either? Or customer's info is enough and can I find these parameters by a code displaying folder structure?
The structure you want to see is called the directory tree and is made of LDAP "entries" (not folders).
You should be able to collect entries from your directory provided that the credentials you've been given are sufficient. For that you need a client library, you can use Perl LDAP.
Using that library you would do something like :
use Net::LDAP;
# Init connection and bind to the directory.
$ldap = Net::LDAP->new('ldaphost.example.com') or die "$#";
$mesg = $ldap->bind ($binddn, password => $password);
# Perform a search on the whole tree below (and including) the base object.
$mesg = $ldap->search(base => $basedn);
# Handle error
$mesg->code && die $mesg->error;
# Display results
foreach $entry ($mesg->entries) { $entry->dump; }
$mesg = $ldap->unbind;
The code above just print entries to stdout ($entry->dump).
$binddn is the dn to authenticate with. If you've been given a simple username that is not a dn, like 'username' instead of something like uid=username,dc=example,dc=com, it would probably mean that you are dealing with Active Directory. In this case you can try to bind with a binddn that corresponds to the following pattern : 'username#example.com'.
basedn is the dn of the base object entry, the search is performed on the whole tree below (and including) the base object. You will have to specify a valid base dn.
You can try to guess which base dn to use. Domain components (dc) usually match the FQDN but it is not guaranteed it results in a valid base dn (e.g. 'ldaphost.example.com' would give 'dc=example,dc=com'). However you can query the server for rootDSE information and retrieve the namingContexts (or defaultNamingContext if any) and use one of them as the base dn :
$ldap->search(base => '', attrs => 'namingContexts');
That said, if you really want to display the directory tree, you have better to go with an LDAP explorer like Apache Directory Studio.
Note that LDAP is not limited to storing information in strict "tree" structures, it just have to respects a Directory Information Tree (DIT) nomenclature.
I am connecting to an Active Directory 2008 Domain Controller using perl Net::LDAPS and trying to set the "User must change password at next logon" account option and it's not working.
I am able to create, modify, delete, and move different objects but I am not able to get that change password setting to take!
This is what I am trying to do and it's not working:
Note: I am using a self written class wrapper for Net::LDAPS and the code below is boiled down to the bare bones of what I am trying to do.
# Binding to LDAP Directory:
$self->{LDAP_INSTANCE} = Net::LDAPS->new($host);
$self->{LDAP_INSTANCE}->bind(dn=>$dn, password=>$password, version=>3 )
my $rc =$self->{LDAP_INSTANCE}->modify(
$DN_OF_USER_ACCOUNT,
[ replace => [userAccountControl => 0x00800000] ]
);
print $rc->error; # Results in an empty string / No error
# Note: I have also tried: hex(800000) instead of 0x00800000 as well.
I am binding with a domain administrator account and I have verified that the $DN_OF_USER_ACCOUNT is correct.
The userAccountControl attribute is a bitfield with many settings. Changing the value for the user to be 0x00800000 would be quite harmful since it would remove the default of 0x200 ADS_UF_NORMAL_ACCOUNT.
The documentation for 0x00800000 is:
0x00800000 ADS_UF_PASSWORD_EXPIRED The user password has expired. This flag is created by the system using data from the Pwd-Last-Set attribute and the domain policy.
If we look at pwdLastSet's documentation, we see:
The date and time that the password for this account was last changed. [...] If this value is set to 0 and the User-Account-Control attribute does not contain the UF_DONT_EXPIRE_PASSWD flag, then the user must set the password at the next logon.
So to expire the password, we need to set pwdLastSet to zero. This is substantiated by a blog entry from Scripting Guy who does it in VBScript.
In Perl:
# Binding to LDAP Directory:
$self->{LDAP_INSTANCE} = Net::LDAPS->new($host);
$self->{LDAP_INSTANCE}->bind(dn=>$dn, password=>$password, version=>3 )
my $rc =$self->{LDAP_INSTANCE}->modify(
$DN_OF_USER_ACCOUNT,
[ replace => [pwdLastSet => 0] ]
);
print $rc->error;
I have the DN of the LDAP entry. I know I could search for it: Doing something like this:
my $search = $ldap->search(
base => $dn,
scope => "base",
filter => "(objectclass=*)",
);
But, I don't need to do a search. I have the DN. I simply want to pull up the DN entry and do my operations directly on that. Something like this:
my $dn_entry = $ldap->get( $dn );
Is there a method to get the DN entry from the DN string itself, or do you have to search for the entry even if you know the DN itself?
Using LDAP, clients must always search or use an extended operation to get data. If you're interested in all the attributes associated with an entry, and the DN is known, use the following parameters in a search request:
baseObject: the DN that is known
search scope: base
filter: either (&) or (objectClass=*)
the list of attributes to be returned. Some APIs use * for all user attributes and + for all operational attributes.
What it sounds like you are saying is that you have stored the "Distinguished Name" (a string) rather than the DN entry (a Net::LDAP::Entry object). If this is the case, I believe you have to create a new Net::LDAP::Entry object from the DN. The documentation indicates that you can apply operations directly to such an object without synchronizing with the server, but this won't supply all the data for the given DN. If you need the server's data, you need to get it via $ldap->search(...).
Have you considered using the Net::LDAP::LDIF mechanism for storing DN data locally?
I am trying to setup LDAP authentication to an Active Directory server for an RT3 site.
I think that there is an error in the LDAP authentication module, but I really do not understand what the filter is trying to do. See RT/Authen/ExternalAuth/LDAP.pm line 126:
$ldap_msg = $ldap->search( base => $group,
filter => $filter,
attrs => \#attrs,
scope => 'base');
There is a config file that specifies the group, group_attr and group_attr_value.
Group is passed as $group, and the filter is created from a group_attr=group string. In my case group_attr = present and the memberOf line comes back with CN=ITAdmins,CN=Builtin,DC=SPGLOBAL,DC=local and the filter becomes Filter: (present=CN=ITAdmins,CN=Builtin,DC=SPGLOBAL,DC=local) and Attrs: dn.
This is invalid for a filter, but can any one who knows a bit more about this see what is meant to be happening?
Thanks for posting the link to the code, that makes it much simpler to understand.
So the model is, authentitcate with username and password, then $group, if defined means check that they are a member of a group to let them in.
$group should be a full LDAP DN, so cn=AccessGroup,ou=Groups,dc=myDomain,dc=com or the like.
$group_attr_val should be member, memberOf, or maybe Member, depending on whatever the member attribute is in your target LDAP directory.
So I think your issue is that group-attr=present is probably wrong and should be as simple as member instead.