Skip to content
This repository has been archived by the owner on Jul 24, 2023. It is now read-only.

Unable to use AdldapAuthUserProvider.retrieveByCredentials. always return false #21

Closed
minkbear opened this issue Nov 4, 2015 · 26 comments

Comments

@minkbear
Copy link

minkbear commented Nov 4, 2015

https://github.com/Adldap2/Adldap2-laravel/blob/master/src/AdldapAuthUserProvider.php#L57

$user is not instanceof Adldap\Models\User so it always return false
because $user is Adldap\Models\Entry

Please advise.

@strebl
Copy link
Member

strebl commented Nov 4, 2015

Can you tell me when this error occurs? Did you call retrieveByCredentials manually or did this happen via Auth::attempt()?

@stevebauman
Copy link
Member

Checking for a User model instance is intended, as @strebl said, we'll need more information on this.

@minkbear
Copy link
Author

minkbear commented Nov 4, 2015

@strebl @stevebauman Thanks for you response.
Can you tell me when this error occurs?
Ans There is no error. But unable to authenticate with my LDAP.

Did you call retrieveByCredentials manually or did this happen via Auth::attempt()?
Ans No, Not call retrieveByCredentials manually and I use AuthController from Laravel so not call Auth::attempt() manually as well. (just switch from eloquent to adldap in config/auth.php)

More info
In AuthController there are just 2 public function (getLogin, getLogout) and
AuthController is using Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers trait

So I face to unable to authenticate then I decide to debug(dd) in retrieveByCredentials and met the problem that I reported.

I welcome to give you more info if this is not clear for you.

@stevebauman
Copy link
Member

Is the returned Entry instance the user you're trying to authenticate? It could possibly be a mapping issue if your object category of the user is not equal to person. Can you paste the non-sensitive attributes of the user if possible (particularly the object class & object category)?

@minkbear
Copy link
Author

minkbear commented Nov 5, 2015

@stevebauman

Is the returned Entry instance the user you're trying to authenticate?
Ans yes, returned as Entry instance

yes, I add this below in config/adldap.php

'personfilter' => ['category' => 'objectclass', 'person' => 'person'],

because of https://github.com/Adldap2/Adldap2-laravel/blob/master/src/AdldapAuthUserProvider.php#L42
add default where cause (objectcategory=person). unfortunately It is not found the record. When change attributes, It founded.

Please advise. I do something wrong or not?

@stevebauman
Copy link
Member

Can you paste the attributes of the returned Adldap\Models\Entry?

@minkbear
Copy link
Author

minkbear commented Nov 5, 2015

Can you paste the attributes of the returned Adldap\Models\Entry?

I have just edited config as below

  1. in adldap.php add

'personfilter' => ['category' => 'objectclass', 'person' => 'person'],

  1. in adldap_auth.php add

'username_attribute' => ['username' => 'uid'],
'sync_attributes' => ['name' => 'givenname'],
'select_attributes' => ['uid', 'givenname']

With my config files above (adldap.php and adldap_auth.php) When I dd in https://github.com/Adldap2/Adldap2-laravel/blob/master/src/AdldapAuthUserProvider.php#L54

I got (&(objectclass=person)(uid=foo)) and found the expected record.
But If I do not add 'personfilter' section in adldap.php so
I got (&(objectcategory=person)(uid=foo)) and not found the expected record.

@stevebauman
Copy link
Member

Okay so if I'm understanding correctly, you were able to solve the issue by changing the personfilter category to objectclass and you received the correct Adldap\Models\User instance?

@minkbear
Copy link
Author

minkbear commented Nov 5, 2015

No.
Changing the personfilter category to objectclass and I recieved the Adldap\Models\Entry instance.
Not Adldap\Models\User instance.

@stevebauman
Copy link
Member

Can you please dump the users attributes on the line you've linked above and paste them into a new comment:

https://github.com/Adldap2/Adldap2-laravel/blob/master/src/AdldapAuthUserProvider.php#L54

Add this line below:

 // Filter the query by the username attribute
$query->whereEquals($attributes[$key], $credentials[$key]);

// Retrieve the first user result
$user = $query->first();

// Add this line
dd($user->getAttributes());

