Adldap2 / Adldap2-Laravel

LDAP Authentication & Management for Laravel
MIT License
910 stars 184 forks source link

OpenLdap Authorization Fails #801

Closed evzharko closed 4 years ago

evzharko commented 4 years ago

Description:

When using the method directly Adldap :: auth () -> attempt ('uid = user, ou = users, dc = domain, dc = com, dc = ua', 'passworduser') Authorization passes as expected. But when using $ credentials = ['uid' => 'user', 'password' => 'passworduser']; dd (Auth :: attempt ($ credentials)); always return false. Please help, I have been struggling with this problem for a week (. Thank you very much.

Authorization takes place by uid.

I use NoDatabaseUserProvider

I give a list of all the fields that I use in the OpenLdap directory.

dn: uid=user,ou=users,dc=reactor,dc=com,dc=ua cn: user@domain.com.ua gidnumber: 20000 givenname:: 0JDQu9C10L3QsA== homedirectory: /home/ldap/user loginshell: /bin/bash mail: user@domain.com.ua objectclass: posixAccount objectclass: inetOrgPerson objectclass: organizationalPerson objectclass: person sn:: 0JDQvdGC0L7QvdC10L3QutC+ st: 1 uid: user uidnumber: 10310 userpassword: {SSHA}this password user

I tried various options that I found here https://github.com/Adldap2/Adldap2-Laravel/issues

but all to no avail.

stevebauman commented 4 years ago

Hi @evzharko,

Can you post your ldap_auth.php file as well as your ldap.php file with any sensitive details omitted?

evzharko commented 4 years ago

file ldap_auth.php

<?php

return [
    'connection' => env('LDAP_CONNECTION', 'default'),
//    'provider' => Adldap\Laravel\Auth\DatabaseUserProvider::class,
    'provider' => Adldap\Laravel\Auth\NoDatabaseUserProvider::class,
//    'model' => App\User::class,

    'rules' => [
        // Denys deleted users from authenticating.
        Adldap\Laravel\Validation\Rules\DenyTrashed::class,
        // Allows only manually imported users to authenticate.
        // Adldap\Laravel\Validation\Rules\OnlyImported::class,
    ],

    'scopes' => [
        // Only allows users with a user principal name to authenticate.
        // Suitable when using ActiveDirectory.
        // Adldap\Laravel\Scopes\UpnScope::class,
        // Only allows users with a uid to authenticate.
        // Suitable when using OpenLDAP.
        // Adldap\Laravel\Scopes\UidScope::class,
//        App\Scopes\AccountingScope::class,
    ],

    'identifiers' => [
        'ldap' => [
//            'locate_users_by' => 'uid',
            'locate_users_by' => 'userprincipalname',
            'bind_users_by' => 'distinguishedname',
        ],

        'database' => [
            'guid_column' => 'objectguid',
            'username_column' => 'userprincipalname',
        ],

        'windows' => [
//            'locate_users_by' => 'samaccountname',
            'locate_users_by' => 'givenName',
            'server_key' => 'AUTH_USER',
        ],
    ],

    'passwords' => [
       'sync' => env('LDAP_PASSWORD_SYNC', false),
        'column' => 'password',
    ],

    'login_fallback' => env('LDAP_LOGIN_FALLBACK', false),

    'sync_attributes' => [
        'email' => 'userprincipalname',
        'name' => 'cn',
    ],

    'logging' => [
        'enabled' => env('LDAP_LOGGING', true),
        'events' => [
            \Adldap\Laravel\Events\Importing::class                 => \Adldap\Laravel\Listeners\LogImport::class,
            \Adldap\Laravel\Events\Synchronized::class              => \Adldap\Laravel\Listeners\LogSynchronized::class,
            \Adldap\Laravel\Events\Synchronizing::class             => \Adldap\Laravel\Listeners\LogSynchronizing::class,
            \Adldap\Laravel\Events\Authenticated::class             => \Adldap\Laravel\Listeners\LogAuthenticated::class,
            \Adldap\Laravel\Events\Authenticating::class            => \Adldap\Laravel\Listeners\LogAuthentication::class,
            \Adldap\Laravel\Events\AuthenticationFailed::class      => \Adldap\Laravel\Listeners\LogAuthenticationFailure::class,
            \Adldap\Laravel\Events\AuthenticationRejected::class    => \Adldap\Laravel\Listeners\LogAuthenticationRejection::class,
            \Adldap\Laravel\Events\AuthenticationSuccessful::class  => \Adldap\Laravel\Listeners\LogAuthenticationSuccess::class,
            \Adldap\Laravel\Events\DiscoveredWithCredentials::class => \Adldap\Laravel\Listeners\LogDiscovery::class,
            \Adldap\Laravel\Events\AuthenticatedWithWindows::class  => \Adldap\Laravel\Listeners\LogWindowsAuth::class,
            \Adldap\Laravel\Events\AuthenticatedModelTrashed::class => \Adldap\Laravel\Listeners\LogTrashedModel::class,
        ],
    ],
];

file ldap.php

<?php

use App\Http\Controllers\SchemeCustom;

return [
    'logging' => env('LDAP_LOGGING', false),

    'connections' => [
        'default' => [
            'auto_connect' => env('LDAP_AUTO_CONNECT', true),
            'connection' => Adldap\Connections\Ldap::class,
            'settings' => [
                'schema' => Adldap\Schemas\OpenLDAP::class,
//                'schema' => App\Http\Controllers\SchemeCustom::class,
                'account_prefix' => env('LDAP_ACCOUNT_PREFIX', ''),
                'account_suffix' => env('LDAP_ACCOUNT_SUFFIX', ''),
                'hosts' => explode(' ', env('LDAP_HOSTS', 'corp-dc1.corp.acme.org corp-dc2.corp.acme.org')),
                'port' => env('LDAP_PORT', 389),
                'timeout' => env('LDAP_TIMEOUT', 5),
                'base_dn' => env('LDAP_BASE_DN', 'dc=corp,dc=acme,dc=org'),
                'username' => env('LDAP_USERNAME'),
                'password' => env('LDAP_PASSWORD'),
                'follow_referrals' => true,
                'use_ssl' => env('LDAP_USE_SSL', false),
                'use_tls' => env('LDAP_USE_TLS', false),
            ],
        ],
    ],
];

My .env

LDAP_SCHEMA=OpenLDAP
LDAP_HOSTS=server
LDAP_BASE_DN=dc=domainт,dc=com,dc=ua
LDAP_USER_FORMAT=uid=%s,ou=users,dc=domain,dc=com,dc=ua
LDAP_CONNECTION=default
LDAP_USE_TLS=true
LDAP_USERNAME=NULL
LDAP_PASSWORD=NULL
LDAP_AUTO_CONNECT=true
LDAP_LOGGING=true
stevebauman commented 4 years ago

First issue is here:

// 'locate_users_by' => 'uid',
'locate_users_by' => 'userprincipalname',

You have commented out uid as the location attribute - which is likely what you want for OpenLDAP.

Second issue:

This option does not exist (in your .env):

LDAP_USER_FORMAT=uid=%s,ou=users,dc=domain,dc=com,dc=ua

Third Issue:

You have follow_referrals enabled - turn this off.

'follow_referrals' => true,

Fourth Issue:

Your sync_attributes are likely incorrect for OpenLDAP usage. OpenLDAP does not include the userPrincipalName by default.

'sync_attributes' => [
    'email' => 'userprincipalname', // Likely incorrect.
    'name' => 'cn',
],

Fifth Issue:

Ensure you can actually connect to your configured LDAP server and get results by performing the following:

use Adldap\Laravel\Facades\Adldap;

$users = Adldap::search()->users()->get();

dd($users);

Fix all the above and that should resolve your issue.

evzharko commented 4 years ago

Thank you very much for the answer, made the changes that you showed me, but still nothing has changed.

This part of the code works perfectly.

use Adldap\Laravel\Facades\Adldap;

$users = Adldap::search()->users()->get();

dd($users);

Return

Collection {#800 ▼
  #items: array:240 [▼
    0 => User {#319 ▶}
    1 => User {#322 ▶}
    2 => User {#324 ▶}
    3 => User {#326 ▶}
    4 => User {#328 ▶}
    5 => User {#330 ▶}
    6 => User {#332 ▶}
    7 => User {#334 ▶}
    8 => User {#336 ▶}
    9 => User {#338 ▶}

File LoginController.php


<?php

namespace App\Http\Controllers\Auth;

use Adldap\AdldapInterface;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';
    /**
     * @var AdldapInterface
     */
    private $adldap;

    /**
     * Create a new controller instance.
     *
     * @param AdldapInterface $adldap
     */
    public function __construct(AdldapInterface $adldap)
    {
        $this->middleware('guest')->except('logout');
        $this->adldap = $adldap;
    }

    public function username()
    {
        return'username';
    }

    public function login(Request $request)
    {
        $credentials = ['uid' => 'e.zharko', 'password' => 'PASS'];
        dd(Auth::attempt($credentials));
    }
}

File login.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('LoginTest') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="username" class="col-sm-4 col-form-label text-md-right">{{ __('username') }}</label>
                            <div class="col-md-6">
                                <input id="username" type="text" class="form-control{{ $errors->has('username') ? ' is-invalid' : '' }}" name="username" value="{{ old('username') }}" required autofocus>
                                @if ($errors->has('username'))
                                    <span class="invalid-feedback" role="alert">
                <strong>{{ $errors->first('username') }}</strong>
            </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>

                                @if (Route::has('password.request'))

                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

File ldap.php

<?php

return [

    'logging' => env('LDAP_LOGGING', false),

    'connections' => [
        'default' => [
            'auto_connect' => env('LDAP_AUTO_CONNECT', true),
            'connection' => Adldap\Connections\Ldap::class,
            'settings' => [
                'schema' => Adldap\Schemas\OpenLDAP::class,
                'account_prefix' => env('LDAP_ACCOUNT_PREFIX', ''),
                'account_suffix' => env('LDAP_ACCOUNT_SUFFIX', ''),
                'hosts' => explode(' ', env('LDAP_HOSTS', 'corp-dc1.corp.acme.org corp-dc2.corp.acme.org')),
                'port' => env('LDAP_PORT', 389),
                'timeout' => env('LDAP_TIMEOUT', 5),
                'base_dn' => env('LDAP_BASE_DN', 'dc=corp,dc=acme,dc=org'),
                'username' => env('LDAP_USERNAME'),
                'password' => env('LDAP_PASSWORD'),
                'follow_referrals' => false,
                'use_ssl' => env('LDAP_USE_SSL', false),
                'use_tls' => env('LDAP_USE_TLS', false),
            ],
        ],
    ],
];

File ldap_auth.php

<?php

return [
    'connection' => env('LDAP_CONNECTION', 'default'),
//    'provider' => Adldap\Laravel\Auth\DatabaseUserProvider::class,
    'provider' => Adldap\Laravel\Auth\NoDatabaseUserProvider::class,
//    'model' => App\User::class,
    'rules' => [
        Adldap\Laravel\Validation\Rules\DenyTrashed::class,
        // Adldap\Laravel\Validation\Rules\OnlyImported::class,
    ],
    'scopes' => [
        // Suitable when using OpenLDAP.
        // Adldap\Laravel\Scopes\UidScope::class,
        // App\Scopes\AccountingScope::class,
    ],
    'identifiers' => [
        'ldap' => [
//            'locate_users_by' => 'userprincipalname',
            'locate_users_by' => 'uid',
            'bind_users_by' => 'distinguishedname',
        ],
        'database' => [
            'guid_column' => 'objectguid',
            'username_column' => 'userprincipalname',
        ],
        'windows' => [
            'locate_users_by' => 'samaccountname',
            'server_key' => 'AUTH_USER',
        ],
    ],
    'passwords' => [
        'sync' => env('LDAP_PASSWORD_SYNC', false),
        'column' => 'password',
    ],
    'login_fallback' => env('LDAP_LOGIN_FALLBACK', false),

    'sync_attributes' => [
//        'email' => 'userprincipalname',
        'email' => 'cn',
//        'name' => 'cn',
        'name' => 'uid',
    ],

    'logging' => [
        'enabled' => env('LDAP_LOGGING', true),
        'events' => [
            \Adldap\Laravel\Events\Importing::class                 => \Adldap\Laravel\Listeners\LogImport::class,
            \Adldap\Laravel\Events\Synchronized::class              => \Adldap\Laravel\Listeners\LogSynchronized::class,
            \Adldap\Laravel\Events\Synchronizing::class             => \Adldap\Laravel\Listeners\LogSynchronizing::class,
            \Adldap\Laravel\Events\Authenticated::class             => \Adldap\Laravel\Listeners\LogAuthenticated::class,
            \Adldap\Laravel\Events\Authenticating::class            => \Adldap\Laravel\Listeners\LogAuthentication::class,
            \Adldap\Laravel\Events\AuthenticationFailed::class      => \Adldap\Laravel\Listeners\LogAuthenticationFailure::class,
            \Adldap\Laravel\Events\AuthenticationRejected::class    => \Adldap\Laravel\Listeners\LogAuthenticationRejection::class,
            \Adldap\Laravel\Events\AuthenticationSuccessful::class  => \Adldap\Laravel\Listeners\LogAuthenticationSuccess::class,
            \Adldap\Laravel\Events\DiscoveredWithCredentials::class => \Adldap\Laravel\Listeners\LogDiscovery::class,
            \Adldap\Laravel\Events\AuthenticatedWithWindows::class  => \Adldap\Laravel\Listeners\LogWindowsAuth::class,
            \Adldap\Laravel\Events\AuthenticatedModelTrashed::class => \Adldap\Laravel\Listeners\LogTrashedModel::class,
        ],
    ],
];

My .env

LDAP_SCHEMA=OpenLDAP
LDAP_HOSTS=server
LDAP_BASE_DN=dc=domain,dc=com,dc=ua
LDAP_CONNECTION=default
LDAP_USE_TLS=true
LDAP_AUTO_CONNECT=false
LDAP_LOGGING=true

With these settings in the logs, the following

local.INFO: LDAP (ldap: //orthus.workdev.tk: 389) - Connection: default - Operation: Search - Base DN: dc = domain, dc = com, dc = ua - Filter: (& (objectclass = inetorgperson ) (objectclass = person) (uid = e.zharko)) - Selected: (*, entryuuid) - Time Elapsed: 65.42

stevebauman commented 4 years ago

Your login.blade.php username input is named username when it should be named uid:

 <input id="uid" type="text" class="form-control{{ $errors->has('uid') ? ' is-invalid' : '' }}" name="uid" value="{{ old('uid') }}" required autofocus>

You also have LDAP_AUTO_CONNECT set to false, which should be set to true unless you are connecting manually somewhere in your application.

Have you modified your create_users_table migration to store your users uid? As I do not see it in the sync_attributes array in your ldap_auth.php config.

evzharko commented 4 years ago

I do not want to sync users to the database and therefore did not configure sync_attributes. I understand that you can not migrate users to the database but use direct authorization via Ldap or I’m mistaken. The remaining edits that you recommended made.

evzharko commented 4 years ago

@stevebauman Any ideas why authorization fails? Thank.