Open devilwacause opened 4 years ago
I've got the system checking both fields now both when AD is Connected & when AD is disconnected.
However when disconnected, it still only logs in via the email. (It is checking the alias (ie - emailMask in my db).
Here's the code I added to get this far.
<div class="input-group">
<div class='input-group-prepend'>
<div class='input-group-text' style='width: 95px;'>
{{ __('E-Mail') }}
</div>
</div>
<input id="login" type="text" c class="form-control{{ $errors->has('emailMask') || $errors->has('email') ? ' is-invalid' : '' }}" name="login" value="{{ old('emailMask') ?: old('email') }}" required autofocus>
@if ($errors->has('emailMask') || $errors->has('email'))
<span class="invalid-feedback">
<strong>{{ $errors->first('emailMask') ?: $errors->first('email') }}</strong>
</span>
@endif
</div>
protected $username;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->username = $this->findUsername();
}
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function findUsername()
{
$login = request()->input('login');
$fieldType = 'email';
if(User::where('email', $login)->first() != null) {
$fieldType = 'email';
}else if (User::where('emailMask', $login)->first() != null) {
$fieldType = 'emailMask';
}
request()->merge([$fieldType => $login]);
return $fieldType;
}
/**
* Get username property.
*
* @return string
*/
public function username()
{
return $this->username;
}
And the returned error when using the alias email account.
Hi @devilwacause,
Login fallback will only work if:
password_sync
was enabled at the time of the user logging in lastThis is because Adldap2 will utilize the EloquentUserProvider to fall back if the LDAP connection cannot be contacted, and will attempt to authenticate the user with their credentials that are stored in the database. If password_sync
was not enabled, then their password has not been saved into the database - it has been set to a random 16 character hashed string.
I hope this answers your question!
Found a solution to this issue.
By rewriting bits of /adldap2/adldap2-laravel/src/Resolvers/UserResolver.php && /adldap2/adldap2-laravel/src/Resolbers/ResolverInterface.php
I'm able to use an array in the ldap_auth.php file for the username_column and the locate_users_by configurations.
Below are the changes I made. If this benefits anybody else I'll gladly push up a branch with changes.
/**
* {@inheritdoc}
*/
public function byCredentials(array $credentials = [])
{
//dd($credentials);
if (empty($credentials)) {
return;
}
$NoDatabaseUser = false;
$attribute = null;
$attrIsArray = false;
$LdapDAttrArray = false;
$key = null;
if($this->getAppAuthProvider() instanceof NoDatabaseUserProvider) {
$NoDatabaseUser = true;
}
if($NoDatabaseUser) {
if(is_array(Config::get('ldap_auth.identifiers.ldap.locate_users_by', 'false'))) {
$attribute = $this->getLdapDiscoveryAttributes();
$attrIsArray = true;
$LdapDAttrArray = true;
}else{
$attribute = $this->getLdapDiscoveryAttribute();
}
}else{
if(is_array(Config::get('ldap_auth.identifiers.ldap.locate_users_by', 'false'))) {
$LdapDAttrArray = true;
}
if(is_array(Config::get('ldap_auth.identifiers.database.username_column', 'false'))) {
$attribute = $this->getDatabaseUsernameColumns();
$attrIsArray = true;
}else{
$attribute = $this->getDatabaseUsernameColumn();
}
}
if($attrIsArray) {
$keyexists = false;
foreach($attribute as $attr) {
if(array_key_exists($attr, $credentials)){
$key = $attr;
$keyexists = true;
}
}
if (!$keyexists) {
throw new RuntimeException(
"The '$attribute' key is missing from the given credentials array."
);
}
}else{
if (! array_key_exists($attribute, $credentials)) {
throw new RuntimeException(
"The '$attribute' key is missing from the given credentials array."
);
}else{
$key = $attribute;
}
}
if($LdapDAttrArray) {
return $this->query()->whereEquals(
$this->getLdapDiscoveryAttributes(),
$credentials[$key]
)->first();
}else{
return $this->query()->whereEquals(
$this->getLdapDiscoveryAttribute(),
$credentials[$key]
)->first();
}
}
/**
* {@inheritdoc}
*/
public function getLdapDiscoveryAttributes() : array
{
$attributes = Config::get('ldap_auth.identifiers.ldap.locate_users_by', ['userprincipalname']);
return $attributes;
}
/**
* {@inheritdoc}
*/
public function getDatabaseUsernameColumns() : array
{
$columns = Config::get('ldap_auth.identifiers.database.username_column', ['email']);
return $columns;
}
/**
* Retrieves the configured LDAP users username attribute.
*
* @return string
*/
public function getLdapDiscoveryAttributes() : array;
/**
* Retrieves the configured database username attributes.
*
* @return string
*/
public function getDatabaseUsernameColumns() : array;
a new example of my ldap_auth.php file
<?php
return [
/*
|--------------------------------------------------------------------------
| Connection
|--------------------------------------------------------------------------
|
| The LDAP connection to use for Laravel authentication.
|
| You must specify connections in your `config/ldap.php` configuration file.
|
*/
'connection' => env('LDAP_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,
/*
|--------------------------------------------------------------------------
| Model
|--------------------------------------------------------------------------
|
| The model to utilize for authentication and importing.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'model' => App\User::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,
App\LDAP\Rules\GetUsers::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.
// 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,
],
*/
'identifiers' => [
/*
|--------------------------------------------------------------------------
| LDAP
|--------------------------------------------------------------------------
|
| Locate Users By:
|
| This value is the users attribute you would like to locate LDAP
| users by in your directory.
|
| For example, using the default configuration below, if you're
| authenticating users with an email address, your LDAP server
| will be queried for a user with the a `userprincipalname`
| equal to the entered email address.
|
| Bind Users By:
|
| This value is the users attribute you would
| like to use to bind to your LDAP server.
|
| For example, when a user is located by the above attribute,
| the users attribute you specify below will be used as
| the 'username' to bind to your LDAP server.
|
| This is usually their distinguished name.
|
*/
'ldap' => [
'locate_users_by' => ['userprincipalname','mail'],
'bind_users_by' => 'distinguishedname',
],
'database' => [
/*
|--------------------------------------------------------------------------
| GUID Column
|--------------------------------------------------------------------------
|
| The value of this option is the database column that will contain the
| LDAP users global identifier. This column does not need to be added
| to the sync attributes below. It is synchronized automatically.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'guid_column' => 'objectguid',
/*
|--------------------------------------------------------------------------
| Username Column
|--------------------------------------------------------------------------
|
| The value of this option is the database column that contains your
| users login username.
|
| This column must be added to your sync attributes below to be
| properly synchronized.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'username_column' => ['email','emailMask'],
],
/*
|--------------------------------------------------------------------------
| Windows Authentication Middleware (SSO)
|--------------------------------------------------------------------------
|
| Local Users By:
|
| This value is the users attribute you would like to locate LDAP
| users by in your directory.
|
| For example, if 'samaccountname' is the value, 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.
|
| Server Key:
|
| This value represents the 'key' of the $_SERVER
| array to pull the users account name from.
|
| For example, $_SERVER['AUTH_USER'].
|
*/
'windows' => [
'locate_users_by' => 'samaccountname',
'server_key' => 'AUTH_USER',
],
],
'passwords' => [
/*
|--------------------------------------------------------------------------
| Password Sync
|--------------------------------------------------------------------------
|
| The password sync option allows you to automatically synchronize users
| LDAP passwords to your local database. These passwords are hashed
| natively by Laravel using the Hash::make() method.
|
| Enabling this option would also allow users to login to their accounts
| using the password last used when an LDAP connection was present.
|
| If this option is disabled, the local database account is applied a
| random 16 character hashed password upon first login, and will
| lose access to this account upon loss of LDAP connectivity.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'sync' => env('LDAP_PASSWORD_SYNC', true),
/*
|--------------------------------------------------------------------------
| Column
|--------------------------------------------------------------------------
|
| This is the column of your users database table
| that is used to store passwords.
|
| Set this to `null` if you do not have a password column.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'column' => 'password',
],
/*
|--------------------------------------------------------------------------
| 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 is only applicable to the DatabaseUserProvider.
|
*/
'login_fallback' => env('LDAP_LOGIN_FALLBACK', true),
/*
|--------------------------------------------------------------------------
| 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 users Laravel model key, and
| the value represents the users LDAP attribute.
|
| You **must** include the users login attribute here.
|
| This option is only applicable to the DatabaseUserProvider.
|
*/
'sync_attributes' => [
'email' => 'userprincipalname',
'emailMask' => 'mail',
'username' => 'mailnickname',
'name' => 'cn',
'orgUnit' => 'distinguishedname',
],
/*
|--------------------------------------------------------------------------
| Logging
|--------------------------------------------------------------------------
|
| User authentication attempts will be logged using Laravel's
| default logger if this setting is enabled.
|
| No credentials are logged, only usernames.
|
| This is usually stored in the '/storage/logs' directory
| in the root of your application.
|
| This option is useful for debugging as well as auditing.
|
| You can freely remove any events you would not like to log below,
| as well as use your own listeners if you would prefer.
|
*/
'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,
],
],
];
To explain - this allowed me to key off of TWO different fields both on the AD side and (if AD is down) the Fall back.
What I was aiming to do was this. Active Directory Attribute <-> Local DB Column userprincipalname <-> email mail <-> emailMask
Example data: User is John Doe. userprincipalname / email = jdoe@company.com mail / mailMask = John.Doe@company.com
I needed the users to be able to login whether they entered their email as stored in the userprincipalname / email OR mail / mailMask
this was because not all of the users have a mail / mailMask value assigned to their AD info, but the userprincipalname / email is ALWAYS there.
However, it is more common in the company to use the mail / mailMask version for anything on our intranet / file servers etc. The only time we typically used the userprincipal name is logging into actual devices (workstations/phones/etc).
I'd be glad to go over anything with you @stevebauman
Laravel Version: 6.10 Adldap2-Laravel Version: 6.0 PHP Version: 7.2 LDAP Type: AD
Description:
I'm rewriting our local intranet to take advantage of AD. Our current intranet site requires the users to be manually added to the system. This is where my issue arises.
Most (almost all) users TRUE email accounts are jdoe@company.com. However in general, most users dont realize this and are using alias emails (ie - John.Doe@company.com)
I've successfully pulled down both the userprincipalname - jdoe@company.com And the alias to my database, both as an import and on login (no import done).
When LDAP is connected, I can validate against the alias, and everything logs in perfectly. When LDAP is NOT connected (given this would be a rarity, but I need the fall back) -> using the users table in Laravel as the source of Authentication fails everytime.
I've tried modifying the username function in my LoginController but it doesnt seem to work. Additionally I really need to be able to go "if alias == null { return email }"
Any pointers on where I could start with this on ADLDAP?
Steps To Reproduce:
.env
LDAP_USERNAME="ldap_user" LDAP_PASSWORD="ldap_user_password" LDAP_ACCOUNT_PREFIX='' LDAP_ACCOUNT_SUFFIX='' LDAP_PORT=389 LDAP_BASE_DN="dc=company,dc=corp" LDAP_HOSTS="ldap_ip_addy" LDAP_USE_SSL=false LDAP_USE_TLS=false
ldap_auth.php
`<?php
return [
];`
Let me know if you need anything else that might help you in pointing me in the correct direction. I'm relatively new (6 mo) to laravel so havent gotten all the ins and outs of it in step yet.
Thanks in Advance.