Skip to content

Commit e50a877

Browse files
authored
Merge pull request #1673 from IrAlfred/fix-ldap-related-issues
fix(backend): fix LDAP address book and authentication issues
2 parents 400cb6d + 835a8ed commit e50a877

File tree

12 files changed

+549
-60
lines changed

12 files changed

+549
-60
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,14 @@ LDAP_USER=''
199199
LDAP_PASS=''
200200
LDAP_OBJECT_CLASS="top,person,organizationalperson,inetorgperson"
201201
LDAP_READ_WRITE=true
202+
LDAP_UID_ATTR="uid"
202203

203204
#app.php
204205
LDAP_AUTH_PORT=389
205206
LDAP_AUTH_SERVER=localhost
206207
LDAP_AUTH_TLS=
207208
LDAP_AUTH_BASE_DN="example,dc=com"
208-
LDAP_UID_ATTR="cn"
209+
LDAP_AUTH_UID_ATTR="uid"
209210

210211
#WordPress
211212
WORDPRESS_CLIENT_ID=

config/app.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
/*
101101
| Attribute to use as the unique identifier for users
102102
*/
103-
'ldap_uid_attr' => env('LDAP_UID_ATTR', 'uid'),
103+
'ldap_auth_uid_attr' => env('LDAP_AUTH_UID_ATTR', 'uid'),
104104

105105
/*
106106
| -------------------

config/ldap.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/*
2323
| Flag to enable or disable TLS connections
2424
*/
25-
'enable_tls' => env('LDAP_ENABLE_TLS', true),
25+
'enable_tls' => env('LDAP_ENABLE_TLS', false),
2626

2727
/*
2828
| Port to connect to
@@ -44,6 +44,11 @@
4444
*/
4545
'auth' => env('LDAP_AUTH', false),
4646

