2018-12-06 14:05:43 -08:00
< ? php
declare ( strict_types = 1 );
2019-01-10 13:20:43 -08:00
namespace App\Services ;
2018-12-06 14:05:43 -08:00
2019-01-10 13:20:43 -08:00
use App\Models\Setting ;
2018-12-06 14:05:43 -08:00
use Exception ;
use Illuminate\Support\Collection ;
/**
* LDAP configuration merge for Adldap2 .
*
* @ see https :// github . com / Adldap2 / Adldap2
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
class LdapAdConfiguration
{
2021-06-10 13:15:52 -07:00
const LDAP_PORT = 389 ;
const CONNECTION_TIMEOUT = 5 ;
const DEFAULT_LDAP_VERSION = 3 ;
2020-02-04 12:47:49 -08:00
const LDAP_BOOLEAN_SETTINGS = [
'ldap_enabled' ,
'ldap_server_cert_ignore' ,
'ldap_tls' ,
'ldap_tls' ,
'ldap_pw_sync' ,
'is_ad' ,
'ad_append_domain' ,
];
2018-12-06 14:05:43 -08:00
/**
* Ldap Settings .
*
* @ var Collection
*/
public $ldapSettings ;
/**
* LDAP Config .
*
* @ var array
*/
public $ldapConfig ;
/**
2019-01-10 13:20:43 -08:00
* Initialize LDAP from user settings
*
* @ since 5.0 . 0
2018-12-06 14:05:43 -08:00
*/
2021-06-10 13:15:52 -07:00
public function init ()
{
2020-09-08 21:13:24 -07:00
// This try/catch is dumb, but is necessary to run initial migrations, since
// this service provider is booted even during migrations. :( - snipe
try {
$this -> ldapSettings = $this -> getSnipeItLdapSettings ();
if ( $this -> isLdapEnabled ()) {
$this -> setSnipeItConfig ();
}
} catch ( \Exception $e ) {
\Log :: debug ( $e );
$this -> ldapSettings = null ;
2018-12-11 21:01:11 -08:00
}
2018-12-06 14:05:43 -08:00
}
/**
* Merge the default Adlap config with the SnipeIT config .
2018-12-11 21:01:11 -08:00
*
2018-12-06 14:05:43 -08:00
* @ author Wes Hulette < jwhulette @ gmail . com >
2018-12-11 21:01:11 -08:00
*
2018-12-06 14:05:43 -08:00
* @ since 5.0 . 0
*/
private function setSnipeItConfig ()
{
$this -> ldapConfig = $this -> setLdapConnectionConfiguration ();
$this -> certificateCheck ();
}
/**
* Get the LDAP settings from the Settings model .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
2018-12-11 21:01:11 -08:00
*
2018-12-06 14:05:43 -08:00
* @ return \Illuminate\Support\Collection
*/
private function getSnipeItLdapSettings () : Collection
{
2020-09-08 21:13:24 -07:00
$ldapSettings = collect ();
2021-06-10 13:15:52 -07:00
if ( Setting :: first ()) { // during early migration steps, there may be no settings table entry to start with
2020-09-08 21:13:24 -07:00
$ldapSettings = Setting :: getLdapSettings ()
-> map ( function ( $item , $key ) {
// Trim the items
if ( is_string ( $item )) {
$item = trim ( $item );
}
// Get the boolean value of the LDAP setting, makes it easier to work with them
if ( in_array ( $key , self :: LDAP_BOOLEAN_SETTINGS )) {
return boolval ( $item );
2018-12-06 14:05:43 -08:00
}
2020-09-08 21:13:24 -07:00
// Decrypt the admin password
2021-06-10 13:15:52 -07:00
if ( 'ldap_pword' === $key && ! empty ( $item )) {
2020-09-08 21:13:24 -07:00
try {
return decrypt ( $item );
} catch ( Exception $e ) {
throw new Exception ( 'Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.' );
}
}
2018-12-11 21:01:11 -08:00
2020-09-08 21:13:24 -07:00
if ( $item && 'ldap_server' === $key ) {
return collect ( parse_url ( $item ));
}
2018-12-06 14:05:43 -08:00
2020-09-08 21:13:24 -07:00
return $item ;
});
}
2021-06-10 13:15:52 -07:00
2018-12-06 14:05:43 -08:00
return $ldapSettings ;
}
/**
* Set the server certificate environment variable .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function certificateCheck () : void
{
// If we are ignoring the SSL cert we need to setup the environment variable
// before we create the connection
if ( $this -> ldapSettings [ 'ldap_server_cert_ignore' ]) {
putenv ( 'LDAPTLS_REQCERT=never' );
}
// If the user specifies where CA Certs are, make sure to use them
if ( env ( 'LDAPTLS_CACERT' )) {
putenv ( 'LDAPTLS_CACERT=' . env ( 'LDAPTLS_CACERT' ));
}
}
/**
* Set the Adlap2 connection configuration values based on SnipeIT settings .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
2018-12-11 21:01:11 -08:00
*
2018-12-06 14:05:43 -08:00
* @ return array
*/
private function setLdapConnectionConfiguration () : array
{
// Create the configuration array.
2021-08-17 14:43:36 -07:00
$ldap_settings = [
2018-12-06 14:05:43 -08:00
// Mandatory Configuration Options
'hosts' => $this -> getServerUrlBase (),
'base_dn' => $this -> ldapSettings [ 'ldap_basedn' ],
'username' => $this -> ldapSettings [ 'ldap_uname' ],
'password' => $this -> ldapSettings [ 'ldap_pword' ],
// Optional Configuration Options
2020-08-14 14:45:05 -07:00
'schema' => $this -> getSchema (), // FIXME - we probably ought not to be using this, right?
2018-12-06 14:05:43 -08:00
'account_prefix' => '' ,
'account_suffix' => '' ,
'port' => $this -> getPort (),
'follow_referrals' => false ,
'use_ssl' => $this -> isSsl (),
'use_tls' => $this -> ldapSettings [ 'ldap_tls' ],
'version' => $this -> ldapSettings [ 'ldap_version' ] ? ? self :: DEFAULT_LDAP_VERSION ,
'timeout' => self :: CONNECTION_TIMEOUT ,
// Custom LDAP Options
'custom_options' => [
// See: http://php.net/ldap_set_option
// LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
],
];
2021-08-17 14:43:36 -07:00
if ( $this -> ldapSettings [ 'ldap_client_tls_cert' ] || $this -> ldapSettings [ 'ldap_client_tls_key' ]) {
$ldap_settings [ 'custom_options' ] = [
LDAP_OPT_X_TLS_CERTFILE => Setting :: get_client_side_cert_path (),
LDAP_OPT_X_TLS_KEYFILE => Setting :: get_client_side_key_path ()
];
}
return $ldap_settings ;
2018-12-06 14:05:43 -08:00
}
/**
* Get the schema to use for the connection .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ return string
*/
2020-08-14 14:45:05 -07:00
private function getSchema () : string //wait, what? This is a little weird, since we have completely separate variables for this; we probably shoulnd't be using any 'schema' at all
2018-12-06 14:05:43 -08:00
{
$schema = \Adldap\Schemas\OpenLDAP :: class ;
if ( $this -> ldapSettings [ 'is_ad' ]) {
$schema = \Adldap\Schemas\ActiveDirectory :: class ;
}
return $schema ;
}
/**
* Get the port number from the connection url .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ return int
*/
private function getPort () : int
{
2018-12-11 21:01:11 -08:00
$port = $this -> getLdapServerData ( 'port' );
if ( $port && is_int ( $port )) {
return $port ;
2018-12-06 14:05:43 -08:00
}
2021-06-10 13:15:52 -07:00
2018-12-06 14:05:43 -08:00
return self :: LDAP_PORT ;
}
/**
* Get ldap scheme from url to determin ssl use .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ return bool
*/
private function isSsl () : bool
{
2018-12-11 21:01:11 -08:00
$scheme = $this -> getLdapServerData ( 'scheme' );
if ( $scheme && 'ldaps' === strtolower ( $scheme )) {
2018-12-06 14:05:43 -08:00
return true ;
}
2021-06-10 13:15:52 -07:00
2018-12-06 14:05:43 -08:00
return false ;
}
/**
* Return the base url to the LDAP server .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ return array
*/
private function getServerUrlBase () : array
{
2020-10-06 18:31:06 -07:00
/* if ( $this -> ldapSettings [ 'is_ad' ]) {
2018-12-06 14:05:43 -08:00
return collect ( explode ( ',' , $this -> ldapSettings [ 'ad_domain' ])) -> map ( function ( $item ) {
return trim ( $item );
}) -> toArray ();
2020-10-06 18:31:06 -07:00
} */ // <- this was the *original* intent of the PR for AdLdap2, but we've been moving away from having
2021-06-10 13:15:52 -07:00
// two separate fields - one for "ldap_host" and one for "ad_domain" - towards just using "ldap_host"
// ad_domain for us just means "append this domain to your usernames for login, if you click that checkbox"
// that's all, nothing more (I hope).
2018-12-06 14:05:43 -08:00
2018-12-11 21:01:11 -08:00
$url = $this -> getLdapServerData ( 'host' );
2021-06-10 13:15:52 -07:00
2018-12-11 21:01:11 -08:00
return $url ? [ $url ] : [];
}
/**
* Get ldap enabled setting
*
* @ author Steffen Buehl < sb @ sbuehl . com >
*
* @ since 5.0 . 0
*
* @ return bool
*/
2019-01-10 13:20:43 -08:00
public function isLdapEnabled () : bool
2018-12-11 21:01:11 -08:00
{
return $this -> ldapSettings && $this -> ldapSettings -> get ( 'ldap_enabled' );
}
/**
* Get parsed ldap server information
*
* @ author Steffen Buehl < sb @ sbuehl . com >
*
* @ since 5.0 . 0
*
* @ param $key
* @ return mixed | null
*/
protected function getLdapServerData ( $key )
{
if ( $this -> ldapSettings ) {
$ldapServer = $this -> ldapSettings -> get ( 'ldap_server' );
if ( $ldapServer && $ldapServer instanceof Collection ) {
return $ldapServer -> get ( $key );
}
2018-12-11 13:07:14 -08:00
}
2018-12-06 14:05:43 -08:00
2018-12-11 21:01:11 -08:00
return null ;
2018-12-06 14:05:43 -08:00
}
}