/** * Responds to validate.edit hook notifications. * * @param Zikula_Event $event The event that triggered this function call. * * @return void * * @throws Zikula_Exception_Forbidden Thrown if the user does not have the appropriate access level for the function, or to * modify the acceptance of policies on a user account other than his own. * * @throws Zikula_Exception_Fatal Thrown if the user record retrieved from the POST is in an unexpected form or its data is * unexpected. */ public function validateEdit(Zikula_Event $event) { if (!$this->request->isPost()) { // Validation is only appropriate for a post, otherwise it is probably a hack attempt. throw new Zikula_Exception_Forbidden(); } // If there is no 'acceptedpolicies_uid' in the POST, then there is no attempt to update the acceptance of policies, // So there is nothing to validate. if ($this->request->getPost()->has('acceptedpolicies_uid')) { // Set up the necessary objects for the validation response $policiesAcceptedAtRegistration = array( 'termsOfUse' => $this->request->getPost()->get('acceptedpolicies_termsofuse', false), 'privacyPolicy' => $this->request->getPost()->get('acceptedpolicies_privacypolicy', false), 'agePolicy' => $this->request->getPost()->get('acceptedpolicies_agepolicy', false), 'cancellationRightPolicy' => $this->request->getPost()->get('acceptedpolicies_cancellationrightpolicy', false), 'tradeConditions' => $this->request->getPost()->get('acceptedpolicies_tradeconditions', false) ); $uid = $this->request->getPost()->get('acceptedpolicies_uid', false); $this->validation = new Zikula_Hook_ValidationResponse($uid ? $uid : '', $policiesAcceptedAtRegistration); $activePolicies = $this->helper->getActivePolicies(); // Get the user record from the event. If there is no user record, create a dummy one. $user = $event->getSubject(); if (!isset($user) || empty($user)) { $user = array( '__ATTRIBUTES__' => array(), ); } $goodUidAcceptPolicies = isset($uid) && !empty($uid) && is_numeric($uid); $goodUidUser = is_array($user) && isset($user['uid']) && is_numeric($user['uid']); if (!UserUtil::isLoggedIn()) { // User is not logged in, so this should be either part of a login attempt or a new user registration. $eventName = $event->getName(); $isRegistration = ($eventName != 'users.login.validate_edit'); if ($isRegistration) { // A registration. There will be no accepted policies stored yet (function returns the appropriate array for a null uid), // and there is no (or at least there *should* be no) uid to set on the validation response. $acceptedPolicies = $this->helper->getAcceptedPolicies(); } else { // A login attempt. $goodUidAcceptPolicies = $goodUidAcceptPolicies && ($uid > 2); $goodUidUser = $goodUidUser && ($user['uid'] > 2); if (!$goodUidUser || !$goodUidAcceptPolicies) { // Critical fail if the $user record is bad, or if the uid used for Legal is bad. throw new Zikula_Exception_Fatal(); } elseif ($user['uid'] != $uid) { // Fail if the uid of the subject does not match the uid from the form. The user changed his // login information, so not only should we not validate what was posted, we should not allow the user // to proceed with this login attempt at all. LogUtil::registerError(__('Sorry! You changed your authentication information, and one or more items displayed on the login screen may not have been applicable for your account. Please try logging in again.', $this->domain)); $this->request->getSession()->clearNamespace('Zikula_Users'); $this->request->getSession()->clearNamespace('Legal'); $this->redirect(ModUtil::url('Users', 'user', 'login')); } $acceptedPolicies = $this->helper->getAcceptedPolicies($uid); } // Do the validation if ($activePolicies['termsOfUse'] && !$acceptedPolicies['termsOfUse'] && (!isset($policiesAcceptedAtRegistration['termsOfUse']) || empty($policiesAcceptedAtRegistration['termsOfUse']) || !$policiesAcceptedAtRegistration['termsOfUse'])) { if ($isRegistration) { $validationErrorMsg = __('In order to register for a new account, you must accept this site\'s Terms of Use.', $this->domain); } else { $validationErrorMsg = __('In order to log in, you must accept this site\'s Terms of Use.', $this->domain); } $this->validation->addError('termsofuse', $validationErrorMsg); } if ($activePolicies['privacyPolicy'] && !$acceptedPolicies['privacyPolicy'] && (!isset($policiesAcceptedAtRegistration['privacyPolicy']) || empty($policiesAcceptedAtRegistration['privacyPolicy']) || !$policiesAcceptedAtRegistration['privacyPolicy'])) { if ($isRegistration) { $validationErrorMsg = __('In order to register for a new account, you must accept this site\'s Privacy Policy.', $this->domain); } else { $validationErrorMsg = __('In order to log in, you must accept this site\'s Privacy Policy.', $this->domain); } $this->validation->addError('privacypolicy', $validationErrorMsg); } if ($activePolicies['agePolicy'] && !$acceptedPolicies['agePolicy'] && (!isset($policiesAcceptedAtRegistration['agePolicy']) || empty($policiesAcceptedAtRegistration['agePolicy']) || !$policiesAcceptedAtRegistration['agePolicy'])) { if ($isRegistration) { $validationErrorMsg = __f('In order to register for a new account, you must confirm that you meet the requirements of this site\'s Minimum Age Policy. If you are not %1$s years of age or older, and you do not have a parent\'s permission to use this site, then you should not continue registering for access to this site.', array(ModUtil::getVar('Legal', Legal_Constant::MODVAR_MINIMUM_AGE, 0)), $this->domain); } else { $validationErrorMsg = __f('In order to log in, you must confirm that you meet the requirements of this site\'s Minimum Age Policy. If you are not %1$s years of age or older, and you do not have a parent\'s permission to use this site, then please ask your parent to contact a site administrator.', array(ModUtil::getVar('Legal', Legal_Constant::MODVAR_MINIMUM_AGE, 0)), $this->domain); } $this->validation->addError('agepolicy', $validationErrorMsg); } if ($activePolicies['cancellationRightPolicy'] && !$acceptedPolicies['cancellationRightPolicy'] && (!isset($policiesAcceptedAtRegistration['cancellationRightPolicy']) || empty($policiesAcceptedAtRegistration['cancellationRightPolicy']) || !$policiesAcceptedAtRegistration['cancellationRightPolicy'])) { if ($isRegistration) { $validationErrorMsg = __('In order to register for a new account, you must accept our cancellation right policy.', $this->domain); } else { $validationErrorMsg = __('In order to log in, you must accept our cancellation right policy.', $this->domain); } $this->validation->addError('cancellationrightpolicy', $validationErrorMsg); } if ($activePolicies['tradeConditions'] && !$acceptedPolicies['tradeConditions'] && (!isset($policiesAcceptedAtRegistration['tradeConditions']) || empty($policiesAcceptedAtRegistration['tradeConditions']) || !$policiesAcceptedAtRegistration['tradeConditions'])) { if ($isRegistration) { $validationErrorMsg = __('In order to register for a new account, you must accept our general terms and conditions of trade.', $this->domain); } else { $validationErrorMsg = __('In order to log in, you must accept our general terms and conditions of trade.', $this->domain); } $this->validation->addError('tradeconditions', $validationErrorMsg); } } else { // Someone is logged in, so either user looking at own record, an admin creating a new user, // an admin editing a user, or an admin editing a registration. // In this instance, we are only checking to see if the user has edit permission for the policy acceptance status // being changed. $editablePolicies = $this->helper->getEditablePolicies(); if (!isset($user) || empty($user) || !is_array($user)) { throw new Zikula_Exception_Fatal(); } $isNewUser = (!isset($user['uid']) || empty($user['uid'])); if (!$isNewUser && !is_numeric($user['uid'])) { throw new Zikula_Exception_Fatal(); } if ($isNewUser || ($user['uid'] > 2)) { if (!$isNewUser) { // Only check this stuff if the admin is not creating a new user. It doesn't make sense otherwise. if (!$goodUidUser || !$goodUidAcceptPolicies || ($user['uid'] != $uid)) { // Fail if the uid of the subject does not match the uid from the form. The user changed the uid // on the account (is that even possible?!) or somehow the main user form and the part for Legal point // to different user account. In any case, that is a bad situation that should cause a critical failure. // Also fail if the $user record is bad, or if the uid used for Legal is bad. throw new Zikula_Exception_Fatal(); } } // Fail on any attempt to accept a policy that is not edtiable. if (isset($policiesAcceptedAtRegistration['termsOfUse']) && !$editablePolicies['termsOfUse']) { throw new Zikula_Exception_Forbidden(); } if (isset($policiesAcceptedAtRegistration['privacyPolicy']) && !$editablePolicies['privacyPolicy']) { throw new Zikula_Exception_Forbidden(); } if (isset($policiesAcceptedAtRegistration['agePolicy']) && !$editablePolicies['agePolicy']) { throw new Zikula_Exception_Forbidden(); } if (isset($policiesAcceptedAtRegistration['cancellationRightPolicy']) && !$editablePolicies['cancellationRightPolicy']) { throw new Zikula_Exception_Forbidden(); } if (isset($policiesAcceptedAtRegistration['tradeConditions']) && !$editablePolicies['tradeConditions']) { throw new Zikula_Exception_Forbidden(); } } } $event->data->set(self::EVENT_KEY, $this->validation); } }
/** * Allow the user to accept active terms of use and/or privacy policy. * * This function is currently used by the Legal module's handler for the users.login.veto event. * * @return string The rendered output from the template. * * @throws Zikula_Exception_Forbidden Thrown if the user is not logged in and the acceptance attempt is not a result of a login attempt. * * @throws Zikula_Exception_Fatal Thrown if the user is already logged in and the acceptance attempt is a result of a login attempt; * also thrown in cases where expected data is not present or not in an expected form; * also thrown if the call to this function is not the result of a POST operation or a GET operation. */ public function acceptPolicies() { // Retrieve and delete any session variables being sent in by the log-in process before we give the function a chance to // throw an exception. We need to make sure no sensitive data is left dangling in the session variables. $sessionVars = $this->request->getSession()->get('Legal_Controller_User_acceptPolicies', null, $this->name); $this->request->getSession()->del('Legal_Controller_User_acceptPolicies', $this->name); $processed = false; $helper = new Legal_Helper_AcceptPolicies(); if ($this->request->isPost()) { $this->checkCsrfToken(); $isLogin = isset($sessionVars) && !empty($sessionVars); if (!$isLogin && !UserUtil::isLoggedIn()) { throw new Zikula_Exception_Forbidden(); } elseif ($isLogin && UserUtil::isLoggedIn()) { throw new Zikula_Exception_Fatal(); } $policiesUid = $this->request->getPost()->get('acceptedpolicies_uid', false); $acceptedPolicies = array( 'termsOfUse' => $this->request->getPost()->get('acceptedpolicies_termsofuse', false), 'privacyPolicy' => $this->request->getPost()->get('acceptedpolicies_privacypolicy', false), 'agePolicy' => $this->request->getPost()->get('acceptedpolicies_agepolicy', false), 'cancellationRightPolicy' => $this->request->getPost()->get('acceptedpolicies_cancellationrightpolicy', false), 'tradeConditions' => $this->request->getPost()->get('acceptedpolicies_tradeconditions', false) ); if (!isset($policiesUid) || empty($policiesUid) || !is_numeric($policiesUid)) { throw new Zikula_Exception_Fatal(); } $activePolicies = $helper->getActivePolicies(); $originalAcceptedPolicies = $helper->getAcceptedPolicies($policiesUid); $fieldErrors = array(); if ($activePolicies['termsOfUse'] && !$originalAcceptedPolicies['termsOfUse'] && !$acceptedPolicies['termsOfUse']) { $fieldErrors['termsofuse'] = $this->__('You must accept this site\'s Terms of Use in order to proceed.'); } if ($activePolicies['privacyPolicy'] && !$originalAcceptedPolicies['privacyPolicy'] && !$acceptedPolicies['privacyPolicy']) { $fieldErrors['privacypolicy'] = $this->__('You must accept this site\'s Privacy Policy in order to proceed.'); } if ($activePolicies['agePolicy'] && !$originalAcceptedPolicies['agePolicy'] && !$acceptedPolicies['agePolicy']) { $fieldErrors['agepolicy'] = $this->__f('In order to log in, you must confirm that you meet the requirements of this site\'s Minimum Age Policy. If you are not %1$s years of age or older, and you do not have a parent\'s permission to use this site, then please ask your parent to contact a site administrator.', array(ModUtil::getVar('Legal', Legal_Constant::MODVAR_MINIMUM_AGE, 0))); } if ($activePolicies['cancellationRightPolicy'] && !$originalAcceptedPolicies['cancellationRightPolicy'] && !$acceptedPolicies['cancellationRightPolicy']) { $fieldErrors['cancellationrightpolicy'] = $this->__('You must accept our cancellation right policy in order to proceed.'); } if ($activePolicies['tradeConditions'] && !$originalAcceptedPolicies['tradeConditions'] && !$acceptedPolicies['tradeConditions']) { $fieldErrors['tradeconditions'] = $this->__('You must accept our general terms and conditions of trade in order to proceed.'); } if (empty($fieldErrors)) { $now = new DateTime('now', new DateTimeZone('UTC')); $nowStr = $now->format(DateTime::ISO8601); if ($activePolicies['termsOfUse'] && $acceptedPolicies['termsOfUse']) { $termsOfUseProcessed = UserUtil::setVar(Legal_Constant::ATTRIBUTE_TERMSOFUSE_ACCEPTED, $nowStr, $policiesUid); } else { $termsOfUseProcessed = !$activePolicies['termsOfUse'] || $originalAcceptedPolicies['termsOfUse']; } if ($activePolicies['privacyPolicy'] && $acceptedPolicies['privacyPolicy']) { $privacyPolicyProcessed = UserUtil::setVar(Legal_Constant::ATTRIBUTE_PRIVACYPOLICY_ACCEPTED, $nowStr, $policiesUid); } else { $privacyPolicyProcessed = !$activePolicies['privacyPolicy'] || $originalAcceptedPolicies['privacyPolicy']; } if ($activePolicies['agePolicy'] && $acceptedPolicies['agePolicy']) { $agePolicyProcessed = UserUtil::setVar(Legal_Constant::ATTRIBUTE_AGEPOLICY_CONFIRMED, $nowStr, $policiesUid); } else { $agePolicyProcessed = !$activePolicies['agePolicy'] || $originalAcceptedPolicies['agePolicy']; } if ($activePolicies['cancellationRightPolicy'] && $acceptedPolicies['cancellationRightPolicy']) { $cancellationRightPolicyProcessed = UserUtil::setVar(Legal_Constant::ATTRIBUTE_CANCELLATIONRIGHTPOLICY_ACCEPTED, $nowStr, $policiesUid); } else { $cancellationRightPolicyProcessed = !$activePolicies['cancellationRightPolicy'] || $originalAcceptedPolicies['cancellationRightPolicy']; } if ($activePolicies['tradeConditions'] && $acceptedPolicies['tradeConditions']) { $tradeConditionsProcessed = UserUtil::setVar(Legal_Constant::ATTRIBUTE_TRADECONDITIONS_ACCEPTED, $nowStr, $policiesUid); } else { $tradeConditionsProcessed = !$activePolicies['tradeConditions'] || $originalAcceptedPolicies['tradeConditions']; } $processed = $termsOfUseProcessed && $privacyPolicyProcessed && $agePolicyProcessed && $cancellationRightPolicyProcessed && $tradeConditionsProcessed; } if ($processed) { if ($isLogin) { $loginArgs = $this->request->getSession()->get('Users_Controller_User_login', array(), 'Zikula_Users'); $loginArgs['authentication_method'] = $sessionVars['authentication_method']; $loginArgs['authentication_info'] = $sessionVars['authentication_info']; $loginArgs['rememberme'] = $sessionVars['rememberme']; return ModUtil::func('Users', 'user', 'login', $loginArgs); } else { $this->redirect(System::getHomepageUrl()); } } } elseif ($this->request->isGet()) { $isLogin = $this->request->getGet()->get('login', false); $fieldErrors = array(); } else { throw new Zikula_Exception_Forbidden(); } // If we are coming here from the login process, then there are certain things that must have been // send along in the session variable. If not, then error. if ($isLogin && (!isset($sessionVars['user_obj']) || !is_array($sessionVars['user_obj']) || !isset($sessionVars['authentication_info']) || !is_array($sessionVars['authentication_info']) || !isset($sessionVars['authentication_method']) || !is_array($sessionVars['authentication_method'])) ) { throw new Zikula_Exception_Fatal(); } if ($isLogin) { $policiesUid = $sessionVars['user_obj']['uid']; } else { $policiesUid = UserUtil::getVar('uid'); } if (!$policiesUid || empty($policiesUid)) { throw new Zikula_Exception_Fatal(); } if ($isLogin) { // Pass along the session vars to updateAcceptance. We didn't want to just keep them in the session variable // Legal_Controller_User_acceptPolicies because if we hit an exception or got redirected, then the data // would have been orphaned, and it contains some sensitive information. SessionUtil::requireSession(); $this->request->getSession()->set('Legal_Controller_User_acceptPolicies', $sessionVars, $this->name); } $templateVars = array( 'login' => $isLogin, 'policiesUid' => $policiesUid, 'activePolicies' => $helper->getActivePolicies(), 'acceptedPolicies' => isset($acceptedPolicies) ? $acceptedPolicies : $helper->getAcceptedPolicies($policiesUid), 'originalAcceptedPolicies' => isset($originalAcceptedPolicies) ? $originalAcceptedPolicies : $helper->getAcceptedPolicies($policiesUid), 'fieldErrors' => $fieldErrors, ); return $this->view->assign($templateVars) ->fetch('legal_user_acceptpolicies.tpl'); }