Files
projectsend/app/Modules/Identity/Ldap/LdapSettings.php
T
veenone 9c43f9cb9a Let directory clients sign in with their username as well as their address
LDAP sign-in only took an email address. The LDAP settings now have an
optional username attribute (cn, uid, sAMAccountName and so on). Once it
is set, the login field also takes a username. The service account looks
the username up, and the login carries on with the address the directory
holds for it, through the same checks, single user bind, provisioning
and rate limiting as an email login.

Whether the input is an address is decided by the same email rule that
accepted every stored address, so an address such as someone@localhost
is never taken for a username. The username goes through the query
builder, so it is escaped, and it has to match exactly one entry. The
directory is client-only, so a username never signs in a staff account.
With the attribute left empty, nothing changes.

This ports feat/ldap_signin_by_username, which was written against v1
and has no history in common with this codebase.
2026-10-05 07:51:21 +07:00

96 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Modules\Identity\Ldap;
use Illuminate\Database\Eloquent\Model;
/**
* The single row describing this installation's directory.
*
* Shaped after MailProviderSettings, including the part that matters most:
* `bind_password` carries an `'encrypted'` cast, so a database dump does
* not hand over a service-account credential. v1 stored this one in plain
* text and then echoed it into the settings form's HTML `value=`.
*
* @property bool $active
* @property string|null $host
* @property int $port
* @property LdapEncryption $encryption
* @property string|null $ca_cert_path
* @property string|null $bind_dn
* @property string|null $bind_password
* @property string|null $base_dn
* @property string|null $user_filter
* @property string $email_attribute
* @property string $name_attribute
* @property string|null $username_attribute
* @property bool $auto_provision
* @property bool $auto_approve
*/
class LdapSettings extends Model
{
protected $table = 'ldap_settings';
protected $guarded = [];
/**
* Column defaults only apply on INSERT, so they never reach the unsaved
* instance `current()` hands back on a fresh install — these do.
*/
protected $attributes = [
'active' => false,
'port' => 389,
'encryption' => 'tls',
'email_attribute' => 'mail',
'name_attribute' => 'cn',
'auto_provision' => false,
'auto_approve' => false,
];
protected function casts(): array
{
return [
'active' => 'boolean',
'port' => 'integer',
'encryption' => LdapEncryption::class,
'bind_password' => 'encrypted',
'auto_provision' => 'boolean',
'auto_approve' => 'boolean',
];
}
public static function current(): self
{
return static::query()->firstOrNew([]);
}
/**
* Whether a login may consult the directory at all.
*
* The extension check is part of the answer rather than a separate
* question: an administrator can save settings on a server that cannot
* talk LDAP, and every login must then behave exactly as if the
* feature were switched off rather than throwing.
*/
public function usable(): bool
{
return $this->active
&& extension_loaded('ldap')
&& is_string($this->host) && $this->host !== ''
&& is_string($this->base_dn) && $this->base_dn !== '';
}
/**
* Whether people may sign in with a directory username as well as an
* address: only once an administrator has named the attribute that
* holds it.
*/
public function allowsUsernameSignIn(): bool
{
return $this->usable()
&& is_string($this->username_attribute) && $this->username_attribute !== '';
}
}