mirror of
https://gitlab.easter-eggs.com/ee/ldapsaisie.git
synced 2024-12-23 17:03:47 +01:00
Add possibily to make global search using API
This commit is contained in:
parent
bd98a8b8ef
commit
2db4d0fbae
3 changed files with 402 additions and 4 deletions
|
@ -176,7 +176,8 @@ HTTP 404 sera générée.
|
|||
|
||||
Permet de réclamer un résultat de recherche dans lequel, la clé `objects` sera une liste et
|
||||
non un dictionnaire. Dans ce cas, le DN de l'objet est fourni dans la clé `dn` des détails
|
||||
des objets.
|
||||
des objets. Seul la présence de ce paramètre suffit à activer ce comportement, sa valeur n'a pas
|
||||
d'importance.
|
||||
|
||||
- `withoutCache`
|
||||
|
||||
|
@ -560,3 +561,168 @@ HTTP 404 sera générée.
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `/api/1.0/search`
|
||||
|
||||
Cette méthode permet d'effectuer une recherche sur plusieurs types d'objets de l'annuaire à la
|
||||
fois. Par mimétisme du comportement de l'interface web, la recherche est paginée et accepte des
|
||||
paramètres similaires en plus de paramètre plus appropriés à un fonctionnement programmatique.
|
||||
|
||||
Les paramètres acceptés par cette méthode sont sensiblement les mêmes que ceux acceptés par la
|
||||
méthode de recherche d'un type d'objet de l'annuaire en particulier et ils ne seront donc pas
|
||||
tous redocumentés ici :
|
||||
|
||||
- `filter`
|
||||
|
||||
- `predefinedFilter`
|
||||
|
||||
- `pattern`
|
||||
|
||||
- `approx`
|
||||
|
||||
- `basedn`
|
||||
|
||||
- `subDn`
|
||||
|
||||
- `scope`
|
||||
|
||||
- `recursive`
|
||||
|
||||
- `displayFormat`
|
||||
|
||||
- `extraDisplayedColumns`
|
||||
|
||||
- `attributes`
|
||||
|
||||
- `attributesDetails`
|
||||
|
||||
- `page`
|
||||
|
||||
- `all`
|
||||
|
||||
- `as_list`
|
||||
|
||||
- `withoutCache`
|
||||
|
||||
- `keepParamsBetweenSearches`
|
||||
|
||||
- `nbObjectsByPage`
|
||||
|
||||
Permet de préciser le nombre maximum d'objets retournés par type d'objet ET par page du résultat
|
||||
de recherche.
|
||||
|
||||
- `types`
|
||||
|
||||
Permet de limiter les types d'objets à inclure dans le résultat de recherche. Par défaut, tous
|
||||
les types d'objets auxquels l'utilisateur à accès et dont la recherche globale n'est pas
|
||||
désactivée seront inclus.
|
||||
|
||||
- `splited_result`
|
||||
|
||||
Permet de faire en sorte que les objets inclus dans le résultat de recherche soient séparés par
|
||||
type dans des sous-clés de `objects`. Par défaut, tous les objets sont retournés dans la clé
|
||||
`objects` et une sous-clé `type` est ajouté à chacun d'eux pour les distinguer. Seul la présence
|
||||
de ce paramètre suffit à activer ce comportement, sa valeur n'a pas d'importance.
|
||||
|
||||
!!! important
|
||||
|
||||
**Pour chaque type d'objets inclus dans la recherche, un filtre et/ou un mot clé de recherche
|
||||
doit être spécifié.** Cette méthode n'a pas vocation à permettre de lister tous les objets de
|
||||
l'annuaire.
|
||||
|
||||
|
||||
**Exemple :**
|
||||
|
||||
```
|
||||
# curl -u username:secret 'https://ldapsaisie/api/api/1.0/search?pattern=LdapSaisie&pretty'
|
||||
{
|
||||
"success": true,
|
||||
"objects": {
|
||||
"uid=s.ldapsaisie,ou=people,o=ls": {
|
||||
"name": "Secretariat LdapSaisie",
|
||||
"type": "LSpeople",
|
||||
"Mail": "secretariat@ldapsaisie.biz"
|
||||
},
|
||||
"uid=ls,ou=people,o=ls": {
|
||||
"name": "LdapSaisie",
|
||||
"type": "LSpeople",
|
||||
"Mail": "ldap.saisie@ls.com"
|
||||
},
|
||||
"uid=erwpa,ou=people,o=ls": {
|
||||
"name": "Erwan PAGE",
|
||||
"type": "LSpeople",
|
||||
"Mail": "erwan.page@ldapsaisie.biz"
|
||||
},
|
||||
"uid=invite,ou=people,o=ls": {
|
||||
"name": "Utilisateur de passage",
|
||||
"type": "LSpeople",
|
||||
"Mail": "invite@ldapsaisie.biz"
|
||||
},
|
||||
"uid=demo,ou=people,o=ls": {
|
||||
"name": "Demonstration LdapSaisie",
|
||||
"type": "LSpeople",
|
||||
"Mail": "demo@ls.com"
|
||||
},
|
||||
"uid=admin,ou=people,o=ls": {
|
||||
"name": "Administration LdapSaisie",
|
||||
"type": "LSpeople",
|
||||
"Mail": "admin@ls.com"
|
||||
},
|
||||
"uid=admin3,ou=people,o=ls": {
|
||||
"name": "ZAdministration LdapSaisie",
|
||||
"type": "LSpeople",
|
||||
"Mail": "admin@ls.com"
|
||||
},
|
||||
"uid=ldapsaisie,ou=sysaccounts,o=ls": {
|
||||
"name": "ldapsaisie",
|
||||
"type": "LSsysaccount"
|
||||
}
|
||||
},
|
||||
"total": null,
|
||||
"params": {
|
||||
"keepParamsBetweenSearches": false,
|
||||
"LSpeople": {
|
||||
"filter": null,
|
||||
"pattern": "LdapSaisie",
|
||||
"predefinedFilter": false,
|
||||
"basedn": null,
|
||||
"scope": null,
|
||||
"sizelimit": 0,
|
||||
"attronly": false,
|
||||
"approx": false,
|
||||
"recursive": true,
|
||||
"attributes": [],
|
||||
"onlyAccessible": true,
|
||||
"sortDirection": null,
|
||||
"sortBy": null,
|
||||
"sortlimit": 0,
|
||||
"displayFormat": "%{cn}",
|
||||
"nbObjectsByPage": 25,
|
||||
"withoutCache": false,
|
||||
"extraDisplayedColumns": true
|
||||
},
|
||||
"LSsysaccount": {
|
||||
"filter": null,
|
||||
"pattern": "LdapSaisie",
|
||||
"predefinedFilter": false,
|
||||
"basedn": null,
|
||||
"scope": null,
|
||||
"sizelimit": 0,
|
||||
"attronly": false,
|
||||
"approx": false,
|
||||
"recursive": false,
|
||||
"attributes": [],
|
||||
"onlyAccessible": true,
|
||||
"sortDirection": null,
|
||||
"sortBy": null,
|
||||
"sortlimit": 0,
|
||||
"displayFormat": "%{uid}",
|
||||
"nbObjectsByPage": 30,
|
||||
"withoutCache": false,
|
||||
"extraDisplayedColumns": true
|
||||
}
|
||||
},
|
||||
"page": 1,
|
||||
"nbPages": 1
|
||||
}
|
||||
```
|
||||
|
|
|
@ -658,11 +658,13 @@ class LSsearch extends LSlog_staticLoggerClass {
|
|||
}
|
||||
|
||||
/**
|
||||
* Define search parameters by reading request data ($_REQUEST)
|
||||
* Define search parameters by reading request data
|
||||
*
|
||||
* @param array<string,mixed>|null $request The request (optional, default: $_REQUEST)
|
||||
*
|
||||
* @return boolean True if all parameters found in request data are handled, False otherwise
|
||||
*/
|
||||
public function setParamsFromRequest() {
|
||||
public function setParamsFromRequest($request=null) {
|
||||
$allowedParams = array(
|
||||
'pattern', 'approx', 'recursive', 'extraDisplayedColumns', 'nbObjectsByPage',
|
||||
'attributes', 'sortBy', 'sortDirection', 'withoutCache', 'predefinedFilter',
|
||||
|
@ -670,8 +672,9 @@ class LSsearch extends LSlog_staticLoggerClass {
|
|||
'filter', 'basedn', 'subDn', 'scope', 'attributes', 'displayFormat',
|
||||
);
|
||||
$data = array();
|
||||
$request = $request?$request:$_REQUEST;
|
||||
|
||||
foreach($_REQUEST as $key => $value) {
|
||||
foreach($request as $key => $value) {
|
||||
if (!in_array($key, $allowedParams))
|
||||
continue;
|
||||
switch($key) {
|
||||
|
|
|
@ -1560,6 +1560,235 @@ function get_LSobject_from_API_request($request, $instanciate=true, $check_acces
|
|||
return get_LSobject_from_request($request, $instanciate, $check_access, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle API global search request
|
||||
* @param LSurlRequest $request The request
|
||||
* @return void
|
||||
*/
|
||||
function handle_api_global_search($request) {
|
||||
// Check global search is enabled
|
||||
if (!LSsession :: globalSearch()) {
|
||||
LSurl :: error_404($request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LSsession :: loadLSclass('LSsearch')) {
|
||||
LSerror :: addErrorCode('LSsession_05','LSsearch');
|
||||
LSsession :: displayAjaxReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LSsession :: loadLSclass('LSform')) {
|
||||
LSerror :: addErrorCode('LSsession_05','LSform');
|
||||
LSsession :: displayAjaxReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
$onlyLSobjects = (isset($_REQUEST['types'])?ensureIsArray($_REQUEST['types']):[]);
|
||||
$keepParamsBetweenSearches = (
|
||||
isset($_REQUEST['keepParamsBetweenSearches'])?
|
||||
boolval($_REQUEST['keepParamsBetweenSearches']):
|
||||
false
|
||||
);
|
||||
$all = isset($_REQUEST['all']);
|
||||
$page_nb = (isset($_REQUEST['page'])?(int)$_REQUEST['page']:1);
|
||||
$allowedParams = array(
|
||||
'pattern', 'approx', 'recursive', 'extraDisplayedColumns', 'nbObjectsByPage',
|
||||
'attributes', 'withoutCache', 'predefinedFilter', 'filter', 'basedn', 'subDn',
|
||||
'scope', 'attributes', 'displayFormat',
|
||||
);
|
||||
|
||||
// Handle JSON output
|
||||
$data = array(
|
||||
'success' => true,
|
||||
'objects' => array(),
|
||||
'total' => 0,
|
||||
'params' => array(
|
||||
'keepParamsBetweenSearches' => $keepParamsBetweenSearches,
|
||||
),
|
||||
);
|
||||
if (!$all) {
|
||||
$data['page'] = $page_nb;
|
||||
$data['nbPages'] = 1;
|
||||
}
|
||||
|
||||
foreach (LSsession :: getLSaccess() as $LSobject => $label) {
|
||||
if ( $LSobject == "SELF" || !LSsession :: loadLSobject($LSobject) )
|
||||
continue;
|
||||
if (!LSconfig::get("LSobjects.$LSobject.globalSearch", true, 'bool'))
|
||||
continue;
|
||||
if ($onlyLSobjects && !in_array($LSobject, $onlyLSobjects))
|
||||
continue;
|
||||
|
||||
$object = new $LSobject();
|
||||
|
||||
$search = new LSsearch(
|
||||
$LSobject,
|
||||
'api',
|
||||
null,
|
||||
!$keepParamsBetweenSearches
|
||||
);
|
||||
$search -> setParam(
|
||||
'extraDisplayedColumns',
|
||||
LSconfig::get("LSobjects.$LSobject.globalSearch_extraDisplayedColumns", true, 'bool')
|
||||
);
|
||||
$search -> setParam('onlyAccessible', True);
|
||||
$params = [];
|
||||
foreach($_REQUEST as $key => $value)
|
||||
if (in_array($key, $allowedParams))
|
||||
$params[$key] = $value;
|
||||
if (isset($_REQUEST['type_params']) && isset($_REQUEST['type_params'][$LSobject]))
|
||||
foreach(ensureIsArray($_REQUEST['type_params'][$LSobject]) as $key => $value)
|
||||
if (in_array($key, $allowedParams))
|
||||
$params[$key] = $value;
|
||||
|
||||
if (
|
||||
(!isset($params['pattern']) || !$params['pattern'])
|
||||
&& (!isset($params['filter']) || !$params['filter'])
|
||||
) {
|
||||
LSerror :: addErrorCode(
|
||||
false,
|
||||
_("No pattern or filter provided for $LSobject (required in global search).")
|
||||
);
|
||||
LSsession :: displayAjaxReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$search -> setParamsFromRequest($params)) {
|
||||
LSerror :: addErrorCode(false, "Invalid search parameters for $LSobject.");
|
||||
LSsession :: displayAjaxReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Run search
|
||||
if (!$search -> run())
|
||||
LSlog :: fatal("Fail to run search on $LSobject.");
|
||||
|
||||
if ($search -> total <= 0)
|
||||
continue;
|
||||
|
||||
$data['total'] += $search -> total;
|
||||
|
||||
if ($all) {
|
||||
$entries = $search -> listEntries();
|
||||
if (!is_array($entries))
|
||||
LSlog :: fatal("Fail to retrieve search result for $LSobject.");
|
||||
}
|
||||
else {
|
||||
// Retrieve page
|
||||
$page = $search -> getPage($page_nb);
|
||||
|
||||
/*
|
||||
* $page = array(
|
||||
* 'nb' => $page,
|
||||
* 'nbPages' => 1,
|
||||
* 'list' => array(),
|
||||
* 'total' => $this -> total
|
||||
* );
|
||||
*/
|
||||
|
||||
// Check page
|
||||
if (!is_array($page))
|
||||
LSlog :: fatal("Fail to retrieve page #$page_nb for $LSobject.");
|
||||
|
||||
if ($page['nb'] >= $data['page'])
|
||||
$data['page'] = $page['nb'];
|
||||
if ($page['nbPages'] >= $data['nbPages'])
|
||||
$data['nbPages'] = $page['nbPages'];
|
||||
}
|
||||
|
||||
// Export search parameters
|
||||
$exportedParams = array(
|
||||
'filter', 'pattern', 'predefinedFilter', 'basedn', 'scope', 'sizelimit', 'attronly',
|
||||
'approx', 'recursive', 'attributes', 'onlyAccessible', 'sortDirection', 'sortBy', 'sortlimit',
|
||||
'displayFormat', 'nbObjectsByPage', 'withoutCache', 'extraDisplayedColumns'
|
||||
);
|
||||
if (LSsession :: subDnIsEnabled())
|
||||
$exportedParams = array_merge($exportedParams, array('displaySubDn', 'subDn'));
|
||||
$data['params'][$LSobject] = [];
|
||||
foreach ($exportedParams as $param) {
|
||||
$data['params'][$LSobject][$param] = $search->getParam($param);
|
||||
if ($param == 'filter' && $data['params'][$LSobject][$param])
|
||||
$data['params'][$LSobject][$param] = $data['params'][$LSobject][$param] -> as_string();
|
||||
}
|
||||
|
||||
// Instanciate LSform export to handle custom requested attributes
|
||||
$object = new $LSobject();
|
||||
$export = new LSform($object, 'export');
|
||||
foreach ($search -> attributes as $attr) {
|
||||
if (array_key_exists($attr, $object -> attrs))
|
||||
$object -> attrs[$attr] -> addToExport($export);
|
||||
}
|
||||
|
||||
// Reset & increase time limit: allow one seconds by object to handle,
|
||||
// with a minimum of 30 seconds
|
||||
$timeout = count($all?$entries:$page['list']); // @phpstan-ignore-line
|
||||
set_time_limit($timeout>30?$timeout:30);
|
||||
|
||||
// Handle objects
|
||||
$data['objects'][$LSobject] = [];
|
||||
foreach(($all?$entries:$page['list']) as $obj) { // @phpstan-ignore-line
|
||||
$data['objects'][$LSobject][$obj -> dn] = array(
|
||||
'name' => $obj -> displayName,
|
||||
);
|
||||
// When as_list enabled, put object DN in object details (otherwise, it's the key)
|
||||
if (isset($_REQUEST['as_list']))
|
||||
$data['objects'][$LSobject][$obj -> dn]['dn'] = $obj -> dn;
|
||||
// When splited_result is disabled, put object type in object details (otherwise, present as key)
|
||||
if (!isset($_REQUEST['splited_result']))
|
||||
$data['objects'][$LSobject][$obj -> dn]['type'] = $LSobject;
|
||||
if ($search -> displaySubDn)
|
||||
$data['objects'][$LSobject][$obj -> dn][$search -> label_level] = $obj -> subDn;
|
||||
if ($search -> extraDisplayedColumns) {
|
||||
foreach ($search -> visibleExtraDisplayedColumns as $cid => $conf) {
|
||||
$data['objects'][$LSobject][$obj -> dn][$conf['label']] = $obj -> $cid;
|
||||
}
|
||||
}
|
||||
foreach ($search -> attributes as $attr) {
|
||||
if (!LSsession :: canAccess($LSobject, $obj -> dn, 'r', $attr))
|
||||
continue;
|
||||
$export -> elements[$attr] -> setValue(
|
||||
$object -> attrs[$attr] -> html -> refreshForm(
|
||||
$object -> attrs[$attr] -> getFormVal($obj -> $attr)
|
||||
)
|
||||
);
|
||||
$data['objects'][$LSobject][$obj -> dn][$attr] = $export -> elements[$attr] -> getApiValue(
|
||||
isset($_REQUEST['attributesDetails'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$search -> afterUsingResult();
|
||||
}
|
||||
|
||||
if (!$all && $data['page'] > $data['nbPages']) {
|
||||
LSerror :: addErrorCode(
|
||||
false,
|
||||
"Requested page too hight ({$data['page']} > {$data['nbPages']})."
|
||||
);
|
||||
LSsession :: displayAjaxReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle as_list parameter
|
||||
if (isset($_REQUEST['as_list']))
|
||||
foreach(array_keys($data['objects']) as $LSobject)
|
||||
$data['objects'][$LSobject] = array_values($data['objects'][$LSobject]);
|
||||
|
||||
// Handle splited_result parameter
|
||||
if (!isset($_REQUEST['splited_result'])) {
|
||||
$objects = [];
|
||||
foreach(array_keys($data['objects']) as $LSobject) {
|
||||
$objects = array_merge($objects, $data['objects'][$LSobject]);
|
||||
unset($data['objects'][$LSobject]);
|
||||
}
|
||||
$data['objects'] = $objects;
|
||||
}
|
||||
|
||||
LSsession :: displayAjaxReturn($data);
|
||||
}
|
||||
LSurl :: add_handler('#^api/1.0/search/?$#', 'handle_api_global_search', true, false, true);
|
||||
|
||||
/*
|
||||
* Handle API LSobject search
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue