diff --git a/.gitignore b/.gitignore index 9771de971b..37e9d3f68c 100755 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ _ide_helper.php .phpstorm.meta.php _ide_helper_models.php /.phplint-cache +storage/ldap_client_tls.cert +storage/ldap_client_tls.key diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 02a0f2c47c..090883b502 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -947,10 +947,14 @@ class SettingsController extends Controller $setting->ldap_jobtitle = $request->input('ldap_jobtitle'); $setting->ldap_country = $request->input('ldap_country'); $setting->ldap_dept = $request->input('ldap_dept'); + $setting->ldap_client_tls_cert = $request->input('ldap_client_tls_cert'); + $setting->ldap_client_tls_key = $request->input('ldap_client_tls_key'); + } if ($setting->save()) { + $setting->update_client_side_cert_files(); return redirect()->route('settings.ldap.index') ->with('success', trans('admin/settings/message.update.success')); } diff --git a/app/Models/Ldap.php b/app/Models/Ldap.php index f06da90581..619f083b06 100644 --- a/app/Models/Ldap.php +++ b/app/Models/Ldap.php @@ -27,7 +27,6 @@ class Ldap extends Model $ldap_server_cert_ignore = Setting::getSettings()->ldap_server_cert_ignore; $ldap_use_tls = Setting::getSettings()->ldap_tls; - // If we are ignoring the SSL cert we need to setup the environment variable // before we create the connection if ($ldap_server_cert_ignore=='1') { @@ -50,10 +49,16 @@ class Ldap extends Model ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version); ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20); + if (Setting::getSettings()->ldap_client_tls_cert && Setting::getSettings()->ldap_client_tls_key) { + ldap_set_option($connection, LDAP_OPT_X_TLS_CERTFILE, Setting::get_client_side_cert_path()); + ldap_set_option($connection, LDAP_OPT_X_TLS_KEYFILE, Setting::get_client_side_key_path()); + } + if ($ldap_use_tls=='1') { ldap_start_tls($connection); } + return $connection; } diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 150e290c7c..e32ffa64b0 100755 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -342,8 +342,56 @@ class Setting extends Model 'is_ad', 'ad_domain', 'ad_append_domain', + 'ldap_client_tls_key', + 'ldap_client_tls_cert' ])->first()->getAttributes(); return collect($ldapSettings); } + + /** + * Return the filename for the client-side SSL cert + * + * @var string + */ + public static function get_client_side_cert_path() + { + return storage_path().'/ldap_client_tls.cert'; + } + + /** + * Return the filename for the client-side SSL key + * + * @var string + */ + public static function get_client_side_key_path() + { + return storage_path().'/ldap_client_tls.key'; + } + + public function update_client_side_cert_files() + { + /** + * I'm not sure if it makes sense to have a cert but no key + * nor vice versa, but for now I'm just leaving it like this. + * + * Also, we could easily set this up with an event handler and + * self::saved() or something like that but there's literally only + * one place where we will do that, so I'll just explicitly call + * this method at that spot instead. It'll be easier to debug and understand. + */ + if($this->ldap_client_tls_cert) { + file_put_contents(self::get_client_side_cert_path(), $this->ldap_client_tls_cert); + } else { + unlink(self::get_client_side_cert_path()); + } + + if($this->ldap_client_tls_key) { + file_put_contents(self::get_client_side_key_path(), $this->ldap_client_tls_key); + } else { + unlink(self::get_client_side_key_path()); + } + } + + } diff --git a/app/Services/LdapAdConfiguration.php b/app/Services/LdapAdConfiguration.php index e9b7e96d42..7a30092b0e 100644 --- a/app/Services/LdapAdConfiguration.php +++ b/app/Services/LdapAdConfiguration.php @@ -156,7 +156,7 @@ class LdapAdConfiguration private function setLdapConnectionConfiguration(): array { // Create the configuration array. - return [ + $ldap_settings = [ // Mandatory Configuration Options 'hosts' => $this->getServerUrlBase(), 'base_dn' => $this->ldapSettings['ldap_basedn'], @@ -180,6 +180,14 @@ class LdapAdConfiguration // LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD, ], ]; + + 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; } /** diff --git a/database/migrations/2021_07_28_031345_add_client_side_l_d_a_p_cert_to_settings.php b/database/migrations/2021_07_28_031345_add_client_side_l_d_a_p_cert_to_settings.php new file mode 100644 index 0000000000..8dae5eb724 --- /dev/null +++ b/database/migrations/2021_07_28_031345_add_client_side_l_d_a_p_cert_to_settings.php @@ -0,0 +1,32 @@ +text('ldap_client_tls_cert')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn('ldap_client_tls_cert'); + }); + } +} diff --git a/database/migrations/2021_07_28_040554_add_client_side_l_d_a_p_key_to_settings.php b/database/migrations/2021_07_28_040554_add_client_side_l_d_a_p_key_to_settings.php new file mode 100644 index 0000000000..46feeea9f0 --- /dev/null +++ b/database/migrations/2021_07_28_040554_add_client_side_l_d_a_p_key_to_settings.php @@ -0,0 +1,32 @@ +text("ldap_client_tls_key")->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + $table->dropColumn("ldap_client_tls_key"); + }); + } +} diff --git a/database/migrations/2021_08_24_124354_make_ldap_client_certs_nullable.php b/database/migrations/2021_08_24_124354_make_ldap_client_certs_nullable.php new file mode 100644 index 0000000000..64cef2f243 --- /dev/null +++ b/database/migrations/2021_08_24_124354_make_ldap_client_certs_nullable.php @@ -0,0 +1,34 @@ +text('ldap_client_tls_cert')->nullable()->change(); + $table->text('ldap_client_tls_key')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function (Blueprint $table) { + // + }); + } +} diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php index e15162210e..fe3341eb3f 100644 --- a/resources/lang/en/admin/settings/general.php +++ b/resources/lang/en/admin/settings/general.php @@ -61,6 +61,9 @@ return array( 'label_logo' => 'Label Logo', 'label_logo_size' => 'Square logos look best - will be displayed in the top right of each asset label. ', 'laravel' => 'Laravel Version', + 'ldap_client_tls_cert' => 'LDAP Client-Side TLS Certificate', + 'ldap_client_tls_cert_help' => 'Client-Side TLS Certificate and Key for LDAP connections are usually only useful in Google Workspace configurations with "Secure LDAP." Both are required.', + 'ldap_client_tls_key' => 'LDAP Client-Side TLS key', 'ldap_enabled' => 'LDAP enabled', 'ldap_integration' => 'LDAP Integration', 'ldap_settings' => 'LDAP Settings', diff --git a/resources/views/models/custom_fields_form.blade.php b/resources/views/models/custom_fields_form.blade.php index bb4d479fe2..f70d8fba2a 100644 --- a/resources/views/models/custom_fields_form.blade.php +++ b/resources/views/models/custom_fields_form.blade.php @@ -9,7 +9,7 @@ @if ($field->element=='listbox') {{ Form::select($field->db_column_name(), $field->formatFieldValuesAsArray(), - Request::old($field->db_column_name(),(isset($item) ? \App\Helpers\Helper::gracefulDecrypt($field, $item->{$field->db_column_name()}) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} + Request::old($field->db_column_name(),(isset($item) ? \App\Helpers\Helper::gracefulDecrypt($field, htmlspecialchars($item->{$field->db_column_name()}, ENT_QUOTES)) : $field->defaultValue($model->id))), ['class'=>'format select2 form-control']) }} @elseif ($field->element=='textarea') diff --git a/resources/views/settings/ldap.blade.php b/resources/views/settings/ldap.blade.php index 61d07a6adf..8abb657e87 100644 --- a/resources/views/settings/ldap.blade.php +++ b/resources/views/settings/ldap.blade.php @@ -138,6 +138,36 @@ + +
{{ trans('general.feature_disabled') }}
+ @endif +{{ trans('admin/settings/general.ldap_client_tls_cert_help') }}
+ {!! $errors->first('ldap_client_tls_cert', '') !!} + @if (config('app.lock_passwords')===true) +{{ trans('general.feature_disabled') }}
+ @endif +