151a5505 by Jeff Balicki

authLdap

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent 55e5d640
Showing 43 changed files with 2160 additions and 335 deletions
1 dn: dc=test space,{{ LDAP_BASE_DN }}
2 changetype: add
3 dc: test space
4 description: LDAP Example with space
5 objectClass: dcObject
6 objectClass: organization
7 o: test space
8
9 dn: cn=Manager,{{ LDAP_BASE_DN }}
10 changetype: add
11 cn: Manager
12 objectClass: organizationalRole
13
14 dn: ou=test,{{ LDAP_BASE_DN }}
15 changetype: add
16 objectClass: organizationalUnit
17 ou: test
18
19 dn: uid=user1,{{ LDAP_BASE_DN }}
20 changetype: add
21 objectClass: account
22 objectClass: simpleSecurityObject
23 uid: user1
24 userPassword: user1
25
26 dn: cn=group1,{{ LDAP_BASE_DN }}
27 changetype: add
28 objectclass: groupOfUniqueNames
29 cn: group1
30 uniqueMember: uid=user1,{{ LDAP_BASE_DN }}
31
32 dn: uid=user2,{{ LDAP_BASE_DN }}
33 changetype: add
34 objectClass: account
35 objectClass: simpleSecurityObject
36 uid: user2
37 userPassword: user2
38
39 dn: cn=group2,{{ LDAP_BASE_DN }}
40 changetype: add
41 objectclass: groupOfUniqueNames
42 cn: group2
43 uniqueMember: uid=user2,{{ LDAP_BASE_DN }}
44
45 dn: uid=user3,{{ LDAP_BASE_DN }}
46 changetype: add
47 objectClass: account
48 objectClass: simpleSecurityObject
49 uid: user3
50 userPassword: user!"
51
52 dn: cn=group3,{{ LDAP_BASE_DN }}
53 changetype: add
54 objectclass: groupOfUniqueNames
55 cn: group3
56 uniqueMember: uid=user2,{{ LDAP_BASE_DN }}
57 uniqueMember: uid=user3,{{ LDAP_BASE_DN }}
58
59 dn: uid=user 4,{{ LDAP_BASE_DN }}
60 changetype: add
61 objectClass: account
62 objectClass: simpleSecurityObject
63 uid: user 4
64 userPassword: user!"
65
66 dn: cn=group4,{{ LDAP_BASE_DN }}
67 changetype: add
68 objectclass: groupOfUniqueNames
69 cn: group4
70 uniqueMember: uid=user 4,{{ LDAP_BASE_DN }}
71
72 dn: uid=user 5,dc=test space,{{ LDAP_BASE_DN }}
73 changetype: add
74 objectClass: account
75 objectClass: simpleSecurityObject
76 uid: user 5
77 userPassword: user!"
78
79 dn: cn=group5,{{ LDAP_BASE_DN }}
80 changetype: add
81 objectclass: groupOfUniqueNames
82 cn: group5
83 uniqueMember: uid=user 5,dc=test space,{{ LDAP_BASE_DN }}
1 .ci
2 .github
3 build
4 config
5 dockersetup
6 svn
7 tests
8 vendor
9 wp-app
10 wd-data
11 .distignore
12 .editorconfig
13 .gitignore
14 .phpunit.result.cache
15 .svnAccess
16 .svnAccess.dist
17 build.xml
18 composer.json
19 composer.lock
20 docker-compose.yml
21 GPG_KEY
22 phpunit.xml.dist
23 .git
1 root = true
2
3 [*]
4 charset = utf-8
5 tab_width = 4
6 trim_trailing_whitespace = true
7 indent_size = tab
8 indent_style = tab
9 insert_final_newline = true
10 end_of_line = lf
11
12 [*.yml]
13 tab_width = 2
14
15 [*.xml]
16 tab_width = 2
17
18 [*.json]
19 tab_width = 2
1 name: Deploy to WordPress.org
2
3 env:
4 SLUG: authldap
5 on:
6 release:
7 types: [published]
8 jobs:
9 tag:
10 name: New release
11 runs-on: ubuntu-latest
12 environment: deploy_on_release
13 steps:
14 - name: Checkout code
15 uses: actions/checkout@v2
16 - name: WordPress Plugin Deploy
17 id: deploy
18 uses: 10up/action-wordpress-plugin-deploy@stable
19 with:
20 generate-zip: true
21 env:
22 SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
23 SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
24 - name: Upload release asset
25 uses: actions/upload-release-asset@v1
26 env:
27 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 with:
29 upload_url: ${{ github.event.release.upload_url }}
30 asset_path: ${{ steps.deploy.outputs.zip-path }}
31 asset_name: ${{ env.SLUG }}.zip
32 asset_content_type: application/zip
33
1 name: CI
2 on: [push, pull_request]
3 jobs:
4 test:
5 runs-on: ubuntu-latest
6 env:
7 PORT_LDAP: 3389
8 PORT_LDAPS: 6363
9 LDAP_ADMIN_PASSWORD: ${{ secrets.LDAP_ADMIN_PASSWORD }}
10 LDAP_LOG_LEVEL: 0
11 strategy:
12 matrix:
13 # operating-system: [ubuntu-latest, windows-latest, macos-latest]
14 php-versions: [ '7.4', '8.0', '8.1' ]
15 name: Test on ${{ matrix.php-versions }}
16 steps:
17 - uses: actions/checkout@v1
18 - name: Build the docker-compose stack
19 run: docker-compose -f docker-compose.yml up -d
20 - name: Check running containers
21 run: docker ps -a
22 - name: Check logs
23 run: docker-compose logs openldap
24 - name: Setup PHP
25 uses: shivammathur/setup-php@v2
26 with:
27 php-version: ${{ matrix.php-versions }}
28 extensions: ldap
29 tools: phive
30 - name: install dependencies
31 run: composer install
32 - name: install tools
33 run: phive install --trust-gpg-keys 4AA394086372C20A phpunit
34 - name: Run Unit-Tests
35 run: ./tools/phpunit --testdox
36 coverage:
37 needs: test
38 runs-on: ubuntu-latest
39 env:
40 PORT_LDAP: 3389
41 PORT_LDAPS: 6363
42 LDAP_ADMIN_PASSWORD: ${{ secrets.LDAP_ADMIN_PASSWORD }}
43 LDAP_LOG_LEVEL: 0
44 continue-on-error: false
45 steps:
46 - name: Checkout
47 uses: actions/checkout@v2
48 - name: Build the docker-compose stack
49 run: docker-compose -f docker-compose.yml up -d
50 - name: Check running containers
51 run: docker ps -a
52 - name: Setup PHP
53 uses: shivammathur/setup-php@v2
54 with:
55 php-version: "8.0"
56 coverage: xdebug
57 tools: phive
58 - name: install dependencies
59 run: composer install
60 - name: install tools
61 run: phive install --trust-gpg-keys 4AA394086372C20A phpunit
62 - name: run testsuite
63 run: ./tools/phpunit --testdox --colors=always --coverage-clover clover.xml
64 - name: upload to codecov
65 uses: codecov/codecov-action@v1
66 with:
67 #token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
68 files: ./clover.xml # optional
69 #flags: unittests # optional
70 #name: codecov-umbrella # optional
71 #fail_ci_if_error: true # optional (default = false)
72 #verbose: true # optional (default = false)
1 <?xml version="1.0" encoding="UTF-8"?>
2 <phive xmlns="https://phar.io/phive">
3 <phar name="phpunit" version="^9.5.21" installed="9.5.21" location="./tools/phpunit" copy="true"/>
4 <phar name="phpcs" version="^3.7.1" installed="3.7.1" location="./tools/phpcs" copy="true"/>
5 <phar name="phpcbf" version="^3.7.1" installed="3.7.1" location="./tools/phpcbf" copy="true"/>
6 </phive>
1 username = svnUsername
2 password = svnPassword
...\ No newline at end of file ...\ No newline at end of file
1 Copyright <YEAR> <COPYRIGHT HOLDER>
2
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
...\ No newline at end of file ...\ No newline at end of file
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
4 4
5 Use your existing LDAP as authentication-backend for your wordpress! 5 Use your existing LDAP as authentication-backend for your wordpress!
6 6
7 [![Build Status](https://travis-ci.org/heiglandreas/authLdap.svg?branch=master)](https://travis-ci.org/heiglandreas/authLdap) 7 [![Build Status](https://github.com/heiglandreas/authLdap/workflows/CI/badge.svg)](https://github.com/heiglandreas/authLdap/workflows/CI)
8 [![WordPress Stats](https://img.shields.io/wordpress/plugin/dt/authldap.svg)](https://wordpress.org/plugins/authldap/stats/) 8 [![WordPress Stats](https://img.shields.io/wordpress/plugin/dt/authldap.svg)](https://wordpress.org/plugins/authldap/stats/)
9 [![WordPress Version](https://img.shields.io/wordpress/plugin/v/authldap.svg)](https://wordpress.org/plugins/authldap/) 9 [![WordPress Version](https://img.shields.io/wordpress/plugin/v/authldap.svg)](https://wordpress.org/plugins/authldap/)
10 [![WordPress testet](https://img.shields.io/wordpress/v/authldap.svg)](https://wordpress.org/plugins/authldap/) 10 [![WordPress testet](https://img.shields.io/wordpress/v/authldap.svg)](https://wordpress.org/plugins/authldap/)
11 [![Code Climate](https://codeclimate.com/github/heiglandreas/authLdap/badges/gpa.svg)](https://codeclimate.com/github/heiglandreas/authLdap) 11 [![Code Climate](https://codeclimate.com/github/heiglandreas/authLdap/badges/gpa.svg)](https://codeclimate.com/github/heiglandreas/authLdap)
12 [![Test Coverage](https://codeclimate.com/github/heiglandreas/authLdap/badges/coverage.svg)](https://codeclimate.com/github/heiglandreas/authLdap) 12 [![codecov](https://codecov.io/gh/heiglandreas/authLdap/branch/master/graph/badge.svg?token=AYAhEeWtRQ)](https://codecov.io/gh/heiglandreas/authLdap)
13 13
14 So what are the differences to other Wordpress-LDAP-Authentication-Plugins? 14 So what are the differences to other Wordpress-LDAP-Authentication-Plugins?
15 15
...@@ -48,8 +48,8 @@ wonderfull plugin of Alistair Young from http://www.weblogs.uhi.ac.uk/sm00ay/?p= ...@@ -48,8 +48,8 @@ wonderfull plugin of Alistair Young from http://www.weblogs.uhi.ac.uk/sm00ay/?p=
48 * **LDAP Uri** This is the URI where your ldap-backend can be reached. More information are actually on the Configuration page 48 * **LDAP Uri** This is the URI where your ldap-backend can be reached. More information are actually on the Configuration page
49 * **Filter** This is the real McCoy! The filter you define here specifies how a user will be found. Before applying the filter a %s will be replaced with the given username. This means, when a user logs in using ‘foobar’ as username the following happens: 49 * **Filter** This is the real McCoy! The filter you define here specifies how a user will be found. Before applying the filter a %s will be replaced with the given username. This means, when a user logs in using ‘foobar’ as username the following happens:
50 50
51 * **uid=%s** check for any LDAP-Entry that has an attribute ‘uid’ with value ‘foobar’ 51 * **uid=%1$s** check for any LDAP-Entry that has an attribute ‘uid’ with value ‘foobar’
52 * **(&(objectclass=posixAccount)((!(uid=%s)(mail=%s)))** check for any LDAP-Entry that has an attribute ‘objectclass’ with value ‘posixAccout’ and either a UID- or a mail-attribute with value ‘foobar’ 52 * **(&(objectclass=posixAccount)(|(uid=%1$s)(mail=%1$s)))** check for any LDAP-Entry that has an attribute ‘objectclass’ with value ‘posixAccout’ and either a UID- or a mail-attribute with value ‘foobar’
53 53
54 This filter is rather powerfull if used wisely. 54 This filter is rather powerfull if used wisely.
55 55
...@@ -92,4 +92,4 @@ wonderfull plugin of Alistair Young from http://www.weblogs.uhi.ac.uk/sm00ay/?p= ...@@ -92,4 +92,4 @@ wonderfull plugin of Alistair Young from http://www.weblogs.uhi.ac.uk/sm00ay/?p=
92 <a href="https://github.com/heiglandreas/authLdap/issues/65">issue 65</a> 92 <a href="https://github.com/heiglandreas/authLdap/issues/65">issue 65</a>
93 where <a href="https://github.com/wtfiwtz">wtfiwtz</a> shows how to implement that feature. 93 where <a href="https://github.com/wtfiwtz">wtfiwtz</a> shows how to implement that feature.
94 </dd> 94 </dd>
95 </dl>
...\ No newline at end of file ...\ No newline at end of file
95 </dl>
......
1 .row {
2 overflow: hidden;
3 padding-top: 10px;
4 }
5
6 .element {
7 float: right;
8 text-align: left;
9 }
10
11 .authldap-options input[type=text] {
12 width: 100%;
13 }
1 <?xml version="1.0" encoding="utf-8"?>
2 <project name="ajaxComments" default="build" basedir=".">
3 <php expression="include('vendor/autoload.php')"/>
4 <input propertyName="version">Which version shall be tagged?</input>
5 <!--loadfile property = "version" file = "VERSION">
6 <filterchain>
7 <striplinebreaks/>
8 </filterchain>
9 </loadfile-->
10 <target name="bumpversion">
11 <echo>Bumping version to ${version}</echo>
12 <reflexive>
13 <fileset dir=".">
14 <include name="index.php"/>
15 <include name="authLdap.php"/>
16 </fileset>
17 <filterchain>
18 <replaceregexp>
19 <regexp pattern="Version:.*" replace="Version: ${version}"/>
20 </replaceregexp>
21 </filterchain>
22 </reflexive>
23 </target>
24 <target name="build" depends="bumpversion,deploy.git,deploy.svn"/>
25 <target name="sync.svn">
26 <copy todir="${project.basedir}/svn/trunk">
27 <fileset dir="${project.basedir}">
28 <include name="src/**"/>
29 <include name="view/**"/>
30 <include name="authLdap.css"/>
31 <include name="authLdap.php"/>
32 <include name="ldap.php"/>
33 <include name="LICENSE.md"/>
34 <include name="README.md"/>
35 <include name="readme.txt"/>
36 </fileset>
37 </copy>
38 </target>
39 <target name="deploy.svn" depends="sync.svn">
40 <property override="true" file="${project.basedir}/.svnAccess" prefix="svnaccess" />
41 <foreach param="dirname" target="svn.addFile">
42 <fileset dir="${project.basedir}/svn/trunk">
43 <include name="**/*"/>
44 </fileset>
45 </foreach>
46 <echo message="${svnaccess.username}"/>
47 <exec outputProperty="committedrevision" executable="svn" dir="${project.basedir}/svn/trunk">
48 <arg value="commit"/>
49 <arg value="--username"/>
50 <arg value="${svnaccess.username}"/>
51 <arg value="--password"/>
52 <arg value="${svnaccess.password}"/>
53 <arg value="--message"/>
54 <arg value="Bumps version to ${version}"/>
55 <arg value="--no-auth-cache"/>
56 <arg value="--quiet"/>
57 </exec>
58 <!--svncommit
59 username="${svnaccess.username}"
60 password="${svnaccess.password}"
61 workingcopy="${project.basedir}/svn"
62 message="Bumps version to ${version}"
63 nocache="true"
64 /-->
65 <echo message="Committed revision: ${committedrevision}"/>
66 </target>
67
68 <target name="svn.addFile">
69 <trycatch>
70 <try>
71 <svninfo workingcopy="${project.basedir}/svn/trunk/${dirname}"/>
72 </try>
73 <catch>
74 <exec command="svn add ${project.basedir}/svn/trunk/${dirname}"/>
75 <echo>${dirname}</echo>
76 </catch>
77 <finally>
78
79 </finally>
80 </trycatch>
81 <echo>${svn.info}</echo>
82 </target>
83 <target name="deploy.git">
84 <exec executable="git" dir=".">
85 <arg value="add" />
86 <arg value="authLdap.php" />
87 </exec>
88 <exec executable="git" dir=".">
89 <arg value="commit" />
90 <arg value="-m"/>
91 <arg value="Bumps version to ${version}"/>
92 </exec>
93 <exec executable="git" dir=".">
94 <arg value="tag"/>
95 <arg value="-s"/>
96 <arg value="-m"/>
97 <arg value="Version ${version}"/>
98 <arg value="${version}"/>
99 </exec>
100 <exec executable="git" dir=".">
101 <arg value="push"/>
102 <arg value="--tags"/>
103 <arg value="origin"/>
104 <arg value="master" />
105 </exec>
106 </target>
107 </project>
...\ No newline at end of file ...\ No newline at end of file
1 {
2 "name" : "org_heigl/authldap",
3 "type" : "library",
4 "description": "Enables wordpress-authentication via LDAP",
5 "keywords": ["ldap","authenticate", "auth", "wordpress"],
6 "homepage": "http://github.com/heiglandreas/authLdap",
7 "license": "MIT",
8 "authors": [{
9 "name": "Andreas Heigl",
10 "email": "andreas@heigl.org",
11 "homepage": "http://andreas.heigl.org",
12 "role": "Developer"
13 }],
14 "require" : {
15 "php": ">=7.4",
16 "ext-ldap": "*"
17 },
18 "require-dev": {
19 "automattic/wordbless": "^0.3.1"
20 },
21 "autoload" : {
22 "classmap" : [
23 "authLdap.php"
24 ],
25 "psr-4" : {
26 "Org_Heigl\\AuthLdap\\" : "src/"
27 }
28 },
29 "autoload-dev" : {
30 "psr-4" : {
31 "Org_Heigl\\AuthLdapTest\\" : "tests/"
32 }
33 },
34 "scripts": {
35 "post-update-cmd": "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
36 },
37 "config": {
38 "allow-plugins": {
39 "roots/wordpress-core-installer": true
40 }
41 }
42 }
1 version: "3.5"
2
3 services:
4 wp:
5 # image: authldap:latest
6 build:
7 context: dockersetup
8 dockerfile: Dockerfile_wordpress
9 ports:
10 - 80:80 # change ip if required
11 volumes:
12 - ./config/php.conf.ini:/usr/local/etc/php/conf.d/conf.ini
13 - ./wp-app:/var/www/html # Full wordpress project
14 - .:/var/www/html/wp-content/plugins/authldap # Plugin development
15 #- ./theme-name/trunk/:/var/www/html/wp-content/themes/theme-name # Theme development
16 environment:
17 WORDPRESS_DB_HOST: db
18 WORDPRESS_DB_NAME: "wordpress"
19 WORDPRESS_DB_USER: root
20 WORDPRESS_DB_PASSWORD: "wppasswd"
21 depends_on:
22 - db
23 links:
24 - db
25
26 wpcli:
27 image: wordpress:cli
28 volumes:
29 - ./config/php.conf.ini:/usr/local/etc/php/conf.d/conf.ini
30 - ./wp-app:/var/www/html
31 depends_on:
32 - db
33 - wp
34
35 db:
36 image: mysql:latest # https://hub.docker.com/_/mysql/ - or mariadb https://hub.docker.com/_/mariadb
37 ports:
38 - 3306:3306 # change ip if required
39 command: [
40 '--default_authentication_plugin=mysql_native_password',
41 '--character-set-server=utf8mb4',
42 '--collation-server=utf8mb4_unicode_ci'
43 ]
44 volumes:
45 - ./wp-data:/docker-entrypoint-initdb.d
46 - db_data:/var/lib/mysql
47 environment:
48 MYSQL_DATABASE: "wordpress"
49 MYSQL_ROOT_PASSWORD: "wppasswd"
50
51 openldap:
52 image: osixia/openldap:latest
53 # build:
54 # context: dockersetup
55 # dockerfile: Dockerfile_ldap
56 ports:
57 - 3389:389
58 volumes:
59 - ./.ci/50-init.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-bootstrap.ldif
60 command: "--copy-service --loglevel debug"
61 restart: always
62 environment:
63 LDAP_LOG_LEVEL: "0"
64 LDAP_TLS: "false"
65 LDAP_ADMIN_PASSWORD: "insecure"
66
67 volumes:
68 db_data:
1 FROM wordpress:latest
2
3 RUN set -x \
4 && apt-get update \
5 && apt-get install -y libldap2-dev \
6 && rm -rf /var/lib/apt/lists/* \
7 && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ \
8 && docker-php-ext-install ldap \
9 && apt-get purge -y --auto-remove libldap2-dev
...\ No newline at end of file ...\ No newline at end of file
1 <?xml version="1.0"?>
2 <ruleset name="Custom Standard" namespace="MyProject\CS\Standard">
3 <description>authLdap codestyle</description>
4 <file>./src</file>
5 <file>./authLdap.php</file>
6 <file>./tests</file>
7
8 <arg name="colors"/>
9 <arg value="sp"/>
10
11 <autoload>./vendor/autoload.php</autoload>
12
13 <rule ref="PSR12">
14 <exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
15 </rule>
16 <rule ref="Generic.WhiteSpace.ScopeIndent">
17 <properties>
18 <property name="tabIndent" value="true"/>
19 </properties>
20 </rule>
21
22 </ruleset>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 bootstrap="tests/bootstrap.php"
4 testdox="true"
5 xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
6 >
7 <coverage>
8 <include>
9 <directory suffix=".php">src</directory>
10 <file>authLdap.php</file>
11 </include>
12 <exclude>
13 <directory>src/Wrapper</directory>
14 </exclude>
15 <report>
16 <html outputDirectory="build/coverage" lowUpperBound="35" highLowerBound="70"/>
17 </report>
18 </coverage>
19 <testsuite name="authLdap Test-Suite">
20 <directory>tests</directory>
21 </testsuite>
22 <groups>
23 <exclude>
24 <group>disable</group>
25 </exclude>
26 </groups>
27 <logging>
28 <!--log type="coverage-xml" target="../report/coverage.xml"/-->
29 <!--log type="graphviz" target="../report/logfile.dot"/-->
30 <!--log type="json" target="../report/logfile.json"/-->
31 <!--log type="metrics-xml" target="../report/metrics.xml"/-->
32 <!--log type="plain" target="../report/logfile.txt"/-->
33 <!--log type="pmd-xml" target="../report/pmd.xml" cpdMinLines="5" cpdMinMatches="70"/-->
34 <!--log type="tap" target="../report/logfile.tap"/-->
35 <!--log type="test-xml" target="../report/logfile.xml" logIncompleteSkipped="false"/-->
36 <!--log type="testdox-html" target="../report/testdox.html"/-->
37 <!--log type="testdox-text" target="../report/testdox.txt"/-->
38 </logging>
39 </phpunit>
1 === authLdap === 1 === authLdap ===
2 Contributors: heiglandreas 2 Contributors: heiglandreas
3 Tags: ldap, auth 3 Tags: ldap, auth, authentication, active directory, AD, openLDAP, Open Directory
4 Requires at least: 2.5.0 4 Requires at least: 2.5.0
5 Tested up to: 4.6.1 5 Tested up to: 5.9.0
6 Requires PHP: 7.4
6 Stable tag: trunk 7 Stable tag: trunk
8 License: MIT
9 License URI: https://opensource.org/licenses/MIT
7 10
8 Use your existing LDAP flexible as authentication backend for WordPress 11 Use your existing LDAP flexible as authentication backend for WordPress
9 12
...@@ -13,13 +16,9 @@ Use your existing LDAP as authentication-backend for your wordpress! ...@@ -13,13 +16,9 @@ Use your existing LDAP as authentication-backend for your wordpress!
13 16
14 So what are the differences to other Wordpress-LDAP-Authentication-Plugins? 17 So what are the differences to other Wordpress-LDAP-Authentication-Plugins?
15 18
16 * Flexible: You are totaly free in which LDAP-backend to use. Due to the extensive configuration you can 19 * Flexible: You are totaly free in which LDAP-backend to use. Due to the extensive configuration you can freely decide how to do the authentication of your users. It simply depends on your filters
17 freely decide how to do the authentication of your users. It simply depends on your 20 * Independent: As soon as a user logs in, it is added/updated to the Wordpress' user-database to allow wordpress to always use the correct data. You only have to administer your users once.
18 filters 21 * Failsafe: Due to the users being created in Wordpress' User-database they can also log in when the LDAP-backend currently is gone.
19 * Independent: As soon as a user logs in, it is added/updated to the Wordpress' user-database
20 to allow wordpress to always use the correct data. You only have to administer your users once.
21 * Failsafe: Due to the users being created in Wordpress' User-database they can
22 also log in when the LDAP-backend currently is gone.
23 * Role-Aware: You can map Wordpress' roles to values of an existing LDAP-attribute. 22 * Role-Aware: You can map Wordpress' roles to values of an existing LDAP-attribute.
24 23
25 For more Information on the configuration have a look at https://github.com/heiglandreas/authLdap 24 For more Information on the configuration have a look at https://github.com/heiglandreas/authLdap
...@@ -41,6 +40,51 @@ Go to https://github.com/heiglandreas/authLdap ...@@ -41,6 +40,51 @@ Go to https://github.com/heiglandreas/authLdap
41 Please use the issuetracker at https://github.com/heiglandreas/authLdap/issues 40 Please use the issuetracker at https://github.com/heiglandreas/authLdap/issues
42 41
43 == Changelog == 42 == Changelog ==
43
44 = 2.5.3 =
45 * Fix issue with broken role-assignement in combination with WooCommerce
46 * Fix spelling issue
47 * Allow DN as role-definition
48
49 = 2.5.0 =
50 * Ignore the order of capabilities to tell the role. In addition the filter `editable_roles` can be used to limit the roles
51
52 = 2.4.11 =
53 * Fix issue with running on PHP8.1
54
55 = 2.4.9 =
56 * Improve group-assignement UI
57
58 = 2.4.8 =
59 * Make textfields in settings-page wider
60
61 = 2.4.7 =
62 * Replace deprecated function
63 * Fix undefined index
64 * Add filter for retrieving other params at login (authLdap_filter_attributes)
65 * Add do_action after successfull login (authLdap_login_successful)
66
67 = 2.4.0 =
68 * Allow to use environment variables for LDAP-URI configuration
69
70 = 2.3.0 =
71 * Allow to not overwrite existing WordPress-Users with LDAP-Users as that can be a security issue.
72
73 = 2.1.0 =
74 * Add search-base for groups. This might come in handy for multisite-instances
75
76 = 2.0.0 =
77 * This new release adds Multi-Site support. It will no longer be possible to use this plugin just in one subsite of a multisite installation!
78 * Adds a warning screen to the config-section when no LDAPextension could be found
79 * Fixes an issue with the max-length of the username
80
81 = 1.5.1 =
82 * Fixes an issue with escaped backslashes and quotes
83
84 = 1.5.0 =
85 * Allows parts of the LDAP-URI to be URLEncoded
86 * Drops support for PHP 5.4
87
44 = 1.4.20 = 88 = 1.4.20 =
45 * Allows multiple LDAP-servers to be queried (given that they use the same attributes) 89 * Allows multiple LDAP-servers to be queried (given that they use the same attributes)
46 * Fixes issue with URL-Encoded informations (see https://github.com/heiglandreas/authLdap/issues/108) 90 * Fixes issue with URL-Encoded informations (see https://github.com/heiglandreas/authLdap/issues/108)
...@@ -77,7 +121,7 @@ Please use the issuetracker at https://github.com/heiglandreas/authLdap/issues ...@@ -77,7 +121,7 @@ Please use the issuetracker at https://github.com/heiglandreas/authLdap/issues
77 * Fixes PSR2 violations 121 * Fixes PSR2 violations
78 122
79 […] 123 […]
80 124
81 = 1.2.1 = 125 = 1.2.1 =
82 * Fixed an issue with group-ids 126 * Fixed an issue with group-ids
83 * Moved the code to GitHub (https://github.com/heiglandreas/authLdap) 127 * Moved the code to GitHub (https://github.com/heiglandreas/authLdap)
......
1 <?php
2
3 /**
4 * Copyright Andrea Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Exception;
12
13 use Exception;
14
15 class Error extends Exception
16 {
17 public function __construct($message, $line = null)
18 {
19 parent::__construct($message);
20 if ($line) {
21 $this -> line = $line;
22 }
23 }
24 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Exception;
12
13 use RuntimeException;
14
15 use function sprintf;
16
17 class InvalidLdapUri extends RuntimeException
18 {
19 public static function cannotparse(string $ldapUri): self
20 {
21 return new self(sprintf(
22 '%1$s seems not to be a valid URI',
23 $ldapUri
24 ));
25 }
26
27 public static function wrongSchema(string $uri): self
28 {
29 return new self(sprintf(
30 '%1$s does not start with a valid schema',
31 $uri
32 ));
33 }
34
35 public static function noSchema(string $uri): self
36 {
37 return new self(sprintf(
38 '%1$s does not provide a schema',
39 $uri
40 ));
41 }
42
43 public static function noEnvironmentVariableSet(string $uri): self
44 {
45 return new self(sprintf(
46 'The environment variable %1$s does not provide a URI',
47 $uri
48 ));
49 }
50
51 public static function noServerProvided(string $uri): self
52 {
53 return new self(sprintf(
54 'The LDAP-URI %1$s does not provide a server',
55 $uri
56 ));
57 }
58
59 public static function noSearchBaseProvided(string $uri): self
60 {
61 return new self(sprintf(
62 'The LDAP-URI %1$s does not provide a search-base',
63 $uri
64 ));
65 }
66
67 public static function invalidSearchBaseProvided(string $uri): self
68 {
69 return new self(sprintf(
70 'The LDAP-URI %1$s does not provide a valid search-base',
71 $uri
72 ));
73 }
74 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Exception;
12
13 use RuntimeException;
14
15 class MissingValidLdapConnection extends Error
16 {
17 public static function get(): self
18 {
19 return new self(sprintf(
20 'No valid LDAP connection available'
21 ));
22 }
23 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Exception;
12
13 use RuntimeException;
14
15 class SearchUnsuccessfull extends RuntimeException
16 {
17 public static function fromSearchFilter(string $filter): self
18 {
19 return new self(sprintf(
20 'Search for %1$s was not successfull',
21 $filter
22 ));
23 }
24 }
1 <?php 1 <?php
2
2 /** 3 /**
3 * Copyright (c) Andreas Heigl<andreas@heigl.org> 4 * Copyright (c) Andreas Heigl<andreas@heigl.org>
4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
...@@ -26,23 +27,29 @@ ...@@ -26,23 +27,29 @@
26 27
27 namespace Org_Heigl\AuthLdap; 28 namespace Org_Heigl\AuthLdap;
28 29
30 use Exception;
31 use Org_Heigl\AuthLdap\Exception\Error;
32 use Org_Heigl\AuthLdap\Exception\SearchUnsuccessfull;
33 use Org_Heigl\AuthLdap\Manager\Ldap;
34
29 class LdapList 35 class LdapList
30 { 36 {
31 /** 37 /**
32 * @var \LDAP[] 38 * @var Ldap[]
33 */ 39 */
34 protected $items = []; 40 protected $items = [];
35 41
36 public function addLdap(LDAP $ldap) 42 public function addLdap(Ldap $ldap)
37 { 43 {
38 $this->items[] = $ldap; 44 $this->items[] = $ldap;
39 } 45 }
40 46
41 public function authenticate($username, $password, $filter = '(uid=%s)') 47 public function authenticate($username, $password, $filter = '(uid=%s)')
42 { 48 {
49 /** @var Ldap $item */
43 foreach ($this->items as $key => $item) { 50 foreach ($this->items as $key => $item) {
44 if (! $item->authenticate($username, $password, $filter)) { 51 if (! $item->authenticate($username, $password, $filter)) {
45 unset ($this->items[$key]); 52 unset($this->items[$key]);
46 continue; 53 continue;
47 } 54 }
48 return true; 55 return true;
...@@ -65,21 +72,22 @@ class LdapList ...@@ -65,21 +72,22 @@ class LdapList
65 } 72 }
66 73
67 if ($allFailed) { 74 if ($allFailed) {
68 throw new AuthLDAP_Exception('No bind successfull'); 75 throw new Error('No bind successfull');
69 } 76 }
77
78 return true;
70 } 79 }
71 80
72 public function search($filter, $attributes = array('uid')) 81 public function search($filter, $attributes = array('uid'), $base = '')
73 { 82 {
74 foreach ($this->items as $item) { 83 foreach ($this->items as $item) {
75 try { 84 try {
76 $result = $item->search($filter, $attributes); 85 $result = $item->search($filter, $attributes, $base);
77 return $result; 86 return $result;
78 } catch (Exception $e) { 87 } catch (Exception $e) {
79 throw $e;
80 } 88 }
81 } 89 }
82 90
83 throw new \AuthLDAP_Exception('No Results found'); 91 throw SearchUnsuccessfull::fromSearchFilter($filter);
84 } 92 }
85 }
...\ No newline at end of file ...\ No newline at end of file
93 }
......
1 <?php
2
3 /**
4 * Copyright (c) Andreas Heigl<andreas@heigl.org>
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 *
21 * @author Andreas Heigl<andreas@heigl.org>
22 * @copyright Andreas Heigl
23 * @license http://www.opensource.org/licenses/mit-license.php MIT-License
24 * @since 19.07.2020
25 * @link http://github.com/heiglandreas/authLDAP
26 */
27
28 declare(strict_types=1);
29
30 namespace Org_Heigl\AuthLdap;
31
32 use Org_Heigl\AuthLdap\Exception\InvalidLdapUri;
33
34 use function array_map;
35 use function error_get_last;
36 use function getenv;
37 use function is_array;
38 use function is_string;
39 use function parse_url;
40 use function preg_replace_callback;
41 use function rawurlencode;
42 use function strlen;
43 use function strpos;
44 use function substr;
45 use function trim;
46 use function urldecode;
47
48 final class LdapUri
49 {
50 private $server;
51
52 private $scheme;
53
54 private $port = 389;
55
56 private string $baseDn;
57
58 private $username = '';
59
60 private $password = '';
61
62 private function __construct(string $uri)
63 {
64 if (!preg_match('/^(ldap|ldaps|env)/', $uri)) {
65 throw InvalidLdapUri::wrongSchema($uri);
66 }
67
68 if (strpos($uri, 'env:') === 0) {
69 $newUri = getenv(substr($uri, 4));
70 if (false === $newUri) {
71 throw InvalidLdapUri::noEnvironmentVariableSet($uri);
72 }
73 $uri = (string) $newUri;
74 }
75
76 $uri = $this->injectEnvironmentVariables($uri);
77
78 $array = parse_url($uri);
79 if (!is_array($array)) {
80 throw InvalidLdapUri::cannotparse($uri);
81 }
82
83 $url = array_map(static function ($item) {
84 if (is_int($item)) {
85 return $item;
86 }
87 return urldecode($item);
88 }, $array);
89
90
91 if (!isset($url['scheme'])) {
92 throw InvalidLdapUri::noSchema($uri);
93 }
94 if (0 !== strpos($url['scheme'], 'ldap')) {
95 throw InvalidLdapUri::wrongSchema($uri);
96 }
97 if (!isset($url['host'])) {
98 throw InvalidLdapUri::noServerProvided($uri);
99 }
100 if (!isset($url['path'])) {
101 throw InvalidLdapUri::noSearchBaseProvided($uri);
102 }
103 if (1 === strlen($url['path'])) {
104 throw InvalidLdapUri::invalidSearchBaseProvided($uri);
105 }
106
107 $this->server = $url['host'];
108 $this->scheme = $url['scheme'];
109 $this->baseDn = substr($url['path'], 1);
110 if (isset($url['user'])) {
111 $this->username = $url['user'];
112 }
113 if ('' === trim($this->username)) {
114 $this->username = 'anonymous';
115 }
116 if (isset($url['pass'])) {
117 $this->password = $url['pass'];
118 }
119 if ($this->scheme === 'ldaps' && $this->port = 389) {
120 $this->port = 636;
121 }
122
123 // When someone sets the port in the URL we overwrite whatever is set.
124 // We have to assume they know what they are doing!
125 if (isset($url['port'])) {
126 $this->port = $url['port'];
127 }
128 }
129
130 public static function fromString(string $uri): LdapUri
131 {
132 return new LdapUri($uri);
133 }
134
135 private function injectEnvironmentVariables(string $base): string
136 {
137 return preg_replace_callback('/%env:([^%]+)%/', static function (array $matches) {
138 return rawurlencode(getenv($matches[1]));
139 }, $base);
140 }
141
142 public function toString(): string
143 {
144 return $this->scheme . '://' . $this->server . ':' . $this->port;
145 }
146
147 public function __toString()
148 {
149 return $this->toString();
150 }
151
152 public function getUsername(): string
153 {
154 return $this->username;
155 }
156
157 public function getPassword(): string
158 {
159 return $this->password;
160 }
161
162 public function getBaseDn(): string
163 {
164 return $this->baseDn;
165 }
166
167 public function isAnonymous(): bool
168 {
169 if ($this->password === '') {
170 return true;
171 }
172
173 if ($this->username === 'anonymous') {
174 return true;
175 }
176
177 return false;
178 }
179 }
1 <?php
2
3 /**
4 * $Id: ldap.php 381646 2011-05-06 09:37:31Z heiglandreas $
5 *
6 * authLdap - Authenticate Wordpress against an LDAP-Backend.
7 * Copyright (c) 2008 Andreas Heigl<andreas@heigl.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 *
23 * This file handles the basic LDAP-Tasks
24 *
25 * @author Andreas Heigl<andreas@heigl.org>
26 * @package authLdap
27 * @category authLdap
28 * @since 2008
29 */
30
31 namespace Org_Heigl\AuthLdap\Manager;
32
33 use Org_Heigl\AuthLdap\Exception\Error;
34 use Org_Heigl\AuthLdap\Exception\MissingValidLdapConnection;
35 use Org_Heigl\AuthLdap\LdapUri;
36 use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
37 use Org_Heigl\AuthLdap\Wrapper\LdapInterface;
38
39 class Ldap
40 {
41 /**
42 * This property contains the connection handle to the ldap-server
43 *
44 * @var LdapInterface|null
45 */
46 private ?LdapInterface $connection;
47
48 private LdapUri $uri;
49
50 private LdapFactory $factory;
51
52 private $starttls;
53
54 public function __construct(LdapFactory $factory, LdapUri $uri, $starttls = false)
55 {
56 $this->starttls = $starttls;
57 $this->uri = $uri;
58 $this->factory = $factory;
59 $this->connection = null;
60 }
61
62 /**
63 * Connect to the given LDAP-Server
64 */
65 public function connect(): self
66 {
67 $this->disconnect();
68
69 $this->connection = $this->factory->createFromLdapUri($this->uri->toString());
70 $this->connection->setOption(LDAP_OPT_PROTOCOL_VERSION, 3);
71 $this->connection->setOption(LDAP_OPT_REFERRALS, 0);
72 //if configured try to upgrade encryption to tls for ldap connections
73 if ($this->starttls) {
74 $this->connection->startTls();
75 }
76 return $this;
77 }
78
79 /**
80 * Disconnect from a resource if one is available
81 */
82 public function disconnect(): self
83 {
84 if (null !== $this->connection) {
85 $this->connection->unbind();
86 }
87 $this->connection = null;
88 return $this;
89 }
90
91 /**
92 * Bind to an LDAP-Server with the given credentials
93 *
94 * @throws Error
95 */
96 public function bind(): self
97 {
98 if (!$this->connection) {
99 $this->connect();
100 }
101 if (null === $this->connection) {
102 throw MissingValidLdapConnection::get();
103 }
104 if ($this->uri->isAnonymous()) {
105 $bind = $this->connection->bind();
106 } else {
107 $bind = $this->connection->bind($this->uri->getUsername(), $this->uri->getPassword());
108 }
109 if (!$bind) {
110 throw new Error('bind was not successfull: ' . $this->connection->error());
111 }
112 return $this;
113 }
114
115 /**
116 * This method does the actual ldap-serch.
117 *
118 * This is using the filter <var>$filter</var> for retrieving the attributes
119 * <var>$attributes</var>
120 *
121 * @return array<string|int, mixed>
122 * @throws Error
123 */
124 public function search(string $filter, array $attributes = ['uid'], ?string $base = ''): array
125 {
126 if (null === $this->connection) {
127 throw new Error('No resource handle available');
128 }
129 if (!$base) {
130 $base = $this->uri->getBaseDn();
131 }
132 $result = $this->connection->search($base, $filter, $attributes);
133 if ($result === false) {
134 throw new Error('no result found');
135 }
136 $info = $this->connection->getEntries($result);
137 if ($info === false) {
138 throw new Error('invalid results found');
139 }
140 return $info;
141 }
142
143 /**
144 * This method authenticates the user <var>$username</var> using the
145 * password <var>$password</var>
146 *
147 * @param string $filter OPTIONAL This parameter defines the Filter to be used
148 * when searchin for the username. This MUST contain the string '%s' which
149 * will be replaced by the vaue given in <var>$username</var>
150 * @throws Error
151 */
152 public function authenticate(string $username, string $password, string $filter = '(uid=%s)'): bool
153 {
154 $this->connect();
155 $this->bind();
156 $res = $this->search(sprintf($filter, $this->factory->escape($username, '', LDAP_ESCAPE_FILTER)));
157 if ($res ['count'] !== 1) {
158 return false;
159 }
160
161 $dn = $res[0]['dn'];
162 return $username && $password && $this->connection->bind($dn, $password);
163 }
164 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap;
12
13 use WP_User;
14
15 use function array_search;
16 use function in_array;
17 use function var_dump;
18
19 class UserRoleHandler
20 {
21 /**
22 * @param WP_User $user
23 * @param string[] $roles
24 * @return void
25 */
26 public function addRolesToUser(WP_User $user, $roles) : void
27 {
28 if ($roles === []) {
29 return;
30 }
31
32 if ($user->roles == $roles) {
33 return;
34 }
35
36 // Remove unused roles from existing.
37 foreach ($user->roles as $role) {
38 if (!in_array($role, $roles)) {
39 // Remove unused roles.
40 $user->remove_role($role);
41 continue;
42 }
43 // Remove the existing role from roles.
44 if (($key = array_search($role, $roles)) !== false) {
45 unset($roles[$key]);
46 }
47 }
48
49 // Add new ones if not already assigned.
50 foreach ($roles as $role) {
51 $user->add_role($role);
52 }
53 }
54 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Wrapper;
12
13 use function ldap_bind;
14 use function ldap_connect;
15 use function ldap_error;
16 use function ldap_escape;
17 use function ldap_get_entries;
18 use function ldap_set_option;
19 use function ldap_start_tls;
20 use function ldap_unbind;
21
22 final class Ldap implements LdapInterface
23 {
24 private $connection;
25
26 public function __construct(string $ldapUri)
27 {
28 $this->connection = ldap_connect($ldapUri);
29 }
30
31 public function bind($dn = null, $password = null)
32 {
33 if (null === $dn && null === $password) {
34 return ldap_bind($this->connection);
35 }
36 return ldap_bind($this->connection, $dn, $password);
37 }
38
39 public function unbind()
40 {
41 return ldap_unbind($this->connection);
42 }
43
44 public function setOption($option, $value)
45 {
46 return ldap_set_option($this->connection, $option, $value);
47 }
48
49 public function startTls()
50 {
51 return ldap_start_tls($this->connection);
52 }
53
54 public function error()
55 {
56 return ldap_error($this->connection);
57 }
58
59 public function errno()
60 {
61 return ldap_errno($this->connection);
62 }
63
64 public function search(
65 $base,
66 $filter,
67 array $attributes = [],
68 $attributes_only = 0,
69 $sizelimit = -1,
70 $timelimit = -1
71 ) {
72 return ldap_search(
73 $this->connection,
74 $base,
75 $filter,
76 $attributes,
77 $attributes_only,
78 $sizelimit,
79 $timelimit
80 );
81 }
82
83 public function getEntries($search_result)
84 {
85 return ldap_get_entries($this->connection, $search_result);
86 }
87
88 public static function escape(string $value, string $ignore = '', int $flags = 0): string
89 {
90 return ldap_escape($value, $ignore, $flags);
91 }
92 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Wrapper;
12
13 class LdapFactory
14 {
15 public function createFromLdapUri(string $ldapUri): LdapInterface
16 {
17 return new Ldap($ldapUri);
18 }
19
20 public function escape($value, $ignore = '', $flags = 0): string
21 {
22 return Ldap::escape($value, $ignore, $flags);
23 }
24 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 declare(strict_types=1);
10
11 namespace Org_Heigl\AuthLdap\Wrapper;
12
13 interface LdapInterface
14 {
15 public function bind($dn = null, $password = null);
16
17 public function unbind();
18
19 public function setOption($option, $value);
20
21 public function startTls();
22
23 public function error();
24
25 public function errno();
26
27 public function search(
28 $base,
29 $filter,
30 array $attributes = [],
31 $attributes_only = 0,
32 $sizelimit = -1,
33 $timelimit = -1
34 );
35
36 public function getEntries($search_result);
37
38 public static function escape(string $value, string $ignore = '', int $flags = 0): string;
39 }
1 <?php
2
3 /**
4 * Copyright (c) 2016-2016} Andreas Heigl<andreas@heigl.org>
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 *
21 * @author Andreas Heigl<andreas@heigl.org>
22 * @copyright 2016-2016 Andreas Heigl
23 * @license http://www.opensource.org/licenses/mit-license.php MIT-License
24 * @version 0.0
25 * @since 07.06.2016
26 * @link http://github.com/heiglandreas/authLDAP
27 */
28
29 namespace Org_Heigl\AuthLdapTest;
30
31 use Closure;
32 use Org_Heigl\AuthLdap\Exception\Error;
33 use Org_Heigl\AuthLdap\Exception\SearchUnsuccessfull;
34 use Org_Heigl\AuthLdap\LdapList;
35 use Org_Heigl\AuthLdap\Manager\Ldap;
36 use Org_Heigl\AuthLdap\LdapUri;
37 use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
38 use Org_Heigl\AuthLdap\Wrapper\LdapInterface;
39 use PHPUnit\Framework\Assert;
40 use PHPUnit\Framework\TestCase;
41
42 class LDAPListBaseTest extends TestCase
43 {
44 private $ldapA;
45
46 private $ldapB;
47
48 public function setUp(): void
49 {
50 $this->ldapA = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->getMock();
51 $this->ldapB = $this->getMockBuilder(Ldap::class)->disableOriginalConstructor()->getMock();
52 Assert::assertNotSame($this->ldapA, $this->ldapB);
53 parent::setUp(); // TODO: Change the autogenerated stub
54 }
55
56 public function addingItemsWorks(): void
57 {
58 $list = new LdapList();
59
60 $list->addLdap($this->ldapA);
61 $list->addLdap($this->ldapB);
62
63 Assert::assertSame([$this->ldapA, $this->ldapB], $this->getLdaps($list));
64 }
65
66 public function testFailingBindRemovesItemsFromList(): void
67 {
68 $list = new LdapList();
69
70 $list->addLdap($this->ldapA);
71 $list->addLdap($this->ldapB);
72
73 $this->ldapA->method('bind')->willThrowException(new Error('throw'));
74 $this->ldapB->method('bind')->willReturn($this->ldapB);
75
76 $list->bind();
77
78 Assert::assertCount(1, $this->getLdaps($list));
79 }
80
81 public function testFailingBindInAllConnectorsThrows(): void
82 {
83 $list = new LdapList();
84
85 $list->addLdap($this->ldapA);
86 $list->addLdap($this->ldapB);
87
88 $this->ldapA->method('bind')->willThrowException(new Error('throw'));
89 $this->ldapB->method('bind')->willThrowException(new Error('throw'));
90
91 $this->expectException(Error::class);
92 $this->expectExceptionMessage('No bind successfull');
93
94 $list->bind();
95
96 Assert::assertCount(0, $this->getLdaps($list));
97 }
98
99 public function testAuthenticatingViaListWorks(): void
100 {
101 $list = new LdapList();
102
103 $list->addLdap($this->ldapA);
104 $list->addLdap($this->ldapB);
105
106 $this->ldapA->method('authenticate')->willReturn(false);
107 $this->ldapB->method('authenticate')->willReturn(true);
108
109 Assert::assertTrue($list->authenticate('foo', 'bar'));
110
111 Assert::assertCount(1, $this->getLdaps($list));
112 }
113
114 public function testAuthenticatingViaListFailsWhenNoConnectorAuthenticates(): void
115 {
116 $list = new LdapList();
117
118 $list->addLdap($this->ldapA);
119 $list->addLdap($this->ldapB);
120
121 $this->ldapA->method('authenticate')->willReturn(false);
122 $this->ldapB->method('authenticate')->willReturn(false);
123
124 Assert::assertFalse($list->authenticate('foo', 'bar'));
125
126 Assert::assertCount(0, $this->getLdaps($list));
127 }
128
129 public function testSuccessfullSearchInOneConnectorReturnsResult(): void
130 {
131 $list = new LdapList();
132
133 $list->addLdap($this->ldapA);
134 $list->addLdap($this->ldapB);
135
136 $this->ldapA->method('search')->willThrowException(new Error('Whoot'));
137 $this->ldapB->method('search')->willReturn(['count' => 1, ['dn' => 'foo']]);
138
139 Assert::assertEquals(['count' => 1, ['dn' => 'foo']], $list->search('uid=foo'));
140 }
141
142 public function testUnsuccessfullSearchWillThrow(): void
143 {
144 $list = new LdapList();
145
146 $list->addLdap($this->ldapA);
147 $list->addLdap($this->ldapB);
148
149 $this->ldapA->method('search')->willThrowException(new Error('Whoot'));
150 $this->ldapB->method('search')->willThrowException(new Error('Whoot2'));
151
152 $this->expectException(SearchUnsuccessfull::class);
153
154 $list->search('uid=foo');
155 }
156
157
158 private function getLdaps(LdapList $list): array
159 {
160
161 // Courtesy of Marco Pivetta
162 // https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/
163 $sweetsThief = function (LdapList $kitchen) {
164 return $kitchen->items;
165 };
166
167 // Closure::bind() actually creates a new instance of the closure
168 $sweetsThief = Closure::bind($sweetsThief, null, $list);
169
170 return $sweetsThief($list);
171 }
172 }
1 <?php
2
3 /**
4 * $Id: LdapTest.php 292156 2010-09-21 19:32:01Z heiglandreas $
5 *
6 * authLdap - Authenticate Wordpress against an LDAP-Backend.
7 * Copyright (c) 2008 Andreas Heigl<andreas@heigl.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 *
23 * This file tests the basic LDAP-Tasks
24 *
25 * @category authLdap
26 * @package authLdap
27 * @subpackage UnitTests
28 * @author Andreas Heigl<andreas@heigl.org>
29 * @copyright 2010 Andreas Heigl<andreas@heigl.org>
30 * @license GPL
31 * @since 21.09.2010
32 */
33
34 namespace Org_Heigl\AuthLdapTest;
35
36 use Exception;
37 use Generator;
38 use Org_Heigl\AuthLdap\LdapUri;
39 use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
40 use PHPUnit\Framework\TestCase;
41 use Org_Heigl\AuthLdap\Manager\Ldap;
42
43 class LdapTest extends TestCase
44 {
45 /**
46 *
47 * @dataProvider dpInstantiateLdapClass
48 * @param array $expected
49 * @param array $given
50 */
51 public function testInstantiateLdapClass($ldapUri, $debug, $startTls)
52 {
53 $ldap = new Ldap(new LdapFactory(), LdapUri::fromString($ldapUri), $debug, $startTls);
54 self::assertInstanceOf(Ldap::class, $ldap);
55 }
56
57 /**
58 * @dataProvider dpExceptionsWhenInstantiatingLdapClass
59 * @param string $expected
60 */
61 public function testExceptionsWhenInstantiatingLdapClass(string $expected)
62 {
63 self::expectException(Exception::class);
64 new Ldap(new LdapFactory(), LdapUri::fromString($expected));
65 }
66
67 public function dpInstantiateLdapClass(): Generator
68 {
69 yield [
70 'ldap://uid=jondoe,cn=users,cn=example,c=org:secret@ldap.example.org/cn=example,c=org',
71 true,
72 false,
73 [
74 'username' => 'uid=jondoe,cn=users,cn=example,c=org',
75 'password' => 'secret',
76 'server' => 'ldap.example.org',
77 'baseDn' => 'cn=example,c=org',
78 'debug' => true,
79 ],
80 ];
81 yield [
82 'ldap://uid=jondoe,cn=users,cn=example,c=org@ldap.example.org/cn=example,c=org',
83 true,
84 false,
85 [
86 'username' => 'uid=jondoe,cn=users,cn=example,c=org',
87 'password' => '',
88 'server' => 'ldap.example.org',
89 'baseDn' => 'cn=example,c=org',
90 'debug' => true,
91 ],
92 ];
93 yield [
94 'ldap://ldap.example.org/cn=example,c=org',
95 true,
96 false,
97 [
98 'username' => 'anonymous',
99 'password' => '',
100 'server' => 'ldap.example.org',
101 'baseDn' => 'cn=example,c=org',
102 'debug' => true,
103 ],
104 ];
105 // yield [
106 // 'ldap://ldap.example.org',
107 // true,
108 // false,
109 // [
110 // 'username' => 'anonymous',
111 // 'password' => '',
112 // 'server' => 'ldap.example.org',
113 // 'baseDn' => '',
114 // 'debug' => true
115 // ]
116 // ];
117 yield [
118 'ldap://uid=jondoe,cn=users,cn=example,c=org:secret@ldap.example.org/cn=example,c=org',
119 false,
120 false,
121 [
122 'username' => 'uid=jondoe,cn=users,cn=example,c=org',
123 'password' => 'secret',
124 'server' => 'ldap.example.org',
125 'baseDn' => 'cn=example,c=org',
126 'debug' => false,
127 ],
128 ];
129 yield [
130 'ldap://ldap.example.org/cn=test%20example,c=org',
131 false,
132 false,
133 [
134 'username' => 'anonymous',
135 'password' => '',
136 'server' => 'ldap.example.org',
137 'baseDn' => 'cn=test example,c=org',
138 'debug' => false,
139 ],
140 ];
141 }
142
143 public function dpExceptionsWhenInstantiatingLdapClass(): Generator
144 {
145 yield ['ldap://ldap.example.org'];
146 yield ['ldap://foo:bar@/cn=example,c=org'];
147 yield ['http://ldap.example.org'];
148 yield ['fooBar'];
149 yield ['ldap://ldap.example.org/'];
150 yield ['()123üäö'];
151 }
152
153 public function testThatGroupMappingWorks()
154 {
155 $groups = [
156 'count' => 1,
157 0 => [
158 'dn' => 'dn-1',
159 'count' => 1,
160 0 => 'group',
161 'group' => [
162 'count' => 2,
163 0 => 'angličtina@ff.cuni.cz',
164 1 => 'literatura@ff.cuni.cz',
165 ],
166 ],
167 ];
168
169 $grp = [];
170 for ($i = 0; $i < $groups ['count']; $i++) {
171 for ($k = 0; $k < $groups[$i][strtolower('group')]['count']; $k++) {
172 $grp[] = $groups[$i][strtolower('group')][$k];
173 }
174 }
175
176 $this->assertEquals([
177 'angličtina@ff.cuni.cz',
178 'literatura@ff.cuni.cz',
179 ], $grp);
180
181 $role = '';
182 foreach (
183 [
184 'testrole' => 'literatura@ff.cuni.cz,literatura@ff.cuni.cz',
185 ] as $key => $val
186 ) {
187 $currentGroup = explode(',', $val);
188 // Remove whitespaces around the group-ID
189 $currentGroup = array_map('trim', $currentGroup);
190 if (0 < count(array_intersect($currentGroup, $grp))) {
191 $role = $key;
192 break;
193 }
194 }
195
196 $this->assertEquals('testrole', $role);
197 }
198 }
1 <?php
2
3 namespace Org_Heigl\AuthLdapTest;
4
5 use Generator;
6 use Org_Heigl\AuthLdap\Exception\InvalidLdapUri;
7 use Org_Heigl\AuthLdap\LdapUri;
8 use PHPUnit\Framework\Assert;
9 use PHPUnit\Framework\TestCase;
10
11 use function getenv;
12 use function putenv;
13
14 class LdapUriTest extends TestCase
15 {
16 public function toStringProvider(): Generator
17 {
18 yield ['ldaps://foo:bar@foo.bar/baz', 'ldaps://foo.bar:636', 'foo', 'bar', 'baz'];
19 yield ['env:LDAP_URI', 'ldaps://foo.bar:636', 'foo', 'bar', 'baz', [
20 'LDAP_URI' => 'ldaps://foo:bar@foo.bar/baz',
21 ]];
22 yield ['ldaps://foo:%env:LDAP_PASSWORD%@foo.bar/baz', 'ldaps://foo.bar:636', 'foo', 'bar', 'baz', [
23 'LDAP_PASSWORD' => 'bar',
24 ]];
25 yield ['ldaps://foo:%env:LDAP_PASSWORD%@foo.bar/baz', 'ldaps://foo.bar:636', 'foo', 'ba r', 'baz', [
26 'LDAP_PASSWORD' => 'ba r',
27 ]];
28 }
29
30 public function fromStringProvider(): Generator
31 {
32 yield ['ldaps://foo:bar@foo.bar/baz', false];
33 yield ['env:LDAP_URI', false];
34 yield ['foo:MyLdapUri', true];
35 }
36
37 /**
38 * @dataProvider toStringProvider
39 */
40 public function testToString(string $uri, string $result, $user, $password, $baseDn, array $env = []): void
41 {
42 foreach ($env as $key => $value) {
43 putenv("$key=$value");
44 }
45 $ldapUri = LdapUri::fromString($uri);
46 Assert::assertSame($result, $ldapUri->toString());
47 Assert::assertSame($user, $ldapUri->getUsername());
48 Assert::assertSame($password, $ldapUri->getPassword());
49 Assert::assertSame($baseDn, $ldapUri->getBaseDn());
50 }
51
52 /** @dataProvider fromStringProvider */
53 public function testFromString(string $uri, bool $failure = false): void
54 {
55 if ($failure) {
56 self::expectException(InvalidLdapUri::class);
57 }
58 $ldapUri = LdapUri::fromString($uri);
59 self::assertInstanceOf(LdapUri::class, $ldapUri);
60 }
61
62 public function testSettingLdapsWillSetCorrectPort(): void
63 {
64 $uri = LdapUri::fromString('ldaps://example.org/foo');
65
66 Assert::assertSame('ldaps://example.org:636', $uri->toString());
67 }
68
69 public function testSettingLdapWillSetCorrectPort(): void
70 {
71 $uri = LdapUri::fromString('ldap://example.org/foo');
72
73 Assert::assertSame('ldap://example.org:389', $uri->toString());
74 }
75
76 /**
77 * @dataProvider anonymousProvider
78 */
79 public function testUriIsAnonymous(string $uri): void
80 {
81 $uri = LdapUri::fromString($uri);
82 Assert::assertTrue($uri->isAnonymous());
83 }
84
85 public function anonymousProvider(): Generator
86 {
87 yield ['ldaps://test.example.com/dc=com'];
88 yield ['ldaps://foo@test.example.com/dc=com'];
89 yield ['ldaps://%20:password@test.example.com/dc=com'];
90 yield ['ldaps://anonymous:password@test.example.com/dc=com'];
91 }
92
93 public function testMissingSchemaThrows(): void
94 {
95 $this->expectException(InvalidLdapUri::class);
96
97 LdapUri::fromString('ldaps.example.com');
98 }
99
100 public function testMWrongSchemaThrows(): void
101 {
102 $this->expectException(InvalidLdapUri::class);
103
104 LdapUri::fromString('environ://ldaps.example.com');
105 }
106
107 public function testMissingHostThrows(): void
108 {
109 $this->expectException(InvalidLdapUri::class);
110
111 LdapUri::fromString('ldaps:/foo=bar');
112 }
113
114 public function testgettingUriFromEnvironment(): void
115 {
116 putenv('URI=ldaps://example.com/foo');
117 $uri = LdapUri::fromString('env:URI');
118
119 Assert::assertSame('ldaps://example.com:636', (string) $uri);
120 Assert::assertSame('foo', $uri->getBaseDn());
121 }
122
123 public function testgettingUriFromEmptyEnvironment(): void
124 {
125 putenv('URI');
126 $this->expectException(InvalidLdapUri::class);
127 $uri = LdapUri::fromString('env:URI');
128 }
129 }
1 <?php
2
3 /**
4 * Copyright (c) 2016-2016} Andreas Heigl<andreas@heigl.org>
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 *
21 * @author Andreas Heigl<andreas@heigl.org>
22 * @copyright 2016-2016 Andreas Heigl
23 * @license http://www.opensource.org/licenses/mit-license.php MIT-License
24 * @version 0.0
25 * @since 07.06.2016
26 * @link http://github.com/heiglandreas/authLDAP
27 */
28
29 namespace Org_Heigl\AuthLdapTest\Manager;
30
31 use Org_Heigl\AuthLdap\Exception\Error;
32 use Org_Heigl\AuthLdap\LdapList;
33 use Org_Heigl\AuthLdap\LdapUri;
34 use Org_Heigl\AuthLdap\Manager\Ldap;
35 use Org_Heigl\AuthLdap\Wrapper\Ldap as LdapWrapper;
36 use Org_Heigl\AuthLdap\Wrapper\LdapFactory;
37 use Org_Heigl\AuthLdap\Wrapper\LdapInterface;
38 use PHPUnit\Framework\Assert;
39 use PHPUnit\Framework\TestCase;
40
41 class LDAPBaseTest extends TestCase
42 {
43 private LdapFactory $factory;
44
45 private LdapInterface $wrapper;
46
47 public function setUp(): void
48 {
49 $this->wrapper = $this->getMockBuilder(LdapInterface::class)->getMock();
50 $this->factory = $this->getMockBuilder(LdapFactory::class)->getMock();
51 $this->factory->method('createFromLdapUri')->willReturn($this->wrapper);
52 $this->factory->method('escape')->willReturnCallback(function ($value, $ignore, $flags) {
53 return \Org_Heigl\AuthLdap\Wrapper\Ldap::escape($value, $ignore, $flags);
54 });
55 }
56
57 /**
58 * @dataProvider bindingWithPasswordProvider
59 * @testdox Binding user $user with password $password using a filter $filter works
60 */
61 public function testThatBindingWithPasswordWorks($user, $password, $filter, $uri)
62 {
63 $uri = LdapUri::fromString($uri);
64 $this->wrapper
65 ->method('bind')
66 ->willReturn(true);
67
68 $this->wrapper
69 ->expects($this->once())
70 ->method('search')
71 ->with($uri->getBaseDn(), sprintf($filter, $user));
72
73 $this->wrapper
74 ->method('getEntries')
75 ->willReturn(['count' => 1, 0 => ['dn' => 'foo']]);
76
77 $ldap = new Ldap($this->factory, $uri);
78 $this->assertTrue($ldap->authenticate($user, $password, $filter));
79 }
80
81 public function bindingWithPasswordProvider()
82 {
83 return [
84 [
85 'user3',
86 'user!"',
87 'uid=%s',
88 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=example,dc=org'
89 ], [
90 // 'admin',
91 // 'insecure',
92 // 'cn=%s',
93 // 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=example,dc=org'
94 // ], [
95 'user1',
96 'user1',
97 'uid=%s',
98 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=example,dc=org'
99 ], [
100 'user 4',
101 'user!"',
102 'uid=%s',
103 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=example,dc=org'
104 ], [
105 'user 5',
106 'user!"',
107 'uid=%s',
108 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=test%20space,dc=example,dc=org'
109 ],
110 ];
111 }
112
113 /**
114 * @param $uri
115 * @dataProvider initialBindingToLdapServerWorksProvider
116 */
117 public function testThatInitialBindingWorks($uri)
118 {
119 $this->wrapper
120 ->method('bind')
121 ->willReturn(true);
122
123 $ldap = new LDAP($this->factory, LdapUri::fromString($uri));
124 $this->assertInstanceof(Ldap::class, $ldap->bind());
125 }
126
127 /**
128 * @param $uri
129 * @dataProvider initialBindingToLdapServerWorksProvider
130 */
131 public function testThatInitialBindingToMultipleLdapsWorks($uri)
132 {
133 $this->wrapper->expects($this->once())
134 ->method('bind')
135 ->with('uid=user 5,dc=test space,dc=example,dc=org', 'user!"')
136 ->willReturn(true);
137
138 $list = new LdapList();
139 $list->addLDAP(new LDAP($this->factory, LdapUri::fromString($uri)));
140 $this->assertTrue($list->bind());
141 }
142
143 public function initialBindingToLdapServerWorksProvider()
144 {
145 return [
146 ['ldap://uid=user%205,dc=test%20space,dc=example,dc=org:user!"' .
147 '@127.0.0.1:3389/dc=test%20space,dc=example,dc=org'],
148 ];
149 }
150
151 /**
152 * @dataProvider provideUnescapedData
153 */
154 public function testThatPassedDataIsEscaped($unescaped, $escaped): void
155 {
156 $ldap = new LDAP($this->factory, LdapUri::fromString(
157 'ldap://cn=admin,dc=example,dc=org:insecure@127.0.0.1:3389/dc=example,dc=org'
158 ));
159
160 $this->wrapper->expects($this->exactly(2))
161 ->method('bind')
162 ->withConsecutive(
163 ['cn=admin,dc=example,dc=org', 'insecure'],
164 ['foo', 'password'],
165 )
166 ->willReturnOnConsecutiveCalls(true, true);
167 $this->wrapper->expects($this->once())->method('search')->with(
168 'dc=example,dc=org',
169 $escaped,
170 ['uid'],
171 );
172 $this->wrapper->method('getEntries')->willReturn(['count' => 1, 0 => ['dn' => 'foo']]);
173
174 $ldap->authenticate($unescaped, 'password');
175 }
176
177 public function provideUnescapedData(): array
178 {
179 return [
180 ['\’foobar', '(uid=\5c’foobar)'],
181 ['XXX;(&(uid=Admin)(userPassword=A*))', '(uid=XXX;\28&\28uid=Admin\29\28userPassword=A\2a\29\29)'],
182 ];
183 }
184
185
186 public function testSettingStartTls(): void
187 {
188 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
189
190 $this->wrapper->expects($this->once())->method('startTls');
191 $this->wrapper->method('bind')->willReturn(true);
192
193 $ldap->bind();
194 }
195
196
197 public function testUnsettingConnectionBeforeBinding(): void
198 {
199 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
200
201 $this->wrapper->method('bind')->willReturn(true);
202 $this->wrapper->expects($this->once())->method('unbind');
203
204 $ldap->connect();
205 $ldap->disconnect();
206 }
207
208 public function testErrorIsThrownOnUnsuccessfullBInd(): void
209 {
210 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
211
212 $this->wrapper->method('bind')->willReturn(false);
213 $this->expectException(Error::class);
214
215 $ldap->bind();
216 }
217
218 public function testFailingSearchThrowsError(): void
219 {
220 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
221
222 $this->wrapper->method('bind')->willReturn(true);
223 $this->wrapper->method('search')->willReturn(false);
224
225 $this->expectException(Error::class);
226 $this->expectExceptionMessage('no result found');
227
228 $ldap->bind();
229 $ldap->search('uid=foo');
230 }
231
232 public function testFailingSearchResultFetchingThrowsError(): void
233 {
234 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
235
236 $this->wrapper->method('bind')->willReturn(true);
237 $this->wrapper->method('search')->willReturn(true);
238 $this->wrapper->method('getEntries')->willReturn(false);
239
240 $this->expectException(Error::class);
241 $this->expectExceptionMessage('invalid results found');
242
243 $ldap->bind();
244 $ldap->search('uid=foo');
245 }
246
247 public function testSearchingWithoutBindingThrowsError(): void
248 {
249 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
250
251 $this->expectException(Error::class);
252 $this->expectExceptionMessage('No resource handle available');
253
254 $ldap->search('uid=foo');
255 }
256
257 public function testAuthenticatingFailsWithNoSearchResults(): void
258 {
259 $ldap = new Ldap($this->factory, LdapUri::fromString('ldap://example.com/foo=bar'), true);
260
261 $this->wrapper->method('bind')->willReturn(true);
262 $this->wrapper->method('search')->willReturn(true);
263 $this->wrapper->method('getEntries')->willReturn(['count' => 0]);
264
265 $ldap->bind();
266 Assert::assertFalse($ldap->authenticate('foo', 'bar'));
267 }
268 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 namespace Org_Heigl\AuthLdapTest;
10
11 use Org_Heigl\AuthLdap\UserRoleHandler;
12 use WorDBless\BaseTestCase;
13 use WP_User;
14
15 class UserRoleHandlerTest extends BaseTestCase
16 {
17 public function testUserRolesAreAssignedAsExpected() : void
18 {
19 $user = new WP_User(1);
20
21 $handler = new UserRoleHandler();
22
23 $handler->addRolesToUser($user, ['author', 'user']);
24
25 self::assertEquals(['author'], $user->roles);
26 }
27
28 public function testEqualUserRolesAreEasy() : void
29 {
30 $user = new WP_User(1);
31 $user->add_role('administrator');
32 $user->add_role('author');
33
34 $handler = new UserRoleHandler();
35
36 $handler->addRolesToUser($user, ['administrator', 'author']);
37
38 self::assertEquals(['administrator', 'author'], $user->roles);
39 }
40
41 public function testUserRolesAreNotAssignedWhenUserAlreadyHasRole() : void
42 {
43 $user = new WP_User(1);
44 $user->add_role('administrator');
45 $user->add_role('author');
46
47 $handler = new UserRoleHandler();
48
49 $handler->addRolesToUser($user, ['author', 'editor']);
50
51 self::assertEquals(['author', 'editor'], $user->roles);
52 }
53
54 public function testEmptyRolesAreIgnored() : void
55 {
56 $user = new WP_User(1);
57 $user->add_role('administrator');
58
59 $handler = new UserRoleHandler();
60 $handler->addRolesToUser($user, []);
61
62 self::assertEquals(['administrator'], $user->roles);
63 }
64 }
1 <?php
2
3 /**
4 * Copyright Andreas Heigl <andreas@heigl.org>
5 *
6 * Licenses under the MIT-license. For details see the included file LICENSE.md
7 */
8
9 require_once __DIR__ . '/../vendor/autoload.php'; // adjust the path as needed
10
11 \WorDBless\Load::load();
1 1.4.20
...\ No newline at end of file ...\ No newline at end of file
1 apiVersion: backstage.io/v1alpha1
2 kind: Component
3 metadata:
4 name: wp-auth-ldap
5 annotations:
6 github.com/project-slug: lampo/wp-auth-ldap
7 spec:
8 type: general
9 lifecycle: production
10 owner: B2C Developers
1 {
2 "name" : "lampo/wp-auth-ldap",
3 "type" : "wordpress-plugin",
4 "description": "Fork of http://github.com/heiglandreas/authLdap, moves settings to defined constants.",
5 "keywords": ["ldap","authenticate", "auth", "wordpress"],
6 "homepage": "http://github.com/lampo/wp-auth-ldap",
7 "license": "MIT",
8 "authors": [{
9 "name": "Andreas Heigl",
10 "email": "andreas@heigl.org",
11 "homepage": "http://andreas.heigl.org",
12 "role": "Developer"
13 },{
14 "name": "Micah Flatt",
15 "email": "mflatt@flattware.net",
16 "role": "Developer"
17 }],
18 "require" : {
19 "php": ">=5.4",
20 "composer/installers": "~1.0"
21 },
22 "autoload" : {
23 "psr-4" : {
24 "Org_Heigl\\AuthLdap\\" : "./"
25 }
26 }
27 }
1 <?php
2 /**
3 * $Id: ldap.php 381646 2011-05-06 09:37:31Z heiglandreas $
4 *
5 * authLdap - Authenticate Wordpress against an LDAP-Backend.
6 * Copyright (c) 2008 Andreas Heigl<andreas@heigl.org>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 *
22 * This file handles the basic LDAP-Tasks
23 *
24 * @author Andreas Heigl<andreas@heigl.org>
25 * @package authLdap
26 * @category authLdap
27 * @since 2008
28 */
29 namespace Org_Heigl\AuthLdap;
30
31 use Exception;
32
33 class LDAP
34 {
35 private $_server = '';
36
37 private $_scheme = 'ldap';
38
39 private $_port = 389;
40
41 private $_baseDn = '';
42
43 private $_debug = false;
44 /**
45 * This property contains the connection handle to the ldap-server
46 *
47 * @var Ressource
48 */
49 private $_ch = null;
50
51 private $_username = '';
52
53 private $_password = '';
54
55 private $_starttls = false;
56
57 public function __construct($URI, $debug = false, $starttls = false)
58 {
59 $this->_debug=$debug;
60 $array = parse_url($URI);
61 if (! is_array($array)) {
62 throw new Exception($URI . ' seems not to be a valid URI');
63 }
64 $url = array_map(function ($item) { return urldecode($item); }, $array);
65 if (false === $url) {
66 throw new Exception($URI . ' is an invalid URL');
67 }
68 if (! isset ( $url['scheme'] )) {
69 throw new Exception($URI . ' does not provide a scheme');
70 }
71 if (0 !== strpos($url['scheme'], 'ldap')) {
72 throw new Exception($URI . ' is an invalid LDAP-URI');
73 }
74 if (! isset ( $url['host'] )) {
75 throw new Exception($URI . ' does not provide a server');
76 }
77 if (! isset ( $url['path'] )) {
78 throw new Exception($URI . ' does not provide a search-base');
79 }
80 if (1 == strlen($url['path'])) {
81 throw new Exception($URI . ' does not provide a valid search-base');
82 }
83 $this -> _server = $url['host'];
84 $this -> _scheme = $url['scheme'];
85 $this -> _baseDn = substr($url['path'], 1);
86 if (isset ( $url['user'] )) {
87 $this -> _username = $url['user'];
88 }
89 if ('' == trim($this -> _username)) {
90 $this -> _username = 'anonymous';
91 }
92 if (isset ( $url['pass'] )) {
93 $this -> _password = $url['pass'];
94 }
95 if (isset ( $url['port'] )) {
96 $this -> _port = $url['port'];
97 }
98 $this->_starttls = $starttls;
99 }
100
101 /**
102 * Connect to the given LDAP-Server
103 *
104 * @return LDAP
105 * @throws AuthLdap_Exception
106 */
107 public function connect()
108 {
109 $this -> disconnect();
110 if ('ldaps' == $this->_scheme && 389 == $this->_port) {
111 $this->_port = 636;
112 }
113
114 $this->_ch = @ldap_connect($this->_scheme . '://' . $this->_server . ':' . $this -> _port);
115 if (! $this->_ch) {
116 throw new AuthLDAP_Exception('Could not connect to the server');
117 }
118 ldap_set_option($this->_ch, LDAP_OPT_PROTOCOL_VERSION, 3);
119 ldap_set_option($this->_ch, LDAP_OPT_REFERRALS, 0);
120 //if configured try to upgrade encryption to tls for ldap connections
121 if ($this->_starttls) {
122 ldap_start_tls($this->_ch);
123 }
124 return $this;
125 }
126
127 /**
128 * Disconnect from a resource if one is available
129 *
130 * @return LDAP
131 */
132 public function disconnect()
133 {
134 if (is_resource($this->_ch)) {
135 @ldap_unbind($this->_ch);
136 }
137 $this->_ch = null;
138 return $this;
139 }
140
141 /**
142 * Bind to an LDAP-Server with the given credentials
143 *
144 * @return LDAP
145 * @throw AuthLdap_Exception
146 */
147 public function bind()
148 {
149 if (! $this->_ch) {
150 $this->connect();
151 }
152 if (! is_resource($this->_ch)) {
153 throw new AuthLDAP_Exception('No Resource-handle given');
154 }
155 $bind = false;
156 if (( ( $this->_username )
157 && ( $this->_username != 'anonymous') )
158 && ( $this->_password != '' ) ) {
159 $bind = @ldap_bind($this->_ch, $this->_username, $this->_password);
160 } else {
161 $bind = @ldap_bind($this->_ch);
162 }
163 if (! $bind) {
164 throw new AuthLDAP_Exception('bind was not successfull: ' . ldap_error($this->_ch));
165 }
166 return $this;
167 }
168
169 public function getErrorNumber()
170 {
171 return @ldap_errno($this->_ch);
172 }
173
174 public function getErrorText()
175 {
176 return @ldap_error($this->_ch);
177 }
178
179 /**
180 * This method does the actual ldap-serch.
181 *
182 * This is using the filter <var>$filter</var> for retrieving the attributes
183 * <var>$attributes</var>
184 *
185 *
186 * @param string $filter
187 * @param array $attributes
188 * @return array
189 */
190 public function search($filter, $attributes = array('uid'))
191 {
192 if (! is_Resource($this->_ch)) {
193 throw new AuthLDAP_Exception('No resource handle avbailable');
194 }
195 $result = @ldap_search($this->_ch, $this->_baseDn, $filter, $attributes);
196 if ($result === false) {
197 throw new AuthLDAP_Exception('no result found');
198 }
199 $this->_info = @ldap_get_entries($this->_ch, $result);
200 if ($this->_info === false) {
201 throw new AuthLDAP_Exception('invalid results found');
202 }
203 return $this -> _info;
204 }
205
206 /**
207 * This method sets debugging to ON
208 */
209 public function debugOn()
210 {
211 $this->_debug = true;
212 return $this;
213 }
214
215 /**
216 * This method sets debugging to OFF
217 */
218 public function debugOff()
219 {
220 $this->_debug = false;
221 return $this;
222 }
223
224 /**
225 * This method authenticates the user <var>$username</var> using the
226 * password <var>$password</var>
227 *
228 * @param string $username
229 * @param string $password
230 * @param string $filter OPTIONAL This parameter defines the Filter to be used
231 * when searchin for the username. This MUST contain the string '%s' which
232 * will be replaced by the vaue given in <var>$username</var>
233 * @return boolean true or false depending on successfull authentication or not
234 */
235 public function authenticate($username, $password, $filter = '(uid=%s)')
236 {
237 //return true;
238 $this->connect();
239 $this->bind();
240 $res = $this->search(sprintf($filter, $username));
241 if (! $res || ! is_array($res) || ( $res ['count'] != 1 )) {
242 return false;
243 }
244 $dn = $res[0]['dn'];
245 if ($username && $password) {
246 if (@ldap_bind($this->_ch, $dn, $password)) {
247 return true;
248 }
249 }
250 return false;
251 }
252 /**
253 * $this method loggs errors if debugging is set to ON
254 */
255 public function logError()
256 {
257 if ($this->_debug) {
258 $_v = debug_backtrace();
259 throw new AuthLDAP_Exception('[LDAP_ERROR]' . ldap_errno($this->_ch) . ':' . ldap_error($this->_ch), $_v[0]['line']);
260 }
261 }
262 }
263
264 class AuthLDAP_Exception extends Exception
265 {
266 public function __construct($message, $line = null)
267 {
268 parent :: __construct($message);
269 if ($line) {
270 $this -> line = $line;
271 }
272 }
273 }