Page tree

Michael O'Brien

Skip to end of metadata
Go to start of metadata

OpenLDAP Docker Container


Repos fork/modify of  see code in

openldap installation

curl | sh 
sudo usermod -aG docker amdocs
amdocs@obriensystemsu0:~$ sudo docker run -p 389:389 -p 636:636 --name ldap-service --hostname ldap-service --detach osixia/openldap:1.2.4
amdocs@obriensystemsu0:~$ sudo docker exec ldap-service ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin

# extended LDIF
# LDAPv3
# base <dc=example,dc=org> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
dn: dc=example,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example Inc.
dc: example
# admin,
dn: cn=admin,dc=example,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
userPassword:: e1NTSEF9RjM5amZtWVRwTmJ0Q2VUVlA3RVg2aWtHc2dHeS9ESGc=
# search result
search: 2 result: 0 Success
# numResponses: 3 numEntries: 2

Setup Admin image - phpldapadmin

docker run --name phpldapadmin-service --hostname phpldapadmin-service --link ldap-service:ldap-host --env PHPLDAPADMIN_LDAP_HOSTS=ldap-host --detach osixia/phpldapadmin:0.8.0
amdocs@obriensystemsu0:~$ sudo docker ps -a
CONTAINER ID        IMAGE                       COMMAND                 CREATED             STATUS              PORTS                                        NAMES
1b83d81c3cd0        osixia/phpldapadmin:0.8.0   "/container/tool/run"   9 seconds ago       Up 8 seconds        80/tcp, 443/tcp                              phpldapadmin-service
b4d5c727a2a5        osixia/openldap:1.2.4       "/container/tool/run"   2 minutes ago       Up 2 minutes>389/tcp,>636/tcp   ldap-service

amdocs@obriensystemsu0:~$ sudo docker inspect -f "{{ .NetworkSettings.IPAddress }}" phpldapadmin-service


sudo docker exec -it ldap-service bash
root@ldap-service:/# ldapsearch -x -H ldap://localhost -b "dc=example,dc=org" -D "cn=admin,dc=example,dc=org" -s sub "(objectclass=*)" -w admin

Create Container or OU

Create 2nd Admin User

# admin ldif export
# LDIF Export for cn=admin,dc=example,dc=org
# Server: ldap-host (ldap-host)
# Search Scope: base
# Search Filter: (objectClass=*)
# Total Entries: 1
# Generated by phpLDAPadmin ( on June 18, 2019 3:42 pm
# Version: 1.2.4
version: 1
# Entry 1: cn=admin,dc=example,dc=org
dn: cn=admin,dc=example,dc=org
cn: admin
description: LDAP administrator
objectclass: simpleSecurityObject
objectclass: organizationalRole
userpassword: {SSHA}HvaAeza5iELgw91g8b4fIP1X6kWjGGGf

# admin2 ldif import

not working yet
version: 1

# Entry 1: cn=admin2,dc=example,dc=org
dn: cn=admin2,dc=example,dc=org
cn: admin2
creatorsname: cn=admin2,dc=example,dc=org
description: LDAP administrator2
entrydn: cn=admin2,dc=example,dc=org
hassubordinates: FALSE
modifiersname: cn=admin2,dc=example,dc=org
objectclass: simpleSecurityObject
objectclass: organizationalRole
structuralobjectclass: organizationalRole
subschemasubentry: cn=Subschema
userpassword: {SSHA}HvaAeza5iELgw91g8b4fIP1X6kWjGGGf

Modify phpldapadmin config


Modify phpldapadmin template

sudo docker exec -it phpldapadmin-service bash

Standing up a Phpldapadmin instance without Docker  

XAMPP 7.3 comes with PHP 7.2 - the versions are aligned - so for some code that only works in php 7.2 - use XAMPP 7.2

This article is for PHP 7.3.6

Phpldapadmin is an excellent front end to openldap.  In the rare case where you do not have access to either a kubernetes cluster or raw docker on a VM/BM instance - try running PhpLdapAdmin natively on a php+apache stack.

I have a lot of new found respect for the PHP stack - the turnaround time on editing the code or php library itself lends itself to very fast development. 

Installation - PHP + Apache2

with docker

without docker - I installed 7.3.6-2 (a week later we are at xampp-windows-x64-7.3.6-3-VC15-installer.exe) - get a specific version from

XAMPP Installation

Run the executable as administrator

deselect everything except apache and php

choose the default folder c:/xampp

XAMPP Installation Logs

5:58:23 PM  [main] 	Initializing Control Panel
5:58:23 PM  [main] 	Windows Version: Windows Server 2012 R2  64-bit
5:58:23 PM  [main] 	XAMPP Version: 7.3.6
5:58:23 PM  [main] 	Control Panel Version: 3.2.4  [ Compiled: Jun 5th 2019 ]
5:58:23 PM  [main] 	Running with Administrator rights - good!
5:58:23 PM  [main] 	XAMPP Installation Directory: "c:\xampp\"
5:58:23 PM  [main] 	Checking for prerequisites
5:58:25 PM  [main] 	All prerequisites found
5:58:25 PM  [main] 	Initializing Modules
5:58:25 PM  [main] 	The MySQL module is disabled
5:58:25 PM  [main] 	The FileZilla module is disabled
5:58:25 PM  [main] 	The Mercury module is disabled
5:58:25 PM  [main] 	The Tomcat module is disabled
5:58:25 PM  [main] 	Starting Check-Timer
5:58:25 PM  [main] 	Control Panel Ready
# manually starting
5:58:48 PM  [Apache] 	Attempting to start Apache app...
5:58:49 PM  [Apache] 	Status change detected: running

php server up on apache 2

Enable LDAP on PHP

uncomment the following lines

$ vi /c/opt/xampp/php/php.ini
extension=php_ldap.dll # addded

Installing phpldapadmin code

mfobrien@biometricvm MINGW64 ~
$ cd /c/opt/xampp/htdocs/config

mfobrien@biometricvm MINGW64 /c/opt/xampp/htdocs/config
$ ls

mfobrien@biometricvm MINGW64 /c/opt/xampp/htdocs/config
$ cp config.php.example config.php

mfobrien@biometricvm MINGW64 /c/opt/xampp/htdocs/config
$ vi config.php

#$servers->setValue('server','name','My LDAP Server');

/* The port your LDAP server listens on (no quotes). 389 is standard. */
// $servers->setValue('server','port',389);
#  $servers->setValue('login','bind_id','cn=Manager,dc=example,dc=com');


/* Your LDAP password. If you specified an empty bind_id above, this MUST also
   be blank. */
// $servers->setValue('login','bind_pass','');

// $servers->setValue('server','base',array(''));

Triage/Fixing of PHP and PhpLDAPAdmin version issues

see changes in or

There are issues with the older version of PhpLDAPAdmin running on certain newer versions of the LAMPP stack - that could be solved quickly by targeting a specific version of php in the docker container image - we don't have access to docker in this case.


Deprecated: __autoload() is deprecated, use spl_autoload_register() instead in C:\opt\xampp\htdocs\lib\functions.php on line 54

Fatal error: Cannot redeclare password_hash() in C:\opt\xampp\htdocs\lib\functions.php on line 2236

see a rename of the function moved into the common lib

function password_hash($password_clear,$enc_type) {
                case 'sha':
                        if (strcasecmp(pla_password_hash($plainpassword,'sha'),'{SHA}'.$cryptedpassword) == 0)
                                return true;
                                return false;
                # MD5 crypted passwords
                case 'md5':
                        if( strcasecmp(pla_password_hash($plainpassword,'md5'),'{MD5}'.$cryptedpassword) == 0)

attempted login ok
now getting

Fatal error: Uncaught Error: Call to undefined function ldap_explode_dn() in C:\opt\xampp\htdocs\lib\functions.php:2508 Stack trace: #0 C:\opt\xampp\htdocs\lib\Tree.php(135): pla_explode_dn('dc=i..d...') #1 C:\opt\xampp\htdocs\lib\Tree.php(173): Tree->indexDN('dc=....,d...') #2 C:\opt\xampp\htdocs\lib\Tree.php(62): Tree->addEntry('dc=....,d...') #3 C:\opt\xampp\htdocs\lib\page.php(227): Tree::getInstance(1) #4 C:\opt\xampp\htdocs\lib\page.php(418): page->tree() #5 C:\opt\xampp\htdocs\htdocs\cmd.php(78): page->display() #6 C:\opt\xampp\htdocs\htdocs\index.php(146): include('C:\\opt\\xampp\\ht...') #7 {main} thrown in C:\opt\xampp\htdocs\lib\functions.php on line 2508


mfobrien@biometricvm MINGW64 /c/opt/xampp/php
$ cp /c/opt/xampp/php/libsasl.dll /c/opt/xampp/apache/bin

mfobrien@biometricvm MINGW64 /c/opt/xampp/php
$ vi /c/opt/xampp/htdocs/config/config.php
               foreach ($dn as $key => $rdn)
                        $a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn);
                return $a;
        } else {
                return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn);

preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$rdn);

preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$dn);

               foreach ($dn as $key => $rdn)
                        $a[$key] = preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$rdn);
                return $a;
        } else {
                return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$dn);

Warning: preg_replace(): The /e modifier is no longer supported, use preg_replace_callback instead in C:\opt\xampp\htdocs\lib\ds_ldap.php on line 1120

                     foreach ($dn as $key => $rdn)
                                $a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn);
                        return $a;
                } else
                        return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn);

same as above
Function create_function() is deprecated in C:\opt\xampp\htdocs\lib\functions.php on line 1083

results: better

User/group security to allow for non-root admin login

Context: phpldapadmin is the non-root front end to oudsm

A default user will be able to log into the gui but will not be able to see any of the DN tree or perform actions.  We require a non-root admin user that has privileges that allow for viewing the entire DN tree.

User/group security to allow for non-root admin user create/delete user via LDIF

Context: phpldapadmin is the non-root front end to oudsm

Extra security config is required to be able to create/delete users using ldif import

User/group security to allow for non-root admin user copy user create

Context: phpldapadmin is the non-root front end to oudsm

Extra security is required to be able to create users via user copy.

Could not add the object to the LDAP server.
LDAP said:	Server is unwilling to perform
Error number:	0x35 (LDAP_UNWILLING_TO_PERFORM)
Description:	The LDAP server refused to perform the operation.


Using Templates in phpldapadmin

Issues with the LDAP schema or objectClass

Production config settings for phpldapadmin

Set the following 3 properties below so that we only see the custom templates for your use case, we disable the default template and we don't see warnings in template loading (make sure all warnings are addressed first)

# in config/config.php

/* Just show your custom templates. */
 $config->custom->appearance['custom_templates_only'] = true;
/* Disable the default template. */
 $config->custom->appearance['disable_default_template'] = true;
/* Hide the warnings for invalid objectClasses/attributes in templates. */
 $config->custom->appearance['hide_template_warning'] = true;

DI 20190704-1: new user creation fails on GID not populated

It looks like the following error may be due to a missing group - or change the template to allow for a groud id of 0 .

Fixed by taking out the dropdown from the the template

DI 20190704-2: Create custom template for custom attributes

DI 20190706-1: password encryption error on new user submit

Fixed by bypassing the password encryption code.

