From c3f60bcdca629e02071b07d9bc82b90698c2e157 Mon Sep 17 00:00:00 2001 From: wangyidong Date: Thu, 21 Dec 2023 19:50:56 +0800 Subject: [PATCH] * Refactor sso-login page. --- module/sso/control.php | 146 ++++++------------------------------- module/sso/model.php | 48 +++---------- module/sso/tao.php | 79 ++++++++++++++++++++ module/sso/zen.php | 160 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 164 deletions(-) create mode 100644 module/sso/tao.php create mode 100644 module/sso/zen.php diff --git a/module/sso/control.php b/module/sso/control.php index 1c7856172a..3922081dfb 100644 --- a/module/sso/control.php +++ b/module/sso/control.php @@ -14,11 +14,11 @@ class sso extends control /** * SSO login. * - * @param string $type + * @param string $type notify|return * @access public * @return void */ - public function login($type = 'notify') + public function login(string $type = 'notify') { $referer = empty($_GET['referer']) ? '' : $this->get->referer; $locate = empty($referer) ? getWebRoot() : base64_decode($referer); @@ -26,101 +26,10 @@ class sso extends control $this->app->loadConfig('sso'); if(!$this->config->sso->turnon) return print($this->locate($locate)); - $userIP = helper::getRemoteIp(); - $code = $this->config->sso->code; - $key = $this->config->sso->key; - if($type != 'return') - { - $token = $this->get->token; - $auth = md5($code . $userIP . $token . $key); + if($type != 'return') return $this->ssoZen->locateNotifyLink($locate, $referer); - $callback = urlencode(common::getSysURL() . inlink('login', "type=return")); - $location = $this->config->sso->addr; - $isGet = strpos($location, '&') !== false; - $requestType = $this->get->requestType; - if(isset($requestType)) $isGet = $this->get->requestType == 'GET' ? true : false; - if($isGet) - { - /* Update location when dburl is path_info but need get. */ - if(strpos($location, '&') === false) - { - $index = strripos($location, '/'); - $uri = substr($location, 0 ,$index + 1); - $param = str_replace('.html', '', substr($location, $index + 1)); - list($module, $method) = explode('-', $param); - $location = $uri . 'index.php?m=' . $module . '&f=' . $method; - } - $location = rtrim($location, '&') . "&token=$token&auth=$auth&userIP=$userIP&callback=$callback&referer=$referer"; - } - else - { - /* Update location when dburl is get but need path_info. */ - if(strpos($location, '&') !== false) - { - list($uri, $param) = explode('index.php', $location); - $param = trim($param, "?"); - list($module, $method) = explode('&', $param); - $module = substr($module, strpos($module, '=') + 1); - $method = substr($method, strpos($method, '=') + 1); - $location = $uri . $module . '-' . $method . '.html'; - } - $location = rtrim($location, '?') . "?token=$token&auth=$auth&userIP=$userIP&callback=$callback&referer=$referer"; - } - - if(!empty($_GET['sessionid'])) - { - $sessionConfig = json_decode(base64_decode($this->get->sessionid), false); - $location .= '&' . $sessionConfig->session_name . '=' . $sessionConfig->session_id; - } - $this->locate($location); - } - - if($this->get->status == 'success' and md5($this->get->data) == $this->get->md5) - { - $last = $this->server->request_time; - $data = json_decode(base64_decode($this->get->data)); - - $token = $data->token; - if($data->auth == md5($code . $userIP . $token . $key)) - { - $user = $this->sso->getBindUser($data->account); - if(!$user) - { - $this->session->set('ssoData', $data); - $this->locate($this->createLink('sso', 'bind', "referer=" . helper::safe64Encode($locate))); - } - - if($this->loadModel('user')->isLogon()) - { - if($this->session->user && $this->session->user->account == $user->account) return print($this->locate($locate)); - } - - $this->user->cleanLocked($user->account); - /* Authorize him and save to session. */ - $user->admin = strpos($this->app->company->admins, ",{$user->account},") !== false; - $user->rights = $this->user->authorize($user->account); - $user->groups = $this->user->getGroups($user->account); - $user->view = $this->user->grantUserView($user->account, $user->rights['acls']); - $user->last = date(DT_DATETIME1, $last); - $user->lastTime = $user->last; - $user->modifyPassword = ($user->visits == 0 and !empty($this->config->safe->modifyPasswordFirstLogin)); - if($user->modifyPassword) $user->modifyPasswordReason = 'modifyPasswordFirstLogin'; - if(!$user->modifyPassword and !empty($this->config->safe->changeWeak)) - { - $user->modifyPassword = $this->loadModel('admin')->checkWeak($user); - if($user->modifyPassword) $user->modifyPasswordReason = 'weak'; - } - - $this->dao->update(TABLE_USER)->set('visits = visits + 1')->set('ip')->eq($userIP)->set('last')->eq($last)->where('account')->eq($user->account)->exec(); - - $this->session->set('user', $user); - $this->app->user = $this->session->user; - $this->loadModel('action')->create('user', $user->id, 'login'); - - return print($this->locate($locate)); - } - } - $this->locate($this->createLink('user', 'login', empty($referer) ? '' : "referer=$referer")); + $this->ssoZen->idenfyFromSSO($locate); + return $this->locate($this->createLink('user', 'login', empty($referer) ? '' : "referer=$referer")); } /** @@ -130,27 +39,19 @@ class sso extends control * @access public * @return void */ - public function logout($type = 'notify') + public function logout(string $type = 'notify') { if($type != 'return') { - $code = $this->config->sso->code; - $userIP = helper::getRemoteIp(); - $token = $this->get->token; - $key = $this->config->sso->key; - $auth = md5($code . $userIP . $token . $key); - + $userIP = helper::getRemoteIp(); + $token = $this->get->token; + $auth = $this->ssoZen->computeAuth($token); $callback = urlencode(common::getSysURL() . inlink('logout', "type=return")); + $location = $this->config->sso->addr; - if(strpos($location, '&') !== false) - { - $location = rtrim($location, '&') . "&token=$token&auth=$auth&userIP=$userIP&callback=$callback"; - } - else - { - $location = rtrim($location, '?') . "?token=$token&auth=$auth&userIP=$userIP&callback=$callback"; - } - $this->locate($location); + $sign = strpos($location, '&') !== false ? '&' : '?'; + $location = rtrim($location, $sign) . "{$sign}token={$token}&auth={$auth}&userIP={$userIP}&callback={$callback}"; + return $this->locate($location); } if($this->get->status == 'success') @@ -158,9 +59,9 @@ class sso extends control session_destroy(); helper::setcookie('za', false); helper::setcookie('zp', false); - $this->locate($this->createLink('user', 'login')); + return $this->locate($this->createLink('user', 'login')); } - $this->locate($this->createLink('user', 'logout')); + return $this->locate($this->createLink('user', 'logout')); } /** @@ -183,7 +84,7 @@ class sso extends control $this->loadModel('setting')->setItems('system.sso', $ssoConfig); if(dao::isError()) return print('fail'); - echo 'success'; + return print('success'); } } @@ -194,15 +95,12 @@ class sso extends control * @access public * @return void */ - public function bind($referer = '') + public function bind(string $referer = '') { if(!$this->session->ssoData) return; $ssoData = $this->session->ssoData; - $userIP = helper::getRemoteIp(); - $code = $this->config->sso->code; - $key = $this->config->sso->key; - if($ssoData->auth != md5($code . $userIP . $ssoData->token . $key)) return; + if($ssoData->auth != $this->computeAuth($ssoData->token)) return; $this->loadModel('user'); if($_POST) @@ -220,7 +118,7 @@ class sso extends control $this->app->user = $this->session->user; $this->loadModel('action')->create('user', $user->id, 'login'); unset($_SESSION['ssoData']); - return print(js::locate(helper::safe64Decode($referer), 'parent')); + return $this->send(array('result' => 'success', 'load' => helper::safe64Decode($referer))); } $this->view->title = $this->lang->sso->bind; $this->view->users = $this->user->getPairs('noclosed|nodeleted'); @@ -295,7 +193,7 @@ class sso extends control * @access public * @return void */ - public function getTodoList($account = '') + public function getTodoList(string $account = '') { if(!$this->sso->checkKey()) return false; $user = $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($account)->andWhere('deleted')->eq(0)->fetch(); @@ -354,7 +252,7 @@ class sso extends control * @access public * @return void */ - public function feishuLogin($code = '') + public function feishuLogin(string $code = '') { if($this->config->requestType == 'PATH_INFO') { @@ -422,7 +320,7 @@ class sso extends control * @access public * @return void */ - public function showError($message = '') + public function showError(string $message = '') { $this->view->title = $this->lang->sso->deny; $this->view->message = $message; diff --git a/module/sso/model.php b/module/sso/model.php index d49d26cfc8..68dcd6b68e 100644 --- a/module/sso/model.php +++ b/module/sso/model.php @@ -17,13 +17,13 @@ class ssoModel extends model /** * Get bind user. * - * @param string $user + * @param string $account * @access public - * @return object + * @return object|false */ - public function getBindUser($user) + public function getBindUser(string $account): object|false { - return $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($user)->andWhere('deleted')->eq('0')->fetch(); + return $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($account)->andWhere('deleted')->eq('0')->fetch(); } /** @@ -41,45 +41,13 @@ class ssoModel extends model * Bind user. * * @access public - * @return object + * @return object|false */ - public function bind() + public function bind(): object|false { $data = fixer::input('post')->get(); - if($data->bindType == 'bind') - { - if(empty($data->bindPassword)) return print(js::alert($this->lang->sso->bindNoPassword)); - $password = md5($data->bindPassword); - $user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($data->bindUser)->andWhere('password')->eq($password)->andWhere('deleted')->eq('0')->fetch(); - if(empty($user)) return print(js::alert($this->lang->sso->bindNoUser)); - $user->ranzhi = $this->session->ssoData->account; - $this->dao->update(TABLE_USER)->set('ranzhi')->eq($user->ranzhi)->where('id')->eq($user->id)->exec(); - } - elseif($data->bindType == 'add') - { - if(!$this->loadModel('user')->checkPassword()) return; - $user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($data->account)->fetch(); - if($user) return print(js::alert($this->lang->sso->bindHasAccount)); - - $user = new stdclass(); - $user->account = $data->account; - $user->password = md5($data->password1); - $user->realname = $data->realname; - $user->gender = isset($data->gender) ? $data->gender : ''; - $user->email = $data->email; - $user->ranzhi = $this->session->ssoData->account; - $user->role = isset($data->role) ? $data->role : ''; - - $this->dao->insert(TABLE_USER)->data($user) - ->autoCheck() - ->batchCheck($this->config->user->create->requiredFields, 'notempty') - ->check('account', 'unique') - ->check('account', 'account') - ->checkIF($user->email != false, 'email', 'email') - ->exec(); - } - - return $user; + if($data->bindType == 'bind') return $this->ssoTao->bindZTUser($data); + if($data->bindType == 'add') return $this->ssoTao->addZTUser($data); } /** diff --git a/module/sso/tao.php b/module/sso/tao.php new file mode 100644 index 0000000000..d4e4b9a60e --- /dev/null +++ b/module/sso/tao.php @@ -0,0 +1,79 @@ + + * @package sso + * @link https://www.zentao.net + */ +class ssoTao extends ssoModel +{ + /** + * Bind ranzhi user to zentao user. + * + * @param object $data + * @access protected + * @return object|false + */ + protected function bindZTUser(object $data): object|false + { + if(empty($data->bindPassword)) + { + dao::$errors[] = $this->lang->sso->bindNoPassword; + return false; + } + + $password = md5($data->bindPassword); + $user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($data->bindUser)->andWhere('password')->eq($password)->andWhere('deleted')->eq('0')->fetch(); + if(empty($user)) + { + dao::$errors[] = $this->lang->sso->bindNoUser; + return false; + } + + $user->ranzhi = $this->session->ssoData->account; + $this->dao->update(TABLE_USER)->set('ranzhi')->eq($user->ranzhi)->where('id')->eq($user->id)->exec(); + + return $user; + } + + /** + * Add to zentao user. + * + * @param object $data + * @access protected + * @return object|false + */ + protected function addZTUser(object $data): object|false + { + if(!$this->loadModel('user')->checkPassword()) return false; + $user = $this->dao->select('*')->from(TABLE_USER)->where('account')->eq($data->account)->fetch(); + if($user) + { + dao::$errors[] = $this->lang->sso->bindHasAccount; + return false; + } + + $user = new stdclass(); + $user->account = $data->account; + $user->password = md5($data->password1); + $user->realname = $data->realname; + $user->gender = isset($data->gender) ? $data->gender : ''; + $user->email = $data->email; + $user->ranzhi = $this->session->ssoData->account; + $user->role = isset($data->role) ? $data->role : ''; + + $this->dao->insert(TABLE_USER)->data($user)->autoCheck() + ->batchCheck($this->config->user->create->requiredFields, 'notempty') + ->check('account', 'unique') + ->check('account', 'account') + ->checkIF($user->email != false, 'email', 'email') + ->exec(); + + return $user; + } +} + diff --git a/module/sso/zen.php b/module/sso/zen.php new file mode 100644 index 0000000000..0affb7e9ab --- /dev/null +++ b/module/sso/zen.php @@ -0,0 +1,160 @@ + + * @package sso + * @link https://www.zentao.net + */ +class ssoZen extends sso +{ + /** + * Idenfy from SSO. + * + * @param string $locate + * @access protected + * @return bool + */ + protected function idenfyFromSSO(string $locate): bool + { + if($this->get->status != 'success' || md5($this->get->data) != $this->get->md5) return false; + + $last = $this->server->request_time; + $data = json_decode(base64_decode($this->get->data)); + + if($data->auth != $this->computeAuth($data->token)) return false; + + $user = $this->sso->getBindUser($data->account); + if(!$user) + { + $this->session->set('ssoData', $data); + return $this->locate($this->createLink('sso', 'bind', "referer=" . helper::safe64Encode($locate))); + } + + if($this->loadModel('user')->isLogon() and $this->session->user->account == $user->account) return $this->locate($locate); + + $this->user->cleanLocked($user->account); + /* Authorize him and save to session. */ + $user->admin = strpos($this->app->company->admins, ",{$user->account},") !== false; + $user->rights = $this->user->authorize($user->account); + $user->groups = $this->user->getGroups($user->account); + $user->view = $this->user->grantUserView($user->account, $user->rights['acls']); + $user->last = date(DT_DATETIME1, $last); + $user->lastTime = $user->last; + $user->modifyPassword = ($user->visits == 0 and !empty($this->config->safe->modifyPasswordFirstLogin)); + if($user->modifyPassword) $user->modifyPasswordReason = 'modifyPasswordFirstLogin'; + if(!$user->modifyPassword and !empty($this->config->safe->changeWeak)) + { + $user->modifyPassword = $this->loadModel('admin')->checkWeak($user); + if($user->modifyPassword) $user->modifyPasswordReason = 'weak'; + } + + $this->dao->update(TABLE_USER)->set('visits = visits + 1')->set('ip')->eq($userIP)->set('last')->eq($last)->where('account')->eq($user->account)->exec(); + + $this->session->set('user', $user); + $this->app->user = $this->session->user; + $this->loadModel('action')->create('user', $user->id, 'login'); + + return $this->locate($locate); + } + + /** + * Locate notify link. + * + * @param string $location + * @param string $referer + * @access protected + * @return void + */ + protected function locateNotifyLink(string $location, string $referer): void + { + $isGet = strpos($location, '&') !== false; + $requestType = $this->get->requestType; + if(isset($requestType)) $isGet = $requestType == 'GET' ? true : false; + + if($isGet) $location = $this->buildLocationByGET($location, $referer); + if(!$isGet) $location = $this->buildLocationByPATHINFO($location, $referer); + + if(!empty($_GET['sessionid'])) + { + $sessionConfig = json_decode(base64_decode($this->get->sessionid), false); + $location .= '&' . $sessionConfig->session_name . '=' . $sessionConfig->session_id; + } + $this->locate($location); + } + + /** + * Build location by GET. + * + * @param string $location + * @param string $referer + * @access private + * @return string + */ + private function buildLocationByGET(string $location, string $referer): string + { + if(strpos($location, '&') === false) + { + $position = strrpos($location, '/') + 1; + $uri = substr($location, 0 ,$position); + $param = str_replace('.html', '', substr($location, $position)); + list($module, $method) = explode('-', $param); + $location = $uri . 'index.php?m=' . $module . '&f=' . $method; + } + return rtrim($location, '&') . '&' . $this->buildSSOParams($referer); + } + + /** + * Build location by PATH_INFO. + * + * @param string $location + * @param string $referer + * @access private + * @return string + */ + private function buildLocationByPATHINFO(string $location, string $referer): string + { + if(strpos($location, '&') !== false) + { + list($uri, $param) = explode('index.php', $location); + $param = substr($param, 1); + parse_str($param, $result); + $location = $uri . $result['m'] . '-' . $result['f'] . '.html'; + } + return rtrim($location, '?') . '?' . $this->buildSSOParams($referer); + } + + /** + * Build SSO params. + * + * @param string $referer + * @access private + * @return string + */ + private function buildSSOParams(string $referer): string + { + $userIP = helper::getRemoteIp(); + $token = $this->get->token; + $auth = $this->computeAuth($token); + $callback = urlencode(common::getSysURL() . inlink('login', "type=return")); + return "token=$token&auth=$auth&userIP=$userIP&callback=$callback&referer=$referer"; + } + + /** + * Compute auth. + * + * @access private + * @return string + */ + private function computeAuth(string $token): string + { + $userIP = helper::getRemoteIp(); + $code = $this->config->sso->code; + $key = $this->config->sso->key; + return md5($code . $userIP . $token . $key); + } +} +