diff --git a/app/modules/web/Controllers/ConfigAccountController.php b/app/modules/web/Controllers/ConfigAccountController.php index 828a2ca0..9160363d 100644 --- a/app/modules/web/Controllers/ConfigAccountController.php +++ b/app/modules/web/Controllers/ConfigAccountController.php @@ -46,7 +46,7 @@ class ConfigAccountController extends SimpleControllerBase */ public function saveAction() { - $configData = clone $this->config->getConfigData(); + $configData = $this->config->getConfigData(); $eventMessage = EventMessage::factory(); diff --git a/app/modules/web/Controllers/ConfigBackupController.php b/app/modules/web/Controllers/ConfigBackupController.php index 2aa77f66..86dcbfa4 100644 --- a/app/modules/web/Controllers/ConfigBackupController.php +++ b/app/modules/web/Controllers/ConfigBackupController.php @@ -54,8 +54,7 @@ class ConfigBackupController extends SimpleControllerBase } try { - $backupService = new FileBackupService(); - $backupService->doBackup(); + $this->dic->get(FileBackupService::class)->doBackup(); $this->eventDispatcher->notifyEvent('run.backup.end', new Event($this, EventMessage::factory() diff --git a/app/modules/web/Controllers/ConfigGeneralController.php b/app/modules/web/Controllers/ConfigGeneralController.php index d17a1a56..3facd175 100644 --- a/app/modules/web/Controllers/ConfigGeneralController.php +++ b/app/modules/web/Controllers/ConfigGeneralController.php @@ -46,7 +46,7 @@ class ConfigGeneralController extends SimpleControllerBase */ public function saveAction() { - $configData = clone $this->config->getConfigData(); + $configData = $this->config->getConfigData(); $eventMessage = EventMessage::factory(); // General diff --git a/app/modules/web/Controllers/ConfigLdapController.php b/app/modules/web/Controllers/ConfigLdapController.php index 00250aa1..dd7c7e29 100644 --- a/app/modules/web/Controllers/ConfigLdapController.php +++ b/app/modules/web/Controllers/ConfigLdapController.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -28,9 +28,15 @@ use SP\Core\Acl\ActionsInterface; use SP\Core\Acl\UnauthorizedPageException; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; +use SP\Core\Exceptions\SPException; +use SP\Core\Exceptions\ValidationException; use SP\Http\JsonResponse; use SP\Http\Request; use SP\Modules\Web\Controllers\Traits\ConfigTrait; +use SP\Providers\Auth\Ldap\LdapParams; +use SP\Services\Ldap\LdapCheckService; +use SP\Services\Ldap\LdapImportParams; +use SP\Services\Ldap\LdapImportService; /** * Class ConfigLdapController @@ -46,48 +52,168 @@ class ConfigLdapController extends SimpleControllerBase */ public function saveAction() { - $eventMessage = EventMessage::factory(); - $configData = clone $this->config->getConfigData(); + try { + $eventMessage = EventMessage::factory(); + $configData = $this->config->getConfigData(); - // LDAP - $ldapEnabled = Request::analyze('ldap_enabled', false, false, true); - $ldapADSEnabled = Request::analyze('ldap_ads', false, false, true); - $ldapServer = Request::analyze('ldap_server'); - $ldapBase = Request::analyze('ldap_base'); - $ldapGroup = Request::analyze('ldap_group'); - $ldapDefaultGroup = Request::analyze('ldap_defaultgroup', 0); - $ldapDefaultProfile = Request::analyze('ldap_defaultprofile', 0); - $ldapBindUser = Request::analyze('ldap_binduser'); - $ldapBindPass = Request::analyzeEncrypted('ldap_bindpass'); + // LDAP + $ldapEnabled = Request::analyze('ldap_enabled', false, false, true); + $ldapDefaultGroup = Request::analyze('ldap_defaultgroup', 0); + $ldapDefaultProfile = Request::analyze('ldap_defaultprofile', 0); - // Valores para la configuración de LDAP - if ($ldapEnabled && (!$ldapServer || !$ldapBase || !$ldapBindUser)) { - $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Faltan parámetros de LDAP')); - } + $ldapParams = $this->getLdapParamsFromRequest(); - if ($ldapEnabled) { - $configData->setLdapEnabled(true); - $configData->setLdapAds($ldapADSEnabled); - $configData->setLdapServer($ldapServer); - $configData->setLdapBase($ldapBase); - $configData->setLdapGroup($ldapGroup); - $configData->setLdapDefaultGroup($ldapDefaultGroup); - $configData->setLdapDefaultProfile($ldapDefaultProfile); - $configData->setLdapBindUser($ldapBindUser); - $configData->setLdapBindPass($ldapBindPass); - - if ($configData->isLdapEnabled() === false) { - $eventMessage->addDescription(__u('LDAP habiltado')); + // Valores para la configuración de LDAP + if ($ldapEnabled && !($ldapParams->getServer() || $ldapParams->getSearchBase() || $ldapParams->getBindDn())) { + $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Faltan parámetros de LDAP')); } - } elseif ($ldapEnabled === false && $configData->isLdapEnabled()) { - $configData->setLdapEnabled(false); - $eventMessage->addDescription(__u('LDAP deshabilitado')); + if ($ldapEnabled) { + $configData->setLdapEnabled(true); + $configData->setLdapAds($ldapParams->isAds()); + $configData->setLdapServer($ldapParams->getServer()); + $configData->setLdapBase($ldapParams->getSearchBase()); + $configData->setLdapGroup($ldapParams->getGroup()); + $configData->setLdapDefaultGroup($ldapDefaultGroup); + $configData->setLdapDefaultProfile($ldapDefaultProfile); + $configData->setLdapBindUser($ldapParams->getBindDn()); + $configData->setLdapBindPass($ldapParams->getBindPass()); + + if ($configData->isLdapEnabled() === false) { + $eventMessage->addDescription(__u('LDAP habiltado')); + } + } elseif ($ldapEnabled === false && $configData->isLdapEnabled()) { + $configData->setLdapEnabled(false); + + $eventMessage->addDescription(__u('LDAP deshabilitado')); + } + + $this->saveConfig($configData, $this->config, function () use ($eventMessage) { + $this->eventDispatcher->notifyEvent('save.config.ldap', new Event($this, $eventMessage)); + }); + } catch (ValidationException $e) { + $this->returnJsonResponseException($e); + } + } + + /** + * @return LdapParams + * @throws ValidationException + */ + protected function getLdapParamsFromRequest() + { + $data = LdapParams::getServerAndPort(Request::analyze('ldap_server')); + + if ($data === false) { + throw new ValidationException(__u('Parámetros de LDAP incorrectos')); } - $this->saveConfig($configData, $this->config, function () use ($eventMessage) { - $this->eventDispatcher->notifyEvent('save.config.ldap', new Event($this, $eventMessage)); - }); + return (new LdapParams()) + ->setServer($data['server']) + ->setPort(isset($data['port']) ? $data['port'] : 389) + ->setSearchBase(Request::analyze('ldap_base')) + ->setGroup(Request::analyze('ldap_group')) + ->setBindDn(Request::analyze('ldap_binduser')) + ->setBindPass(Request::analyzeEncrypted('ldap_bindpass')) + ->setAds(Request::analyze('ldap_ads', false, false, true)); + } + + /** + * checkAction + */ + public function checkAction() + { + try { + $ldapParams = $this->getLdapParamsFromRequest(); + + // Valores para la configuración de LDAP + if (!($ldapParams->getServer() || $ldapParams->getSearchBase() || $ldapParams->getBindDn())) { + $this->returnJsonResponse(JsonResponse::JSON_ERROR, __u('Faltan parámetros de LDAP')); + } + + $ldapCheckService = $this->dic->get(LdapCheckService::class); + $ldapCheckService->checkConnection($ldapParams); + + $results = $ldapCheckService->getUsersAndGroups(); + + $this->returnJsonResponseData( + $results, + JsonResponse::JSON_SUCCESS, + __u('Conexión a LDAP correcta'), + [sprintf(__('Objetos encontrados: %d'), $results['count'])] + ); + } catch (\Exception $e) { + processException($e); + + $this->returnJsonResponseException($e); +// $this->JsonResponse->addMessage(__('Revise el registro de eventos para más detalles', false)); + } + } + + /** + * importAction + * + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface + */ + public function importAction() + { + try { + $ldapImportParams = new LdapImportParams(); + + $ldapImportParams->loginAttribute = Request::analyze('ldap_login_attribute'); + $ldapImportParams->userNameAttribute = Request::analyze('ldap_username_attribute'); + $ldapImportParams->userGroupNameAttribute = Request::analyze('ldap_groupname_attribute'); + $ldapImportParams->defaultUserGroup = Request::analyze('ldap_defaultgroup', 0); + $ldapImportParams->defaultUserProfile = Request::analyze('ldap_defaultprofile', 0); + + $checkImportGroups = Request::analyze('ldap_group_import', false, false, true); + + if ((empty($ldapImportParams->loginAttribute) + || empty($ldapImportParams->userNameAttribute) + || empty($ldapImportParams->defaultUserGroup) + || empty($ldapImportParams->defaultUserProfile)) + && ($checkImportGroups === true && empty($ldapImportParams->userGroupNameAttribute)) + ) { + throw new ValidationException(__u('Parámetros de LDAP incorrectos')); + } + + $ldapParams = $this->getLdapParamsFromRequest(); + + $userLdapService = $this->dic->get(LdapImportService::class); + + $this->eventDispatcher->notifyEvent('import.ldap.start', + new Event($this, EventMessage::factory()->addDescription(__u('Importación LDAP'))) + ); + + $userLdapService->importUsers($ldapParams, $ldapImportParams); + + if ($checkImportGroups === true) { + $userLdapService->importGroups($ldapParams, $ldapImportParams); + } + + $this->eventDispatcher->notifyEvent('import.ldap.end', + new Event($this, EventMessage::factory()->addDescription(__u('Importación finalizada'))) + ); + + if ($userLdapService->getTotalObjects() === 0) { + throw new SPException(__u('No se encontraron objetos para sincronizar')); + } + + $this->returnJsonResponse( + JsonResponse::JSON_SUCCESS, + __u('Importación de usuarios de LDAP realizada'), + [ + sprintf(__('Usuarios importados: %d/%d'), $userLdapService->getSyncedObjects(), $userLdapService->getTotalObjects()), + sprintf(__('Errores: %d'), $userLdapService->getErrorObjects()) + + ] + ); + } catch (\Exception $e) { + processException($e); + + $this->returnJsonResponseException($e); + } } protected function initialize() diff --git a/app/modules/web/Controllers/ConfigMailController.php b/app/modules/web/Controllers/ConfigMailController.php index c981dc94..da4730bb 100644 --- a/app/modules/web/Controllers/ConfigMailController.php +++ b/app/modules/web/Controllers/ConfigMailController.php @@ -47,7 +47,7 @@ class ConfigMailController extends SimpleControllerBase public function saveAction() { $eventMessage = EventMessage::factory(); - $configData = clone $this->config->getConfigData(); + $configData = $this->config->getConfigData(); // Mail $mailEnabled = Request::analyze('mail_enabled', false, false, true); diff --git a/app/modules/web/Controllers/ConfigWikiController.php b/app/modules/web/Controllers/ConfigWikiController.php index 9e5045b6..ce3d3e6b 100644 --- a/app/modules/web/Controllers/ConfigWikiController.php +++ b/app/modules/web/Controllers/ConfigWikiController.php @@ -47,7 +47,7 @@ class ConfigWikiController extends SimpleControllerBase public function saveAction() { $eventMessage = EventMessage::factory(); - $configData = clone $this->config->getConfigData(); + $configData = $this->config->getConfigData(); // Wiki $wikiEnabled = Request::analyze('wiki_enabled', false, false, true); diff --git a/app/modules/web/Controllers/Traits/JsonTrait.php b/app/modules/web/Controllers/Traits/JsonTrait.php index 7a6e5daa..f317185f 100644 --- a/app/modules/web/Controllers/Traits/JsonTrait.php +++ b/app/modules/web/Controllers/Traits/JsonTrait.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -38,9 +38,9 @@ trait JsonTrait /** * Returns JSON response * - * @param int $status Status code - * @param string $description Untranslated description string - * @param array|null $messages Untranslated massages array of strings + * @param int $status Status code + * @param string $description Untranslated description string + * @param array|null $messages Untranslated massages array of strings */ protected function returnJsonResponse($status, $description, array $messages = null) { @@ -58,11 +58,12 @@ trait JsonTrait /** * Returns JSON response * - * @param mixed $data - * @param int $status Status code - * @param null $description Untranslated description string + * @param mixed $data + * @param int $status Status code + * @param string $description Untranslated description string + * @param array $messages */ - protected function returnJsonResponseData($data, $status = JsonResponse::JSON_SUCCESS, $description = null) + protected function returnJsonResponseData($data, $status = JsonResponse::JSON_SUCCESS, $description = null, array $messages = null) { $jsonResponse = new JsonResponse(); $jsonResponse->setStatus($status); @@ -72,6 +73,9 @@ trait JsonTrait $jsonResponse->setDescription($description); } + if (null !== $messages) { + $jsonResponse->setMessages($messages); + } Json::returnJson($jsonResponse); } @@ -80,7 +84,7 @@ trait JsonTrait * Returns JSON response * * @param \Exception $exception - * @param int $status Status code + * @param int $status Status code */ protected function returnJsonResponseException(\Exception $exception, $status = JsonResponse::JSON_ERROR) { diff --git a/app/modules/web/themes/material-blue/js/app-theme.js b/app/modules/web/themes/material-blue/js/app-theme.js index 915c8591..b431aae6 100644 --- a/app/modules/web/themes/material-blue/js/app-theme.js +++ b/app/modules/web/themes/material-blue/js/app-theme.js @@ -479,27 +479,21 @@ sysPass.Theme = function (Common) { /** * Elementos HTML del tema - * - * @type {{getList: html.getList}} */ - var html = { + const html = { getList: function (items, icon) { - var $ul = $(""); - var $li = $("
  • "); - var $span = $(""); + const $ul = $(""); + const $li = $("
  • "); + const $span = $(""); - if (icon === undefined) { - icon = "person"; - } else { - icon = "" + icon + ""; - } + const i = "" + (icon === undefined ? "person" : icon) + ""; items.forEach(function (value) { - var $spanClone = $span.clone(); - $spanClone.append(icon); + const $spanClone = $span.clone(); + $spanClone.append(i); $spanClone.append(value); - var $item = $li.clone().append($spanClone); + const $item = $li.clone().append($spanClone); $ul.append($item); }); @@ -507,15 +501,15 @@ sysPass.Theme = function (Common) { }, tabs: { add: function (header, index, title, isActive) { - var $header = $(header); - var active = ""; + const $header = $(header); + let active; if (isActive === 1) { $header.parent().find("#tabs-" + index).addClass("is-active"); active = "is-active"; } - var tab = "" + title + ""; + const tab = "" + title + ""; $header.append(tab); } diff --git a/app/modules/web/themes/material-blue/js/app-theme.min.js b/app/modules/web/themes/material-blue/js/app-theme.min.js index 86393686..2f283f66 100644 --- a/app/modules/web/themes/material-blue/js/app-theme.min.js +++ b/app/modules/web/themes/material-blue/js/app-theme.min.js @@ -1,20 +1,20 @@ -var $jscomp={scope:{},findInternal:function(a,e,c){a instanceof String&&(a=String(a));for(var g=a.length,k=0;k"); -a.passwordData.complexity.numbers&&(d+="1234567890");a.passwordData.complexity.chars&&(d+="abcdefghijklmnopqrstuvwxyz",a.passwordData.complexity.uppercase&&(d+="ABCDEFGHIJKLMNOPQRSTUVWXYZ"));for(;f++"); +a.passwordData.complexity.numbers&&(d+="1234567890");a.passwordData.complexity.chars&&(d+="abcdefghijklmnopqrstuvwxyz",a.passwordData.complexity.uppercase&&(d+="ABCDEFGHIJKLMNOPQRSTUVWXYZ"));for(;m++
    ";mdlDialog().show({title:a.config().LANG[29],text:b,negative:{title:a.config().LANG[44]},positive:{title:a.config().LANG[43],onClick:function(f){f.preventDefault();a.passwordData.complexity.chars=$("#checkbox-chars").is(":checked");a.passwordData.complexity.numbers=$("#checkbox-numbers").is(":checked");a.passwordData.complexity.uppercase=$("#checkbox-uppercase").is(":checked");a.passwordData.complexity.symbols=$("#checkbox-symbols").is(":checked");a.passwordData.complexity.numlength= -parseInt($("#passlength").val())}},cancelable:!0,contentStyle:{"max-width":"300px"},onLoaded:function(){$("#checkbox-chars").prop("checked",a.passwordData.complexity.chars);$("#checkbox-numbers").prop("checked",a.passwordData.complexity.numbers);$("#checkbox-uppercase").prop("checked",a.passwordData.complexity.uppercase);$("#checkbox-symbols").prop("checked",a.passwordData.complexity.symbols);$("#passlength").val(a.passwordData.complexity.numlength)}})},l=function(b){b.find(".passwordfield__input").each(function(){var f= -$(this);if("true"!==f.attr("data-pass-upgraded")){var d=f.parent(),b=f.attr("id"),c='',c=c+('
      ')+('
    • settings'+a.config().LANG[28]+"
    • "),c=c+('
    • vpn_key'+ -a.config().LANG[29]+"
    • "),c=c+('
    • refresh'+a.config().LANG[30]+"
    • ");d.after('
      ');d.next(".password-actions").prepend('').prepend('remove_red_eye').prepend(c);f.on("keyup",function(){a.checkPassLevel(f)});d=f.parent().next();d.find(".passGen").on("click", -function(){g(f);f.focus()});d.find(".passComplexity").on("click",function(){k()});d.find(".showpass").on("mouseover",function(){$(this).attr("title",f.val())});d.find(".reset").on("click",function(){f.val("");var a=$("#"+b+"R");0remove_red_eye');if(1===b.data("clipboard")){var c= -$('content_paste');b.parent().after(c).after(d)}else b.parent().after(d);d.on("mouseover",function(){d.attr("title",b.val())})})},m=function(b){e.info("setupDatePicker");var c={format:"YYYY-MM-DD",lang:a.config().LOCALE.substr(0,2),time:!1,cancelText:a.config().LANG[44],okText:a.config().LANG[43],clearText:a.config().LANG[30],nowText:a.config().LANG[56],minDate:new Date,triggerEvent:"dateIconClick"}; -b.find(".password-datefield__input").each(function(){var b=$(this);b.bootstrapMaterialDatePicker(c);b.parent().append("");b.parent().next("i").on("click",function(){b.trigger("dateIconClick")});b.on("change",function(){var c;c=moment.tz(b.val(),a.config().TIMEZONE).format("X");b.parent().find("input[name='passworddatechange_unix']").val(c)})})};return{passwordDetect:l,password:g,viewsTriggers:{main:function(){var a= +"
      ";mdlDialog().show({title:a.config().LANG[29],text:b,negative:{title:a.config().LANG[44]},positive:{title:a.config().LANG[43],onClick:function(b){b.preventDefault();a.passwordData.complexity.chars=$("#checkbox-chars").is(":checked");a.passwordData.complexity.numbers=$("#checkbox-numbers").is(":checked");a.passwordData.complexity.uppercase=$("#checkbox-uppercase").is(":checked");a.passwordData.complexity.symbols=$("#checkbox-symbols").is(":checked");a.passwordData.complexity.numlength= +parseInt($("#passlength").val())}},cancelable:!0,contentStyle:{"max-width":"300px"},onLoaded:function(){$("#checkbox-chars").prop("checked",a.passwordData.complexity.chars);$("#checkbox-numbers").prop("checked",a.passwordData.complexity.numbers);$("#checkbox-uppercase").prop("checked",a.passwordData.complexity.uppercase);$("#checkbox-symbols").prop("checked",a.passwordData.complexity.symbols);$("#passlength").val(a.passwordData.complexity.numlength)}})},l=function(b){b.find(".passwordfield__input").each(function(){var b= +$(this);if("true"!==b.attr("data-pass-upgraded")){var d=b.parent(),c=b.attr("id"),g='',g=g+('
        ')+('
      • settings'+a.config().LANG[28]+"
      • "),g=g+('
      • vpn_key'+ +a.config().LANG[29]+"
      • "),g=g+('
      • refresh'+a.config().LANG[30]+"
      • ");d.after('
        ');d.next(".password-actions").prepend('').prepend('remove_red_eye').prepend(g);b.on("keyup",function(){a.checkPassLevel(b)});d=b.parent().next();d.find(".passGen").on("click", +function(){h(b);b.focus()});d.find(".passComplexity").on("click",function(){k()});d.find(".showpass").on("mouseover",function(){$(this).attr("title",b.val())});d.find(".reset").on("click",function(){b.val("");var a=$("#"+c+"R");0remove_red_eye');if(1===b.data("clipboard")){var c= +$('content_paste');b.parent().after(c).after(d)}else b.parent().after(d);d.on("mouseover",function(){d.attr("title",b.val())})})},n=function(b){f.info("setupDatePicker");var c={format:"YYYY-MM-DD",lang:a.config().LOCALE.substr(0,2),time:!1,cancelText:a.config().LANG[44],okText:a.config().LANG[43],clearText:a.config().LANG[30],nowText:a.config().LANG[56],minDate:new Date,triggerEvent:"dateIconClick"}; +b.find(".password-datefield__input").each(function(){var b=$(this);b.bootstrapMaterialDatePicker(c);b.parent().append("");b.parent().next("i").on("click",function(){b.trigger("dateIconClick")});b.on("change",function(){var c;c=moment.tz(b.val(),a.config().TIMEZONE).format("X");b.parent().find("input[name='passworddatechange_unix']").val(c)})})};return{passwordDetect:l,password:h,viewsTriggers:{main:function(){var a= document.querySelector(".mdl-layout");$(".mdl-layout__drawer").find("a").click(function(){a.MaterialLayout.toggleDrawer()})},search:function(){var b=$("#frmSearch"),c=$("#res-content");b.find("button.btn-clear").on("click",function(a){$(".icon-searchfav").find("i").removeClass("mdl-color-text--amber-A200")});b.find(".icon-searchfav").on("click",function(){var c=$(this).find("i"),d=b.find("input[name='searchfav']");0==d.val()?(c.addClass("mdl-color-text--amber-A200"),c.attr("title",a.config().LANG[53]), -d.val(1)):(c.removeClass("mdl-color-text--amber-A200"),c.attr("title",a.config().LANG[52]),d.val(0));b.submit()});var d=b.find("#tags")[0],h=b.find(".search-filters-tags"),e=b.find("i.show-filter");c.on("click","#data-search-header .sort-down,#data-search-header .sort-up",function(){var b=$(this);b.parent().find("a").addClass("filterOn");a.appActions().account.sort(b)}).on("click","#search-rows i.icon-favorite",function(){var b=$(this);a.appActions().account.saveFavorite(b,function(){"on"===b.data("status")? -(b.addClass("mdl-color-text--amber-A100"),b.attr("title",a.config().LANG[50]),b.html("star")):(b.removeClass("mdl-color-text--amber-A100"),b.attr("title",a.config().LANG[49]),b.html("star_border"))})}).on("click","#search-rows span.tag",function(){h.is(":hidden")&&e.trigger("click");d.selectize.addItem($(this).data("tag-id"),!1)});e.on("click",function(){var a=$(this);h.is(":hidden")?(h.slideDown("slow"),a.html(a.data("icon-up"))):(h.slideUp("slow"),a.html(a.data("icon-down")))});0
      '),f=$('
    • '),e=$('');c=void 0===c?'person':''+c+"";a.forEach(function(a){var d=e.clone();d.append(c); -d.append(a);a=f.clone().append(d);b.append(a)});return b},tabs:{add:function(a,c,d,e){a=$(a);var b="";1===e&&(a.parent().find("#tabs-"+c).addClass("is-active"),b="is-active");a.append(''+d+"")}}}}}; +d.val(1)):(c.removeClass("mdl-color-text--amber-A200"),c.attr("title",a.config().LANG[52]),d.val(0));b.submit()});var d=b.find("#tags")[0],e=b.find(".search-filters-tags"),g=b.find("i.show-filter");c.on("click","#data-search-header .sort-down,#data-search-header .sort-up",function(){var b=$(this);b.parent().find("a").addClass("filterOn");a.appActions().account.sort(b)}).on("click","#search-rows i.icon-favorite",function(){var b=$(this);a.appActions().account.saveFavorite(b,function(){"on"===b.data("status")? +(b.addClass("mdl-color-text--amber-A100"),b.attr("title",a.config().LANG[50]),b.html("star")):(b.removeClass("mdl-color-text--amber-A100"),b.attr("title",a.config().LANG[49]),b.html("star_border"))})}).on("click","#search-rows span.tag",function(){e.is(":hidden")&&g.trigger("click");d.selectize.addItem($(this).data("tag-id"),!1)});g.on("click",function(){var a=$(this);e.is(":hidden")?(e.slideDown("slow"),a.html(a.data("icon-up"))):(e.slideUp("slow"),a.html(a.data("icon-down")))});0
    '),e=$('
  • '),g=$(''),f=''+(void 0===c?"person":c)+"";a.forEach(function(a){var c=g.clone();c.append(f);c.append(a);a=e.clone().append(c);b.append(a)}); +return b},tabs:{add:function(a,c,d,e){a=$(a);var b;1===e&&(a.parent().find("#tabs-"+c).addClass("is-active"),b="is-active");a.append(''+d+"")}}}}}; diff --git a/app/modules/web/themes/material-blue/views/config/ldap.inc b/app/modules/web/themes/material-blue/views/config/ldap.inc index 907428ed..10f90559 100644 --- a/app/modules/web/themes/material-blue/views/config/ldap.inc +++ b/app/modules/web/themes/material-blue/views/config/ldap.inc @@ -1,7 +1,7 @@
    - +
    + + + + + +
    +
    + + + + + + + + + + + + +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    + +
    + +
    + + + + + + + + + + +
    -
    getIconHelp()->getIcon(); ?>
    -
    +

    @@ -263,7 +317,7 @@
    -
    -
    getIconHelp()->getIcon(); ?>
    -
    +

    @@ -286,7 +340,7 @@
    -
    + +
    + +
    getIconHelp()->getIcon(); ?>
    +
    +

    + +

    +
    +
    +
    + + +
    -
    -
    -
      -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    +
    +
      +
    • + +
    • +
    • + +
    • +
    + \ No newline at end of file diff --git a/lib/Definitions.php b/lib/Definitions.php index 44b49d6c..2f8f33b4 100644 --- a/lib/Definitions.php +++ b/lib/Definitions.php @@ -30,7 +30,7 @@ return [ \SP\Core\Session\Session::class => object(\SP\Core\Session\Session::class), \SP\Config\Config::class => object(\SP\Config\Config::class) ->constructor(object(\SP\Storage\XmlHandler::class) - ->constructor(CONFIG_FILE)), + ->constructor(CONFIG_FILE), get(\SP\Core\Session\Session::class)), \SP\Core\Language::class => object(\SP\Core\Language::class), \SP\Config\ConfigData::class => function (\SP\Config\Config $config) { return $config->getConfigData(); diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php index f8295563..5767080f 100644 --- a/lib/SP/Config/Config.php +++ b/lib/SP/Config/Config.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,10 +24,11 @@ namespace SP\Config; +use DI\Container; use ReflectionObject; use SP\Core\Exceptions\ConfigException; use SP\Core\Session\Session; -use SP\Core\Traits\InjectableTrait; +use SP\Services\Config\ConfigBackupService; use SP\Storage\XmlFileStorageInterface; use SP\Storage\XmlHandler; @@ -38,8 +39,6 @@ defined('APP_ROOT') || die(); */ class Config { - use InjectableTrait; - /** * @var bool */ @@ -56,18 +55,24 @@ class Config * @var Session */ private $session; + /** + * @var Container + */ + private $dic; /** * Config constructor. * * @param XmlFileStorageInterface $fileStorage - * @throws \SP\Core\Exceptions\ConfigException - * @throws \SP\Core\Dic\ContainerException + * @param Session $session + * @param Container $dic + * @throws ConfigException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface */ - public function __construct(XmlFileStorageInterface $fileStorage) + public function __construct(XmlFileStorageInterface $fileStorage, Session $session, Container $dic) { - $this->injectDependencies(); - + $this->session = $session; $this->fileStorage = $fileStorage; if (!self::$configLoaded) { @@ -77,6 +82,7 @@ class Config self::$configLoaded = true; } + $this->dic = $dic; } /** @@ -90,11 +96,11 @@ class Config ConfigUtil::checkConfigDir(); try { - // Mapear el array de elementos de configuración con las propieades de la clase configData + // Mapear el array de elementos de configuración con las propiedades de la clase configData $items = $this->fileStorage->load('config')->getItems(); - $Reflection = new ReflectionObject($this->configData); + $reflectionObject = new ReflectionObject($this->configData); - foreach ($Reflection->getProperties() as $property) { + foreach ($reflectionObject->getProperties() as $property) { $property->setAccessible(true); if (isset($items[$property->getName()])) { @@ -112,16 +118,6 @@ class Config return $this->configData; } - /** - * Obtener la configuración o devolver una nueva - * - * @return void - * @deprecated - */ - public static function getConfig() - { - } - /** * Cargar la configuración desde el archivo * @@ -156,29 +152,21 @@ class Config * * @param ConfigData $configData * @param bool $backup + * @throws \DI\DependencyException + * @throws \DI\NotFoundException */ - public function saveConfig(ConfigData $configData = null, $backup = true) + public function saveConfig(ConfigData $configData, $backup = true) { - $configData = null === $configData ? $this->configData : $configData; + if ($backup) { + $this->dic->get(ConfigBackupService::class)->backup(); + } + $configData->setConfigDate(time()); $configData->setConfigSaver($this->session->getUserData()->getLogin()); $configData->setConfigHash(); $this->fileStorage->setItems($configData); $this->fileStorage->save('config'); - -// if ($backup) { -// $this->backupToDB(); -// } - } - - /** - * Realizar un backup de la configuración en la BD - */ - private function backupToDB() - { - ConfigDB::setValue('config_backup', json_encode($this->configData), true, true); - ConfigDB::setValue('config_backupdate', time()); } /** @@ -186,26 +174,6 @@ class Config */ public function getConfigData() { - return $this->configData; - } - - /** - * @param Session $session - */ - public function inject(Session $session) - { - $this->session = $session; - } - - /** - * Restaurar la configuración desde la BD - * - * @return array - */ - private function restoreBackupFromDB() - { - $configBackup = ConfigDB::getValue('config_backup'); - - return json_decode($configBackup); + return clone $this->configData; } } diff --git a/lib/SP/Config/ConfigUtil.php b/lib/SP/Config/ConfigUtil.php index 485b914d..5ba05006 100644 --- a/lib/SP/Config/ConfigUtil.php +++ b/lib/SP/Config/ConfigUtil.php @@ -81,7 +81,7 @@ class ConfigUtil throw new ConfigException( __u('Los permisos del directorio "/config" son incorrectos'), ConfigException::ERROR, - sprintf(__u('Actual: %s - Necesario: 750'), $configPerms)); + sprintf(__('Actual: %s - Necesario: 750'), $configPerms)); } } } \ No newline at end of file diff --git a/lib/SP/Core/CryptPKI.php b/lib/SP/Core/CryptPKI.php index 394b90b4..df010b1a 100644 --- a/lib/SP/Core/CryptPKI.php +++ b/lib/SP/Core/CryptPKI.php @@ -30,7 +30,6 @@ use phpseclib\Crypt\RSA; use SP\Core\Exceptions\FileNotFoundException; use SP\Core\Exceptions\SPException; use SP\Core\Traits\InjectableTrait; -use SP\Log\Log; /** * Class CryptPKI para el manejo de las funciones para PKI @@ -130,8 +129,6 @@ class CryptPKI $file = $this->getPublicKeyFile(); if (!file_exists($file)) { - Log::writeNewLog(__FUNCTION__, __('El archivo de clave no existe', false), Log::NOTICE); - throw new FileNotFoundException(SPException::ERROR, __('El archivo de clave no existe')); } @@ -150,7 +147,7 @@ class CryptPKI $this->rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1); $this->rsa->loadKey($this->getPrivateKey()); - return $this->rsa->decrypt($data); + return @$this->rsa->decrypt($data); } /** @@ -164,8 +161,6 @@ class CryptPKI $file = $this->getPrivateKeyFile(); if (!file_exists($file)) { - Log::writeNewLog(__FUNCTION__, __('El archivo de clave no existe', false), Log::NOTICE); - throw new FileNotFoundException(SPException::ERROR, __('El archivo de clave no existe')); } diff --git a/lib/SP/DataModel/UserData.php b/lib/SP/DataModel/UserData.php index fdca7220..8f9c354a 100644 --- a/lib/SP/DataModel/UserData.php +++ b/lib/SP/DataModel/UserData.php @@ -36,23 +36,23 @@ class UserData extends UserPassData implements DataModelInterface /** * @var string */ - public $login = ''; + public $login; /** * @var string */ - public $ssoLogin = ''; + public $ssoLogin; /** * @var string */ - public $name = ''; + public $name; /** * @var string */ - public $email = ''; + public $email; /** * @var string */ - public $notes = ''; + public $notes; /** * @var int */ @@ -92,11 +92,11 @@ class UserData extends UserPassData implements DataModelInterface /** * @var string */ - public $lastLogin = ''; + public $lastLogin; /** * @var string */ - public $lastUpdate = ''; + public $lastUpdate; /** * @var bool */ @@ -108,7 +108,7 @@ class UserData extends UserPassData implements DataModelInterface /** * @var string */ - public $userGroupName = ''; + public $userGroupName; /** * @return int diff --git a/lib/SP/DataModel/UserPassData.php b/lib/SP/DataModel/UserPassData.php index dbf06d29..aeb1436b 100644 --- a/lib/SP/DataModel/UserPassData.php +++ b/lib/SP/DataModel/UserPassData.php @@ -38,19 +38,19 @@ class UserPassData extends DataModelBase /** * @var string */ - public $pass = ''; + public $pass; /** * @var string */ - public $hashSalt = ''; + public $hashSalt; /** * @var string */ - public $mPass = ''; + public $mPass; /** * @var string */ - public $mKey = ''; + public $mKey; /** * @var int */ diff --git a/lib/SP/Http/Request.php b/lib/SP/Http/Request.php index 97546502..46195f17 100644 --- a/lib/SP/Http/Request.php +++ b/lib/SP/Http/Request.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -118,10 +118,14 @@ class Request try { // Desencriptar con la clave RSA - $CryptPKI = new CryptPKI(); - $clearData = $CryptPKI->decryptRSA(base64_decode($encryptedData)); + if (($clearData = (new CryptPKI())->decryptRSA(base64_decode($encryptedData))) === false) { + debugLog('No RSA encrypted data from request'); + + return $encryptedData; + } } catch (\Exception $e) { - debugLog($e->getMessage()); + processException($e); + return $encryptedData; } diff --git a/lib/SP/Providers/Auth/Auth.php b/lib/SP/Providers/Auth/Auth.php index fb74891c..9f85d080 100644 --- a/lib/SP/Providers/Auth/Auth.php +++ b/lib/SP/Providers/Auth/Auth.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -25,7 +25,6 @@ namespace SP\Providers\Auth; use SP\Config\ConfigData; -use SP\Core\Exceptions\SPException; use SP\DataModel\UserLoginData; use SP\Providers\Auth\Browser\Browser; use SP\Providers\Auth\Browser\BrowserAuthData; @@ -33,7 +32,12 @@ use SP\Providers\Auth\Database\Database; use SP\Providers\Auth\Database\DatabaseAuthData; use SP\Providers\Auth\Ldap\LdapAuthData; use SP\Providers\Auth\Ldap\LdapMsAds; +use SP\Providers\Auth\Ldap\LdapParams; use SP\Providers\Auth\Ldap\LdapStd; +use SP\Providers\Provider; +use SP\Services\Auth\AuthException; +use SP\Services\User\UserPassService; +use SP\Services\User\UserService; defined('APP_ROOT') || die(); @@ -44,7 +48,7 @@ defined('APP_ROOT') || die(); * * @package SP\Providers\Auth */ -class Auth +class Auth extends Provider { /** * @var array @@ -59,55 +63,16 @@ class Auth */ protected $configData; - /** - * Auth constructor. - * - * @param UserLoginData $userLoginData - * @param ConfigData $configData - * @throws SPException - */ - public function __construct(UserLoginData $userLoginData, ConfigData $configData) - { - $this->userLoginData = $userLoginData; - $this->configData = $configData; - - if ($this->configData->isAuthBasicEnabled()) { - $this->registerAuth('authBrowser'); - } - - if ($this->configData->isLdapEnabled()) { - $this->registerAuth('authLdap'); - } - - $this->registerAuth('authDatabase'); - } - - /** - * Registrar un método de autentificación primarios - * - * @param string $auth Función de autentificación - * @throws SPException - */ - protected function registerAuth($auth) - { - if (array_key_exists($auth, $this->auths)) { - throw new SPException(__u('Método ya inicializado'), SPException::ERROR, __FUNCTION__); - } - - if (!method_exists($this, $auth)) { - throw new SPException(__u('Método no disponible'), SPException::ERROR, __FUNCTION__); - } - - $this->auths[$auth] = $auth; - } - /** * Probar los métodos de autentificación * + * @param UserLoginData $userLoginData * @return bool|array */ - public function doAuth() + public function doAuth(UserLoginData $userLoginData) { + $this->userLoginData = $userLoginData; + $auths = []; /** @var AuthDataBase $pAuth */ @@ -129,7 +94,18 @@ class Auth */ public function authLdap() { - $ldap = $this->configData->isLdapAds() ? new LdapMsAds() : new LdapStd(); + $ldapParams = (new LdapParams()) + ->setServer($this->configData->getLdapServer()) + ->setBindDn($this->configData->getLdapBindUser()) + ->setBindPass($this->configData->getLdapBindPass()) + ->setSearchBase($this->configData->getLdapBase()) + ->setAds($this->configData->isLdapAds()); + + if ($this->configData->isLdapAds()) { + $ldap = new LdapMsAds($ldapParams, $this->eventDispatcher, $this->configData->isDebug()); + } else { + $ldap = new LdapStd($ldapParams, $this->eventDispatcher, $this->configData->isDebug()); + } $ldapAuthData = $ldap->getLdapAuthData(); @@ -156,12 +132,13 @@ class Auth * se ejecuta el proceso para actualizar la clave. * * @return DatabaseAuthData - * @throws \ReflectionException - * @throws \SP\Core\Dic\ContainerException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface */ public function authDatabase() { - return (new Database())->authenticate($this->userLoginData); + return (new Database($this->dic->get(UserService::class), $this->dic->get(UserPassService::class))) + ->authenticate($this->userLoginData); } /** @@ -171,6 +148,45 @@ class Auth */ public function authBrowser() { - return (new Browser())->authenticate($this->userLoginData); + return (new Browser($this->configData))->authenticate($this->userLoginData); + } + + /** + * Auth constructor. + * + * @throws AuthException + */ + protected function initialize() + { + $this->configData = $this->config->getConfigData(); + + if ($this->configData->isAuthBasicEnabled()) { + $this->registerAuth('authBrowser'); + } + + if ($this->configData->isLdapEnabled()) { + $this->registerAuth('authLdap'); + } + + $this->registerAuth('authDatabase'); + } + + /** + * Registrar un método de autentificación primarios + * + * @param string $auth Función de autentificación + * @throws AuthException + */ + protected function registerAuth($auth) + { + if (array_key_exists($auth, $this->auths)) { + throw new AuthException(__u('Método ya inicializado'), AuthException::ERROR, __FUNCTION__); + } + + if (!method_exists($this, $auth)) { + throw new AuthException(__u('Método no disponible'), AuthException::ERROR, __FUNCTION__); + } + + $this->auths[$auth] = $auth; } } diff --git a/lib/SP/Providers/Auth/Browser/Browser.php b/lib/SP/Providers/Auth/Browser/Browser.php index 9bbecfd4..89b30e50 100644 --- a/lib/SP/Providers/Auth/Browser/Browser.php +++ b/lib/SP/Providers/Auth/Browser/Browser.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,9 +24,7 @@ namespace SP\Providers\Auth\Browser; -use SP\Config\Config; use SP\Config\ConfigData; -use SP\Core\Traits\InjectableTrait; use SP\DataModel\UserLoginData; use SP\Providers\Auth\AuthInterface; @@ -39,25 +37,19 @@ use SP\Providers\Auth\AuthInterface; */ class Browser implements AuthInterface { - use InjectableTrait; - - /** @var ConfigData */ - protected $configData; + /** + * @var ConfigData + */ + private $configData; /** * Browser constructor. + * + * @param ConfigData $configData */ - public function __construct() + public function __construct(ConfigData $configData) { - $this->injectDependencies(); - } - - /** - * @param Config $config - */ - public function inject(Config $config) - { - $this->configData = $config->getConfigData(); + $this->configData = $configData; } /** diff --git a/lib/SP/Providers/Auth/Database/Database.php b/lib/SP/Providers/Auth/Database/Database.php index 341f9e11..0243b440 100644 --- a/lib/SP/Providers/Auth/Database/Database.php +++ b/lib/SP/Providers/Auth/Database/Database.php @@ -25,9 +25,9 @@ namespace SP\Providers\Auth\Database; use SP\Core\Crypt\Hash; -use SP\Core\Exceptions\SPException; use SP\DataModel\UserLoginData; use SP\Providers\Auth\AuthInterface; +use SP\Services\User\UserLoginResponse; use SP\Services\User\UserPassService; use SP\Services\User\UserService; @@ -44,14 +44,33 @@ class Database implements AuthInterface * @var UserLoginData $userLoginData */ protected $userLoginData; + /** + * @var UserService + */ + private $userService; + /** + * @var UserPassService + */ + private $userPassService; + + /** + * Database constructor. + * + * @param UserService $userService + * @param UserPassService $userPassService + */ + public function __construct(UserService $userService, UserPassService $userPassService) + { + $this->userService = $userService; + $this->userPassService = $userPassService; + } + /** * Autentificar al usuario * * @param UserLoginData $userLoginData Datos del usuario * @return DatabaseAuthData - * @throws \SP\Core\Dic\ContainerException - * @throws \ReflectionException */ public function authenticate(UserLoginData $userLoginData) { @@ -81,23 +100,22 @@ class Database implements AuthInterface * se ejecuta el proceso para actualizar la clave. * * @return bool - * @throws \SP\Core\Dic\ContainerException - * @throws \ReflectionException */ protected function authUser() { try { - $userLoginResponse = (new UserService())->getByLogin($this->userLoginData->getLoginUser()); + $userLoginResponse = $this->userService->getByLogin($this->userLoginData->getLoginUser()); $this->userLoginData->setUserLoginResponse($userLoginResponse); - if ($userLoginResponse->getIsMigrate() && $this->checkMigrateUser()) { - return (new UserPassService())->migrateUserPassById($userLoginResponse->getId(), $this->userLoginData->getLoginPass()); + if ($userLoginResponse->getIsMigrate() && $this->checkMigrateUser($userLoginResponse)) { + return $this->userPassService->migrateUserPassById($userLoginResponse->getId(), $this->userLoginData->getLoginPass()); } return Hash::checkHashKey($this->userLoginData->getLoginPass(), $userLoginResponse->getPass()); - } catch (SPException $e) { + } catch (\Exception $e) { + processException($e); // $Log = new Log(); // $LogMessage = $Log->getLogMessage(); // $LogMessage->setAction(__FUNCTION__); @@ -110,12 +128,11 @@ class Database implements AuthInterface } /** + * @param UserLoginResponse $userLoginResponse * @return bool */ - protected function checkMigrateUser() + protected function checkMigrateUser(UserLoginResponse $userLoginResponse) { - $userLoginResponse = $this->userLoginData->getUserLoginResponse(); - return ($userLoginResponse->getPass() === sha1($userLoginResponse->getHashSalt() . $this->userLoginData->getLoginPass()) || $userLoginResponse->getPass() === md5($this->userLoginData->getLoginPass()) || hash_equals($userLoginResponse->getPass(), crypt($this->userLoginData->getLoginPass(), $userLoginResponse->getHashSalt())) diff --git a/lib/SP/Providers/Auth/Ldap/LdapBase.php b/lib/SP/Providers/Auth/Ldap/LdapBase.php index dbef4a3c..7c1dd7f6 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapBase.php +++ b/lib/SP/Providers/Auth/Ldap/LdapBase.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,13 +24,10 @@ namespace SP\Providers\Auth\Ldap; -use SP\Config\Config; -use SP\Config\ConfigData; -use SP\Core\Exceptions\SPException; -use SP\Core\Messages\LogMessage; -use SP\Core\Traits\InjectableTrait; +use SP\Core\Events\Event; +use SP\Core\Events\EventDispatcher; +use SP\Core\Events\EventMessage; use SP\DataModel\UserLoginData; -use SP\Log\Log; use SP\Providers\Auth\AuthInterface; /** @@ -62,34 +59,6 @@ abstract class LdapBase implements LdapInterface, AuthInterface * @var resource */ protected $ldapHandler; - /** - * @var string - */ - protected $server; - /** - * @var int - */ - protected $serverPort; - /** - * @var string - */ - protected $searchBase; - /** - * @var string - */ - protected $bindDn; - /** - * @var string - */ - protected $bindPass; - /** - * @var string - */ - protected $group; - /** - * @var array - */ - protected $searchData; /** * @var string */ @@ -97,237 +66,194 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * @var LdapAuthData */ - protected $LdapAuthData; + protected $ldapAuthData; /** - * @var LogMessage + * @var LdapParams */ - protected $LogMessage; - - use InjectableTrait; - - /** @var ConfigData */ - protected $ConfigData; + protected $ldapParams; + /** + * @var EventDispatcher + */ + protected $eventDispatcher; + /** + * @var string + */ + protected $server; + /** + * @var bool + */ + protected $isConnected = false; + /** + * @var bool + */ + protected $isBound = false; + /** + * @var bool + */ + private $debug; /** * LdapBase constructor. - */ - public function __construct() - { - $this->injectDependencies(); - - $this->LdapAuthData = new LdapAuthData(); - $this->LogMessage = new LogMessage(); - } - - /** - * @param Config $config - */ - public function inject(Config $config) - { - $this->Config = $config; - $this->ConfigData = $config->getConfigData(); - } - - /** - * Indica si es requerida para acceder a la aplicación * - * @return boolean + * @param LdapParams $ldapParams + * @param EventDispatcher $eventDispatcher + * @param bool $debug */ - public function isAuthGranted() + public function __construct(LdapParams $ldapParams, EventDispatcher $eventDispatcher, $debug = false) { - return true; + $this->ldapParams = $ldapParams; + $this->eventDispatcher = $eventDispatcher; + $this->debug = (bool)$debug; + + $this->ldapAuthData = new LdapAuthData(); } /** * Comprobar la conexión al servidor de LDAP. * - * @return false|array Con el número de entradas encontradas - * @throws \SP\Core\Exceptions\SPException + * @throws LdapException */ public function checkConnection() { - $this->LogMessage->setAction(__FUNCTION__); - - if (!$this->searchBase || !$this->server || !$this->bindDn || !$this->bindPass) { - $this->LogMessage->addDescription(__('Los parámetros de LDAP no están configurados', false)); - $this->writeLog(); - - return false; - } - try { - $this->connect(); - $this->bind(); - $results = $this->searchBase(); - } catch (SPException $e) { + $this->connectAndBind(); + + $this->eventDispatcher->notifyEvent('ldap.check.connection', + new Event($this, EventMessage::factory() + ->addDescription(__u('Conexión a LDAP correcta'))) + ); + } catch (LdapException $e) { throw $e; } - - $this->LogMessage->addDescription(__('Conexión a LDAP correcta', false)); - $this->LogMessage->addDetails(__('Objetos encontrados', false), (int)$results['count']); - $this->writeLog(Log::INFO); - - return $results; } /** - * Escribir en el registro de eventos - * - * @param string $level + * @throws LdapException */ - protected function writeLog($level = Log::ERROR) + protected function connectAndBind() { - $Log = new Log($this->LogMessage); - $Log->setLogLevel($level); - $Log->writeLog(); + if (!$this->isConnected && !$this->isBound) { + $this->connect(); + $this->bind(); + } } /** * Realizar la conexión al servidor de LDAP. * - * @throws SPException + * @throws LdapException * @return bool */ protected function connect() { + $this->checkParams(); + // Habilitar la traza si el modo debug está habilitado - if ($this->ConfigData->isDebug()) { + if ($this->debug) { @ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7); } - $this->ldapHandler = @ldap_connect($this->server, $this->serverPort); + $this->ldapHandler = @ldap_connect($this->server, $this->ldapParams->getPort()); // Conexión al servidor LDAP if (!is_resource($this->ldapHandler)) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('No es posible conectar con el servidor de LDAP', false)); - $this->LogMessage->addDetails(__('Servidor'), $this->server); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.connection', + new Event($this, EventMessage::factory() + ->addDescription(__u('No es posible conectar con el servidor de LDAP')) + ->addDetail(__('Servidor'), $this->server) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage())) + ); - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR, $this->LogMessage->getDetails()); + throw new LdapException(__u('No es posible conectar con el servidor de LDAP')); } @ldap_set_option($this->ldapHandler, LDAP_OPT_NETWORK_TIMEOUT, 10); @ldap_set_option($this->ldapHandler, LDAP_OPT_PROTOCOL_VERSION, 3); + $this->isConnected = true; + return true; } + /** + * Comprobar si los parámetros necesario de LDAP están establecidos. + * + * @throws LdapException + */ + public function checkParams() + { + if (!$this->ldapParams->getSearchBase() + || !$this->ldapParams->getServer() + || !$this->ldapParams->getBindDn() + ) { + $this->eventDispatcher->notifyEvent('ldap.check.params', + new Event($this, EventMessage::factory() + ->addDescription(__u('Los parámetros de LDAP no están configurados'))) + ); + + throw new LdapException(__u('Los parámetros de LDAP no están configurados')); + } + + $this->server = $this->pickServer(); + $this->ldapAuthData->setServer($this->server); + } + + /** + * Obtener el servidor de LDAP a utilizar + * + * @return mixed + */ + protected abstract function pickServer(); + /** * Registrar error de LDAP y devolver el mensaje de error * * @return string */ - protected function ldapError() + protected function getLdapErrorMessage() { - $error = ldap_error($this->ldapHandler); - $errno = ldap_errno($this->ldapHandler); + $this->ldapAuthData->setStatusCode(ldap_errno($this->ldapHandler)); - $this->LdapAuthData->setStatusCode($errno); - - return sprintf('%s (%d)', $error, $errno); + return sprintf('%s (%d)', ldap_error($this->ldapHandler), $this->ldapAuthData->getStatusCode()); } /** * Realizar la autentificación con el servidor de LDAP. * - * @param string $bindDn con el DN del usuario + * @param string $bindDn con el DN del usuario * @param string $bindPass con la clave del usuario - * @throws SPException + * @throws LdapException * @return bool */ - protected function bind($bindDn = '', $bindPass = '') + protected function bind($bindDn = null, $bindPass = null) { - if ($bindDn && $bindPass) { - $this->LdapAuthData->setAuthenticated(1); + $this->ldapAuthData->setAuthenticated($bindDn && $bindPass); + + $dn = $bindDn ?: $this->ldapParams->getBindDn(); + $pass = $bindPass ?: $this->ldapParams->getBindPass(); + + if (@ldap_bind($this->ldapHandler, $dn, $pass) === false) { + $this->eventDispatcher->notifyEvent('ldap.bind', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al conectar (BIND)')) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP DN', $dn)) + ); + + throw new LdapException(__u('Error al conectar (BIND)')); } - $dn = $bindDn ?: $this->bindDn; - $pass = $bindPass ?: $this->bindPass; - - if (!@ldap_bind($this->ldapHandler, $dn, $pass)) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Error al conectar (BIND)', false)); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->LogMessage->addDetails('LDAP DN', $dn); - $this->writeLog(); - - throw new SPException(__($this->LogMessage->getDescription()), SPException::ERROR); - } + $this->isBound = true; return true; } - /** - * Realizar una búsqueda de objetos en la ruta indicada. - * - * @throws SPException - * @return array Con los resultados de la búsqueda - */ - protected function searchBase() - { - $searchResults = $this->getResults($this->getGroupDnFilter(), ['dn']); - - if ($searchResults === false) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Error al buscar objetos en DN base', false)); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->LogMessage->addDetails('LDAP FILTER', $this->getGroupDnFilter()); - $this->writeLog(); - - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); - } - - return $searchResults; - } - - /** - * Devolver los resultados de una paginación - * - * @param string $filter Filtro a utilizar - * @param array $attributes Atributos a devolver - * @return bool|array - */ - protected function getResults($filter, array $attributes = null) - { - $cookie = ''; - $results = []; - - do { - ldap_control_paged_result($this->ldapHandler, 1000, false, $cookie); - - if (!$searchRes = @ldap_search($this->ldapHandler, $this->searchBase, $filter, $attributes)) { - return false; - } - - if (@ldap_count_entries($this->ldapHandler, $searchRes) === 0 - || !$entries = @ldap_get_entries($this->ldapHandler, $searchRes) - ) { - return false; - } - - $results = array_merge($results, $entries); - - ldap_control_paged_result_response($this->ldapHandler, $searchRes, $cookie); - } while (!empty($cookie)); - - return $results; - } - - /** - * Devolver el filtro para comprobar la pertenecia al grupo - * - * @return mixed - */ - protected abstract function getGroupDnFilter(); - /** * @return LdapAuthData */ public function getLdapAuthData() { - return $this->LdapAuthData; + return $this->ldapAuthData; } /** @@ -340,97 +266,6 @@ abstract class LdapBase implements LdapInterface, AuthInterface return is_resource($this->ldapHandler) ? $this->ldapHandler : false; } - /** - * @return string - */ - public function getServer() - { - return $this->server; - } - - /** - * @param string $server - */ - public function setServer($server) - { - $this->server = $server; - $this->serverPort = $this->getServerPort(); - } - - /** - * Devolver el puerto del servidor si está establecido - * - * @return int - */ - protected function getServerPort() - { - return preg_match('/[\d\.]+:(\d+)/', $this->server, $port) ? $port[1] : 389; - } - - /** - * @return string - */ - public function getSearchBase() - { - return $this->searchBase; - } - - /** - * @param string $searchBase - */ - public function setSearchBase($searchBase) - { - $this->searchBase = $searchBase; - } - - /** - * @return string - */ - public function getBindDn() - { - return $this->bindDn; - } - - /** - * @param string $bindDn - */ - public function setBindDn($bindDn) - { - $this->bindDn = $bindDn; - } - - /** - * @return string - */ - public function getBindPass() - { - return $this->bindPass; - } - - /** - * @param string $bindPass - */ - public function setBindPass($bindPass) - { - $this->bindPass = $bindPass; - } - - /** - * @return string - */ - public function getGroup() - { - return $this->group; - } - - /** - * @param string $group - */ - public function setGroup($group) - { - $this->group = $group; - } - /** * @return string */ @@ -450,25 +285,22 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * Autentificar al usuario * - * @param UserLoginData $UserData Datos del usuario + * @param UserLoginData $userLoginData Datos del usuario * @return bool */ - public function authenticate(UserLoginData $UserData) + public function authenticate(UserLoginData $userLoginData) { - if (!$this->checkParams()) { - return false; - } - - $this->LdapAuthData->setAuthGranted($this->isAuthGranted()); try { - $this->setUserLogin($UserData->getLoginUser()); + $this->ldapAuthData->setAuthGranted($this->isAuthGranted()); + $this->setUserLogin($userLoginData->getLoginUser()); - $this->connect(); - $this->bind(); + $this->connectAndBind(); $this->getAttributes(); - $this->bind($this->LdapAuthData->getDn(), $UserData->getLoginPass()); - } catch (SPException $e) { + $this->bind($this->ldapAuthData->getDn(), $userLoginData->getLoginPass()); + } catch (LdapException $e) { + processException($e); + return false; } @@ -476,44 +308,20 @@ abstract class LdapBase implements LdapInterface, AuthInterface } /** - * Comprobar si los parámetros necesario de LDAP están establecidos. + * Indica si es requerida para acceder a la aplicación * - * @return bool + * @return boolean */ - public function checkParams() + public function isAuthGranted() { - $this->searchBase = $this->ConfigData->getLdapBase(); - $this->server = $this->pickServer(); - $this->serverPort = $this->getServerPort(); - $this->bindDn = $this->ConfigData->getLdapBindUser(); - $this->bindPass = $this->ConfigData->getLdapBindPass(); - $this->group = $this->ConfigData->getLdapGroup(); - - if (!$this->searchBase || !$this->server || !$this->bindDn || !$this->bindPass) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Los parámetros de LDAP no están configurados', false)); - $this->writeLog(); - - return false; - } - - $this->LdapAuthData->setServer($this->server); - return true; } - /** - * Obtener el servidor de LDAP a utilizar - * - * @return mixed - */ - protected abstract function pickServer(); - /** * Obtener los atributos del usuario. * * @return LdapAuthData con los atributos disponibles y sus valores - * @throws SPException + * @throws LdapException */ public function getAttributes() { @@ -560,44 +368,45 @@ abstract class LdapBase implements LdapInterface, AuthInterface } if (!empty($res['fullname'])) { - $this->LdapAuthData->setName($res['fullname']); + $this->ldapAuthData->setName($res['fullname']); } else { - $this->LdapAuthData->setName($res['name'] . ' ' . $res['sn']); + $this->ldapAuthData->setName($res['name'] . ' ' . $res['sn']); } - $this->LdapAuthData->setDn($searchResults[0]['dn']); - $this->LdapAuthData->setEmail($res['mail']); - $this->LdapAuthData->setExpire($res['expire']); - $this->LdapAuthData->setGroups($res['group']); + $this->ldapAuthData->setDn($searchResults[0]['dn']); + $this->ldapAuthData->setEmail($res['mail']); + $this->ldapAuthData->setExpire($res['expire']); + $this->ldapAuthData->setGroups($res['group']); if (!empty($this->group) && $this->group !== '*') { - $this->LdapAuthData->setGroupDn($this->searchGroupDN()); + $this->ldapAuthData->setGroupDn($this->searchGroupDN()); } - $this->LdapAuthData->setInGroup($this->searchUserInGroup()); + $this->ldapAuthData->setInGroup($this->searchUserInGroup()); - return $this->LdapAuthData; + return $this->ldapAuthData; } /** * Obtener el RDN del usuario que realiza el login. * * @return array - * @throws SPException + * @throws LdapException */ protected function getUserAttributes() { $searchResults = $this->getResults($this->getUserDnFilter(), self::SEARCH_ATTRIBUTES); if ($searchResults === false || $searchResults['count'] > 1) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Error al localizar el usuario en LDAP', false)); - $this->LogMessage->addDetails(__('Usuario', false), $this->userLogin); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->LogMessage->addDetails('LDAP FILTER', $this->getUserDnFilter()); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.getAttributes', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al localizar el usuario en LDAP')) + ->addDetail(__u('Usuario'), $this->userLogin) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP DN', $this->getGroupMembershipFilter())) + ); - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); + throw new LdapException(__u('Error al localizar el usuario en LDAP')); } return $searchResults; @@ -613,25 +422,26 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * Obtener el RDN del grupo. * - * @throws SPException + * @throws LdapException * @return string con el RDN del grupo */ protected function searchGroupDN() { - $group = $this->getGroupName() ?: $this->group; + $group = $this->getGroupName() ?: $this->ldapParams->getGroup(); $filter = '(cn=' . ldap_escape($group) . ')'; $searchResults = $this->getResults($filter, ['dn', 'cn']); if ($searchResults === false || $searchResults['count'] > 1) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Error al buscar RDN de grupo', false)); - $this->LogMessage->addDetails(__('Grupo', false), $filter); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->LogMessage->addDetails('LDAP FILTER', $filter); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.search.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al buscar RDN de grupo')) + ->addDetail(__u('Grupo'), $filter) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP FILTER', $filter)) + ); - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); + throw new LdapException(__u('Error al buscar RDN de grupo')); } return $searchResults[0]['dn']; @@ -644,10 +454,10 @@ abstract class LdapBase implements LdapInterface, AuthInterface */ protected function getGroupName() { - if (null !== $this->group - && preg_match('/^cn=([\w\s-]+)(,.*)?/i', $this->group, $groupName) + if ($this->ldapParams->getGroup() + && preg_match('/^cn=(?P[\w\s-]+)(?:,.*)?/i', $this->ldapParams->getGroup(), $matches) ) { - return $groupName[1]; + return $matches['groupname']; } return false; @@ -656,7 +466,6 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * Buscar al usuario en un grupo. * - * @throws SPException * @return bool */ protected abstract function searchUserInGroup(); @@ -664,46 +473,118 @@ abstract class LdapBase implements LdapInterface, AuthInterface /** * Devolver los objetos disponibles * + * @param array $attributes * @return array|bool + * @throws LdapException */ - public function findObjects() + public function findUsersByGroupFilter(array $attributes = self::SEARCH_ATTRIBUTES) { - if (!$this->checkParams()) { - return false; - } + $this->connectAndBind(); - try { - $this->connect(); - $this->bind(); - return $this->getObjects(); - } catch (SPException $e) { - return false; - } + return $this->getObjects($this->getGroupMembershipFilter(), $attributes); } /** - * Obtener los objetos que se pertenecen al grupo filtrado + * Obtener los objetos según el filtro indicado * - * @return int - * @throws SPException + * @param string $filter + * @param array $attributes + * @return array + * @throws LdapException */ - protected function getObjects() + protected function getObjects($filter, array $attributes = self::SEARCH_ATTRIBUTES) { - $searchResults = $this->getResults($this->getGroupDnFilter(), self::SEARCH_ATTRIBUTES); + if (($searchResults = $this->getResults($filter, $attributes)) === false) { + $this->eventDispatcher->notifyEvent('ldap.search', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al buscar objetos en DN base')) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP FILTER', $this->getGroupMembershipFilter())) + ); - if ($searchResults === false) { - $this->LogMessage->setAction(__FUNCTION__); - $this->LogMessage->addDescription(__('Error al buscar objetos en DN base', false)); - $this->LogMessage->addDetails('LDAP ERROR', $this->ldapError()); - $this->LogMessage->addDetails('LDAP FILTER', $this->getGroupDnFilter()); - $this->writeLog(); - - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); + throw new LdapException(__u('Error al buscar objetos en DN base')); } return $searchResults; } + /** + * Devolver los objetos disponibles + * + * @param array $attributes + * @return array|bool + * @throws LdapException + */ + public function findGroups($attributes = self::SEARCH_ATTRIBUTES) + { + $this->connectAndBind(); + + return $this->getObjects($this->getGroupObjectFilter(), $attributes); + } + + /** + * Devolver el filtro para objetos del tipo grupo + * + * @return mixed + */ + protected abstract function getGroupObjectFilter(); + + /** + * @return bool + */ + public function isConnected() + { + return $this->isConnected; + } + + /** + * @return bool + */ + public function isBound() + { + return $this->isBound; + } + + /** + * Devolver los resultados de una paginación + * + * @param string $filter Filtro a utilizar + * @param array $attributes Atributos a devolver + * @return bool|array + */ + protected function getResults($filter, array $attributes = null) + { + $cookie = ''; + $results = []; + + do { + ldap_control_paged_result($this->ldapHandler, 1000, false, $cookie); + + if (!$searchRes = @ldap_search($this->ldapHandler, $this->ldapParams->getSearchBase(), $filter, $attributes)) { + return false; + } + + if (@ldap_count_entries($this->ldapHandler, $searchRes) === 0 + || !$entries = @ldap_get_entries($this->ldapHandler, $searchRes) + ) { + return false; + } + + $results = array_merge($results, $entries); + + ldap_control_paged_result_response($this->ldapHandler, $searchRes, $cookie); + } while (!empty($cookie)); + + return $results; + } + + /** + * Devolver el filtro para comprobar la pertenecia al grupo + * + * @return mixed + */ + protected abstract function getGroupMembershipFilter(); + /** * Realizar la desconexión del servidor de LDAP. */ diff --git a/lib/SP/Providers/Auth/Ldap/LdapException.php b/lib/SP/Providers/Auth/Ldap/LdapException.php new file mode 100644 index 00000000..509b96fd --- /dev/null +++ b/lib/SP/Providers/Auth/Ldap/LdapException.php @@ -0,0 +1,37 @@ +. + */ + +namespace SP\Providers\Auth\Ldap; + +use SP\Core\Exceptions\SPException; + +/** + * Class LdapException + * + * @package SP\Providers\Auth\Ldap + */ +class LdapException extends SPException +{ + +} \ No newline at end of file diff --git a/lib/SP/Providers/Auth/Ldap/LdapInterface.php b/lib/SP/Providers/Auth/Ldap/LdapInterface.php index cd60298d..6d3dd75d 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapInterface.php +++ b/lib/SP/Providers/Auth/Ldap/LdapInterface.php @@ -33,8 +33,6 @@ interface LdapInterface { /** * Comprobar la conexión al servidor de LDAP. - * - * @return bool */ public function checkConnection(); diff --git a/lib/SP/Providers/Auth/Ldap/LdapMsAds.php b/lib/SP/Providers/Auth/Ldap/LdapMsAds.php index f84abdda..8a3f1933 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapMsAds.php +++ b/lib/SP/Providers/Auth/Ldap/LdapMsAds.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Providers\Auth\Ldap; -use SP\Core\Exceptions\SPException; -use SP\Log\Log; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; /** * Class LdapAds @@ -36,6 +36,8 @@ use SP\Log\Log; */ class LdapMsAds extends LdapBase { + const userObjectFilter = '(|(objectCategory=person)(objectClass=user))'; + const groupObjectFilter = '(objectCategory=group)'; /** * Devolver el filtro para comprobar la pertenecia al grupo @@ -43,15 +45,15 @@ class LdapMsAds extends LdapBase * @return mixed * @throws \SP\Core\Exceptions\SPException */ - protected function getGroupDnFilter() + protected function getGroupMembershipFilter() { - if (empty($this->group)) { - return '(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject))'; + if (empty($this->ldapParams->getGroup())) { + return self::userObjectFilter; } $groupDN = ldap_escape($this->searchGroupDN()); - return '(&(|(memberOf=' . $groupDN . ')(groupMembership=' . $groupDN . ')(memberof:1.2.840.113556.1.4.1941:=' . $groupDN . '))(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject)))'; + return '(&(|(memberOf=' . $groupDN . ')(groupMembership=' . $groupDN . ')(memberof:1.2.840.113556.1.4.1941:=' . $groupDN . '))' . self::userObjectFilter . ')'; } /** @@ -61,7 +63,7 @@ class LdapMsAds extends LdapBase */ protected function pickServer() { - $server = $this->ConfigData->getLdapServer(); + $server = $this->ldapParams->getServer(); if (preg_match('/[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}/', $server)) { return $server; @@ -101,62 +103,68 @@ class LdapMsAds extends LdapBase { $userLogin = ldap_escape($this->userLogin); - return '(&(|(samaccountname=' . $userLogin . ')(cn=' . $userLogin . ')(uid=' . $userLogin . '))(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject))(objectCategory=person))'; + return '(&(|(samaccountname=' . $userLogin . ')(cn=' . $userLogin . ')(uid=' . $userLogin . '))' . self::userObjectFilter . ')'; } /** * Buscar al usuario en un grupo. * - * @throws SPException + * @throws LdapException * @return bool */ protected function searchUserInGroup() { - $this->LogMessage->setAction(__FUNCTION__); - // Comprobar si está establecido el filtro de grupo o el grupo coincide con // los grupos del usuario - if (!$this->group - || $this->group === '*' - || in_array($this->LdapAuthData->getGroupDn(), $this->LdapAuthData->getGroups()) + if (!$this->ldapParams->getGroup() + || $this->ldapParams->getGroup() === '*' + || in_array($this->ldapAuthData->getGroupDn(), $this->ldapAuthData->getGroups()) ) { - $this->LogMessage->addDescription(__('Usuario verificado en grupo', false)); - $this->writeLog(Log::INFO); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Usuario verificado en grupo'))) + ); return true; } - $groupDN = $this->LdapAuthData->getGroupDn(); + $groupDN = $this->ldapAuthData->getGroupDn(); $filter = '(memberof:1.2.840.113556.1.4.1941:=' . ldap_escape($groupDN) . ')'; $searchResults = $this->getResults($filter, ['sAMAccountName']); if ($searchResults === false) { - $this->LogMessage->addDescription(__('Error al buscar el grupo de usuarios', false)); - $this->LogMessage->addDetails(__('Grupo', false), $groupDN); - $this->LogMessage->addDetails('LDAP ERROR', sprintf('%s (%d)', ldap_error($this->ldapHandler), ldap_errno($this->ldapHandler))); - $this->LogMessage->addDetails('LDAP FILTER', $filter); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al buscar el grupo de usuarios')) + ->addDetail(__u('Grupo'), $groupDN) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP FILTER', $filter)) + ); - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); + throw new LdapException(__u('Error al buscar el grupo de usuarios')); } foreach ($searchResults as $entry) { if (is_array($entry)) { if ($this->userLogin === strtolower($entry['samaccountname'][0])) { - $this->LogMessage->addDescription(__('Usuario verificado en grupo', false)); - $this->LogMessage->addDetails(__('Grupo', false), $groupDN); - $this->writeLog(Log::INFO); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Usuario verificado en grupo')) + ->addDetail(__u('Grupo'), $groupDN)) + ); return true; } } } - $this->LogMessage->addDescription(__('Usuario no pertenece al grupo', false)); - $this->LogMessage->addDetails(__('Usuario', false), $this->LdapAuthData->getDn()); - $this->LogMessage->addDetails(__('Grupo', false), $groupDN); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Usuario no pertenece al grupo')) + ->addDetail(__u('Usuario'), $this->ldapAuthData->getDn()) + ->addDetail(__u('Grupo'), $groupDN)) + ); return false; } @@ -173,4 +181,14 @@ class LdapMsAds extends LdapBase return true; } + + /** + * Devolver el filtro para objetos del tipo grupo + * + * @return mixed + */ + protected function getGroupObjectFilter() + { + return self::groupObjectFilter; + } } \ No newline at end of file diff --git a/lib/SP/Providers/Auth/Ldap/LdapParams.php b/lib/SP/Providers/Auth/Ldap/LdapParams.php new file mode 100644 index 00000000..ceb9ec66 --- /dev/null +++ b/lib/SP/Providers/Auth/Ldap/LdapParams.php @@ -0,0 +1,200 @@ +. + */ + +namespace SP\Providers\Auth\Ldap; + +/** + * Class LdapParams + * + * @package SP\Providers\Auth\Ldap + */ +class LdapParams +{ + /** + * @var string + */ + protected $server; + /** + * @var int + */ + protected $port; + /** + * @var string + */ + protected $searchBase; + /** + * @var string + */ + protected $bindDn; + /** + * @var string + */ + protected $bindPass; + /** + * @var string + */ + protected $group; + /** + * @var bool + */ + protected $ads; + + /** + * Devolver el puerto del servidor si está establecido + * + * @param $server + * @return array|false + */ + public static function getServerAndPort($server) + { + return preg_match('/(?P[\w\.]+)(:(?P\d+))?/', $server, $matches) ? $matches : false; + } + + /** + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * @param int $port + * @return LdapParams + */ + public function setPort($port) + { + $this->port = $port; + return $this; + } + + /** + * @return string + */ + public function getSearchBase() + { + return $this->searchBase; + } + + /** + * @param string $searchBase + * @return LdapParams + */ + public function setSearchBase($searchBase) + { + $this->searchBase = $searchBase; + return $this; + } + + /** + * @return string + */ + public function getBindDn() + { + return $this->bindDn; + } + + /** + * @param string $bindDn + * @return LdapParams + */ + public function setBindDn($bindDn) + { + $this->bindDn = $bindDn; + return $this; + } + + /** + * @return string + */ + public function getBindPass() + { + return $this->bindPass; + } + + /** + * @param string $bindPass + * @return LdapParams + */ + public function setBindPass($bindPass) + { + $this->bindPass = $bindPass; + return $this; + } + + /** + * @return string + */ + public function getGroup() + { + return $this->group; + } + + /** + * @param string $group + * @return LdapParams + */ + public function setGroup($group) + { + $this->group = $group; + return $this; + } + + /** + * @return string + */ + public function getServer() + { + return $this->server; + } + + /** + * @param string $server + * @return LdapParams + */ + public function setServer($server) + { + $this->server = $server; + return $this; + } + + /** + * @return bool + */ + public function isAds() + { + return $this->ads; + } + + /** + * @param bool $ads + * @return LdapParams + */ + public function setAds($ads) + { + $this->ads = (bool)$ads; + return $this; + } + +} \ No newline at end of file diff --git a/lib/SP/Providers/Auth/Ldap/LdapStd.php b/lib/SP/Providers/Auth/Ldap/LdapStd.php index c3d9bc68..3089ae16 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapStd.php +++ b/lib/SP/Providers/Auth/Ldap/LdapStd.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Providers\Auth\Ldap; -use SP\Core\Exceptions\SPException; -use SP\Log\Log; +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; /** * Class LdapStd @@ -36,21 +36,24 @@ use SP\Log\Log; */ class LdapStd extends LdapBase { + const userObjectFilter = '(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject))'; + const groupObjectFilter = '(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)(objectClass=group))'; + /** * Devolver el filtro para comprobar la pertenecia al grupo * * @return mixed * @throws \SP\Core\Exceptions\SPException */ - protected function getGroupDnFilter() + protected function getGroupMembershipFilter() { - if (empty($this->group)) { - return '(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject))'; + if (empty($this->ldapParams->getGroup())) { + return self::userObjectFilter; } $groupDN = ldap_escape($this->searchGroupDN()); - return '(&(|(memberOf=' . $groupDN . ')(groupMembership=' . $groupDN . '))(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject)))'; + return '(&(|(memberOf=' . $groupDN . ')(groupMembership=' . $groupDN . '))' . self::userObjectFilter . ')'; } /** @@ -60,7 +63,7 @@ class LdapStd extends LdapBase */ protected function pickServer() { - return $this->ConfigData->getLdapServer(); + return $this->ldapParams->getServer(); } /** @@ -72,52 +75,57 @@ class LdapStd extends LdapBase { $userLogin = ldap_escape($this->userLogin); - return '(&(|(samaccountname=' . $userLogin . ')(cn=' . $userLogin . ')(uid=' . $userLogin . '))(|(objectClass=inetOrgPerson)(objectClass=person)(objectClass=simpleSecurityObject)))'; + return '(&(|(samaccountname=' . $userLogin . ')(cn=' . $userLogin . ')(uid=' . $userLogin . '))' . self::userObjectFilter . ')'; } /** * Buscar al usuario en un grupo. * - * @throws SPException + * @throws LdapException * @return bool */ protected function searchUserInGroup() { - $this->LogMessage->setAction(__FUNCTION__); - // Comprobar si está establecido el filtro de grupo o el grupo coincide con // los grupos del usuario - if (!$this->group - || $this->group === '*' - || in_array($this->LdapAuthData->getGroupDn(), $this->LdapAuthData->getGroups()) + if (!$this->ldapParams->getGroup() + || $this->ldapParams->getGroup() === '*' + || in_array($this->ldapAuthData->getGroupDn(), $this->ldapAuthData->getGroups()) ) { - $this->LogMessage->addDescription(__('Usuario verificado en grupo', false)); - $this->writeLog(Log::INFO); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Usuario verificado en grupo'))) + ); return true; } - $userDN = $this->LdapAuthData->getDn(); - $groupName = $this->getGroupName() ?: $this->group; + $userDN = ldap_escape($this->ldapAuthData->getDn()); + $groupName = $this->getGroupName() ?: $this->ldapParams->getGroup(); - $filter = '(&(cn=' . ldap_escape($groupName) . ')(|(member=' . ldap_escape($userDN) . ')(uniqueMember=' . ldap_escape($userDN) . '))(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames)(objectClass=group)))'; + $filter = '(&(cn=' . ldap_escape($groupName) . ')(|(member=' . $userDN . ')(uniqueMember=' . $userDN . '))' . self::groupObjectFilter . ')'; $searchResults = $this->getResults($filter, ['member', 'uniqueMember']); if ($searchResults === false) { - $this->LogMessage->addDescription(__('Error al buscar el grupo de usuarios', false)); - $this->LogMessage->addDetails(__('Grupo', false), $groupName); - $this->LogMessage->addDetails(__('Usuario', false), $userDN); - $this->LogMessage->addDetails('LDAP ERROR', sprintf('%s (%d)', ldap_error($this->ldapHandler), ldap_errno($this->ldapHandler))); - $this->LogMessage->addDetails('LDAP FILTER', $filter); - $this->writeLog(); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Error al buscar el grupo de usuarios')) + ->addDetail(__u('Usuario'), $this->ldapAuthData->getDn()) + ->addDetail(__u('Grupo'), $groupName) + ->addDetail('LDAP ERROR', $this->getLdapErrorMessage()) + ->addDetail('LDAP FILTER', $filter)) + ); - throw new SPException($this->LogMessage->getDescription(), SPException::ERROR); + throw new LdapException(__u('Error al buscar el grupo de usuarios')); } - $this->LogMessage->addDescription(__('Usuario verificado en grupo', false)); - $this->LogMessage->addDescription($groupName); - $this->writeLog(Log::INFO); + $this->eventDispatcher->notifyEvent('ldap.check.group', + new Event($this, EventMessage::factory() + ->addDescription(__u('Usuario no pertenece al grupo')) + ->addDetail(__u('Usuario'), $this->ldapAuthData->getDn()) + ->addDetail(__u('Grupo'), $groupName)) + ); return true; } @@ -134,4 +142,14 @@ class LdapStd extends LdapBase return true; } + + /** + * Devolver el filtro para objetos del tipo grupo + * + * @return mixed + */ + protected function getGroupObjectFilter() + { + return self::groupObjectFilter; + } } \ No newline at end of file diff --git a/lib/SP/Providers/Auth/Ldap/LdapUtil.php b/lib/SP/Providers/Auth/Ldap/LdapUtil.php index 6e13615f..87626f2b 100644 --- a/lib/SP/Providers/Auth/Ldap/LdapUtil.php +++ b/lib/SP/Providers/Auth/Ldap/LdapUtil.php @@ -24,7 +24,6 @@ namespace SP\Providers\Auth\Ldap; - /** * Class LdapUtil * @@ -32,27 +31,5 @@ namespace SP\Providers\Auth\Ldap; */ class LdapUtil { - /** - * Obtener los datos de una búsqueda de LDAP de un atributo - * - * @param array $results - * @param string $attribute - * @return array - */ - public static function getResultsData(array &$results, $attribute) - { - $out = []; - foreach ($results as $result) { - if (is_array($result)) { - foreach ($result as $ldapAttribute => $value) { - if (strtolower($ldapAttribute) === $attribute) { - $out[] = $value; - } - } - } - } - - return $out; - } } \ No newline at end of file diff --git a/lib/SP/Providers/Provider.php b/lib/SP/Providers/Provider.php index c4021129..d568ee1f 100644 --- a/lib/SP/Providers/Provider.php +++ b/lib/SP/Providers/Provider.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Providers; +use DI\Container; use Psr\Container\ContainerInterface; -use SP\Bootstrap; use SP\Config\Config; use SP\Core\Events\EventDispatcher; use SP\Core\Session\Session; @@ -57,18 +57,19 @@ abstract class Provider protected $dic; /** - * Service constructor. + * Provider constructor. * - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface + * @param Container $dic + * @param Config $config + * @param Session $session + * @param EventDispatcher $eventDispatcher */ - final public function __construct() + final public function __construct(Container $dic, Config $config, Session $session, EventDispatcher $eventDispatcher) { - $this->dic = Bootstrap::getContainer(); - - $this->config = $this->dic->get(Config::class); - $this->session = $this->dic->get(Session::class); - $this->eventDispatcher = $this->dic->get(EventDispatcher::class); + $this->dic = $dic; + $this->config = $config; + $this->session = $session; + $this->eventDispatcher = $eventDispatcher; if (method_exists($this, 'initialize')) { $this->initialize(); diff --git a/lib/SP/Repositories/Config/ConfigRepository.php b/lib/SP/Repositories/Config/ConfigRepository.php index dcead969..ec4fa401 100644 --- a/lib/SP/Repositories/Config/ConfigRepository.php +++ b/lib/SP/Repositories/Config/ConfigRepository.php @@ -46,17 +46,18 @@ class ConfigRepository extends Repository */ public function update(ConfigData $configData) { - $Data = new QueryData(); - $Data->setQuery('UPDATE Config SET value = ? WHERE parameter = ?'); - $Data->addParam($configData->getValue()); - $Data->addParam($configData->getParam()); + $queryData = new QueryData(); + $queryData->setQuery('UPDATE Config SET value = ? WHERE parameter = ?'); + $queryData->addParam($configData->getValue()); + $queryData->addParam($configData->getParam()); - return DbWrapper::getQuery($Data, $this->db); + return DbWrapper::getQuery($queryData, $this->db); } /** * @param ConfigData[] $data * @return bool + * @throws \SP\Core\Exceptions\SPException */ public function updateBatch(array $data) { @@ -64,12 +65,12 @@ class ConfigRepository extends Repository try { foreach ($data as $configData) { - $Data = new QueryData(); - $Data->setQuery('UPDATE Config SET value = ? WHERE parameter = ?'); - $Data->addParam($configData->getValue()); - $Data->addParam($configData->getParam()); + $queryData = new QueryData(); + $queryData->setQuery('UPDATE Config SET value = ? WHERE parameter = ?'); + $queryData->addParam($configData->getValue()); + $queryData->addParam($configData->getParam()); - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); } } catch (QueryException $e) { debugLog($e->getMessage()); @@ -88,15 +89,12 @@ class ConfigRepository extends Repository */ public function create(ConfigData $configData) { - $query = /** @lang SQL */ - 'INSERT INTO Config SET parameter = ?, value = ?'; + $queryData = new QueryData(); + $queryData->setQuery('INSERT INTO Config SET parameter = ?, value = ?'); + $queryData->addParam($configData->getParam()); + $queryData->addParam($configData->getValue()); - $Data = new QueryData(); - $Data->setQuery($query); - $Data->addParam($configData->getParam()); - $Data->addParam($configData->getValue()); - - return DbWrapper::getQuery($Data, $this->db); + return DbWrapper::getQuery($queryData, $this->db); } /** @@ -106,10 +104,10 @@ class ConfigRepository extends Repository */ public function getAll() { - $Data = new QueryData(); - $Data->setQuery('SELECT parameter, value FROM Config'); + $queryData = new QueryData(); + $queryData->setQuery('SELECT parameter, value FROM Config'); - return DbWrapper::getResults($Data); + return DbWrapper::getResults($queryData); } /** @@ -118,11 +116,11 @@ class ConfigRepository extends Repository */ public function getByParam($param) { - $Data = new QueryData(); - $Data->setQuery('SELECT value FROM Config WHERE parameter = ? LIMIT 1'); - $Data->addParam($param); + $queryData = new QueryData(); + $queryData->setQuery('SELECT value FROM Config WHERE parameter = ? LIMIT 1'); + $queryData->addParam($param); - return DbWrapper::getResults($Data, $this->db); + return DbWrapper::getResults($queryData, $this->db); } /** @@ -133,11 +131,11 @@ class ConfigRepository extends Repository */ public function has($param) { - $Data = new QueryData(); - $Data->setQuery('SELECT parameter FROM Config WHERE parameter = ? LIMIT 1'); - $Data->addParam($param); + $queryData = new QueryData(); + $queryData->setQuery('SELECT parameter FROM Config WHERE parameter = ? LIMIT 1'); + $queryData->addParam($param); - DbWrapper::getQuery($Data, $this->db); + DbWrapper::getQuery($queryData, $this->db); return $this->db->getNumRows() === 1; } @@ -150,10 +148,10 @@ class ConfigRepository extends Repository */ public function deleteByParam($param) { - $Data = new QueryData(); - $Data->setQuery('DELETE FROM Config WHERE parameter = ? LIMIT 1'); - $Data->addParam($param); + $queryData = new QueryData(); + $queryData->setQuery('DELETE FROM Config WHERE parameter = ? LIMIT 1'); + $queryData->addParam($param); - return DbWrapper::getQuery($Data, $this->db); + return DbWrapper::getQuery($queryData, $this->db); } } \ No newline at end of file diff --git a/lib/SP/Repositories/Repository.php b/lib/SP/Repositories/Repository.php index 8966eb4c..7869935f 100644 --- a/lib/SP/Repositories/Repository.php +++ b/lib/SP/Repositories/Repository.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Repositories; -use SP\Bootstrap; use SP\Config\Config; +use SP\Core\Dic\Container; use SP\Core\Events\EventDispatcher; use SP\Core\Session\Session; use SP\Storage\Database; @@ -38,29 +38,45 @@ use SP\Storage\DatabaseInterface; */ abstract class Repository { - /** @var Config */ + /** + * @var Config + */ protected $config; - /** @var Session */ + /** + * @var Session + */ protected $session; - /** @var EventDispatcher */ + /** + * @var EventDispatcher + */ protected $eventDispatcher; - /** @var DatabaseInterface */ + /** + * @var DatabaseInterface + */ protected $db; + /** + * @var Container + */ + private $dic; /** * Repository constructor. * + * @param Container $dic + * @param Config $config + * @param Database $database + * @param Session $session + * @param EventDispatcher $eventDispatcher * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface */ - final public function __construct() + final public function __construct(Container $dic, Config $config, Database $database, Session $session, EventDispatcher $eventDispatcher) { - $dic = Bootstrap::getContainer(); - - $this->config = $dic->get(Config::class); - $this->db = $dic->get(Database::class); - $this->session = $dic->get(Session::class); - $this->eventDispatcher = $dic->get(EventDispatcher::class); + $this->dic = $dic; + $this->config = $config; + $this->db = $database; + $this->session = $session; + $this->eventDispatcher = $eventDispatcher; if (method_exists($this, 'initialize')) { $this->initialize(); diff --git a/lib/SP/Repositories/User/UserRepository.php b/lib/SP/Repositories/User/UserRepository.php index 969c803b..6c92ac49 100644 --- a/lib/SP/Repositories/User/UserRepository.php +++ b/lib/SP/Repositories/User/UserRepository.php @@ -480,8 +480,8 @@ class UserRepository extends Repository implements RepositoryItemInterface 'SELECT login, email FROM User WHERE UPPER(login) = UPPER(?) - OR UPPER(ssoLogin) = UPPER(?) - OR UPPER(email) = UPPER(?)'; + OR UPPER(?) IN (SELECT ssoLogin FROM User WHERE ssoLogin IS NOT NULL OR ssoLogin <> \'\') + OR UPPER(?) IN (SELECT email FROM User WHERE email IS NOT NULL OR email <> \'\')'; $queryData = new QueryData(); $queryData->setQuery($query); diff --git a/lib/SP/Services/Auth/LoginService.php b/lib/SP/Services/Auth/LoginService.php index 60015ca0..1978e039 100644 --- a/lib/SP/Services/Auth/LoginService.php +++ b/lib/SP/Services/Auth/LoginService.php @@ -147,9 +147,7 @@ class LoginService extends Service ); } - $auth = new Auth($this->userLoginData, $this->configData); - - if (($result = $auth->doAuth()) !== false) { + if (($result = $this->dic->get(Auth::class)->doAuth($this->userLoginData)) !== false) { // Ejecutar la acción asociada al tipo de autentificación foreach ($result as $authResult) { /** @var AuthResult $authResult */ diff --git a/lib/SP/Services/Backup/FileBackupService.php b/lib/SP/Services/Backup/FileBackupService.php index 91e194b7..919a20d0 100644 --- a/lib/SP/Services/Backup/FileBackupService.php +++ b/lib/SP/Services/Backup/FileBackupService.php @@ -67,7 +67,7 @@ class FileBackupService extends Service // Generar hash unico para evitar descargas no permitidas $backupUniqueHash = sha1(uniqid('sysPassBackup', true)); $this->configData->setBackupHash($backupUniqueHash); - $this->config->saveConfig(); + $this->config->saveConfig($this->configData); $bakFileApp = BACKUP_PATH . DIRECTORY_SEPARATOR . $siteName . '-' . $backupUniqueHash . '.tar'; $bakFileDB = BACKUP_PATH . DIRECTORY_SEPARATOR . $siteName . '_db-' . $backupUniqueHash . '.sql'; @@ -130,11 +130,8 @@ class FileBackupService extends Service * Utilizar '*' para toda la BBDD o 'table1 table2 table3...' * * @param string|array $tables - * @param FileHandler $fileHandler - * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface - * @throws SPException - * @throws \SP\Core\Dic\ContainerException + * @param FileHandler $fileHandler + * @throws \Exception * @throws \SP\Storage\FileException */ private function backupTables($tables = '*', FileHandler $fileHandler) diff --git a/lib/SP/Services/Config/ConfigBackupService.php b/lib/SP/Services/Config/ConfigBackupService.php new file mode 100644 index 00000000..a5852620 --- /dev/null +++ b/lib/SP/Services/Config/ConfigBackupService.php @@ -0,0 +1,67 @@ +. + */ + +namespace SP\Services\Config; + +use SP\Services\Service; +use SP\Services\ServiceException; + +/** + * Class ConfigBackupService + * + * @package SP\Services\Config + */ +class ConfigBackupService extends Service +{ + /** + * @var ConfigService + */ + protected $configService; + + /** + * Backups the config data into the database + */ + public function backup() + { + try { + $this->configService->save('config_backup', json_encode($this->config->getConfigData())); + $this->configService->save('config_backup_date', time()); + } catch (\Exception $e) { + processException($e); + } + } + + /** + * @throws ServiceException + */ + public function restore() + { + throw new ServiceException('Not implemented'); + } + + protected function initialize() + { + $this->configService = $this->dic->get(ConfigService::class); + } +} \ No newline at end of file diff --git a/lib/SP/Services/Ldap/LdapCheckService.php b/lib/SP/Services/Ldap/LdapCheckService.php new file mode 100644 index 00000000..b964931e --- /dev/null +++ b/lib/SP/Services/Ldap/LdapCheckService.php @@ -0,0 +1,99 @@ +. + */ + +namespace SP\Services\Ldap; + +use SP\Providers\Auth\Ldap\LdapBase; +use SP\Providers\Auth\Ldap\LdapMsAds; +use SP\Providers\Auth\Ldap\LdapParams; +use SP\Providers\Auth\Ldap\LdapStd; +use SP\Services\Service; + +/** + * Class LdapCheckService + * + * @package SP\Services\Ldap + */ +class LdapCheckService extends Service +{ + /** + * @var LdapBase + */ + protected $ldap; + + /** + * @param LdapParams $ldapParams + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function checkConnection(LdapParams $ldapParams) + { + if ($ldapParams->isAds()) { + $this->ldap = new LdapMsAds($ldapParams, $this->eventDispatcher, true); + } else { + $this->ldap = new LdapStd($ldapParams, $this->eventDispatcher, true); + } + + $this->ldap->checkConnection(); + } + + /** + * @return array + * @throws \SP\Providers\Auth\Ldap\LdapException + */ + public function getUsersAndGroups() + { + $users = $this->ldapResultsMapper($this->ldap->findUsersByGroupFilter(['dn'])); + $groups = $this->ldapResultsMapper($this->ldap->findGroups(['dn'])); + + return [ + 'count' => count($users) + count($groups), + 'users' => $users, + 'groups' => $groups + ]; + } + + /** + * Obtener los datos de una búsqueda de LDAP de un atributo + * + * @param array $data + * @param string $attribute + * @return array + */ + public function ldapResultsMapper($data, $attribute = 'dn') + { + $out = []; + + foreach ($data as $result) { + if (is_array($result)) { + foreach ($result as $ldapAttribute => $value) { + if (strtolower($ldapAttribute) === $attribute) { + $out[] = $value; + } + } + } + } + + return $out; + } +} \ No newline at end of file diff --git a/lib/SP/Services/Ldap/LdapImportParams.php b/lib/SP/Services/Ldap/LdapImportParams.php new file mode 100644 index 00000000..4fcdc45c --- /dev/null +++ b/lib/SP/Services/Ldap/LdapImportParams.php @@ -0,0 +1,54 @@ +. + */ + +namespace SP\Services\Ldap; + +/** + * Class LdapImportParams + * + * @package SP\Services\Ldap + */ +class LdapImportParams +{ + /** + * @var int + */ + public $defaultUserGroup; + /** + * @var int + */ + public $defaultUserProfile; + /** + * @var string + */ + public $loginAttribute; + /** + * @var string + */ + public $userNameAttribute; + /** + * @var string + */ + public $userGroupNameAttribute; +} \ No newline at end of file diff --git a/lib/SP/Services/Ldap/LdapImportService.php b/lib/SP/Services/Ldap/LdapImportService.php new file mode 100644 index 00000000..6c4a20f1 --- /dev/null +++ b/lib/SP/Services/Ldap/LdapImportService.php @@ -0,0 +1,232 @@ +. + */ + +namespace SP\Services\Ldap; + +use SP\Core\Events\Event; +use SP\Core\Events\EventMessage; +use SP\DataModel\UserData; +use SP\DataModel\UserGroupData; +use SP\Providers\Auth\Ldap\LdapBase; +use SP\Providers\Auth\Ldap\LdapException; +use SP\Providers\Auth\Ldap\LdapMsAds; +use SP\Providers\Auth\Ldap\LdapParams; +use SP\Providers\Auth\Ldap\LdapStd; +use SP\Services\Service; +use SP\Services\User\UserService; +use SP\Services\UserGroup\UserGroupService; + +/** + * Class UserLdapService + * + * @package SP\Services\User + */ +class LdapImportService extends Service +{ + /** + * @var int + */ + protected $totalObjects = 0; + /** + * @var int + */ + protected $syncedObjects = 0; + /** + * @var int + */ + protected $errorObjects = 0; + + /** + * @return int + */ + public function getTotalObjects() + { + return $this->totalObjects; + } + + /** + * @return int + */ + public function getSyncedObjects() + { + return $this->syncedObjects; + } + + /** + * @return int + */ + public function getErrorObjects() + { + return $this->errorObjects; + } + + /** + * Sincronizar usuarios de LDAP + * + * @param LdapParams $ldapParams + * @param LdapImportParams $ldapImportParams + * @throws LdapException + */ + public function importGroups(LdapParams $ldapParams, LdapImportParams $ldapImportParams) + { + $objects = $this->getLdap($ldapParams)->findGroups(); + + $numObjects = (int)$objects['count']; + + $this->eventDispatcher->notifyEvent('import.ldap.groups', + new Event($this, EventMessage::factory() + ->addDetail(__u('Objetos encontrados'), $numObjects)) + ); + + $this->totalObjects += $numObjects; + + if ($numObjects > 0) { + $userGroupService = $this->dic->get(UserGroupService::class); + + foreach ($objects as $result) { + if (is_array($result)) { + $userGroupData = new UserGroupData(); + + foreach ($result as $attribute => $values) { + + $value = $values[0]; + + switch (strtolower($attribute)) { + case $ldapImportParams->userGroupNameAttribute: + $userGroupData->setName($value); + break; + } + } + + if (!empty($userGroupData->getName())) { + try { + $userGroupData->setDescription(__('Importado desde LDAP')); + + $userGroupService->create($userGroupData); + + $this->eventDispatcher->notifyEvent('import.ldap.progress.groups', + new Event($this, EventMessage::factory() + ->addDetail(__u('Grupo'), sprintf('%s', $userGroupData->getName()))) + ); + + $this->syncedObjects++; + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + $this->errorObjects++; + } + } + } + } + } + } + + /** + * @param LdapParams $ldapParams + * + * @return LdapBase + */ + protected function getLdap(LdapParams $ldapParams) + { + if ($ldapParams->isAds()) { + return new LdapMsAds($ldapParams, $this->eventDispatcher, true); + } else { + return new LdapStd($ldapParams, $this->eventDispatcher, true); + } + } + + /** + * @param LdapParams $ldapParams + * @param LdapImportParams $ldapImportParams + * @throws LdapException + */ + public function importUsers(LdapParams $ldapParams, LdapImportParams $ldapImportParams) + { + $objects = $this->getLdap($ldapParams)->findUsersByGroupFilter(); + + $numObjects = (int)$objects['count']; + + $this->eventDispatcher->notifyEvent('import.ldap.users', + new Event($this, EventMessage::factory() + ->addDetail(__u('Objetos encontrados'), $numObjects)) + ); + + $this->totalObjects += $numObjects; + + if ($numObjects > 0) { + $userService = $this->dic->get(UserService::class); + + foreach ($objects as $result) { + if (is_array($result)) { + $userData = new UserData(); + + foreach ($result as $attribute => $values) { + + $value = $values[0]; + + switch (strtolower($attribute)) { + case $ldapImportParams->userNameAttribute: + $userData->setName($value); + break; + case $ldapImportParams->loginAttribute: + $userData->setLogin($value); + break; + case 'mail': + $userData->setEmail($value); + break; + } + } + + if (!empty($userData->getName()) + && !empty($userData->getLogin()) + ) { + try { + $userData->setNotes(__('Importado desde LDAP')); + $userData->setUserGroupId($ldapImportParams->defaultUserGroup); + $userData->setUserProfileId($ldapImportParams->defaultUserProfile); + $userData->setIsLdap(true); + + $userService->create($userData); + + $this->eventDispatcher->notifyEvent('import.ldap.progress.users', + new Event($this, EventMessage::factory() + ->addDetail(__u('Usuario'), sprintf('%s (%s)', $userData->getName(), $userData->getLogin()))) + ); + + $this->syncedObjects++; + } catch (\Exception $e) { + processException($e); + + $this->eventDispatcher->notifyEvent('exception', new Event($e)); + + $this->errorObjects++; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/lib/SP/Services/Service.php b/lib/SP/Services/Service.php index 36eac5dc..f688270e 100644 --- a/lib/SP/Services/Service.php +++ b/lib/SP/Services/Service.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,8 @@ namespace SP\Services; +use DI\Container; use Psr\Container\ContainerInterface; -use SP\Bootstrap; use SP\Config\Config; use SP\Core\Events\EventDispatcher; use SP\Core\Session\Session; @@ -59,16 +59,17 @@ abstract class Service /** * Service constructor. * - * @throws \Psr\Container\ContainerExceptionInterface - * @throws \Psr\Container\NotFoundExceptionInterface + * @param Container $dic + * @param Config $config + * @param Session $session + * @param EventDispatcher $eventDispatcher */ - final public function __construct() + final public function __construct(Container $dic, Config $config, Session $session, EventDispatcher $eventDispatcher) { - $this->dic = Bootstrap::getContainer(); - - $this->config = $this->dic->get(Config::class); - $this->session = $this->dic->get(Session::class); - $this->eventDispatcher = $this->dic->get(EventDispatcher::class); + $this->dic = $dic; + $this->config = $config; + $this->session = $session; + $this->eventDispatcher = $eventDispatcher; if (method_exists($this, 'initialize')) { $this->initialize(); diff --git a/lib/SP/Services/UserGroup/UserGroupService.php b/lib/SP/Services/UserGroup/UserGroupService.php index 2fd28a59..8c3ad184 100644 --- a/lib/SP/Services/UserGroup/UserGroupService.php +++ b/lib/SP/Services/UserGroup/UserGroupService.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -100,14 +100,14 @@ class UserGroupService extends Service } /** - * @param $itemData - * @param array $users + * @param UserGroupData $itemData + * @param array $users * @return int * @throws SPException * @throws \SP\Core\Exceptions\ConstraintException * @throws \SP\Core\Exceptions\QueryException */ - public function create($itemData, array $users) + public function create($itemData, array $users = []) { $userGroupId = $this->userGroupRepository->create($itemData); diff --git a/public/js/app-actions.js b/public/js/app-actions.js index e17242a7..d4e7ef2b 100644 --- a/public/js/app-actions.js +++ b/public/js/app-actions.js @@ -729,24 +729,6 @@ sysPass.Actions = function (Common) { * @type {{ldap: checks.ldap, wiki: checks.wiki}} */ const checks = { - ldap: function ($obj) { - log.info("checks:ldap"); - - const $form = $($obj.data("src")); - $form.find("[name='sk']").val(Common.sk.get()); - - const opts = Common.appRequests().getRequestOpts(); - opts.url = ajaxUrl.entrypoint; - opts.data = $form.serialize(); - - Common.appRequests().getActionCall(opts, function (json) { - Common.msg.out(json); - - var $results = $("#ldap-results"); - $results.find(".list-wrap").html(Common.appTheme().html.getList(json.data)); - $results.show("slow"); - }); - }, wiki: function ($obj) { log.info("checks:wiki"); @@ -1190,42 +1172,6 @@ sysPass.Actions = function (Common) { log.info("appMgmt:nav"); grid.nav($obj); - }, - ldapSync: function ($obj) { - log.info("appMgmt:ldapSync"); - - var atext = "

    " + Common.config().LANG[57] + "

    "; - - mdlDialog().show({ - text: atext, - negative: { - title: Common.config().LANG[44], - onClick: function (e) { - e.preventDefault(); - - Common.msg.error(Common.config().LANG[44]); - } - }, - positive: { - title: Common.config().LANG[43], - onClick: function (e) { - var opts = Common.appRequests().getRequestOpts(); - opts.url = ajaxUrl.entrypoint; - opts.data = { - actionId: $obj.data("action-id"), - sk: Common.sk.get(), - isAjax: 1, - ldap_loginattribute: $("#ldap_loginattribute").val(), - ldap_nameattribute: $("#ldap_nameattribute").val(), - ldap_ads: $("#ldap_ads").prop("checked") - }; - - Common.appRequests().getActionCall(opts, function (json) { - Common.msg.out(json); - }); - } - } - }); } }; @@ -1559,6 +1505,62 @@ sysPass.Actions = function (Common) { } }; + const ldap = { + check: function ($obj) { + log.info("checks:ldap"); + + const $form = $($obj.data("src")); + $form.find("[name='sk']").val(Common.sk.get()); + + const opts = Common.appRequests().getRequestOpts(); + opts.url = ajaxUrl.entrypoint + '?r=' + $obj.data("action-route"); + opts.data = $form.serialize(); + + Common.appRequests().getActionCall(opts, function (json) { + Common.msg.out(json); + + const $results = $("#ldap-results"); + $results.find(".list-wrap") + .empty() + .append(Common.appTheme().html.getList(json.data.users)) + .append(Common.appTheme().html.getList(json.data.groups, 'group')); + $results.show("slow"); + }); + }, + import: function ($obj) { + log.info("appMgmt:ldapSync"); + + const atext = "

    " + Common.config().LANG[57] + "

    "; + + mdlDialog().show({ + text: atext, + negative: { + title: Common.config().LANG[44], + onClick: function (e) { + e.preventDefault(); + + Common.msg.error(Common.config().LANG[44]); + } + }, + positive: { + title: Common.config().LANG[43], + onClick: function (e) { + const $form = $($obj.data("src")); + $form.find("[name='sk']").val(Common.sk.get()); + + const opts = Common.appRequests().getRequestOpts(); + opts.url = ajaxUrl.entrypoint + "?r=" + $obj.data("action-route"); + opts.data = $form.serialize(); + + Common.appRequests().getActionCall(opts, function (json) { + Common.msg.out(json); + }); + } + } + }); + } + }; + return { doAction: doAction, appMgmt: appMgmt, @@ -1574,6 +1576,7 @@ sysPass.Actions = function (Common) { plugin: plugin, notification: notification, wiki: wiki, - items: items + items: items, + ldap: ldap }; }; diff --git a/public/js/app-actions.min.js b/public/js/app-actions.min.js index ceb5d7e0..27e154a9 100644 --- a/public/js/app-actions.min.js +++ b/public/js/app-actions.min.js @@ -1,45 +1,45 @@ -var $jscomp={scope:{},findInternal:function(c,e,l){c instanceof String&&(c=String(c));for(var f=c.length,g=0;g'+b+""),h=d.find("img");if(0===h.length)return n(b);h.hide();$.magnificPopup.open({items:{src:d,type:"inline"},callbacks:{open:function(){var a= -this;h.on("click",function(){a.close()});setTimeout(function(){var a=c.resizeImage(h);d.css({backgroundColor:"#fff",width:a.width,height:"auto"});h.show("slow")},500)}}})},p={view:function(a){e.info("account:show");g(c.appRequests().getRouteForQuery(a.data("action-route"),a.data("item-id")),"account")},viewHistory:function(a){e.info("account:showHistory");g(c.appRequests().getRouteForQuery(a.data("action-route"),a.val()),"account")},edit:function(a){e.info("account:edit");g(c.appRequests().getRouteForQuery(a.data("action-route"), -a.data("item-id")),"account")},"delete":function(a){e.info("account:delete");var b='

    '+c.config().LANG[3]+"

    ";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault();c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(d){d=c.appRequests().getRequestOpts();d.url=f.entrypoint;d.data={r:"account/saveDelete/"+a.data("item-id"),sk:c.sk.get()};c.appRequests().getActionCall(d,function(a){c.msg.out(a); -p.search()})}}})},viewPass:function(a){e.info("account:showpass");var b=a.data("parent-id")||0,b=0===b?a.data("item-id"):b,d=a.data("history")||0,h=c.appRequests().getRequestOpts();h.url=f.entrypoint;h.method="get";h.data={r:a.data("action-route")+"/"+b+"/"+d,sk:c.sk.get(),isAjax:1};c.appRequests().getActionCall(h,function(a){0!==a.status?c.msg.out(a):(a=$(a.data.html),n(a),l=setTimeout(function(){$.magnificPopup.close()},3E4),a.on("mouseleave",function(){clearTimeout(l);l=setTimeout(function(){$.magnificPopup.close()}, -3E4)}).on("mouseenter",function(){0!==l&&clearTimeout(l)}))})},copyPass:function(a){e.info("account:copypass");var b=a.data("parent-id"),b=0===b?a.data("item-id"):b,d=c.appRequests().getRequestOpts();d.url=f.entrypoint;d.method="get";d.async=!1;d.data={r:a.data("action-route")+"/"+b+"/"+a.data("history"),sk:c.sk.get(),isAjax:1};return c.appRequests().getActionCall(d)},copy:function(a){e.info("account:copy");g(c.appRequests().getRouteForQuery(a.data("action-route"),a.data("item-id")),"account")},saveFavorite:function(a, -b){e.info("account:saveFavorite");var d="on"===a.data("status"),h=d?a.data("action-route-off"):a.data("action-route-on"),q=c.appRequests().getRequestOpts();q.url=f.entrypoint;q.data={r:h+"/"+a.data("item-id"),sk:c.sk.get(),isAjax:1};c.appRequests().getActionCall(q,function(h){c.msg.out(h);0===h.status&&(a.data("status",d?"off":"on"),"function"===typeof b&&b())})},request:function(a){e.info("account:request");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.data=a.serialize();c.appRequests().getActionCall(b, -function(a){c.msg.out(a)})},menu:function(a){a.hide();a.parent().children(".actions-optional").show(250)},sort:function(a){e.info("account:sort");var c=$("#frmSearch");c.find('input[name="skey"]').val(a.data("key"));c.find('input[name="sorder"]').val(a.data("dir"));c.find('input[name="start"]').val(a.data("start"));p.search()},editPass:function(a){e.info("account:editpass");var b=a.data("parent-id"),b=void 0===b?a.data("item-id"):b;g(c.appRequests().getRouteForQuery(a.data("action-route"),b),"account")}, -saveEditRestore:function(a){e.info("account:restore");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint+"?r="+a.data("action-route")+"/"+a.data("history-id")+"/"+a.data("item-id");b.data=a.serialize();c.appRequests().getActionCall(b,function(a){c.msg.out(a);void 0!==a.data.itemId&&void 0!==a.data.nextAction&&g(c.appRequests().getRouteForQuery(a.data.nextAction,a.data.itemId),"account")})},listFiles:function(a){e.info("account:getfiles");var b=c.appRequests().getRequestOpts();b.method="get"; -b.type="html";b.url=f.entrypoint;b.data={r:a.data("action-route")+"/"+a.data("item-id"),del:a.data("delete"),sk:c.sk.get()};c.appRequests().getActionCall(b,function(c){a.html(c)})},search:function(a){e.info("account:search");var b=$("#frmSearch");b.find("input[name='sk']").val(c.sk.get());b.find("input[name='skey']").val();b.find("input[name='sorder']").val();void 0!==a&&b.find("input[name='start']").val(0);var d=c.appRequests().getRequestOpts();d.url=f.entrypoint+"?r="+a.data("action-route");d.method= -"get";d.data=b.serialize();c.appRequests().getActionCall(d,function(a){10===a.status&&c.msg.out(a);c.sk.set(a.data.sk);$("#res-content").empty().html(a.data.html)})},save:function(a){e.info("account:save");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint+"?r="+a.data("action-route")+"/"+a.data("item-id");b.data=a.serialize();c.appRequests().getActionCall(b,function(a){c.msg.out(a);void 0!==a.data.itemId&&void 0!==a.data.nextAction&&g(c.appRequests().getRouteForQuery(a.data.nextAction,a.data.itemId), -"account")})}},r={get:function(a){e.info("items:get");var b=a[0].selectize;b.clearOptions();b.load(function(d){var h=c.appRequests().getRequestOpts();h.url=f.entrypoint;h.method="get";h.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:a.data("sk")};c.appRequests().getActionCall(h,function(h){d(h.data);b.setValue(a.data("selected-id"),!0);c.appTriggers().updateFormHash()})})},update:function(a){e.info("items:update");var b=$("#"+a.data("item-dst"))[0].selectize;b.clearOptions();b.load(function(d){var b= -c.appRequests().getRequestOpts();b.url=f.entrypoint;b.method="get";b.data={r:a.data("item-route"),sk:c.sk.get()};c.appRequests().getActionCall(b,function(a){d(a)})})}},t={logout:function(){c.redirect("index.php?r=login/logout")},login:function(a){e.info("main:login");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint+"?r="+a.data("route");b.method="get";b.data=a.serialize();c.appRequests().getActionCall(b,function(d){var b=$(".extra-hidden");switch(d.status){case 0:c.redirect(d.data.url);break; -case 2:c.msg.out(d);a.find("input[type='text'],input[type='password']").val("");a.find("input:first").focus();0";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault(); -c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(b){b=a.find("input[name='useTask']");var d=$("#taskStatus");d.empty().html(c.config().LANG[62]);if(0";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault(); -c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(b){b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.data={actionId:a.data("action-id"),sk:c.sk.get(),isAjax:1,ldap_loginattribute:$("#ldap_loginattribute").val(),ldap_nameattribute:$("#ldap_nameattribute").val(),ldap_ads:$("#ldap_ads").prop("checked")};c.appRequests().getActionCall(b,function(a){c.msg.out(a)})}}})}},m={search:function(a){e.info("grid:search");var b=$(a.data("target")),d=c.appRequests().getRequestOpts(); -d.url=f.entrypoint+"?r="+a.data("action-route");d.method="get";d.data=a.serialize();c.appRequests().getActionCall(d,function(a){0===a.status?b.html(a.data.html):b.html(c.msg.html.error(a.description));c.sk.set(a.csrf)})},nav:function(a,b){e.info("grid:nav");var d=$("#"+a.data("action-form"));d.find("[name='start']").val(a.data("start"));d.find("[name='count']").val(a.data("count"));d.find("[name='sk']").val(c.sk.get());"function"===typeof b?b(d):m.search(d)},"delete":function(a,b){var d='

    '+ -c.config().LANG[12]+"

    ",e=a.data("selection"),f=[];if(e&&($(e).find(".is-selected").each(function(){f.push($(this).data("item-id"))}),0===f.length))return;mdlDialog().show({text:d,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault();c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(a){a.preventDefault();"function"===typeof b&&b(f)}}})}};return{doAction:function(a,b){var d={r:a.r+(void 0!==a.itemId?"/"+a.itemId:""),isAjax:1},e=c.appRequests().getRequestOpts(); -e.url=f.entrypoint;e.method="get";e.type="html";e.addHistory=!0;e.data=d;c.appRequests().getActionCall(e,function(a){var d=$("#content");d.empty().html(a);a=c.triggers().views;a.common(d);if(void 0!==b&&"function"===typeof a[b])a[b]();d=$(".mdl-layout__content");0";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault(); -c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(b){b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.method="get";b.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:c.sk.get()};c.appRequests().getActionCall(b,function(a){c.msg.out(a);0===a.status&&p.listFiles($("#list-account-files"))})}}})}},checks:{ldap:function(a){e.info("checks:ldap");a=$(a.data("src"));a.find("[name='sk']").val(c.sk.get());var b=c.appRequests().getRequestOpts();b.url=f.entrypoint; -b.data=a.serialize();c.appRequests().getActionCall(b,function(a){c.msg.out(a);var b=$("#ldap-results");b.find(".list-wrap").html(c.appTheme().html.getList(a.data));b.show("slow")})},wiki:function(a){e.info("checks:wiki");a=$(a.data("src"));a.find("[name='sk']").val(c.sk.get());var b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.data=a.serialize();c.appRequests().getActionCall(b,function(a){c.msg.out(a);0===a.status&&$("#dokuWikiResCheck").html(a.data)})}},config:{save:function(a){e.info("config:save"); -k.save(a)},masterpass:function(a){var b='

    '+c.config().LANG[59]+"

    ";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(b){b.preventDefault();c.msg.error(c.config().LANG[44]);a.find(":input[type=password]").val("")}},positive:{title:c.config().LANG[43],onClick:function(b){b=a.find("input[name='useTask']");var d=$("#taskStatus");d.empty().html(c.config().LANG[62]);0";mdlDialog().show({text:h,negative:{title:c.config().LANG[44],onClick:function(e){e.preventDefault();c.appRequests().getActionCall(d,function(d){c.msg.out(d);0===d.status&&g({r:a.data("action-next")+"/"+b})})}},positive:{title:c.config().LANG[43],onClick:function(e){e.preventDefault(); -d.data.notify=1;c.appRequests().getActionCall(d,function(d){c.msg.out(d);0===d.status&&g({r:a.data("action-next")+"/"+b})})}}})},refresh:function(a){e.info("link:refresh");k.state.update(a);var b=a.data("item-id"),d=c.appRequests().getRequestOpts();d.url=f.entrypoint;d.data={r:a.data("action-route")+"/"+b,sk:c.sk.get(),isAjax:1};c.appRequests().getActionCall(d,function(d){c.msg.out(d);0===d.status&&((d=a.data("action-next"))?g({r:d+"/"+b}):g({r:k.state.tab.route,tabIndex:k.state.tab.index}))})}}, -eventlog:{search:function(a){e.info("eventlog:search");m.search(a)},nav:function(a){e.info("eventlog:nav");m.nav(a)},clear:function(a){var b='

    '+c.config().LANG[20]+"

    ";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault();c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(b){b.preventDefault();b=c.appRequests().getRequestOpts();b.url=f.entrypoint+"?r="+a.data("action-route"); -b.method="get";b.data={sk:c.sk.get(),isAjax:1};c.appRequests().getActionCall(b,function(b){c.msg.out(b);0===b.status&&g({r:a.data("nextaction")});c.sk.set(b.csrf)})}}})}},ajaxUrl:f,plugin:{toggle:function(a){e.info("plugin:enable");a={itemId:a.data("item-id"),actionId:a.data("action-id"),sk:c.sk.get(),activeTab:a.data("activetab")};var b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.data=a;c.appRequests().getActionCall(b,function(a){c.msg.out(a);0===a.status&&setTimeout(function(){c.redirect("index.php")}, -2E3)})},reset:function(a){e.info("plugin:reset");var b='

    '+c.config().LANG[58]+"

    ";mdlDialog().show({text:b,negative:{title:c.config().LANG[44],onClick:function(a){a.preventDefault();c.msg.error(c.config().LANG[44])}},positive:{title:c.config().LANG[43],onClick:function(b){b.preventDefault();b={itemId:a.data("item-id"),actionId:a.data("action-id"),sk:c.sk.get(),activeTab:a.data("activetab")};var d=c.appRequests().getRequestOpts();d.url=f.entrypoint;d.data= -b;c.appRequests().getActionCall(d,function(a){c.msg.out(a)})}}})}},notification:{check:function(a){e.info("notification:check");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint;b.method="get";b.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:c.sk.get(),isAjax:1};c.appRequests().getActionCall(b,function(b){c.msg.out(b);0===b.status&&g({r:a.data("nextaction")});c.sk.set(b.csrf)})},search:function(a){e.info("notification:search");m.search(a)},show:function(a){e.info("notification:show"); -u.show(a)},save:function(a){e.info("notification:save");var b=c.appRequests().getRequestOpts();b.url=f.entrypoint+"?r="+a.data("route");b.data=a.serialize();c.appRequests().getActionCall(b,function(b){c.msg.out(b);0===b.status&&(g({r:a.data("nextaction")}),$.magnificPopup.close())})},"delete":function(a){e.info("notification:delete");m["delete"](a,function(b){0'+c+""),h=d.find("img");if(0===h.length)return n(c);h.hide();$.magnificPopup.open({items:{src:d,type:"inline"},callbacks:{open:function(){var a= +this;h.on("click",function(){a.close()});setTimeout(function(){var a=b.resizeImage(h);d.css({backgroundColor:"#fff",width:a.width,height:"auto"});h.show("slow")},500)}}})},p={view:function(a){e.info("account:show");g(b.appRequests().getRouteForQuery(a.data("action-route"),a.data("item-id")),"account")},viewHistory:function(a){e.info("account:showHistory");g(b.appRequests().getRouteForQuery(a.data("action-route"),a.val()),"account")},edit:function(a){e.info("account:edit");g(b.appRequests().getRouteForQuery(a.data("action-route"), +a.data("item-id")),"account")},"delete":function(a){e.info("account:delete");var c='

    '+b.config().LANG[3]+"

    ";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(d){d=b.appRequests().getRequestOpts();d.url=f.entrypoint;d.data={r:"account/saveDelete/"+a.data("item-id"),sk:b.sk.get()};b.appRequests().getActionCall(d,function(a){b.msg.out(a); +p.search()})}}})},viewPass:function(a){e.info("account:showpass");var c=a.data("parent-id")||0,c=0===c?a.data("item-id"):c,d=a.data("history")||0,h=b.appRequests().getRequestOpts();h.url=f.entrypoint;h.method="get";h.data={r:a.data("action-route")+"/"+c+"/"+d,sk:b.sk.get(),isAjax:1};b.appRequests().getActionCall(h,function(a){0!==a.status?b.msg.out(a):(a=$(a.data.html),n(a),l=setTimeout(function(){$.magnificPopup.close()},3E4),a.on("mouseleave",function(){clearTimeout(l);l=setTimeout(function(){$.magnificPopup.close()}, +3E4)}).on("mouseenter",function(){0!==l&&clearTimeout(l)}))})},copyPass:function(a){e.info("account:copypass");var c=a.data("parent-id"),c=0===c?a.data("item-id"):c,d=b.appRequests().getRequestOpts();d.url=f.entrypoint;d.method="get";d.async=!1;d.data={r:a.data("action-route")+"/"+c+"/"+a.data("history"),sk:b.sk.get(),isAjax:1};return b.appRequests().getActionCall(d)},copy:function(a){e.info("account:copy");g(b.appRequests().getRouteForQuery(a.data("action-route"),a.data("item-id")),"account")},saveFavorite:function(a, +c){e.info("account:saveFavorite");var d="on"===a.data("status"),h=d?a.data("action-route-off"):a.data("action-route-on"),q=b.appRequests().getRequestOpts();q.url=f.entrypoint;q.data={r:h+"/"+a.data("item-id"),sk:b.sk.get(),isAjax:1};b.appRequests().getActionCall(q,function(h){b.msg.out(h);0===h.status&&(a.data("status",d?"off":"on"),"function"===typeof c&&c())})},request:function(a){e.info("account:request");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint;c.data=a.serialize();b.appRequests().getActionCall(c, +function(a){b.msg.out(a)})},menu:function(a){a.hide();a.parent().children(".actions-optional").show(250)},sort:function(a){e.info("account:sort");var b=$("#frmSearch");b.find('input[name="skey"]').val(a.data("key"));b.find('input[name="sorder"]').val(a.data("dir"));b.find('input[name="start"]').val(a.data("start"));p.search()},editPass:function(a){e.info("account:editpass");var c=a.data("parent-id"),c=void 0===c?a.data("item-id"):c;g(b.appRequests().getRouteForQuery(a.data("action-route"),c),"account")}, +saveEditRestore:function(a){e.info("account:restore");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint+"?r="+a.data("action-route")+"/"+a.data("history-id")+"/"+a.data("item-id");c.data=a.serialize();b.appRequests().getActionCall(c,function(a){b.msg.out(a);void 0!==a.data.itemId&&void 0!==a.data.nextAction&&g(b.appRequests().getRouteForQuery(a.data.nextAction,a.data.itemId),"account")})},listFiles:function(a){e.info("account:getfiles");var c=b.appRequests().getRequestOpts();c.method="get"; +c.type="html";c.url=f.entrypoint;c.data={r:a.data("action-route")+"/"+a.data("item-id"),del:a.data("delete"),sk:b.sk.get()};b.appRequests().getActionCall(c,function(b){a.html(b)})},search:function(a){e.info("account:search");var c=$("#frmSearch");c.find("input[name='sk']").val(b.sk.get());c.find("input[name='skey']").val();c.find("input[name='sorder']").val();void 0!==a&&c.find("input[name='start']").val(0);var d=b.appRequests().getRequestOpts();d.url=f.entrypoint+"?r="+a.data("action-route");d.method= +"get";d.data=c.serialize();b.appRequests().getActionCall(d,function(a){10===a.status&&b.msg.out(a);b.sk.set(a.data.sk);$("#res-content").empty().html(a.data.html)})},save:function(a){e.info("account:save");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint+"?r="+a.data("action-route")+"/"+a.data("item-id");c.data=a.serialize();b.appRequests().getActionCall(c,function(a){b.msg.out(a);void 0!==a.data.itemId&&void 0!==a.data.nextAction&&g(b.appRequests().getRouteForQuery(a.data.nextAction,a.data.itemId), +"account")})}},r={get:function(a){e.info("items:get");var c=a[0].selectize;c.clearOptions();c.load(function(d){var h=b.appRequests().getRequestOpts();h.url=f.entrypoint;h.method="get";h.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:a.data("sk")};b.appRequests().getActionCall(h,function(h){d(h.data);c.setValue(a.data("selected-id"),!0);b.appTriggers().updateFormHash()})})},update:function(a){e.info("items:update");var c=$("#"+a.data("item-dst"))[0].selectize;c.clearOptions();c.load(function(d){var c= +b.appRequests().getRequestOpts();c.url=f.entrypoint;c.method="get";c.data={r:a.data("item-route"),sk:b.sk.get()};b.appRequests().getActionCall(c,function(a){d(a)})})}},t={logout:function(){b.redirect("index.php?r=login/logout")},login:function(a){e.info("main:login");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint+"?r="+a.data("route");c.method="get";c.data=a.serialize();b.appRequests().getActionCall(c,function(d){var c=$(".extra-hidden");switch(d.status){case 0:b.redirect(d.data.url);break; +case 2:b.msg.out(d);a.find("input[type='text'],input[type='password']").val("");a.find("input:first").focus();0";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault(); +b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(c){c=a.find("input[name='useTask']");var d=$("#taskStatus");d.empty().html(b.config().LANG[62]);if(0",e=a.data("selection"),f=[];if(e&&($(e).find(".is-selected").each(function(){f.push($(this).data("item-id"))}), +0===f.length))return;mdlDialog().show({text:d,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(a){a.preventDefault();"function"===typeof c&&c(f)}}})}};return{doAction:function(a,c){var d={r:a.r+(void 0!==a.itemId?"/"+a.itemId:""),isAjax:1},e=b.appRequests().getRequestOpts();e.url=f.entrypoint;e.method="get";e.type="html";e.addHistory=!0;e.data=d;b.appRequests().getActionCall(e,function(a){var d= +$("#content");d.empty().html(a);a=b.triggers().views;a.common(d);if(void 0!==c&&"function"===typeof a[c])a[c]();d=$(".mdl-layout__content");0";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(c){c=b.appRequests().getRequestOpts();c.url=f.entrypoint;c.method="get"; +c.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:b.sk.get()};b.appRequests().getActionCall(c,function(a){b.msg.out(a);0===a.status&&p.listFiles($("#list-account-files"))})}}})}},checks:{wiki:function(a){e.info("checks:wiki");a=$(a.data("src"));a.find("[name='sk']").val(b.sk.get());var c=b.appRequests().getRequestOpts();c.url=f.entrypoint;c.data=a.serialize();b.appRequests().getActionCall(c,function(a){b.msg.out(a);0===a.status&&$("#dokuWikiResCheck").html(a.data)})}},config:{save:function(a){e.info("config:save"); +k.save(a)},masterpass:function(a){var c='

    '+b.config().LANG[59]+"

    ";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(c){c.preventDefault();b.msg.error(b.config().LANG[44]);a.find(":input[type=password]").val("")}},positive:{title:b.config().LANG[43],onClick:function(c){c=a.find("input[name='useTask']");var d=$("#taskStatus");d.empty().html(b.config().LANG[62]);0";mdlDialog().show({text:h,negative:{title:b.config().LANG[44],onClick:function(e){e.preventDefault();b.appRequests().getActionCall(d,function(d){b.msg.out(d);0===d.status&&g({r:a.data("action-next")+"/"+c})})}},positive:{title:b.config().LANG[43],onClick:function(e){e.preventDefault(); +d.data.notify=1;b.appRequests().getActionCall(d,function(d){b.msg.out(d);0===d.status&&g({r:a.data("action-next")+"/"+c})})}}})},refresh:function(a){e.info("link:refresh");k.state.update(a);var c=a.data("item-id"),d=b.appRequests().getRequestOpts();d.url=f.entrypoint;d.data={r:a.data("action-route")+"/"+c,sk:b.sk.get(),isAjax:1};b.appRequests().getActionCall(d,function(d){b.msg.out(d);0===d.status&&((d=a.data("action-next"))?g({r:d+"/"+c}):g({r:k.state.tab.route,tabIndex:k.state.tab.index}))})}}, +eventlog:{search:function(a){e.info("eventlog:search");m.search(a)},nav:function(a){e.info("eventlog:nav");m.nav(a)},clear:function(a){var c='

    '+b.config().LANG[20]+"

    ";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(c){c.preventDefault();c=b.appRequests().getRequestOpts();c.url=f.entrypoint+"?r="+a.data("action-route"); +c.method="get";c.data={sk:b.sk.get(),isAjax:1};b.appRequests().getActionCall(c,function(c){b.msg.out(c);0===c.status&&g({r:a.data("nextaction")});b.sk.set(c.csrf)})}}})}},ajaxUrl:f,plugin:{toggle:function(a){e.info("plugin:enable");a={itemId:a.data("item-id"),actionId:a.data("action-id"),sk:b.sk.get(),activeTab:a.data("activetab")};var c=b.appRequests().getRequestOpts();c.url=f.entrypoint;c.data=a;b.appRequests().getActionCall(c,function(a){b.msg.out(a);0===a.status&&setTimeout(function(){b.redirect("index.php")}, +2E3)})},reset:function(a){e.info("plugin:reset");var c='

    '+b.config().LANG[58]+"

    ";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(c){c.preventDefault();c={itemId:a.data("item-id"),actionId:a.data("action-id"),sk:b.sk.get(),activeTab:a.data("activetab")};var d=b.appRequests().getRequestOpts();d.url=f.entrypoint;d.data= +c;b.appRequests().getActionCall(d,function(a){b.msg.out(a)})}}})}},notification:{check:function(a){e.info("notification:check");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint;c.method="get";c.data={r:a.data("action-route")+"/"+a.data("item-id"),sk:b.sk.get(),isAjax:1};b.appRequests().getActionCall(c,function(c){b.msg.out(c);0===c.status&&g({r:a.data("nextaction")});b.sk.set(c.csrf)})},search:function(a){e.info("notification:search");m.search(a)},show:function(a){e.info("notification:show"); +u.show(a)},save:function(a){e.info("notification:save");var c=b.appRequests().getRequestOpts();c.url=f.entrypoint+"?r="+a.data("route");c.data=a.serialize();b.appRequests().getActionCall(c,function(c){b.msg.out(c);0===c.status&&(g({r:a.data("nextaction")}),$.magnificPopup.close())})},"delete":function(a){e.info("notification:delete");m["delete"](a,function(c){0";mdlDialog().show({text:c,negative:{title:b.config().LANG[44],onClick:function(a){a.preventDefault();b.msg.error(b.config().LANG[44])}},positive:{title:b.config().LANG[43],onClick:function(c){c=$(a.data("src"));c.find("[name='sk']").val(b.sk.get());var d=b.appRequests().getRequestOpts();d.url=f.entrypoint+"?r="+a.data("action-route");d.data=c.serialize(); +b.appRequests().getActionCall(d,function(a){b.msg.out(a)})}}})}}}};