Commit 85bc453c authored by Carsten  Rose's avatar Carsten Rose
Browse files

#3552 / typeAheadLdapSearchPerToken - webpass kann nicht gleichzeitig nach...

#3552 / typeAheadLdapSearchPerToken - webpass kann nicht gleichzeitig nach Vornamen und Nachnamen suchen
Neuen Modus 'typeAheadLdapSearchPerToken' implementiert.
Manual.rst: neues Feature dokumentiert.
Ldap.php: Neue Funktion 'explodePermutSearch()'.
parent 22f3d7d3
......@@ -844,6 +844,8 @@ To decide which Parameter should be placed on *Form.parameter* and which on *For
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapSearchPrefetch | `(mail=?)` | Regular LDAP search expresssion, typically return one record | x | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapSearchPerToken | - | Split search value in token and permutate search combination | x | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapValuePrintf | `'%s / %s', cn, mail` | Custom format to display attributes, as `value` | x | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapIdPrintf | `'%s', mail` | Custom format to display attributes, as `id` | x | x | TA |
......@@ -909,6 +911,29 @@ To identify the exact *id*, an additional search filter is necessary.
* *typeAheadPedantic*
* *typeAheadLdapSearchPrefetch* = `(mail=?)`
PerToken
^^^^^^^^
In the rare cases, where a user types more than token, e.g. firstname and lastname, **and** the LDAP server do not provide
an attribute with the combination of both: This mode will
* split the search string in individuell user tokens,
* split the ldap search string in individuell search tokens,
* create tuple permutations of all search tokens,
and fill all search token with the first user token and the second user token (only two user tokens are supported at the
moment).
E.g.::
User search string: X Y
Ldap search string: (|(a=?)(b=?)(c=?))
Result: (| (&(a=X)(b=Y)) (&(a=Y)(b=X)) (&(a=X)(c=Y)) (&(a=Y)(c=X)) (&(b=X)(c=Y)) (&(=Y)(c=X)) )
* *Form.parameter* or *FormElement.parameter*:
* *typeAheadLdapSearchPerToken* - no value needed.
.. _Fill_LDAP_STORE:
......@@ -1040,60 +1065,62 @@ parameter
* The following parameter are optional and can be configured in the *Form.parameter* field.
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| Name | Type | Description |
+==========================+========+==========================================================================================================+
| bsColumns | int | Wrap the whole form in '<div class="col-md-??"> |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| maxVisiblePill | int | Show pills upto <maxVisiblePill> as button, all further in a drop-down menu. Eg.: maxVisiblePill=3 |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| class | string | HTML div with given class, surrounding the whole form. Eg.: class=container-fluid |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| classPill | string | HTML div with given class, surrounding the `pill` title line. |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| classBody | string | HTML div with given class, surrounding all *FormElement*. |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| submitButtonText | string | Show save button, with the <submitButtonText> at the bottom of the form |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| extraDeleteForm | string | Name of a form which specifies how to delete the primary record and optional slave records |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-pattern-error | string | Pattern violation: Text for error message used for all FormElements of current form |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-required-error | string | Required violation: Text for error message used for all FormElements of current form |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-match-error | string | Match violation: Text for error message used for all FormElements of current form |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-error | string | If none specific is defined: Text for error message used for all FormElements of current form |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| buttonOnChangeClass | string | Color for save button after user modified some content or current form. E.g.: 'btn-info alert-info' |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapServer | string | FQDN Ldap Server. E.g.: directory.example.com |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapBaseDn | string | E.g.: `ou=Addressbook,dc=example,dc=com` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapAttributes | string | List of attributes to fill STORE_LDAP with. E.g.: cn, email |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapSearch | string | E.g.: `(mail={{email::alnumx:l}})` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapTimeLimit | int | Maximum time to wait for an answer of the LDAP Server |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdap | - | Enable LDAP as 'Typeahead' data source |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapSearch | string | Regular LDAP search expresssion. E.g.: `(|(cn=*?*)(mail=*?*))` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapValuePrintf | string | Value formatting of LDAP result, per entry. E.g.: `'%s / %s / %s', mail, roomnumber, telephonenumber` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapIdPrintf | string | Key formatting of LDAP result, per entry. E.g.: `'%s', mail` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLimit | int | Maximum number of entries. The limit is applied to the server (LDAP or SQL) and the Client |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadMinLength | int | Minimum number of characters which have to typed to start the search. |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| mode | string | The value `readonly` will activate a global readonly mode of the form - the user can't change any data. |
| | | See :ref:`form-mode-readonly` |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
| saveButtonActive | - | Make the 'save'-button active on *Form* load (instead of waiting for the first user change) |
+--------------------------+--------+----------------------------------------------------------------------------------------------------------+
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| Name | Type | Description |
+=============================+========+==========================================================================================================+
| bsColumns | int | Wrap the whole form in '<div class="col-md-??"> |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| maxVisiblePill | int | Show pills upto <maxVisiblePill> as button, all further in a drop-down menu. Eg.: maxVisiblePill=3 |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| class | string | HTML div with given class, surrounding the whole form. Eg.: class=container-fluid |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| classPill | string | HTML div with given class, surrounding the `pill` title line. |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| classBody | string | HTML div with given class, surrounding all *FormElement*. |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| submitButtonText | string | Show save button, with the <submitButtonText> at the bottom of the form |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| extraDeleteForm | string | Name of a form which specifies how to delete the primary record and optional slave records |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-pattern-error | string | Pattern violation: Text for error message used for all FormElements of current form |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-required-error | string | Required violation: Text for error message used for all FormElements of current form |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-match-error | string | Match violation: Text for error message used for all FormElements of current form |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| data-error | string | If none specific is defined: Text for error message used for all FormElements of current form |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| buttonOnChangeClass | string | Color for save button after user modified some content or current form. E.g.: 'btn-info alert-info' |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapServer | string | FQDN Ldap Server. E.g.: directory.example.com |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapBaseDn | string | E.g.: `ou=Addressbook,dc=example,dc=com` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapAttributes | string | List of attributes to fill STORE_LDAP with. E.g.: cn, email |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapSearch | string | E.g.: `(mail={{email::alnumx:l}})` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| ldapTimeLimit | int | Maximum time to wait for an answer of the LDAP Server |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdap | - | Enable LDAP as 'Typeahead' data source |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapSearch | string | Regular LDAP search expresssion. E.g.: `(|(cn=*?*)(mail=*?*))` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapValuePrintf | string | Value formatting of LDAP result, per entry. E.g.: `'%s / %s / %s', mail, roomnumber, telephonenumber` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapIdPrintf | string | Key formatting of LDAP result, per entry. E.g.: `'%s', mail` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLdapSearchPerToken | - | Split search value in token and permutate search combination |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadLimit | int | Maximum number of entries. The limit is applied to the server (LDAP or SQL) and the Client |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| typeAheadMinLength | int | Minimum number of characters which have to typed to start the search. |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| mode | string | The value `readonly` will activate a global readonly mode of the form - the user can't change any data. |
| | | See :ref:`form-mode-readonly` |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
| saveButtonActive | - | Make the 'save'-button active on *Form* load (instead of waiting for the first user change) |
+-----------------------------+--------+----------------------------------------------------------------------------------------------------------+
* Example:
......
......@@ -518,8 +518,9 @@ abstract class AbstractBuildForm {
$config = array();
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_LDAP_SEARCH_PREFETCH, F_TYPEAHEAD_LIMIT, F_TYPEAHEAD_MINLENGTH, F_TYPEAHEAD_LDAP_VALUE_PRINTF,
$keyNames = [F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_ATTRIBUTES,
F_LDAP_SEARCH, F_TYPEAHEAD_LDAP_SEARCH, F_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN, F_TYPEAHEAD_LDAP_SEARCH_PREFETCH,
F_TYPEAHEAD_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 {
......@@ -936,10 +937,13 @@ abstract class AbstractBuildForm {
FE_TYPEAHEAD_LDAP_SEARCH => $formElement[FE_TYPEAHEAD_LDAP_SEARCH],
FE_TYPEAHEAD_LDAP_SEARCH_PREFETCH => $formElement[FE_TYPEAHEAD_LDAP_SEARCH_PREFETCH],
FE_TYPEAHEAD_LDAP_VALUE_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_VALUE_PRINTF],
FE_TYPEAHEAD_LDAP_KEY_PRINTF => $formElement[FE_TYPEAHEAD_LDAP_KEY_PRINTF],
FE_TYPEAHEAD_LIMIT => $formElement[FE_TYPEAHEAD_LIMIT],
];
if (isset($formElement[FE_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN])) {
$arr[FE_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN] = '1';
}
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);
......
......@@ -589,6 +589,7 @@ const F_TYPEAHEAD_LDAP_VALUE_PRINTF = 'typeAheadLdapValuePrintf';
const F_TYPEAHEAD_LDAP_ID_PRINTF = 'typeAheadLdapIdPrintf';
const F_TYPEAHEAD_LDAP_SEARCH = 'typeAheadLdapSearch';
const F_TYPEAHEAD_LDAP_SEARCH_PREFETCH = 'typeAheadLdapSearchPrefetch';
const F_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN = 'typeAheadLdapSearchPerToken';
const F_MODE = 'mode';
const F_MODE_READONLY = 'readonly';
......@@ -694,10 +695,13 @@ const FE_TYPEAHEAD_LDAP_KEY_PRINTF = F_TYPEAHEAD_LDAP_ID_PRINTF;
const FE_TYPEAHEAD_LDAP = 'typeAheadLdap';
const FE_TYPEAHEAD_LDAP_SEARCH = F_TYPEAHEAD_LDAP_SEARCH;
const FE_TYPEAHEAD_LDAP_SEARCH_PREFETCH = F_TYPEAHEAD_LDAP_SEARCH_PREFETCH;
const FE_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN = F_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN;
const FE_FILL_STORE_LDAP = 'fillStoreLdap';
const FE_CHARACTER_COUNT_WRAP = 'characterCountWrap';
const RETYPE_FE_NAME_EXTENSION = 'RETYPE';
const TYPEAHEAD_PLACEHOLDER = '?';
const FE_HTML_ID = 'htmlId'; // Will be dynamically computed during runtime.
// FormElement Types
......
......@@ -65,6 +65,53 @@ class Ldap {
return $sr;
}
/**
* Very specific function. Not generic. Rearranges ldapSearch and might break the original logic.
*
* Explode $ldapValue by ' '. If two entries found, do the special PermutSearch, else take the search at it is.
*
* PermutSearch: Explode ldapSearch by '(...)'. Assume that all of them 'or' combined.
* Calculate all tuple permutations of the search token. From every permutation, take the reverse order.
* Fill the tuple with the two entries. 'and' combine the tuple. 'or' combine all the 'and' tuples.
*
* (|(a=?)(b=?)(c=?)), ?=X|Y: (| (&(a=X)(b=Y)) (&(a=Y)(b=X)) (&(a=X)(c=Y)) (&(a=Y)(c=X)) (&(b=X)(c=Y)) (&(=Y)(c=X)) )
*
* @param string $ldapSearch
* @param string $searchValue
* @return string
*/
private function explodePermutSearch($ldapSearch, $searchValue) {
if ($ldapSearch == '' || $searchValue == '') {
return '';
}
$tokenArr = explode(' ', trim($searchValue));
if (count($tokenArr) != 2) {
// If there is only one token (or more than two, which can't be handled now): replace and return.
return str_replace(TYPEAHEAD_PLACEHOLDER, $searchValue, $ldapSearch);
}
// Fill array with all search token
preg_match_all('/\(\w*=.*?\)/', $ldapSearch, $searchArr);
// Prefill value '1' in $searchArrA and value '2' in $searchArrB
$searchArrA = OnArray::arrayValueReplace($searchArr[0], TYPEAHEAD_PLACEHOLDER, $tokenArr[0]);
$searchArrB = OnArray::arrayValueReplace($searchArr[0], TYPEAHEAD_PLACEHOLDER, $tokenArr[1]);
$permut = array();
$max = count($searchArrA);
for ($ii = 0; $ii < $max; $ii++) {
for ($jj = $ii + 1; $jj < $max; $jj++) {
$permut[] = '(&' . $searchArrA[$ii] . $searchArrB[$jj] . ')';
$permut[] = '(&' . $searchArrA[$jj] . $searchArrB[$ii] . ')';
}
}
$searchString = '(|' . implode('', $permut) . ')';
return $searchString;
}
/**
* @param array $config
* @param string $searchValue
......@@ -77,8 +124,11 @@ class Ldap {
$config[FE_LDAP_ATTRIBUTES] = Support::setIfNotSet($config, FE_LDAP_ATTRIBUTES, '');
$config[FE_LDAP_TIME_LIMIT] = Support::setIfNotSet($config, FE_LDAP_TIME_LIMIT, DEFAULT_LDAP_TIME_LIMIT);
$config[FE_LDAP_SEARCH] = str_replace('?', $searchValue, $config[FE_LDAP_SEARCH]);
if ($mode == MODE_LDAP_MULTI && isset($config[F_TYPEAHEAD_LDAP_SEARCH_PER_TOKEN])) {
$config[FE_LDAP_SEARCH] = $this->explodePermutSearch($config[FE_LDAP_SEARCH], $searchValue);
} else {
$config[FE_LDAP_SEARCH] = str_replace(TYPEAHEAD_PLACEHOLDER, $searchValue, $config[FE_LDAP_SEARCH]);
}
$config[FE_TYPEAHEAD_LIMIT] = ($mode == MODE_LDAP_MULTI) ? $config[FE_TYPEAHEAD_LIMIT] : 1;
......@@ -94,7 +144,7 @@ class Ldap {
$config[FE_TYPEAHEAD_LDAP_VALUE_PRINTF] = $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF];
}
if ($mode == MODE_LDAP_MULTI && $config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') {
if ($config[FE_TYPEAHEAD_LDAP_KEY_PRINTF] == '') {
throw new UserFormException("Missing parameter '" . FE_TYPEAHEAD_LDAP_KEY_PRINTF . "' and/or '" . FE_TYPEAHEAD_LDAP_VALUE_PRINTF);
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment