From 8cb94e926381a654a5bf048e7045b8fa73d5202e Mon Sep 17 00:00:00 2001 From: Carsten Rose <carsten.rose@math.uzh.ch> Date: Tue, 28 Mar 2017 21:16:13 +0200 Subject: [PATCH] #3456 / LDAP: with Credentials to access 'webpass' Manual.rst: Updated doc for a) config.qfq.ini: LDAP_1_RDN, LDAP_1_PASSWORD, b) Form.parameter|FormElement.parameter: ldapUseBindCredentials ErrorHandler.php: removed details - the end user should not too many details. FormAction.php, Ldap.php, QuickFormQuery.php: implement 'ldapUseBindCredentials' Ldap.php: set_error_handler() to catch ldap_bind() problems. Always set LDAP_OPT_PROTOCOL_VERSION=3 - this might cause problems with som LDAP Servers - we will see. --- extension/Documentation/Manual.rst | 193 ++++++++++-------- extension/config.qfq.example.ini | 5 +- extension/qfq/qfq/AbstractBuildForm.php | 13 +- extension/qfq/qfq/Constants.php | 6 + extension/qfq/qfq/QuickFormQuery.php | 1 + extension/qfq/qfq/exceptions/ErrorHandler.php | 4 +- extension/qfq/qfq/form/FormAction.php | 7 +- extension/qfq/qfq/helper/Ldap.php | 23 +++ 8 files changed, 158 insertions(+), 94 deletions(-) diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst index fad399ca7..7be8172cd 100644 --- a/extension/Documentation/Manual.rst +++ b/extension/Documentation/Manual.rst @@ -166,66 +166,70 @@ Setup a *report* to manage all *forms*: config.qfq.ini -------------- -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| Keyword | Example | Description | -+=============================+=========================================+============================================================================+ -| DB_USER | DB_USER=qfqUser | Credentials configured in MySQL | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DB_PASSWORD | DB_PASSWORD=12345678 | Credentials configured in MySQL | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DB_SERVER | DB_SERVER=localhost | Hostname of MySQL Server | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DB_NAME | DB_NAME=qfq_db | Database name | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DB_NAME_TEST | DB_NAME_TEST=qfq_db_test | Used during development of QFQ | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DB_INIT | DB_INIT=set names utf8 | Global init for using the database. | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| SQL_LOG | SQL_LOG=sql.log | Filename to log SQL commands: relative to <ext_dir> or absolute. | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| SQL_LOG_MODE | SQL_LOG_MODE=modify | *all*: every statement will be logged - this is a lot | -| | | *modify*: log only statements who change data | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| SHOW_DEBUG_INFO | SHOW_DEBUG_INFO=auto | Possible values: auto|yes|no. For 'auto': If a BE User is logged in, | -| | | debug information will be shown on the fronend. | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| CSS_LINK_CLASS_INTERNA L | CSS_LINK_CLASS_INTERNAL=internal | CSS class name of links which points to internal tagets | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| CSS_LINK_CLASS_EXTERNAL | CSS_LINK_CLASS_EXTERNAL=external | CSS class name of links which points to internal tagets | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| CSS_CLASS_QFQ_CONTAINER |CSS_CLASS_QFQ_CONTAINER=container | QFQ with own Bootstrap: 'container'. | -| | | QFQ already nested in Bootstrap of mainpage: <empty> | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| CSS_CLASS_QFQ_FORM_PILL |CSS_CLASS_QFQ_FORM_PILL=qfq-color-grey-1 | Wrap around title bar for pills: CSS Class, typically a background color | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| CSS_CLASS_QFQ_FORM_BODY |CSS_CLASS_QFQ_FORM_BODY=qfq-color-grey-2 | Wrap around formelements: CSS Class, typically a background color | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| DATE_FORMAT | DATE_FORMAT= yyyy-mm-dd | Possible options: yyyy-mm-dd, dd.mm.yyyy | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_DATA_PATTERN_ERROR |FORM_DATA_PATTERN_ERROR=please check pa. | Customizable error message used in validator.js. 'pattern' violation | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_DATA_REQUIRED_ERROR |FORM_DATA_REQUIRED_ERROR=missing value | Customizable error message used in validator.js. 'required' fields | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_DATA_MATCH_ERROR |FORM_DATA_MATCH_ERROR=type error | Customizable error message used in validator.js. 'match' retype mismatch | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_DATA_ERROR |FORM_DATA_ERROR=generic error | Customizable error message used in validator.js. 'no specific' given | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_BS_COLUMNS | FORM_BS_COLUMNS=12 | The whole form will be wrapped in 'col-md-??'. Default is 12 for 100% | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_BS_LABEL_COLUMNS | FORM_BS_LABEL_COLUMNS = 3 | Default number of BS columns for the 'label'-column | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_BS_INPUT_COLUMNS | FORM_BS_INPUT_COLUMNS = 6 | Default number of BS columns for the 'input'-column | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| FORM_BS_NOTE_COLUMNS | FORM_BS_NOTE_COLUMNS = 3 | Default number of BS columns for the 'note'-column | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| Keyword | Example | Description | ++=============================+=================================================+============================================================================+ +| DB_USER | DB_USER=qfqUser | Credentials configured in MySQL | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DB_PASSWORD | DB_PASSWORD=12345678 | Credentials configured in MySQL | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DB_SERVER | DB_SERVER=localhost | Hostname of MySQL Server | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DB_NAME | DB_NAME=qfq_db | Database name | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DB_NAME_TEST | DB_NAME_TEST=qfq_db_test | Used during development of QFQ | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DB_INIT | DB_INIT=set names utf8 | Global init for using the database. | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| SQL_LOG | SQL_LOG=sql.log | Filename to log SQL commands: relative to <ext_dir> or absolute. | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| SQL_LOG_MODE | SQL_LOG_MODE=modify | *all*: every statement will be logged - this is a lot | +| | | *modify*: log only statements who change data | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| SHOW_DEBUG_INFO | SHOW_DEBUG_INFO=auto | Possible values: auto|yes|no. For 'auto': If a BE User is logged in, | +| | | debug information will be shown on the fronend. | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| CSS_LINK_CLASS_INTERNA L | CSS_LINK_CLASS_INTERNAL=internal | CSS class name of links which points to internal tagets | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| CSS_LINK_CLASS_EXTERNAL | CSS_LINK_CLASS_EXTERNAL=external | CSS class name of links which points to internal tagets | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| CSS_CLASS_QFQ_CONTAINER |CSS_CLASS_QFQ_CONTAINER=container | QFQ with own Bootstrap: 'container'. | +| | | QFQ already nested in Bootstrap of mainpage: <empty> | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| CSS_CLASS_QFQ_FORM_PILL |CSS_CLASS_QFQ_FORM_PILL=qfq-color-grey-1 | Wrap around title bar for pills: CSS Class, typically a background color | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| CSS_CLASS_QFQ_FORM_BODY |CSS_CLASS_QFQ_FORM_BODY=qfq-color-grey-2 | Wrap around formelements: CSS Class, typically a background color | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| DATE_FORMAT | DATE_FORMAT= yyyy-mm-dd | Possible options: yyyy-mm-dd, dd.mm.yyyy | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_DATA_PATTERN_ERROR |FORM_DATA_PATTERN_ERROR=please check pa. | Customizable error message used in validator.js. 'pattern' violation | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_DATA_REQUIRED_ERROR |FORM_DATA_REQUIRED_ERROR=missing value | Customizable error message used in validator.js. 'required' fields | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_DATA_MATCH_ERROR |FORM_DATA_MATCH_ERROR=type error | Customizable error message used in validator.js. 'match' retype mismatch | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_DATA_ERROR |FORM_DATA_ERROR=generic error | Customizable error message used in validator.js. 'no specific' given | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_BS_COLUMNS | FORM_BS_COLUMNS=12 | The whole form will be wrapped in 'col-md-??'. Default is 12 for 100% | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_BS_LABEL_COLUMNS | FORM_BS_LABEL_COLUMNS = 3 | Default number of BS columns for the 'label'-column | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_BS_INPUT_COLUMNS | FORM_BS_INPUT_COLUMNS = 6 | Default number of BS columns for the 'input'-column | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| FORM_BS_NOTE_COLUMNS | FORM_BS_NOTE_COLUMNS = 3 | Default number of BS columns for the 'note'-column | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ | FORM_BUTTON_ON_CHANGE_CLASS | FORM_BUTTON_ON_CHANGE_CLASS=alert-info btn-info | Color for save button after modification | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| BASE_URL_PRINT | BASE_URL_PRINT=http://example.com | URL where wkhtmltopdf will fetch the HTML (no parameter, those comes later)| -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| WKHTMLTOPDF | WKHTMLTOPDF=/usr/bin/wkhtmltopdf | Binary where to find wkhtmltopdf | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ -| EDIT_FORM_PAGE | EDIT_FORM_PAGE = form | T3 Pagealias to edit a form. | -+-----------------------------+-----------------------------------------+----------------------------------------------------------------------------+ ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| BASE_URL_PRINT | BASE_URL_PRINT=http://example.com | URL where wkhtmltopdf will fetch the HTML (no parameter, those comes later)| ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| WKHTMLTOPDF | WKHTMLTOPDF=/usr/bin/wkhtmltopdf | Binary where to find wkhtmltopdf. | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| EDIT_FORM_PAGE | EDIT_FORM_PAGE = form | T3 Pagealias to edit a form. | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ +| LDAP_1_RDN | LDAP_1_RDN='ou=Admin,ou=example,dc=com' | Credentials for non-anonymous LDAP access | ++-----------------------------+-------------------------------------------------+ | +| LDAP_1_PASSWORD | LDAP_1_PASSWORD=mySecurePassword | | ++-----------------------------+-------------------------------------------------+----------------------------------------------------------------------------+ Example: *typo3conf/config.qfq.ini* @@ -257,6 +261,8 @@ Example: *typo3conf/config.qfq.ini* BASE_URL_PRINT=http://example.com WKHTMLTOPDF=/usr/bin/wkhtmltopdf ;EDIT_FORM_PAGE = form + ;LDAP_1_RDN='ou=Admin,dc=example,dc=com' + ;LDAP_1_PASSWORD=mySecurePassword .. _local-documentation: @@ -807,43 +813,48 @@ LDAP A form can retrieve values from an LDAP server to display or to save them. Configuration options for LDAP will be specified in the *parameter* field of the *Form* and/or the *FormElement*. Definitions of the *FormElement* will overwrite definitions -of the *Form*. If LDAP access is: +of the *Form*. + +Best practice for configuration - if LDAP access is: * only necessary in one *FormElement*, most usefull setup is to specify all values in that specific *FormElement*, * needed on multiple *FormElement*s (of the same *Form*, e.g. one *input* with *typeAhead*, one *note* and one *action*), it's more efficient to specify the base parameter *ldapServer*, *ldapBaseDn* in *Form.parameter* and the rest on the current *FormElement*. -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| Parameter | Example | Description | Form | FormElement | Used for | -+==========================+==================================+============================================================+======+=============+==========+ -| ldapServer | directory.example.com | Hostname | x | x | TA, FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| ldapBaseDn | ou=Addressbook,dc=example,dc=com | Base DN to start the search | x | x | TA, FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| ldapAttributes | cn, email | List of attributes to save in STORE_LDAP | x | x | FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| ldapSearch | (mail=john.doe@example.com) | Regular LDAP search expresssion | x | x | FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| ldapTimeLimit | 3 (default) | Maximum time to wait for an answer of the LDAP Server | x | x | TA, FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadLdap | - | Enable LDAP as 'Typeahead' data source | | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadLdapSearch | `(|(cn=*?*)(mail=*?*))` | Regular LDAP search expresssion | x | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadLdapValuePrintf | `'%s / %s', cn, email` | Custom format to display attributes, as `value` | x | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadLdapIdPrintf | `'%s', email` | Custom format to display attributes, as `id` | x | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadLimit | 20 (default) | Result will be limited to this number of entries | x | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| typeAheadMinLength | 2 (default) | Minimum number of characters before starting the search | x | x | TA | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ -| fillStoreLdap | - | Activate `Fill STORE LDAP` with the first retrieved record | | x | FSL | -+--------------------------+----------------------------------+------------------------------------------------------------+------+-------------+----------+ - -* At the moment only anonymous access is supported. ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| Parameter | Example | Description | Form | FormElement | Used for | ++==========================+==================================+===============================================================+======+=============+==========+ +| ldapServer | directory.example.com | Hostname. For LDAPS: `ldaps://directory.example.com:636` | x | x | TA, FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| ldapBaseDn | ou=Addressbook,dc=example,dc=com | Base DN to start the search | x | x | TA, FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| ldapAttributes | cn, email | List of attributes to save in STORE_LDAP | x | x | FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| ldapSearch | (mail=john.doe@example.com) | Regular LDAP search expresssion | x | x | FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| ldapTimeLimit | 3 (default) | Maximum time to wait for an answer of the LDAP Server | x | x | TA, FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| ldapUseBindCredentials | ldapUseBindCredentials=1 | Use LDAP_1_* crendentials from config.qfq.ini for ldap_bind() | x | x | TA, FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadLdap | - | Enable LDAP as 'Typeahead' data source | | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadLdapSearch | `(|(cn=*?*)(mail=*?*))` | Regular LDAP search expresssion | x | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadLdapValuePrintf | `'%s / %s', cn, email` | Custom format to display attributes, as `value` | x | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadLdapIdPrintf | `'%s', email` | Custom format to display attributes, as `id` | x | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadLimit | 20 (default) | Result will be limited to this number of entries | x | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| typeAheadMinLength | 2 (default) | Minimum number of characters before starting the search | x | x | TA | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ +| fillStoreLdap | - | Activate `Fill STORE LDAP` with the first retrieved record | | x | FSL | ++--------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+ + * *typeAheadLimit*: there might be a hard limit on the server side (e.g. 100) - which can't be extended. +* *ldapUseBindCredentials* is only necessary if `anonymous` access is not possible. RDN and password has to be configured in + `config.qfq.ini`. .. _LDAP_Typeahead: @@ -863,6 +874,7 @@ The *FormElement.parameter*=*typeAheadLdap* will trigger LDAP searches on every * *typeAheadLdapSearch* = `(|(cn=*?*)(mail=*?*))` * *typeAheadLdapValuePrintf* = `'%s / %s', cn, email` * *typeAheadLdapIdPrintf* = `'%s', email` + * Optional: *ldapUseBindCredentials*=1 All fetched LDAP values will be formatted with: * *typeAheadLdapValuePrintf*, shown to the user in a drop-down box and @@ -876,7 +888,7 @@ To examine all possible values of an LDAP server, use the commandline tool `ldap All occurences of a '?' in *ldapSearch* will be replaced by the user data typed in via the text-*FormElement*. The typed data will be escaped to fullfill LDAP search limitations. -Regular *Form* variables might be used on all parameter and will be evaluated during form load - *not* at the time when +Regular *Form* variables might be used on all parameter and will be evaluated during form load (!) - *not* at the time when the user types something. .. _Fill_LDAP_STORE: @@ -908,6 +920,7 @@ Important: LDAP access might slow down the *Form* processing on load, update or * *typeAheadLdapSearch* = `(|(cn=*?*)(mail=*?*))` * *ldapAttributes* = `givenName, sn, telephoneNumber, email` * *ldapSearch* = `(mail={{email::l}})` + * Optional: *ldapUseBindCredentials*=1 After filling the store, access the content via `{{<attributename>:allbut:L:s}}`. diff --git a/extension/config.qfq.example.ini b/extension/config.qfq.example.ini index 66eaa11a8..0fc314e9e 100644 --- a/extension/config.qfq.example.ini +++ b/extension/config.qfq.example.ini @@ -49,4 +49,7 @@ DATE_FORMAT = yyyy-mm-dd BASE_URL_PRINT = http://example.com/ WKHTMLTOPDF = /opt/wkhtmltox/bin/wkhtmltopdf -;EDIT_FORM_PAGE = form \ No newline at end of file +;EDIT_FORM_PAGE = form + +;LDAP_1_RDN = +;LDAP_1_PASSWORD = diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php index cacbe4743..1d4c4f29b 100644 --- a/extension/qfq/qfq/AbstractBuildForm.php +++ b/extension/qfq/qfq/AbstractBuildForm.php @@ -511,7 +511,7 @@ abstract class AbstractBuildForm { if (isset($formElement[FE_FILL_STORE_LDAP]) || isset($formElement[FE_TYPEAHEAD_LDAP])) { $keyNames = [F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_ATTRIBUTES, F_LDAP_SEARCH, F_TYPEAHEAD_LDAP_SEARCH, F_TYPEAHEAD_LIMIT, - F_TYPEAHEAD_MINLENGTH, F_TYPEAHEAD_LDAP_VALUE_PRINTF, F_TYPEAHEAD_LDAP_ID_PRINTF, F_LDAP_TIME_LIMIT]; + F_TYPEAHEAD_MINLENGTH, F_TYPEAHEAD_LDAP_VALUE_PRINTF, F_TYPEAHEAD_LDAP_ID_PRINTF, F_LDAP_TIME_LIMIT, F_LDAP_USE_BIND_CREDENTIALS]; $formElement = OnArray::copyArrayItemsIfNotAlreadyExist($this->formSpec, $formElement, $keyNames); } else { return $formElement; // nothing to do. @@ -523,6 +523,11 @@ abstract class AbstractBuildForm { $config = OnArray::getArrayItems($formElement, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES]); $config = $this->evaluate->parseArray($config); + if($formElement[FE_LDAP_USE_BIND_CREDENTIALS]==1) { + $config[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM); + $config[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM); + } + $ldap = new Ldap(); $arr = $ldap->process($config, '', MODE_LDAP_SINGLE); $this->store->setStore($arr, STORE_LDAP, true); @@ -900,6 +905,7 @@ abstract class AbstractBuildForm { $formElement[FE_TYPEAHEAD_LDAP_SEARCH] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_SEARCH); $formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_VALUE_PRINTF); $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF] = Support::setIfNotSet($formElement, FE_TYPEAHEAD_LDAP_KEY_PRINTF); + $formElement[FE_LDAP_USE_BIND_CREDENTIALS] = Support::setIfNotSet($formElement, FE_LDAP_USE_BIND_CREDENTIALS); foreach ([FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_TYPEAHEAD_LDAP_SEARCH] as $key) { if ($formElement[$key] == '') { @@ -920,6 +926,11 @@ abstract class AbstractBuildForm { FE_TYPEAHEAD_LIMIT => $formElement[FE_TYPEAHEAD_LIMIT], ]; + if($formElement[FE_LDAP_USE_BIND_CREDENTIALS]=='1') { + $arr[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM); + $arr[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM); + } + $urlParam = OnArray::toString($arr); } diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php index e0def040b..3f12d1a09 100644 --- a/extension/qfq/qfq/Constants.php +++ b/extension/qfq/qfq/Constants.php @@ -207,6 +207,7 @@ const ERROR_NO_TARGET_PATH_FILE_NAME = 1503; const ERROR_LDAP_CONNECT = 1600; const ERROR_MISSING_TYPE_AHEAD_LDAP_SEARCH = 1601; +const ERROR_LDAP_BIND = 1602; // KeyValueParser const ERROR_KVP_VALUE_HAS_NO_KEY = 1900; @@ -358,6 +359,9 @@ const SYSTEM_REPORT_COLUMN_NAME = 'reportColumnName'; // Keyname of SQL-column p const SYSTEM_REPORT_COLUMN_VALUE = 'reportColumnValue'; // Keyname of SQL-column processed at the moment. const SYSTEM_REPORT_FULL_LEVEL = 'reportFullLevel'; // Keyname of SQL-column processed at the moment. +const SYSTEM_LDAP_1_RDN = 'LDAP_1_RDN'; // Credentials to access LDAP +const SYSTEM_LDAP_1_PASSWORD = 'LDAP_1_PASSWORD'; // Credentials to access LDAP + // die folgenden Elemente sind vermutlich nicht noetig, wenn Store Klassen gloable Vars benutzt. //const SYSTEM_FORM_DEF = 'formDefinition'; // Type: SANITIZE_ALNUMX / AssocArray. Final form to process. Useful for error reporting. //const SYSTEM_FORM_ELEMENT_DEF = 'formElementDefinition'; // Type: SANITIZE_ALL / AssocArray. Formelement which are processed at the moment. Useful for error reporting. @@ -564,6 +568,7 @@ const F_LDAP_BASE_DN = 'ldapBaseDn'; const F_LDAP_SEARCH = 'ldapSearch'; const F_LDAP_ATTRIBUTES = 'ldapAttributes'; const F_LDAP_TIME_LIMIT = 'ldapTimeLimit'; +const F_LDAP_USE_BIND_CREDENTIALS = 'ldapUseBindCredentials'; const F_TYPEAHEAD_LIMIT = 'typeAheadLimit'; const F_TYPEAHEAD_MINLENGTH = 'typeAheadMinLength'; const F_TYPEAHEAD_LDAP_VALUE_PRINTF = 'typeAheadLdapValuePrintf'; @@ -655,6 +660,7 @@ const FE_LDAP_BASE_DN = F_LDAP_BASE_DN; const FE_LDAP_SEARCH = F_LDAP_SEARCH; const FE_LDAP_ATTRIBUTES = F_LDAP_ATTRIBUTES; const FE_LDAP_TIME_LIMIT = F_LDAP_TIME_LIMIT; +const FE_LDAP_USE_BIND_CREDENTIALS = F_LDAP_USE_BIND_CREDENTIALS; const FE_TYPEAHEAD_LIMIT = F_TYPEAHEAD_LIMIT; const FE_TYPEAHEAD_MINLENGTH = F_TYPEAHEAD_MINLENGTH; const FE_TYPEAHEAD_SQL = 'typeAheadSql'; diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php index c2b8e602c..9219261ee 100644 --- a/extension/qfq/qfq/QuickFormQuery.php +++ b/extension/qfq/qfq/QuickFormQuery.php @@ -580,6 +580,7 @@ class QuickFormQuery { Support::setIfNotSet($config, F_EXTRA_DELETE_FORM, ''); Support::setIfNotSet($config, F_SUBMIT_BUTTON_TEXT, ''); Support::setIfNotSet($config, F_BUTTON_ON_CHANGE_CLASS, ''); + Support::setIfNotSet($config, F_LDAP_USE_BIND_CREDENTIALS, ''); return $config; } diff --git a/extension/qfq/qfq/exceptions/ErrorHandler.php b/extension/qfq/qfq/exceptions/ErrorHandler.php index 409e22b26..e2965f6ed 100644 --- a/extension/qfq/qfq/exceptions/ErrorHandler.php +++ b/extension/qfq/qfq/exceptions/ErrorHandler.php @@ -18,7 +18,9 @@ class ErrorHandler { // This error code is not included in error_reporting return false; } - throw new CodeException(": Catchable Error in '$file' on line $line: " . $message . " - CWD: " . getcwd(), $severity, NULL); +// throw new CodeException(": Catchable Error in '$file' on line $line: " . $message . " - CWD: " . getcwd(), $severity, NULL); + // Do not show too much to the user. E.g. 'ldap_bind()' might have problems, but the user should not see the file and linenumber. + throw new CodeException($message, $severity, NULL); } } \ No newline at end of file diff --git a/extension/qfq/qfq/form/FormAction.php b/extension/qfq/qfq/form/FormAction.php index 4a6742fd2..3233b57d1 100644 --- a/extension/qfq/qfq/form/FormAction.php +++ b/extension/qfq/qfq/form/FormAction.php @@ -121,9 +121,14 @@ class FormAction { $fe = OnArray::copyArrayItemsIfNotAlreadyExist($this->formSpec, $fe, $keyNames); // Extract necessary elements - $config = OnArray::getArrayItems($fe, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES]); + $config = OnArray::getArrayItems($fe, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES, FE_LDAP_USE_BIND_CREDENTIALS]); $config = $this->evaluate->parseArray($config); + if($fe[FE_LDAP_USE_BIND_CREDENTIALS]==1) { + $config[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM); + $config[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM); + } + $ldap = new Ldap(); $arr = $ldap->process($config, '', MODE_LDAP_SINGLE); $this->store->setStore($arr, STORE_LDAP, true); diff --git a/extension/qfq/qfq/helper/Ldap.php b/extension/qfq/qfq/helper/Ldap.php index c966fd224..d0e5e3729 100644 --- a/extension/qfq/qfq/helper/Ldap.php +++ b/extension/qfq/qfq/helper/Ldap.php @@ -12,9 +12,21 @@ use qfq; require_once(__DIR__ . '/KeyValueStringParser.php'); require_once(__DIR__ . '/OnArray.php'); +require_once(__DIR__ . '/../exceptions/ErrorHandler.php'); +require_once(__DIR__ . '/../exceptions/UserFormException.php'); + class Ldap { + /** + * + */ + public function __construct() { + // This handler is necessary to catch 'ldap_bind()' errors. + set_error_handler("\\qfq\\ErrorHandler::exception_error_handler"); + + } + /** * @param $ldapServer * @return resource @@ -26,6 +38,11 @@ class Ldap { if (!$ds) { throw new UserFormException("Unable to connect to LDAP server: $ldapServer", ERROR_LDAP_CONNECT); } + + // http://php.net/manual/en/function.ldap-set-option.php >> This function is only available when using OpenLDAP 2.x.x OR Netscape Directory SDK x.x. + // Do not check for success. + ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3); + return $ds; } @@ -113,6 +130,12 @@ class Ldap { $ds = $this->ldapConnect($config[FE_LDAP_SERVER]); // must be a valid LDAP server! + if (isset($config[SYSTEM_LDAP_1_RDN]) && isset($config[SYSTEM_LDAP_1_PASSWORD])) { + if (false === ldap_bind($ds, $config[SYSTEM_LDAP_1_RDN], $config[SYSTEM_LDAP_1_PASSWORD])) { + throw new UserFormException("LDAP: Error trying to bind: " . ldap_error($ds), ERROR_LDAP_BIND); + } + } + $keyArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_KEY_PRINTF, $keyFormat); $valueArr = $this->preparePrintf($config, FE_TYPEAHEAD_LDAP_VALUE_PRINTF, $valueFormat); $specificArr = OnArray::arrayValueToLower(OnArray::trimArray(explode(',', $config[FE_LDAP_ATTRIBUTES]))); -- GitLab