error	Error
E_WARNING: password_hash() expects parameter 2 to be int, string given
PHP Debug Backtrace
File	C:\opt\xampp\htdocs\lib\functions.php (184)
 	Function	error (a:5:{i:0;s:70:"E_WARNING: password_hash() expects ...)
File	C:\opt\xampp\htdocs\lib\functions.php ()
 	Function	app_error_handler (a:5:{i:0;i:2;i:1;s:59:"password_hash() expects par...)
File	C:\opt\xampp\htdocs\lib\PageRender.php (290)
 	Function	password_hash (a:2:{i:0;s:8:"password";i:1;s:0:"";})
File	C:\opt\xampp\htdocs\lib\Visitor.php (58)
 	Function	getPostAttribute (a:2:{i:0;O:17:"PasswordAttribute":34:{s:4:"name";s...)
File	C:\opt\xampp\htdocs\lib\PageRender.php (924)
 	Function	__call (a:2:{i:0;s:3:"get";i:1;a:2:{i:0;O:17:"PasswordAttr...)
File	C:\opt\xampp\htdocs\lib\Visitor.php (58)
 	Function	getAutoPostPasswordAttribute (a:2:{i:0;O:17:"PasswordAttribute":34:{s:4:"name";s...)
File	C:\opt\xampp\htdocs\lib\PageRender.php (92)
 	Function	__call (a:2:{i:0;s:3:"get";i:1;a:2:{i:0;O:17:"PasswordAttr...)
File	C:\opt\xampp\htdocs\htdocs\create_confirm.php (19)
 	Function	accept (a:0:{})
File	C:\opt\xampp\htdocs\htdocs\cmd.php (59)
 	Function	include (a:1:{i:0;s:45:"C:\opt\xampp\htdocs\htdocs\create_c...)

using the default template
<attribute id="userPassword">
	<!-- <helper>
	</helper> -->

# fix in PageRender.php:924
	protected function getAutoPostPasswordAttribute($attribute,$i) {
+		# 20190706: Just return the password enc or not - it will get encrypted on the server
+		return;
		# If the password is already encoded, then we'll return
		if (preg_match('/^\{.+\}.+/',$attribute->getValue($i)))


DI 20190708: Copy user fails on permission violation - server unwilling

Getting the following error attempting to copy a user.

Could not add the object to the LDAP server.
LDAP said:	Server is unwilling to perform
Error number:	0x35 (LDAP_UNWILLING_TO_PERFORM)
Description:	The LDAP server refused to perform the operation.

# logs in 

[08/Jul/2019:11:30:29 -0400] CONNECT conn=856 from= to= protocol=LDAP
[08/Jul/2019:11:30:29 -0400] BIND REQ conn=856 op=0 msgID=1 type=SIMPLE dn="cn=michaelobrien,cn=admin,dc=ca" version=3
[08/Jul/2019:11:30:29 -0400] BIND RES conn=856 op=0 msgID=1 result=0 authDN="cn=michaelobrien,cn=admin,dc=ca" etime=2
[08/Jul/2019:11:30:29 -0400] SEARCH REQ conn=856 op=1 msgID=2 base="ou=People,dc=ca" scope=base filter="(&(objectClass=*))" attrs="*,+"
[08/Jul/2019:11:30:29 -0400] SEARCH RES conn=856 op=1 msgID=2 result=0 nentries=1 etime=0
[08/Jul/2019:11:30:29 -0400] SEARCH REQ conn=856 op=2 msgID=3 base="dc=ca" scope=sub filter="(|(uid=u20190708b_copy)(uidnumber=1010))" attrs="uid,uidnumber"
[08/Jul/2019:11:30:29 -0400] SEARCH RES conn=856 op=2 msgID=3 result=0 nentries=0 etime=0
[08/Jul/2019:11:30:29 -0400] ADD REQ conn=856 op=3 msgID=4 dn="cn=u20190708b_copy,ou=People,dc=ca"
[08/Jul/2019:11:30:29 -0400] ADD RES conn=856 op=3 msgID=4 result=53 message="Pre-encoded passwords are not allowed for the password attribute userPassword" etime=0
[08/Jul/2019:11:30:29 -0400] UNBIND REQ conn=856 op=4 msgID=5
[08/Jul/2019:11:30:29 -0400] DISCONNECT conn=856 reason="Client Disconnect"

copy is OK if the password field is cleared
Q) can we set it separately - yes

Server:    Distinguished Name: cn=u20190708b_copy,ou=People,dc=ca
Do you want to make these changes?
Attribute	Old Value	New Value	Skip
Password	[attribute doesnt exist]	****************

Could not perform ldap_modify operation.
LDAP said:	Insufficient access
Description:	You do not have sufficient permissions to perform that operation.

[08/Jul/2019:11:41:58 -0400] BIND RES conn=872 op=0 msgID=1 result=0 authDN="cn=michaelobrien,cn=admin,dc=ca" etime=1
[08/Jul/2019:11:41:58 -0400] MODIFY REQ conn=872 op=4 msgID=5 dn="cn=u20190708b_copy,ou=People,dc=ca"
[08/Jul/2019:11:41:58 -0400] MODIFY RES conn=872 op=4 msgID=5 result=50 message="You do not have sufficient privileges to reset user passwords" etime=1

was using michaelobrien,admin - tried with Admin.Lab
[08/Jul/2019:11:43:49 -0400] BIND REQ conn=881 op=0 msgID=1 type=SIMPLE dn="cn=Admin.Lab,ou=People,dc=ca" version=3
[08/Jul/2019:11:43:49 -0400] MODIFY REQ conn=881 op=4 msgID=5 dn="cn=u20190708b_copy,ou=People,dc=ca"
[08/Jul/2019:11:43:49 -0400] MODIFY RES conn=881 op=4 msgID=5 result=0 etime=1

DI 20190708-2: Delete user

Are you sure you want to permanently delete this object?
DN	cn=u20190704i,ou=People,dc=ca
	Delete DN
Successfully deleted DN cn=u20190704i,ou=People,dc=ca

# logs in 
[08/Jul/2019:14:03:27 -0400] BIND REQ conn=896 op=0 msgID=1 type=SIMPLE dn="cn=Admin.Lab,ou=People,dc=ca" version=3
[08/Jul/2019:14:03:27 -0400] DELETE REQ conn=896 op=2 msgID=3 dn="cn=u20190704i,ou=People,dc=ca"
[08/Jul/2019:14:03:27 -0400] DELETE RES conn=896 op=2 msgID=3 result=0 etime=3

DI 20190708-3: Search User PHP error

Getting the following - should be able to adjust the code.

Search user
E_WARNING: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"?
PHP Debug Backtrace
File	C:\opt\xampp\htdocs\lib\functions.php (184)
 	Function	error (a:5:{i:0;s:98:"E_WARNING: "continue" targeting swi...)
File	C:\opt\xampp\htdocs\lib\functions.php (58)
 	Function	app_error_handler (a:5:{i:0;i:2;i:1;s:87:""continue" targeting switch...)
File	C:\opt\xampp\htdocs\lib\functions.php (58)
 	Function	require_once
File	C:\opt\xampp\htdocs\lib\functions.php ()
 	Function	my_autoload (a:1:{i:0;s:11:"QueryRender";})
File	C:\opt\xampp\htdocs\htdocs\query_engine.php (17)
 	Function	spl_autoload_call (a:1:{i:0;s:11:"QueryRender";})
File	C:\opt\xampp\htdocs\htdocs\cmd.php (59)
 	Function	include (a:1:{i:0;s:43:"C:\opt\xampp\htdocs\htdocs\query_en...)

Downgrade PHP from 7.3 to 7.2 in XAMPP

Trying this solution


DI: 20190709: expose member or isMemberOf attributes on OneToMany group membership

Need a way to view group membership via 3rd party ldap client.

Oracle Unified Directory

I have a client that is migrating from on-premises to the cloud - part of the work is retrofitting existing non-cloud tech as part of the migration.  One of the modules is an LDAP server that will not be containerized.  There is an additional issue where we need non-root admin users with CRUD capability - ideally in a reduced admin web based environment.  

Normally I use docker containers on ubuntu or OSX under an orchestration framework like Kubernetes - this particular OUD is on windows - there is a port to docker though I will look at -

Oracle Unified Directory 12cPS3 (

The OUD and OUDSM can be installed on a standard Windows 10 Pro VM on VMware for developer productivity.

There is a 2 min default timeout on the oudsm war - even though the timeout it set to 3600 sec - change it to 7200 to kick in the change - click ok on new plan and redeploy.


PS C:\oracle\middleware_oud1\user_projects\domains\oud_domain\system_components\OUD\oud1\bat> ./ldapsearch.bat -h biometricvm -p 17489 -D cn=oudadmin -w password -b "dc=obrienlabs,dc=cloud" cn=admin
dn: cn=admin,dc=obrienlabs,dc=cloud
objectClass: top
objectClass: orclContainer
cn: admin

check acls
PS C:\oracle\middleware_oud1\user_projects\domains\oud_domain\system_components\OUD\oud1\bat> ./ldapsearch.bat -h biometricvm -p 17489 -D cn=oudadmin -w IDMoudpwd1# -b "cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud" -s base "(objectclass=*)"  aci
dn: cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud

ldapadd or ldapmodify -a


PS C:\oracle\middleware_oud1\user_projects\domains\oud_domain\system_components\OUD\oud1\bat> ./ldapmodify.bat -a  -h biometricvm -p 17489 -w password -D "cn=oudadmin" -f C:\_dev\michaelobrien_add_ldif.txt
Processing ADD request for cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud
ADD operation successful for DN cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud

# check it
PS C:\oracle\middleware_oud1\user_projects\domains\oud_domain\system_components\OUD\oud1\bat> ./ldapsearch.bat -h biometricvm -p 17489 -D cn=oudadmin -w password -b "dc=obrienlabs,dc=cloud" cn=michaelobrien
dn: cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud
sn: michaelobrien
cn: .....

ldapmodify for ACI changes


PS C:\oracle\middleware_oud1\user_projects\domains\oud_domain\system_components\OUD\oud1\bat> ./ldapmodify.bat -a  -h biometricvm -p 17489  -w IDMoudpwd1# -D "cn=oudadmin" -f C:\_dev\michaelobrien_acl_add_ldif.txt

Processing MODIFY request for cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud
MODIFY operation successful for DN cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud

# using ldif file
dn: cn=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud
changetype: modify
add: aci
aci: (targetattr="*")(version 3.0; acl "give full rights"; allow(all) userdn = "ldap:///uid=michaelobrien,cn=admin,dc=obrienlabs,dc=cloud";)



Alternative/Legacy LDAP clients

I would recommend you try to use helm charts, kubernetes or even bare docker.  However in some cases legacy applications will not have access to VMs or a docker orchestration system - you will need to supply a web based interface old-school via a deployed war or other distribution like 90's PHP in some cases.  Here I will detail how to get these alternative clients running.


Connecting ldapadmin to Oracle Unified Directory - bypassing Oracle Unified Directory Services Manager

There are use cases where non-root admin user gui access is required.  Currently the oudsm accepts connections to the gui app only from root users.  Ldapadmin.exe is one option to use non-root users for actions where CLI access is not available for ldapsearch/ldapmodify and dsconfig.

  • No labels