@minkbear
Copy link
Author

minkbear commented Nov 6, 2015

here is from dd.

array:7 [▼
  "uid" => array:1 [▼
    0 => "user.foo"
  ]
  0 => "uid"
  "givenname" => array:1 [▼
    0 => "userf userl"
  ]
  1 => "givenname"
  "objectclass" => array:6 [▼
    0 => "top"
    1 => "person"
    2 => "inetorgperson"
    3 => "organizationalperson"
    4 => "orcluser"
    5 => "orcluserv2"
  ]
  2 => "objectclass"
  "dn" => "cn=user.foo,cn=Users,dc=bar,dc=mo,dc=nt"
]

@minkbear
Copy link
Author

@stevebauman Any news? Am i do something wrong on configuration file? or this is a bug.

Thank you.

@stevebauman
Copy link
Member

Inside the select_attributes configuration option, can you insert objectcategory as well?

Such as:

'select_attributes' => ['uid', 'givenname', 'objectcategory'];

@minkbear
Copy link
Author

@stevebauman I add objectcategory in select_attributes that you suggest above.
Below is result from dd($user). The result is same as previous.

Entry {#621 ▼
  +exists: true
  +dateFormat: "Y-m-d H:i:s"
  #query: Builder {#623 ▶}
  #attributes: array:7 [▼
      "uid" => array:1 [▼
    0 => "user.foo"
  ]
    0 => "uid"
    "givenname" => array:1 [▼
      0 => "userf userl"
    ]
    1 => "givenname"
    "objectclass" => array:6 [▼
      0 => "top"
      1 => "person"
      2 => "inetorgperson"
      3 => "organizationalperson"
      4 => "orcluser"
      5 => "orcluserv2"
    ]
    2 => "objectclass"
    "dn" => "cn=user.foo,cn=Users,dc=bar,dc=mo,dc=nt"
  ]
  #original: array:7 [▶]
  #modifications: []
}

@stevebauman
Copy link
Member

Hmm, looks like we've found the issue. Seems like the objectcategory attribute is missing from your user, but I'm not sure why this would be the case. The object category attribute needs to be visible so the query builder can create the correct model instance.

Are you using administrator credentials in your configuration?

@minkbear
Copy link
Author

@stevebauman
Are you using administrator credentials in your configuration?
No, I connected to LDAP as anonymous.

Should I need to be use administrator credentials?
It just checks user name and password is matching or not? Do I miss something?

Please advice.

@stevebauman
Copy link
Member

It depends on your AD server I believe, you may need to enter in admin credentials to retrieve an entry's object category. I would try it to see if that fixes your issue.

@scottjs
Copy link

scottjs commented Jan 28, 2016

Hi,

I think I have exactly the same issue discussed in this thread.

I can confirm my set up is working by performing:

Adldap::authenticate($username, $password).

However, I'm not able to use the AdldapAuthUserProvider due to the same issue of objectcategory being missing. I'm using OpenLDAP which apparently does not support objectcategory.

If I use the code suggested here and replace objectcategory with objectclass, I'm able to get results from the query but the object returned is of type Entry, not of type User.

Is there anything that can be done to work around this? I've been trying to solve this for a couple of days. I really want to use this library, it's exactly what I need.

Thanks,
Scott.

@stevebauman
Copy link
Member

Hi @scottjs, yea you're right this should be changed for OpenLDAP compatibility. Going to work on this now.

@scottjs
Copy link

scottjs commented Jan 29, 2016

Hi @stevebauman, thanks for the quick response and confirming that it's a compatibility issue and not me doing something wrong!

You're working on it now? That's amazing, please point me in the direction of your donate button.

Keep me posted if you have any updates or if there's anything I can do to help. I can give you access to my OpenLDAP testing server if required.

@stevebauman
Copy link
Member

Hi @scottjs, not a problem!

Thanks so much for your donation offer! At the moment I don't have donations set up. If this changes you'll be seeing it available on the landing page of Adldap!

I'm currently installing OpenLDAP as we speak, going to play with it to see how many changes are needed to get it working.

@scottjs
Copy link

scottjs commented Feb 2, 2016

Hi @stevebauman,

