Open justageek opened 4 years ago
Hi @justageek, thanks so much for the sponsorship! ❤️
This may be tricky since multi-domain support isn’t built in, but I’ll try to help as much as I can.
I’m tied up for bit of the night, but I’ll write up a response later and we can get try to get this working.
Hi @justageek, I'm ready to help tackle this with you.
Before you call Resolver::setConnection()
I believe you should first attempt connecting to that server -- otherwise the Resolver::byCredentials()
method will return null
since the LDAP query will fail to run. I believe this may be a step that is missing.
Can you share your code along with where you are doing this logic during authentication?
@stevebauman , here is my custom user provider and the ldap_auth.php file so you can see how it is used.
Ok great thanks!
So you have two options:
auto_connect
to true
in your config/ldap.php
file for both of your LDAP connectionsResolver::byCredentials()
Do you have auto_connect
enabled on your connections? This option will attempt to bind to your configured LDAP servers when the Resolver
is called -- otherwise you have to connect manually.
Also, if you could post the logs that get generated upon login, that would help me debug with you.
You should only need to call Resolver::setConnection('audit-portal');
once in the providers retrieveByCredentials
method.
However, I'm not seeing any kind of fallback logic here -- only one LDAP connection is being used.
I would suggest looping through your configured LDAP connections in the retrieveByCredentials
method and setting the Resolver
connection like so:
public function retrieveByCredentials(array $credentials)
{
foreach (config('ldap.connections', []) as $name => $config) {
Resolver::setConnection($name);
if (!$user = parent::retrieveByCredentials($credentials)) {
continue;
}
return $user;
}
}
Let me know if that works! 👍
Thanks again Steve, I have to admit I don't 100% follow the logic, we do have auto_connect set to true for both connections by default. What do I call in order to do the following: Attempt connecting to the LDAP connection prior to calling Resolver::byCredentials() One thing that confused me is since ldap_auth.php has one single key/value for 'connection', it seems like every time you try to authenticate, it is going to try to use that connection value, maybe that doesn't matter. So, the first time it tries to authenticate someone, it will use my custom user provider by try to use the default connection, since that is how ldap_auth.php is set up right now. I will give your loop idea a try and see what happens. Do I need to disable the "fallback" logic so if one authentication fails it doesnt' automatically try to fallback to the next?
Steve, I have a weird mess here, my user is being found in the default ldap server, which is correct for my test, but then fails authentication because it looks like the code moves on to the second connection before it tries the authentication step.
User 'Matthew Pearce' has been successfully found for authentication.
[2020-05-27 21:50:33] local.INFO: User 'Matthew Pearce' is being synchronized.
[2020-05-27 21:50:33] local.INFO: User 'Matthew Pearce' has been successfully synchronized.
[2020-05-27 21:50:33] local.INFO: User 'Matthew Pearce' is authenticating with username:
I don't know why it moves on to the second connection after finding the user in the default ldap server. Maybe because I am setting the connection in ::validateCredentials() method where I should not be doing that? I think that might be the problem.
One thing that confused me is since ldap_auth.php has one single key/value for 'connection', it seems like every time you try to authenticate, it is going to try to use that connection value, maybe that doesn't matter.
Adldap2 doesn't support multi-domain authentication out of the box. This is basically what you're attempting to patch in here. LdapRecord-Laravel (the successor to Adldap2) does however support multi-domain authentication.
Do I need to disable the "fallback" logic so if one authentication fails it doesn't automatically try to fallback to the next?
The fallback feature allows non LDAP users to sign into your Laravel application. For example, if you have a regular user register in your Laravel application, they can continue to sign in without existing in your LDAP server.
Disable it if you do not need / want non LDAP users to sign into your app.
I don't know why it moves on to the second connection after finding the user in the default ldap server. Maybe because I am setting the connection in ::validateCredentials() method where I should not be doing that? I think that might be the problem.
Yup absolutely -- I would remove that from the validateCredentials()
method. Once a user is successfully retrieved from your LDAP server via the retrieveByCredentials()
method, we want to leave the set connection in place so authentication is done on that connection.
Steve, thanks for your help , I am really close, I have the authentication working with your loop through the connections, however for some reason if I the login_fallback set to true, it connects and binds to the ldap server but never searches for and locates the user and then proceeds to authentication for some reason, it works if I set it to false. I guess it falls back to fast to the Eloquent login credentials, which my test user doesn't have.
No problem @justageek! Sounds like we're getting close.
however for some reason if I the login_fallback set to true, it connects and binds to the ldap server but never searches for and locates the user and then proceeds to authentication for some reason, it works if I set it to false.
In your application, are you only wanting LDAP users to be able to sign in? Or will your app support registering / creating local users that must be able to sign in as well -- alongside LDAP users?
The login fallback option is for the latter of the above (supporting sign in for local app users who do not exist in your LDAP servers).
If you wish to support this, we will need to override a bit more logic and I will help you with that once I know the direction you're looking to take in your app. Thanks!
Steve, so I don't know what to think, if I have login_fallback set to true, I cannot login with a user that is in the second Active Directory server. But if I set it to false, the login for the default AD server doesn't work. We do have database users synced up to the default ldap server, so if I have the login_fallback set to false, it isn't authenticated against the default AD server, then it is not falling back to the database, I don't know why the Active Directory isn't finding the user and then authenticating them for the default connection my new loop logic, but it isn't. The logs seem to indicate it is going nuts cycling between the connections
[2020-05-28 20:09:38] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:38] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:38] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:39] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:43] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:44] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:44] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:44] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:48] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:49] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:49] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:49] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:54] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:55] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:56] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:56] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:57] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:09:57] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Binding - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:57] local.INFO: LDAP (ldaps://cashtn.com:636) - Connection: audit-portal - Operation: Bound - Username: CASHTN\svc_auditor_portal
[2020-05-28 20:09:59] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Binding - Username: CASHEXPRESSLLC\auditorportal
[2020-05-28 20:10:00] local.INFO: LDAP (ldaps://dc-cluster.cashexpressllc.com:636) - Connection: default - Operation: Bound - Username: CASHEXPRESSLLC\auditorportal
Ok -- let me make some patches here. Shouldn't take long. Once I'm done, I'll need you to perform a composer update
. I'll post here again shortly!
ok
Ok -- now I'll need you to update your composer.json
file with the following to receive the latest patch:
"adldap2/adldap2-laravel": "dev-master",
Then, run the following to download it:
composer update
Afterwards, in your custom user provider you have, paste in the following code:
/**
* Whether to fallback to Eloquent authentication.
*
* @var bool
*/
protected $fallback = false;
public function retrieveByCredentials(array $credentials)
{
foreach ($connections = config('ldap.connections', []) as $name => $config) {
Resolver::setConnection($name);
if ($user = parent::retrieveByCredentials($credentials)) {
return $user;
}
end($connections);
// Here we will determine if we are on the last connection. If
// so, we will enable fallback to Eloquent authentication and
// attempt to retrieve the local database user to fallback to.
if ($name == key($connections)) {
$this->fallback = true;
return $this->eloquent->retrieveByCredentials($credentials);
}
}
}
protected function isFallingBack()
{
return $this->fallback;
}
Once complete, try authenticating against both domains you have configured, and then a local database user.
Let me know if it works!
Hi Steve, I need to run a full set of tests, but I think it is going to work. I cannot thank you enough for all the work on this, you are awesome for doing it for us!
Sounds good! And I’m happy to help and I will always support my sponsors 😄
Will wait for your update 👍
Description:
We are trying to use 2 ldap servers we are required to maintain, where one is default and one is a fallback if authentication in the default fails. What I have tried is
So far this doesn't seem to be working and I wanted to see if you see a flaw in my logic, I cannot tell what is failing right now. I did notice though when I open up the laravel log file, even after authentication fails and the application is not running, the ldap library continues to log connection attempts in the log file, like it is running in the background, trying to connect to both ldap servers in a loop, it looks like it is stuck in some sort of loop.
Any help is appreciated.
Steps To Reproduce: