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

Login authenticates properly with AD first time. Afterward any pw will work. #263

Closed
2Foot opened this issue Mar 3, 2017 · 13 comments
Closed
Labels

Comments

@2Foot
Copy link

2Foot commented Mar 3, 2017

  • Laravel Version: 5.4
  • Adldap2-Laravel Version: 7.0.7
  • PHP Version: 7.0

Description:

If I clear the users table and try to sign in with the wrong password it will fail. Once I sign in successfully it stores my user into the user's table. Seems fine. Then I logout and try to sign in again but with the wrong password and it lets me login with a bad password.

Steps To Reproduce:

Once a user logs in fine through AD and there is a user entry in the table for that user then using that login with any password allows login. It's like it's only verifying username exists... ?

auth.php
`<?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',
    ],

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

/*
|--------------------------------------------------------------------------
| 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' => 'adldap',//'adldap', // Was 'eloquent'.
        'model' => App\User::class,
    ],

    // '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,
    ],
],

];`

adldap.php
`<?php

return [

/*
|--------------------------------------------------------------------------
| Connections
|--------------------------------------------------------------------------
|
| This array stores the connections that are added to Adldap. You can add
| as many connections as you like.
|
| The key is the name of the connection you wish to use and the value is
| an array of configuration settings.
|
*/

'connections' => [

    'default' => [

        /*
        |--------------------------------------------------------------------------
        | Auto Connect
        |--------------------------------------------------------------------------
        |
        | If auto connect is true, Adldap will try to automatically connect to
        | your LDAP server in your configuration. This allows you to assume
        | connectivity rather than having to connect manually
        | in your application.
        |
        | If this is set to false, you must connect manually before running
        | LDAP operations using: Adldap::connect();
        |
        */

        'auto_connect' => true,

        /*
        |--------------------------------------------------------------------------
        | Connection
        |--------------------------------------------------------------------------
        |
        | The connection class to use to run raw LDAP operations on.
        |
        | Custom connection classes must implement:
        |  \Adldap\Contracts\Connections\ConnectionInterface
        |
        */

        'connection' => Adldap\Connections\Ldap::class,

        /*
        |--------------------------------------------------------------------------
        | Schema
        |--------------------------------------------------------------------------
        |
        | The schema class to use for retrieving attributes and generating models.
        |
        | You can also set this option to `null` to use the default schema class.
        |
        | Custom schema classes must implement \Adldap\Contracts\Schemas\SchemaInterface
        |
        */

        'schema' => Adldap\Schemas\ActiveDirectory::class,

        /*
        |--------------------------------------------------------------------------
        | Connection Settings
        |--------------------------------------------------------------------------
        |
        | This connection settings array is directly passed into the Adldap constructor.
        |
        | Feel free to add or remove settings you don't need.
        |
        */

        'connection_settings' => [

            /*
            |--------------------------------------------------------------------------
            | Account Prefix
            |--------------------------------------------------------------------------
            |
            | The account prefix option is the prefix of your user accounts in AD.
            |
            | For example, if you'd prefer your users to use only their username instead
            | of specifying a domain ('ACME\jdoe'), enter your domain name.
            |
            */

            //'account_prefix' => '',//env('ADLDAP_ACCOUNT_PREFIX', 'blah'),

            /*
            |--------------------------------------------------------------------------
            | Account Suffix
            |--------------------------------------------------------------------------
            |
            | The account suffix option is the suffix of your user accounts in AD.
            |
            | For example, if your domain DN is DC=corp,DC=acme,DC=org, then your
            | account suffix would be @corp.acme.org. This is then appended to
            | then end of your user accounts on authentication.
            |
            */

            'account_suffix' => env('ADLDAP_ACCOUNT_SUFFIX', '@my_dc.local'),//'',

            /*
            |--------------------------------------------------------------------------
            | Domain Controllers
            |--------------------------------------------------------------------------
            |
            | The domain controllers option is an array of servers located on your
            | network that serve Active Directory. You can insert as many servers or
            | as little as you'd like depending on your forest (with the
            | minimum of one of course).
            |
            | These can be IP addresses of your server(s), or the host name.
            |
            */

            'domain_controllers' => explode(' ', env('ADLDAP_CONTROLLERS', '###.###.###.###')),

            /*
            |--------------------------------------------------------------------------
            | Port
            |--------------------------------------------------------------------------
            |
            | The port option is used for authenticating and binding to your AD server.
            |
            */

            'port' => env('ADLDAP_PORT', 389),

            /*
            |--------------------------------------------------------------------------
            | Timeout
            |--------------------------------------------------------------------------
            |
            | The timeout option allows you to configure the amount of time in
            | seconds that your application waits until a response
            | is received from your LDAP server.
            |
            */

            'timeout' => env('ADLDAP_TIMEOUT', 5),

            /*
            |--------------------------------------------------------------------------
            | Base Distinguished Name
            |--------------------------------------------------------------------------
            |
            | The base distinguished name is the base distinguished name you'd
            | like to perform query operations on. An example base DN would be:
            |
            |        dc=corp,dc=acme,dc=org
            |
            | A correct base DN is required for any query results to be returned.
            |
            */

            'base_dn' => env('ADLDAP_BASEDN', 'ou=my_ou,dc=my_dc,dc=local'),

            /*
            |--------------------------------------------------------------------------
            | Administrator Account Suffix
            |--------------------------------------------------------------------------
            |
            | This option allows you to set a different account suffix for your
            | configured administrator account upon binding.
            |
            | If left empty, your `account_suffix` option will be used.
            |
            */

            'admin_account_suffix' => env('ADLDAP_ADMIN_ACCOUNT_SUFFIX', '@abc.xyz'),

            /*
            |--------------------------------------------------------------------------
            | Administrator Username & Password
            |--------------------------------------------------------------------------
            |
            | When connecting to your AD server, a username and password is required
            | to be able to query and run operations on your server(s). You can
            | use any user account that has these permissions. This account
            | does not need to be a domain administrator unless you
            | require changing and resetting user passwords.
            |
            */

            'admin_username' =>  env('ADLDAP_ADMIN_USERNAME', 'username'),
            'admin_password' => env('ADLDAP_ADMIN_PASSWORD', 'password'),

            /*
            |--------------------------------------------------------------------------
            | Follow Referrals
            |--------------------------------------------------------------------------
            |
            | The follow referrals option is a boolean to tell active directory
            | to follow a referral to another server on your network if the
            | server queried knows the information your asking for exists,
            | but does not yet contain a copy of it locally.
            |
            | This option is defaulted to false.
            |
            */

            'follow_referrals' => true,

            /*
            |--------------------------------------------------------------------------
            | SSL & TLS
            |--------------------------------------------------------------------------
            |
            | If you need to be able to change user passwords on your server, then an
            | SSL or TLS connection is required. All other operations are allowed
            | on unsecured protocols.
            |
            | One of these options are definitely recommended if you
            | have the ability to connect to your server securely.
            |
            */

            'use_ssl' => false,
            'use_tls' => false,

        ],

    ],

],

];
`

adldap_auth.php
`<?php

return [

/*
|--------------------------------------------------------------------------
| Connection
|--------------------------------------------------------------------------
|
| The LDAP connection to use for laravel authentication.
|
| You must specify connections in your `config/adldap.php` configuration file.
|
| This must be a string.
|
*/

'connection' => env('ADLDAP_CONNECTION', 'default'),

/*
|--------------------------------------------------------------------------
| Provider
|--------------------------------------------------------------------------
|
| The LDAP authentication provider to use depending
| if you require database synchronization.
|
| For synchronizing LDAP users to your local applications database, use the provider:
|
| Adldap\Laravel\Auth\DatabaseUserProvider::class
|
| Otherwise, if you just require LDAP authentication, use the provider:
|
| Adldap\Laravel\Auth\NoDatabaseUserProvider::class
|
*/

'provider' => Adldap\Laravel\Auth\DatabaseUserProvider::class,

/*
|--------------------------------------------------------------------------
| Resolver
|--------------------------------------------------------------------------
|
| The resolver that locates users from your LDAP server.
|
| Custom resolvers must implement the following interface:
|
|   Adldap\Laravel\Auth\ResolverInterface
|
*/

'resolver' => Adldap\Laravel\Auth\Resolver::class,

/*
|--------------------------------------------------------------------------
| Importer
|--------------------------------------------------------------------------
|
| The importer that imports LDAP users into your local database.
|
| Custom importers must implement the following interface:
|
|   Adldap\Laravel\Auth\ImporterInterface
|
*/

'importer' => Adldap\Laravel\Auth\Importer::class,

/*
|--------------------------------------------------------------------------
| Rules
|--------------------------------------------------------------------------
|
| Rules allow you to control user authentication requests depending on scenarios.
|
| You can create your own rules and insert them here.
|
| All rules must extend from the following class:
|
|   Adldap\Laravel\Validation\Rules\Rule
|
*/

'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
|--------------------------------------------------------------------------
|
| Scopes allow you to restrict the LDAP query that locates
| users upon import and authentication.
|
| All scopes must implement the following interface:
|
|   Adldap\Laravel\Scopes\ScopeInterface
|
*/

'scopes' => [

    // Only allows users with a user principal name to authenticate.

    Adldap\Laravel\Scopes\UpnScope::class,

],

'usernames' => [

    /*
    |--------------------------------------------------------------------------
    | LDAP
    |--------------------------------------------------------------------------
    |
    | This is the LDAP users attribute that you use to authenticate
    | against your LDAP server. This is usually the users
    |'sAMAccountName' / 'userprincipalname' attribute.
    |
    | If you'd like to use their username to login instead, insert `samaccountname`.
    |
    */

    'ldap' => 'samaccountname',

    /*
    |--------------------------------------------------------------------------
    | Eloquent
    |--------------------------------------------------------------------------
    |
    | This is the attribute that is used for locating
    | and storing the LDAP username above.
    |
    | If you're using a `username` field instead, change this to `username`.
    |
    | This option is only applicable to the DatabaseUserProvider.
    |
    */

    'eloquent' => 'username',

],

/*
|--------------------------------------------------------------------------
| Login Fallback
|--------------------------------------------------------------------------
|
| The login fallback option allows you to login as a user located on the
| local database if active directory authentication fails.
|
| Set this to true if you would like to enable it.
|
| This option must be true or false and is only
| applicable to the DatabaseUserProvider.
|
*/

'login_fallback' => env('ADLDAP_LOGIN_FALLBACK', true),

/*
|--------------------------------------------------------------------------
| Password Sync
|--------------------------------------------------------------------------
|
| The password sync option allows you to automatically synchronize
| users AD passwords to your local database. These passwords are
| hashed natively by laravel using the bcrypt() method.
|
| Enabling this option would also allow users to login to their
| accounts using the password last used when an AD connection
| was present.
|
| If this option is disabled, the local user account is applied
| a random 16 character hashed password, and will lose access
| to this account upon loss of AD connectivity.
|
| This option must be true or false and is only applicable
| to the DatabaseUserProvider.
|
*/

'password_sync' => env('ADLDAP_PASSWORD_SYNC', true),

/*
|--------------------------------------------------------------------------
| Windows Auth Attribute
|--------------------------------------------------------------------------
|
| This array represents how a user is found when
| utilizing the Adldap Windows Auth Middleware.
|
| The key of the array represents the attribute that the user is located by.
|
|     For example, if 'samaccountname' is the key, then your LDAP server is
|     queried for a user with the 'samaccountname' equal to the value of
|     $_SERVER['AUTH_USER'].
|
|     If a user is found, they are imported (if using the DatabaseUserProvider)
|     into your local database, then logged in.
|
| The value of the array represents the 'key' of the $_SERVER
| array to pull the users username from.
|
|    For example, $_SERVER['AUTH_USER'].
|
| This must be an array with a key - value pair.
|
*/

'windows_auth_attribute' => ['samaccountname' => 'AUTH_USER'],

/*
|--------------------------------------------------------------------------
| Sync Attributes
|--------------------------------------------------------------------------
|
| Attributes specified here will be added / replaced on the user model
| upon login, automatically synchronizing and keeping the attributes
| up to date.
|
| The array key represents the Laravel model key, and the value
| represents the users LDAP attribute.
|
| This option must be an array and is only applicable
| to the DatabaseUserProvider.
|
*/

'sync_attributes' => [

    'username' => 'samaccountname',//userprincipalname
    'name' => 'cn',

],

];
`

I'm sure it's something small and stupid I am missing but... I can't stare at it any longer! Lol.

@stevebauman
Copy link
Member

Once a user logs in fine through AD and there is a user entry in the table for that user then using that login with any password allows login. It's like it's only verifying username exists... ?

I can definitely assure you this is not the case. Something else is going on here.

Have a couple questions:

  • Do you literally mean you can authenticate using any password? Even blank?
  • Can you post your login controller code?
  • Can you set follow_referrals to false and try again?

@2Foot
Copy link
Author

2Foot commented Mar 6, 2017

I tried setting the "follow_referrals" to false and it still does the same thing. If I leave the password field blank it complains about it, so that is working to properly. Here is the login controller...

LoginController.php

middleware('guest', ['except' => 'logout']); } public function username() { return 'username'; } }

@stevebauman
Copy link
Member

stevebauman commented Mar 6, 2017

Ok, in your routes file, can you try authenticating with the user and see if it just returns true?

// routes/web.php

$auth = Auth::attempt([
    'username' => 'my-username',
    'password' => 'random password',
]);

dd($auth); // What does this return?

Also, in your case above is true, this should also return true:

// routes/web.php

$auth = Adldap::auth()->attempt('my-username', 'random password');

dd($auth);

If the second case returns true, something is going on with your AD server.

@2Foot
Copy link
Author

2Foot commented Mar 6, 2017

The first method returned the following error regardless of proper credentials or not:

InvalidArgumentException in CreatesUserProviders.php line 40:
Authentication user provider [adldap] is not defined.

in CreatesUserProviders.php line 40
at AuthManager->createUserProvider('users') in AuthManager.php line 123
at AuthManager->createSessionDriver('web', array('driver' => 'session', 'provider' => 'users')) in AuthManager.php line 96
at AuthManager->resolve('web') in AuthManager.php line 70
at AuthManager->guard() in AuthManager.php line 294

The result of the second attempt is simply the message: false.

That being said, if I put the proper credentials in for the second method it does return true.

@stevebauman
Copy link
Member

stevebauman commented Mar 6, 2017

@2Foot, sorry I should have specified, insert the methods inside a route:

// routes/web.php

Route::get('/', function () {

    $auth = Auth::attempt([
        'username' => 'my-username',
        'password' => 'random password',
    ]);

    dd($auth); // What does this return?

});

You'll receive that exception if it isn't contained in a closure in your routes file, since the adldap auth provider hasn't been registered yet.

@2Foot
Copy link
Author

2Foot commented Mar 6, 2017

hahaha, do not apologize. That is MY fault. It's still too early in the day on a Monday where I am and my brain hasn't had enough caffeine yet! Ugh. Sorry.

So now the first method returns true with the WRONG credentials as well as the right ones.

@stevebauman
Copy link
Member

stevebauman commented Mar 6, 2017

Fixed! This was actually a major security issue so I'm extremely glad you brought this to my attention.

The series of events for this to occur:

  1. You're using the DatabaseUserProvider
  2. The LDAP user will have to have logged in once with a valid password for their model to exist
  3. Upon authentication, the Importer would update their existing models password in preparation
  4. Then, once LDAP authentication failed, and fallback is enabled, the EloquentUserProvider would be called with the users updated model (with their updated password from the importer)
  5. The Hash::check() would return true in the EloquentUserProvider due to their new hashed password being set on their model.

Please update to v3.0.2 for this patch, thanks!

@2Foot
Copy link
Author

2Foot commented Mar 6, 2017

No problem! Glad I could help. Thank you for the quick response.

I hope to be able to use SSO soon but my web server is CentOS and I need it to talk securely to AD through Kerberos. If you have any tips or resources please let me know! Communicating with AD is all new to me. I thought I had done everything properly but... you never know. Lol.

Thanks again.

@riskinovitasari
Copy link

then how about to call data (eg email) from ldap server to display into my system? For example I have a db employee which stores name data, username and password. Then I also have ldap server that stores data name, username, password, position. Then when i login, i will be authenticated in db employee and ldap server to check my username exists or not. Then, after I successfully login, then I will be able to see the data name, email, and position taken from ldap server which is displayed into my system web.

@stevebauman
Copy link
Member

If you insert the HasLdapUser trait onto your User.php model, then you'll be able to access all the users LDAP details from the authenticated Laravel user:

$user = Auth::user();

$user->ldap->getCommonName();
$user->ldap->getGroups();

// Etc.

Please refer to the documentation for this feature:

https://github.com/Adldap2/Adldap2-Laravel/blob/master/docs/auth/binding.md

@riskinovitasari
Copy link

where i should put this code ?

if (Auth::attempt($credentials)) {
    $user = Auth::user();
    
    var_dump($user); // Returns instance of App\User;
    
    var_dump($user->ldap); // Returns instance of Adldap\Models\User;
   
    // Examples:
    
    $user->ldap->getGroups();
    
    $user->ldap->getCommonName();
    
    $user->ldap->getConvertedSid();
}

@stevebauman
Copy link
Member

That is just example usage. This is located in your LoginController.php using the AuthenticatesUsers trait.

@riskinovitasari
Copy link

here's my code ;

user.php

<?php
namespace App;

use Adldap\Laravel\Traits\HasLdapUser;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable, HasLdapUser;

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

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

logincontroller.php

<?php
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
use AuthenticatesUsers;

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

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    public function username()
{
    return 'username';
}
public function login(Request $request)
{
    if (Auth::attempt($request->only(['username', 'password'])) {
        
        // Returns \App\User model configured in `config/auth.php`.
        $user = Auth::user();
        var_dump($user); // Returns instance of App\User;
        var_dump($user->ldap); // Returns instance of Adldap\Models\User;
        
        $user->ldap->getCommonName();
        $user->ldap->getGroups();
        
        return redirect()->to('home')
            ->withMessage('Logged in!');
    }
    
    return redirect()->to('login')
        ->withMessage('Hmm... Your username or password is incorrect');
}

please correct me if i'm wrong.

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

No branches or pull requests

3 participants