47+
/*
48+
| Attribute to use for user identification
49+
*/
50+
'ldap_uid_attr' => env('LDAP_UID_ATTR', 'uid'),
51+
4752
/*
4853
| Global username and password to bind with if auth is set to true. If left
4954
| blank, users will have a setting on the Settings -> Site page for this

lib/auth.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,11 @@ private function connect_details() {
237237
$prefix = 'ldaps://';
238238
$server = $this->apply_config_value('server', 'localhost');
239239
$port = $this->apply_config_value('port', 389);
240-
if (empty($this->config['enable_tls'])) {
240+
if (
241+
empty($this->config['enable_tls']) ||
242+
$this->config['enable_tls'] === false ||
243+
strtolower($this->config['enable_tls']) === "false"
244+
) {
241245
$prefix = 'ldap://';
242246
}
243247
return $prefix.$server.':'.$port;
@@ -261,9 +265,9 @@ private function apply_config_value($name, $default) {
261265
public function check_credentials($user, $pass) {
262266
list($server, $port, $tls) = get_auth_config($this->site_config, 'ldap');
263267
$base_dn = $this->site_config->get('ldap_auth_base_dn', false);
264-
$uid_attr = $this->site_config->get('ldap_uid_attr', false);
268+
$uid_auth_attr = $this->site_config->get('ldap_auth_uid_attr', false);
265269
if ($server && $port && $base_dn) {
266-
$user = sprintf('%s=%s,%s', $uid_attr, $user, $base_dn);
270+
$user = sprintf('%s=%s,%s', $uid_auth_attr, $user, $base_dn);
267271
$this->config = [
268272
'server' => $server,
269273
'port' => $port,
@@ -302,7 +306,6 @@ public function connect() {
302306
*/
303307
protected function auth() {
304308
$result = @ldap_bind($this->fh, $this->config['user'], $this->config['pass']);
305-
ldap_unbind($this->fh);
306309
if (!$result) {
307310
Hm_Debug::add(sprintf('LDAP AUTH failed for %s', $this->config['user']));
308311
}

modules/contacts/js_modules/route_handlers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
function applyContactsPageHandlers() {
2-
$('.delete_contact').on("click", function() {
2+
$('.delete_contact:not([data-ldap-dn])').on("click", function() {
33
delete_contact($(this).data('id'), $(this).data('source'), $(this).data('type'));
44
return false;
55
});

modules/contacts/modules.php

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public function process() {
123123
if (array_key_exists('contact_id', $this->request->get)) {
124124
$contacts = $this->get('contact_store');
125125
$contact = $contacts->get($this->request->get['contact_id']);
126+
126127
if ($contact) {
127128
$to = sprintf('%s <%s>', $contact->value('display_name'), $contact->value('email_address'));
128129
$this->out('compose_draft', array('draft_to' => $to, 'draft_subject' => '', 'draft_body' => ''));
@@ -396,16 +397,34 @@ protected function output() {
396397
$this->html_safe($c->value('phone_number')).'</a></td>'.
397398
'<td class="text-end" style="width : 100px">';
398399
if (in_array($c->value('type').':'.$c->value('source'), $editable, true)) {
399-
$res .= '<a data-id="'.$this->html_safe($c->value('id')).'" data-type="'.$this->html_safe($c->value('type')).'" data-source="'.$this->html_safe($c->value('source')).
400-
'" class="delete_contact cursor-pointer" title="'.$this->trans('Delete').'"><i class="bi bi-trash3 text-danger ms-2"></i></a>'.
401-
'<a href="?page=contacts&amp;contact_id='.$this->html_safe($c->value('id')).'&amp;contact_source='.
400+
$delete_attrs = 'data-id="'.$this->html_safe($c->value('id')).'" data-type="'.$this->html_safe($c->value('type')).'" data-source="'.$this->html_safe($c->value('source')).'"';
401+
402+
if (class_exists('Hm_LDAP_Contact')) {
403+
$delete_attrs .= Hm_LDAP_Contact::generateDeleteAttributes($c, [$this, 'html_safe']);
404+
}
405+
406+
$edit_url = '?page=contacts&amp;contact_id='.$this->html_safe($c->value('id')).'&amp;contact_source='.
402407
$this->html_safe($c->value('source')).'&amp;contact_type='.
403-
$this->html_safe($c->value('type')).'&amp;contact_page='.$current_page.
404-
'" class="edit_contact cursor-pointer" title="'.$this->trans('Edit').'"><i class="bi bi-gear ms-2"></i></a>';
408+
$this->html_safe($c->value('type')).'&amp;contact_page='.$current_page;
409+
410+
if (class_exists('Hm_LDAP_Contact')) {
411+
$edit_url = Hm_LDAP_Contact::addDNToUrl($c, $edit_url);
412+
}
413+
414+
$res .= '<a '.$delete_attrs.' class="delete_contact cursor-pointer" title="'.$this->trans('Delete').'"><i class="bi bi-trash3 text-danger ms-2"></i></a>'.
415+
'<a href="'.$edit_url.'" class="edit_contact cursor-pointer" title="'.$this->trans('Edit').'"><i class="bi bi-pencil-square ms-2"></i></a>';
405416
}
406-
$res .= '<a href="?page=compose&amp;contact_id='.$this->html_safe($c->value('id')).
407-
'" class="send_to_contact cursor-pointer" title="'.$this->trans('Send To').'">'.
408-
'<i class="bi bi-file-earmark-text ms-2"></i></a>';
417+
418+
$send_to_url = '?page=compose&amp;contact_id='.$this->html_safe($c->value('id')).
419+
'&amp;contact_source='.$this->html_safe($c->value('source')).
420+
'&amp;contact_type='.$this->html_safe($c->value('type'));
421+
422+
if (class_exists('Hm_LDAP_Contact')) {
423+
$send_to_url = Hm_LDAP_Contact::addDNToUrl($c, $send_to_url);
424+
}
425+
426+
$res .= '<a href="'.$send_to_url.'" class="send_to_contact cursor-pointer" title="'.$this->trans('Send To').'">'.
427+
'<i class="bi bi-envelope-arrow-up ms-2"></i></a>';
409428

410429
$res .= '</td></tr>';
411430
$res .= '<tr><td id="contact_'.$this->html_safe($c->value('id')).'_detail" class="contact_detail_row" colspan="6">';
@@ -607,6 +626,7 @@ function build_contact_detail($output_mod, $contact, $id) {
607626
$all_fields = false;
608627
$contacts = $contact->export();
609628
ksort($contacts);
629+
610630
foreach ($contacts as $name => $val) {
611631
if ($name == 'all_fields') {
612632
$all_fields = $val;
@@ -624,7 +644,7 @@ function build_contact_detail($output_mod, $contact, $id) {
624644
if ($all_fields) {
625645
ksort($all_fields);
626646
foreach ($all_fields as $name => $val) {
627-
if (in_array($name, array(0, 'raw', 'objectclass', 'dn', 'ID', 'APP:EDITED', 'UPDATED'), true)) {
647+
if (in_array($name, array(0, 'raw', 'objectclass', 'ID', 'APP:EDITED', 'UPDATED'), true)) {
628648
continue;
629649
}
630650
$res .= '<tr><th>'.$output_mod->trans(name_map($name)).'</th>';
@@ -681,7 +701,8 @@ function name_map($val) {
681701
'fn' => 'Full Name',
682702
'uid' => 'Uid',
683703
'src_url' => 'URL',
684-
'adr' => 'Address'
704+
'adr' => 'Address',
705+
'dn' => 'Distinguished Name'
685706
);
686707
if (array_key_exists($val, $names)) {
687708
return $names[$val];

modules/contacts/setup.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,16 @@
7979
'contact_auto_collect' => FILTER_VALIDATE_BOOLEAN,
8080
'enable_warn_contacts_cc_not_exist_in_list_contact' => FILTER_VALIDATE_INT,
8181
'enable_collect_address_on_send' => FILTER_VALIDATE_INT,
82-
'email_address' => FILTER_UNSAFE_RAW
82+
'email_address' => FILTER_UNSAFE_RAW,
83+
'ldap_dn' => FILTER_UNSAFE_RAW,
8384
),
8485
'allowed_get' => array(
8586
'contact_id' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
8687
'contact_page' => FILTER_VALIDATE_INT,
8788
'contact_type' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
8889
'contact_source' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
8990
'import_contact' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
91+
'dn' => FILTER_UNSAFE_RAW,
9092
),
9193
'allowed_output' => array(
9294
'contact_deleted' => array(FILTER_VALIDATE_INT, false),

modules/contacts/site.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ var delete_contact = function(id, source, type) {
44
if (!hm_delete_prompt()) {
55
return false;
66
}
7-
Hm_Ajax.request(
8-
[{'name': 'hm_ajax_hook', 'value': 'ajax_delete_contact'},
7+
var request_data = [
8+
{'name': 'hm_ajax_hook', 'value': 'ajax_delete_contact'},
99
{'name': 'contact_id', 'value': id},
1010
{'name': 'contact_type', 'value': type},
11-
{'name': 'contact_source', 'value': source}],
11+
{'name': 'contact_source', 'value': source}
12+
];
13+
14+
Hm_Ajax.request(
15+
request_data,
1216
function(res) {
1317
if (res.contact_deleted && res.contact_deleted === 1) {
1418
$('.contact_row_'+id).remove();

modules/ldap_contacts/hm-ldap-contacts.php

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ protected function auth() {
4242
if (array_key_exists('auth', $this->config) && $this->config['auth'] &&
4343
array_key_exists('user', $this->config) && $this->config['user'] &&
4444
array_key_exists('pass', $this->config) && $this->config['pass']) {
45-
46-
return @ldap_bind($this->fh, $this->config['user'], $this->config['pass']);
45+
$uid_attr = $this->config['ldap_uid_attr'];
46+
$user_dn = sprintf('%s=%s,%s', $uid_attr, $this->config['user'], $this->config['base_dn']);
47+
return @ldap_bind($this->fh, $user_dn, $this->config['pass']);
4748
}
4849
else {
4950
return @ldap_bind($this->fh);
@@ -103,3 +104,83 @@ public function fetch() {
103104
return array();
104105
}
105106
}
107+
108+
/**
109+
* @subpackage ldap_contacts/lib
110+
*/
111+
class Hm_LDAP_Contact extends Hm_Contact {
112+
113+
public function getDN() {
114+
$all_fields = $this->value('all_fields');
115+
if ($all_fields && isset($all_fields['dn'])) {
116+
return $all_fields['dn'];
117+
}
118+
return null;
119+
}
120+
121+
public static function findByDN($contact_store, $target_dn, $contact_source) {
122+
$all_contacts = $contact_store->dump();
123+
124+
foreach ($all_contacts as $contact_id => $contact_obj) {
125+
if ($contact_obj->value('source') == $contact_source &&
126+
$contact_obj->value('type') == 'ldap') {
127+
128+
$all_fields = $contact_obj->value('all_fields');
129+
130+
if (isset($all_fields['dn']) && $all_fields['dn'] === $target_dn) {
131+
return $contact_obj;
132+
}
133+
}
134+
}
135+
return null;
136+
}
137+
138+
public static function isLdapContact($contact) {
139+
return $contact instanceof self || $contact->value('type') === 'ldap';
140+
}
141+
142+
public static function decodeDN($encoded_dn) {
143+
return urldecode($encoded_dn);
144+
}
145+
146+
public static function generateDeleteAttributes($contact, $html_safe) {
147+
if (!self::isLdapContact($contact)) {
148+
return '';
149+
}
150+
151+
$all_fields = $contact->value('all_fields');
152+
if ($all_fields && isset($all_fields['dn'])) {
153+
return ' data-ldap-dn="'.$html_safe($all_fields['dn']).'"';
154+
}
155+
156+
return '';
157+
}
158+
159+
public static function addDNToUrl($contact, $base_url) {
160+
if (!self::isLdapContact($contact)) {
161+
return $base_url;
162+
}
163+
164+
$all_fields = $contact->value('all_fields');
165+
if ($all_fields && isset($all_fields['dn'])) {
166+
return $base_url . '&amp;dn='.urlencode($all_fields['dn']);
167+
}
168+
169+
return $base_url;
170+
}
171+
172+
public static function fromContact($contact) {
173+
if ($contact->value('type') !== 'ldap') {
174+
return null;
175+
}
176+
177+
if ($contact instanceof self) {
178+
return $contact;
179+
}
180+
181+
$contact_data = $contact->export();
182+
$ldap_contact = new self($contact_data);
183+
184+
return $ldap_contact;
185+
}
186+
}

0 commit comments

Comments
 (0)