Have you had any joy with this? I've spent a bit of time playing around with it and I've managed to come to a solution that works around the issue for me by making a small change to AdldapAuthUserProvider.php.

I'm essentially creating the user object manually instead of relying on Builder.php to return the correct object, shared the code below just in case it's useful to understand my setup.

Config adldap.php:

'account_suffix' => '', // Intentionally blank
'domain_controllers' => 'demo.ldap.dev',
'base_dn' => 'cn=mygroup,dc=demo,dc=ldap,dc=dev',
'admin_username' => 'cn=admin,dc=demo,dc=ldap,dc=dev',
'admin_password' => 'password',
'personfilter' => ['category' => 'objectclass', 'person' => 'person']

Config adldap_auth.php

'username_attribute' => ['email' => 'mail'],
'password_key' => 'password',
'login_attribute' => 'dn',
'sync_attributes' => [
    'name' => 'cn',
 ]

The changes I've made to AdldapAuthUserProvider.php are as follows, between the start/end comments:

public function retrieveByCredentials(array $credentials)
{
    // Get the search query for users only
    $query = $this->newAdldapUserQuery();

    // Get the username input attributes
    $attributes = $this->getUsernameAttribute();

    // Get the input key
    $key = key($attributes);

    // Filter the query by the username attribute
    //$query->whereEquals($attributes[$key], $credentials[$key]); // Rewritten below to get raw output

    // Retrieve the first user result
    //$user = $query->first();

    /**
     * START ADDITIONAL EDITS
     */

    // Filter the query by the username attribute, return raw output
    $results = $query->whereEquals($attributes[$key], $credentials[$key])->raw()->get();

    $raw_attributes = [];
    if(isset($results['count']) && $results['count'] > 0) {

        // Loop results and build array
        foreach($results as $result) {
            if(is_array($result)) {
                $raw_attributes[] = $result;
            }
        }

        // Retrieve the first user result
        $raw_attributes = reset($raw_attributes);
    }

    // Manually construct user object instead of using newLdapEntry() within Builder.php
    $user = (new User([], $query))->setRawAttributes($raw_attributes);

    /**
     * END ADDITIONAL EDITS
     */

    // If the user is an Adldap User model instance.
    if ($user instanceof User) {
        // Retrieve the users login attribute.
        $username = $user->{$this->getLoginAttribute()};

        if (is_array($username)) {
            // We'll make sure we retrieve the users first username
            // attribute if it's contained in an array.
            $username = Arr::get($username, 0);
        }

        // Get the password input array key.
        $key = $this->getPasswordKey();

        // Try to log the user in.
        if ($this->authenticate($username, $credentials[$key])) {
            // Login was successful, we'll create a new
            // Laravel model with the Adldap user.
            return $this->getModelFromAdldap($user, $credentials[$key]);
        }
    }

    if ($this->getLoginFallback()) {
        // Login failed. If login fallback is enabled
        // we'll call the eloquent driver.
        return parent::retrieveByCredentials($credentials);
    }

    return;
}

I'm not sure how robust this solution is, but it's definitely solved the issue for my particular configuration.

Let me know how you get on :)

@stevebauman
Copy link
Member

Hi @scottjs, I tried setting up an OpenLDAP server locally but ran into some problems (seems setting it up on Windows is tricky). Are you able to email me your OpenLDAP test server details @ steven_bauman@outlook.com?

Your above solution works, though like you mentioned it's a work around. I'd rather try to work in direct support for OpenLDAP if possible before having to use the work around.

@minkbear
Copy link
Author

@stevebauman I got info that my client uses Oracle LDAP but there is no administrator credentials so still no objectcategory attribute.

Please advice.
Thanks.

@stevebauman
Copy link
Member

Hi @minkbear, at the moment, OpenLDAP isn't supported (the package is called AD-Ldap after all). I don't have an oracle or an OpenLDAP server for testing, so I'd be coding blind if I tried to support either. I'd be happy to accept PR's though!

@groundbreaker08
Copy link

Hi @stevebauman , open an issue related to this. Returns an instance of Entry but needs an instance of User

was there any updates regarding of not having an objectcategory attribute but can return an instance of User?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants