From 226c4f2c5a7b2d086e96383e5fd58a1398326ba6 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Thu, 23 Apr 2020 21:08:32 +0200 Subject: [PATCH] add support for webauthn --- controllers/class.Login.php | 213 ++++++++++++++++++++++-------------- op/op.Login.php | 33 +++++- 2 files changed, 161 insertions(+), 85 deletions(-) diff --git a/controllers/class.Login.php b/controllers/class.Login.php index f5a902f8f..f33aae161 100644 --- a/controllers/class.Login.php +++ b/controllers/class.Login.php @@ -31,93 +31,50 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common { return self::$user; } /* }}} */ - public function run() { /* {{{ */ + function preparelogin() { /* {{{ */ $dms = $this->params['dms']; + $login = $this->params['login']; + + if($user = $dms->getUserByLogin($login)) { + $webauthn = new \Davidearl\WebAuthn\WebAuthn($_SERVER['HTTP_HOST']); + + $j['challenge'] = $webauthn->prepareForLogin($user->getWebauthn()); + } else { + http_response_code(401); + echo 'failed to authenticate with that key'; + exit; + } + header('Content-type: application/json'); + echo json_encode($j); + } /* }}} */ + + function login() { /* {{{ */ + $dms = $this->params['dms']; + $login = $this->params['login']; + $logininfo = $this->params['logininfo']; + + if($user = $dms->getUserByLogin($login)) { + $webauthn = new \Davidearl\WebAuthn\WebAuthn($_SERVER['HTTP_HOST']); + + if (! $webauthn->authenticate($logininfo, $user->getWebauthn())) { + return false; + } + + if(!self::_finalize($user)) { + return false; + } + + } else { + return false; + } + return true; + } /* }}} */ + + protected function _finalize($user) { /* {{{ */ $settings = $this->params['settings']; $session = $this->params['session']; $sesstheme = $this->params['sesstheme']; $lang = $this->params['lang']; - $login = $this->params['login']; - $pwd = $this->params['pwd']; - - self::$user = null; - - /* The preLogin hook may set self::$user which will prevent any further - * authentication process. - */ - if($this->callHook('preLogin')) { - } - - $user = self::$user; - - /* The password may only be empty if the guest user tries to log in. - * There is just one guest account with id $settings->_guestID which - * is allowed to log in without a password. All other guest accounts - * are treated like regular logins - */ - if(!$user && $settings->_enableGuestLogin && (int) $settings->_guestID) { - $guestUser = $dms->getUser((int) $settings->_guestID); - if(!$guestUser) { - $this->setErrorMsg("login_error_text"); - return false; - } - if(($login != $guestUser->getLogin())) { - if ((!isset($pwd) || strlen($pwd)==0)) { - $this->setErrorMsg("login_error_text"); - return false; - } - } else { - $user = $guestUser; - } - } - - /* Run any additional authentication method. The hook must return a - * valid user, if the authentication succeeded. If it fails, it must - * return false and if the hook doesn't care at all, if must return null. - */ - if(!$user) { - $user = $this->callHook('authenticate'); - if(false === $user) { - if(empty($this->errormsg)) - $this->setErrorMsg("authentication_failed"); - return false; - } - } - - /* Deprecated: Run any additional authentication implemented in a hook */ - if(!is_object($user) && isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) { - foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authObj) { - if(!$user && method_exists($authObj, 'authenticate')) { - $user = $authObj->authenticate($dms, $settings, $login, $pwd); - if(false === $user) { - if(empty($this->errormsg)) - $this->setErrorMsg("authentication_failed"); - return false; - } - } - } - } - - /* Authenticate against LDAP server {{{ */ - if (!is_object($user) && isset($settings->_ldapHost) && strlen($settings->_ldapHost)>0) { - require_once("../inc/inc.ClassLdapAuthentication.php"); - $authobj = new SeedDMS_LdapAuthentication($dms, $settings); - $user = $authobj->authenticate($login, $pwd); - } /* }}} */ - - /* Authenticate against SeedDMS database {{{ */ - if(!is_object($user)) { - require_once("../inc/inc.ClassDbAuthentication.php"); - $authobj = new SeedDMS_DbAuthentication($dms, $settings); - $user = $authobj->authenticate($login, $pwd); - } /* }}} */ - - /* If the user is still not authenticated, then exit with an error */ - if(!is_object($user)) { - $this->callHook('loginFailed'); - $this->setErrorMsg("login_error_text"); - return false; - } self::$user = $user; @@ -236,4 +193,96 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common { return true; } /* }}} */ + + public function run() { /* {{{ */ + $dms = $this->params['dms']; + $settings = $this->params['settings']; + $session = $this->params['session']; + $sesstheme = $this->params['sesstheme']; + $lang = $this->params['lang']; + $login = $this->params['login']; + $pwd = $this->params['pwd']; + + self::$user = null; + + /* The preLogin hook may set self::$user which will prevent any further + * authentication process. + */ + if($this->callHook('preLogin')) { + } + + $user = self::$user; + + /* The password may only be empty if the guest user tries to log in. + * There is just one guest account with id $settings->_guestID which + * is allowed to log in without a password. All other guest accounts + * are treated like regular logins + */ + if(!$user && $settings->_enableGuestLogin && (int) $settings->_guestID) { + $guestUser = $dms->getUser((int) $settings->_guestID); + if(!$guestUser) { + $this->setErrorMsg("login_error_text"); + return false; + } + if(($login != $guestUser->getLogin())) { + if ((!isset($pwd) || strlen($pwd)==0)) { + $this->setErrorMsg("login_error_text"); + return false; + } + } else { + $user = $guestUser; + } + } + + /* Run any additional authentication method. The hook must return a + * valid user, if the authentication succeeded. If it fails, it must + * return false and if the hook doesn't care at all, if must return null. + */ + if(!$user) { + $user = $this->callHook('authenticate'); + if(false === $user) { + if(empty($this->errormsg)) + $this->setErrorMsg("authentication_failed"); + return false; + } + } + + /* Deprecated: Run any additional authentication implemented in a hook */ + if(!is_object($user) && isset($GLOBALS['SEEDDMS_HOOKS']['authentication'])) { + foreach($GLOBALS['SEEDDMS_HOOKS']['authentication'] as $authObj) { + if(!$user && method_exists($authObj, 'authenticate')) { + $user = $authObj->authenticate($dms, $settings, $login, $pwd); + if(false === $user) { + if(empty($this->errormsg)) + $this->setErrorMsg("authentication_failed"); + return false; + } + } + } + } + + /* Authenticate against LDAP server {{{ */ + if (!is_object($user) && isset($settings->_ldapHost) && strlen($settings->_ldapHost)>0) { + require_once("../inc/inc.ClassLdapAuthentication.php"); + $authobj = new SeedDMS_LdapAuthentication($dms, $settings); + $user = $authobj->authenticate($login, $pwd); + } /* }}} */ + + /* Authenticate against SeedDMS database {{{ */ + if(!is_object($user)) { + require_once("../inc/inc.ClassDbAuthentication.php"); + $authobj = new SeedDMS_DbAuthentication($dms, $settings); + $user = $authobj->authenticate($login, $pwd); + } /* }}} */ + + /* If the user is still not authenticated, then exit with an error */ + if(!is_object($user)) { + $this->callHook('loginFailed'); + $this->setErrorMsg("login_error_text"); + return false; + } + + return self::_finalize($user); + + } /* }}} */ } diff --git a/op/op.Login.php b/op/op.Login.php index ca4a8b714..88b4af57c 100644 --- a/op/op.Login.php +++ b/op/op.Login.php @@ -28,6 +28,7 @@ include("../inc/inc.ClassSession.php"); include("../inc/inc.DBInit.php"); include("../inc/inc.ClassUI.php"); include("../inc/inc.ClassController.php"); +require_once("../inc/inc.WebAuthn.php"); include $settings->_rootDir . "languages/" . $settings->_language . "/lang.inc"; @@ -85,14 +86,40 @@ else if (isset($_GET["referuri"]) && strlen($_GET["referuri"])>0) { add_log_line(); $controller->setParam('login', $login); +$controller->setParam('logininfo', $_POST['logininfo'] ? $_POST['logininfo'] : ''); $controller->setParam('pwd', $pwd); $controller->setParam('lang', $lang); $controller->setParam('sesstheme', $sesstheme); $controller->setParam('session', $session); -if(!$controller->run()) { - add_log_line("login failed", PEAR_LOG_ERR); - _printMessage(getMLText($controller->getErrorMsg()), getMLText($controller->getErrorMsg())."\n"); +switch($_POST['action']) { +case 'preparelogin': + $controller->preparelogin(); + exit(); + break; +case 'login': + if($controller->login()) { + if (isset($referuri) && strlen($referuri)>0) { + $j = $referuri; + } else { + $user = $controller->getUser(); + $j = $settings->_httpRoot.(isset($settings->_siteDefaultPage) && strlen($settings->_siteDefaultPage)>0 ? $settings->_siteDefaultPage : "out/out.ViewFolder.php?folderid=".($user->getHomeFolder() ? $user->getHomeFolder() : $settings->_rootFolderID)); + } + header('Content-type: application/json'); + echo json_encode($j); + exit; + } else { + http_response_code(401); + echo 'failed to authenticate with that key'; + exit; + } exit; + break; +default: + if(!$controller->run()) { + add_log_line("login failed", PEAR_LOG_ERR); + _printMessage(getMLText($controller->getErrorMsg()), getMLText($controller->getErrorMsg())."\n"); + exit; + } } $user = $controller->getUser();