DirectoryTree / LdapRecord-Laravel

Multi-domain LDAP Authentication & Management for Laravel.
https://ldaprecord.com/docs/laravel/v3
MIT License
501 stars 53 forks source link

[Bug] Eloquent Model Binding not working #205

Closed wjarka closed 3 years ago

wjarka commented 3 years ago

Environment (please complete the following information):

Describe the bug: Auth::user()->ldap returns null Auth::user()->ldap->getFirstAttribute('cn') returns an error that I am trying to call a method on null (which is to be expected)

wjarka commented 3 years ago

Also this happens just after I successfully import users from LDAP and I am able to log in with LDAP user.

stevebauman commented 3 years ago

Hi @wjarka,

Can you post your Eloquent user model as well as your config/auth.php file?

wjarka commented 3 years ago

config/auth.php:

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
//            'provider' => 'users',
            'provider' => 'ldap',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'ldap' => [
            'driver' => 'ldap',
            'model' => \App\Ldap\User::class,
            'database' => [
                'model' => App\Models\User::class,
                'sync_password' => false,
                'sync_attributes' => [
                    'name' => 'displayname',
                    'username' => 'uid',

                ],
            ]
        ]

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the amount of seconds before a password confirmation
    | times out and the user is prompted to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => 10800,

];

app/Model/User.php:

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use LdapRecord\Laravel\Auth\HasLdapUser;
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;

class User extends Authenticatable implements LdapAuthenticatable
{

    use AuthenticatesWithLdap;
    use HasLdapUser;
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use Notifiable;
    use TwoFactorAuthenticatable;
    use SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'username',
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_recovery_codes',
        'two_factor_secret',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = [
        'profile_photo_url',
    ];

    public function accounts() {
        return $this->hasMany('App\Models\Account');
    }

}

app/Model/Ldap/User.php:

namespace App\Ldap;

use LdapRecord\Models\Model;

class User extends Model
{
    /**
     * The object classes of the LDAP model.
     *
     * @var array
     */
    public static $objectClasses = [];

    protected $guidKey = 'uid';
}
wjarka commented 3 years ago

I guess one thing to point out is also that we don't have guid in our LDAP, so I am using uid or uidnumber to identify records. (tried both, but not sure if that's relevant)

wjarka commented 3 years ago

Alright, solved it (I guess I just needed sleep). So, maybe it's going to be useful for others who are new to playing with LDAP.

I am using FreeIPA and guidKey needs to be set to "ipauniqueid" (in my case) for this to actually work.

I found it by creating a test route and executing this:

var_dump(LdapRecord\Models\FreeIPA\Entry::get());

That basically told me what "guidKey" for those entries are.

@stevebauman maybe it would be worth it to update the docs and tell people how to change guidKey and where to find what it is for their LDAP documentation?

For me, issue closed :)

wjarka commented 3 years ago

For those who use FreeIPA, this can be useful:

namespace App\Ldap;

use LdapRecord\Models\FreeIPA\User as IPAUser;

class User extends IPAUser
{
    /**
     * The object classes of the LDAP model.
     *
     * @var array
     */
    public static $objectClasses = [];
}

Basically, make sure your Ldap User Class uses FreeIPA implementation and you don't have to worry about guid :)