/** * Execute the action */ public function execute() { // get parameters $this->id = $this->getParameter('id', 'int'); // does the user exist if ($this->id !== null && BackendUsersModel::exists($this->id) && BackendAuthentication::getUser()->getUserId() != $this->id) { parent::execute(); // get data $user = new BackendUser($this->id); // God-users can't be deleted if ($user->isGod()) { $this->redirect(BackendModel::createURLForAction('Index') . '&error=cant-delete-god'); } // delete item BackendUsersModel::delete($this->id); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_delete', array('id' => $this->id)); // item was deleted, so redirect $this->redirect(BackendModel::createURLForAction('Index') . '&report=deleted&var=' . $user->getSetting('nickname')); } else { $this->redirect(BackendModel::createURLForAction('Index') . '&error=non-existing'); } }
/** * Execute the action */ public function execute() { $email = $this->getParameter('email', 'string'); // does the user exist if ($email !== null) { parent::execute(); // delete item if (BackendUsersModel::undoDelete($email)) { // get user $user = new BackendUser(null, $email); // trigger event $item = array('id' => $user->getUserId(), 'email' => $email); BackendModel::triggerEvent($this->getModule(), 'after_undelete', array('item' => $item)); // item was deleted, so redirect $this->redirect(BackendModel::createURLForAction('edit') . '&id=' . $user->getUserId() . '&report=restored&var=' . $user->getSetting('nickname') . '&highlight=row-' . $user->getUserId()); } else { // invalid user $this->redirect(BackendModel::createURLForAction('index') . '&error=non-existing'); } } else { $this->redirect(BackendModel::createURLForAction('index') . '&error=non-existing'); } }
/** * Validate the forms */ private function validateForm() { if ($this->frm->isSubmitted()) { $txtEmail = $this->frm->getField('backend_email'); $txtPassword = $this->frm->getField('backend_password'); // required fields if (!$txtEmail->isFilled() || !$txtPassword->isFilled()) { // add error $this->frm->addError('fields required'); // show error $this->tpl->assign('hasError', true); } $this->getContainer()->get('logger')->info("Trying to authenticate user '{$txtEmail->getValue()}'."); // invalid form-token? if ($this->frm->getToken() != $this->frm->getField('form_token')->getValue()) { // set a correct header, so bots understand they can't mess with us. if (!headers_sent()) { header('400 Bad Request', true, 400); } } // get the user's id $userId = BackendUsersModel::getIdByEmail($txtEmail->getValue()); // all fields are ok? if ($txtEmail->isFilled() && $txtPassword->isFilled() && $this->frm->getToken() == $this->frm->getField('form_token')->getValue()) { // try to login the user if (!BackendAuthentication::loginUser($txtEmail->getValue(), $txtPassword->getValue())) { $this->getContainer()->get('logger')->info("Failed authenticating user '{$txtEmail->getValue()}'."); // add error $this->frm->addError('invalid login'); // store attempt in session $current = \SpoonSession::exists('backend_login_attempts') ? (int) \SpoonSession::get('backend_login_attempts') : 0; // increment and store \SpoonSession::set('backend_login_attempts', ++$current); // save the failed login attempt in the user's settings if ($userId !== false) { BackendUsersModel::setSetting($userId, 'last_failed_login_attempt', time()); } // show error $this->tpl->assign('hasError', true); } } // check sessions if (\SpoonSession::exists('backend_login_attempts') && (int) \SpoonSession::get('backend_login_attempts') >= 5) { // get previous attempt $previousAttempt = \SpoonSession::exists('backend_last_attempt') ? \SpoonSession::get('backend_last_attempt') : time(); // calculate timeout $timeout = 5 * (\SpoonSession::get('backend_login_attempts') - 4); // too soon! if (time() < $previousAttempt + $timeout) { // sleep until the user can login again sleep($timeout); // set a correct header, so bots understand they can't mess with us. if (!headers_sent()) { header('503 Service Unavailable', true, 503); } } else { // increment and store \SpoonSession::set('backend_last_attempt', time()); } // too many attempts $this->frm->addEditor('too many attempts'); $this->getContainer()->get('logger')->info("Too many login attempts for user '{$txtEmail->getValue()}'."); // show error $this->tpl->assign('hasTooManyAttemps', true); $this->tpl->assign('hasError', false); } // no errors in the form? if ($this->frm->isCorrect()) { // cleanup sessions \SpoonSession::delete('backend_login_attempts'); \SpoonSession::delete('backend_last_attempt'); // save the login timestamp in the user's settings $lastLogin = BackendUsersModel::getSetting($userId, 'current_login'); BackendUsersModel::setSetting($userId, 'current_login', time()); if ($lastLogin) { BackendUsersModel::setSetting($userId, 'last_login', $lastLogin); } $this->getContainer()->get('logger')->info("Successfully authenticated user '{$txtEmail->getValue()}'."); // redirect to the correct URL (URL the user was looking for or fallback) $this->redirectToAllowedModuleAndAction(); } } // is the form submitted if ($this->frmForgotPassword->isSubmitted()) { // backend email $email = $this->frmForgotPassword->getField('backend_email_forgot')->getValue(); // required fields if ($this->frmForgotPassword->getField('backend_email_forgot')->isEmail(BL::err('EmailIsInvalid'))) { // check if there is a user with the given emailaddress if (!BackendUsersModel::existsEmail($email)) { $this->frmForgotPassword->getField('backend_email_forgot')->addError(BL::err('EmailIsUnknown')); } } // no errors in the form? if ($this->frmForgotPassword->isCorrect()) { // generate the key for the reset link and fetch the user ID for this email $key = BackendAuthentication::getEncryptedString($email, uniqid()); // insert the key and the timestamp into the user settings $userId = BackendUsersModel::getIdByEmail($email); $user = new User($userId); $user->setSetting('reset_password_key', $key); $user->setSetting('reset_password_timestamp', time()); // variables to parse in the e-mail $variables['resetLink'] = SITE_URL . BackendModel::createURLForAction('ResetPassword') . '&email=' . $email . '&key=' . $key; // send e-mail to user $from = $this->get('fork.settings')->get('Core', 'mailer_from'); $replyTo = $this->get('fork.settings')->get('Core', 'mailer_reply_to'); $message = \Common\Mailer\Message::newInstance(\SpoonFilter::ucfirst(BL::msg('ResetYourPasswordMailSubject')))->setFrom(array($from['email'] => $from['name']))->setTo(array($email))->setReplyTo(array($replyTo['email'] => $replyTo['name']))->parseHtml(BACKEND_MODULES_PATH . '/Authentication/Layout/Templates/Mails/ResetPassword.tpl', $variables); $this->get('mailer')->send($message); // clear post-values $_POST['backend_email_forgot'] = ''; // show success message $this->tpl->assign('isForgotPasswordSuccess', true); // show form $this->tpl->assign('showForm', true); } else { // errors? $this->tpl->assign('showForm', true); } } }
/** * Remove a device from a user. * * @param string $uri The uri of the channel opened for the device. * @param string $email The emailaddress for the user to link the device to. */ public static function microsoftRemoveDevice($uri, $email) { if (BaseAPI::isAuthorized()) { // redefine $uri = (string) $uri; // validate if ($uri == '') { BaseAPI::output(BaseAPI::BAD_REQUEST, array('message' => 'No uri-parameter provided.')); } if ($email == '') { BaseAPI::output(BaseAPI::BAD_REQUEST, array('message' => 'No email-parameter provided.')); } try { // load user $user = new User(null, $email); // get current uris $uris = (array) $user->getSetting('microsoft_channel_uri'); // not already in array? $index = array_search($uri, $uris); if ($index !== false) { // remove from array unset($uris[$index]); // save it $user->setSetting('microsoft_channel_uri', $uris); } } catch (Exception $e) { BaseAPI::output(BaseAPI::FORBIDDEN, array('message' => 'Can\'t authenticate you.')); } } }
/** * Returns the encrypted password for a user by giving a email/password * Returns false if no user was found for this user/pass combination * * @param string $email The email. * @param string $password The password. * * @return string */ public static function getEncryptedPassword($email, $password) { $email = (string) $email; $password = (string) $password; // fetch user ID by email $userId = BackendUsersModel::getIdByEmail($email); // check if a user ID was found, return false if no user exists if ($userId === false) { return false; } // fetch user record $user = new User($userId); $key = $user->getSetting('password_key'); // return the encrypted string return (string) self::getEncryptedString($password, $key); }
/** * Default authentication * * @return bool */ public static function isAuthorized() { // grab data $email = \SpoonFilter::getGetValue('email', null, ''); $nonce = \SpoonFilter::getGetValue('nonce', null, ''); $secret = \SpoonFilter::getGetValue('secret', null, ''); // data can be available in the POST, so check it if ($email == '') { $email = \SpoonFilter::getPostValue('email', null, ''); } if ($nonce == '') { $nonce = \SpoonFilter::getPostValue('nonce', null, ''); } if ($secret == '') { $secret = \SpoonFilter::getPostValue('secret', null, ''); } // check if needed elements are available if ($email === '' || $nonce === '' || $secret === '') { return self::output(self::NOT_AUTHORIZED, array('message' => 'Not authorized.')); } // get the user try { $user = new BackendUser(null, $email); } catch (\Exception $e) { return self::output(self::FORBIDDEN, array('message' => 'This account does not exist.')); } // get settings $apiAccess = $user->getSetting('api_access', false); $apiKey = $user->getSetting('api_key'); // no API-access if (!$apiAccess) { return self::output(self::FORBIDDEN, array('message' => 'Your account isn\'t allowed to use the API. Contact an administrator.')); } // create hash $hash = BackendAuthentication::getEncryptedString($email . $apiKey, $nonce); // output if ($secret != $hash) { return self::output(self::FORBIDDEN, array('message' => 'Invalid secret.')); } // return return true; }
/** * Get the HTML for a user to use in a datagrid * * @param int $id The Id of the user. * @return string */ public static function getUser($id) { $id = (int) $id; // nothing in cache if (!isset(self::$dataGridUsers[$id])) { // create user instance $user = new User($id); // get settings $avatar = $user->getSetting('avatar', 'no-avatar.gif'); $nickname = $user->getSetting('nickname'); $allowed = Authentication::isAllowedAction('Edit', 'Users'); // build html $html = '<div class="dataGridAvatar">' . "\n"; $html .= ' <div class="avatar av24">' . "\n"; if ($allowed) { $html .= ' <a href="' . BackendModel::createURLForAction('Edit', 'Users') . '&id=' . $id . '">' . "\n"; } $html .= ' <img src="' . FRONTEND_FILES_URL . '/backend_users/avatars/32x32/' . $avatar . '" width="24" height="24" alt="' . $nickname . '" />' . "\n"; if ($allowed) { $html .= ' </a>' . "\n"; } $html .= ' </div>'; $html .= ' <p><a href="' . BackendModel::createURLForAction('edit', 'users') . '&id=' . $id . '">' . $nickname . '</a></p>' . "\n"; $html .= '</div>'; self::$dataGridUsers[$id] = $html; } return self::$dataGridUsers[$id]; }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); $fields = $this->frm->getFields(); // email is present if (!$this->user->isGod()) { if ($fields['email']->isFilled(BL::err('EmailIsRequired'))) { // is this an email-address if ($fields['email']->isEmail(BL::err('EmailIsInvalid'))) { // was this emailaddress deleted before if (BackendUsersModel::emailDeletedBefore($fields['email']->getValue())) { $fields['email']->addError(sprintf(BL::err('EmailWasDeletedBefore'), BackendModel::createURLForAction('UndoDelete', null, null, array('email' => $fields['email']->getValue())))); } elseif (BackendUsersModel::existsEmail($fields['email']->getValue(), $this->id)) { // email already exists $fields['email']->addError(BL::err('EmailAlreadyExists')); } } } } // required fields if ($this->user->isGod() && $fields['email']->getValue() != '' && $this->user->getEmail() != $fields['email']->getValue()) { $fields['email']->addError(BL::err('CantChangeGodsEmail')); } if (!$this->user->isGod()) { $fields['email']->isEmail(BL::err('EmailIsInvalid')); } $fields['nickname']->isFilled(BL::err('NicknameIsRequired')); $fields['name']->isFilled(BL::err('NameIsRequired')); $fields['surname']->isFilled(BL::err('SurnameIsRequired')); $fields['interface_language']->isFilled(BL::err('FieldIsRequired')); $fields['date_format']->isFilled(BL::err('FieldIsRequired')); $fields['time_format']->isFilled(BL::err('FieldIsRequired')); $fields['number_format']->isFilled(BL::err('FieldIsRequired')); if ($this->allowUserRights) { $fields['groups']->isFilled(BL::err('FieldIsRequired')); } if (isset($fields['new_password']) && $fields['new_password']->isFilled()) { if ($fields['new_password']->getValue() !== $fields['confirm_password']->getValue()) { $fields['confirm_password']->addError(BL::err('ValuesDontMatch')); } } // validate avatar if ($fields['avatar']->isFilled()) { // correct extension if ($fields['avatar']->isAllowedExtension(array('jpg', 'jpeg', 'gif', 'png'), BL::err('JPGGIFAndPNGOnly'))) { // correct mimetype? $fields['avatar']->isAllowedMimeType(array('image/gif', 'image/jpg', 'image/jpeg', 'image/png'), BL::err('JPGGIFAndPNGOnly')); } } // no errors? if ($this->frm->isCorrect()) { // build user-array $user['id'] = $this->id; if (!$this->user->isGod()) { $user['email'] = $fields['email']->getValue(true); } if ($this->authenticatedUser->getUserId() != $this->record['id']) { $user['active'] = $fields['active']->isChecked() ? 'Y' : 'N'; } // user is now de-activated, we now remove all sessions for this user so he is logged out immediately if (isset($user['active']) && $user['active'] === 'N' && $this->record['active'] !== $user['active']) { // delete all sessions for user BackendModel::get('database')->delete('users_sessions', 'user_id = ?', array($this->user->getUserId())); } // build settings-array $settings['nickname'] = $fields['nickname']->getValue(); $settings['name'] = $fields['name']->getValue(); $settings['surname'] = $fields['surname']->getValue(); $settings['interface_language'] = $fields['interface_language']->getValue(); $settings['date_format'] = $fields['date_format']->getValue(); $settings['time_format'] = $fields['time_format']->getValue(); $settings['datetime_format'] = $settings['date_format'] . ' ' . $settings['time_format']; $settings['number_format'] = $fields['number_format']->getValue(); $settings['csv_split_character'] = $fields['csv_split_character']->getValue(); $settings['csv_line_ending'] = $fields['csv_line_ending']->getValue(); $settings['api_access'] = $this->allowUserRights ? (bool) $fields['api_access']->getChecked() : $this->record['settings']['api_access']; // update password (only if filled in) if (isset($fields['new_password']) && $fields['new_password']->isFilled()) { $user['password'] = BackendAuthentication::getEncryptedString($fields['new_password']->getValue(), $this->record['settings']['password_key']); // the password has changed if ($this->record['password'] != $user['password']) { // save the login timestamp in the user's settings $lastPasswordChange = BackendUsersModel::getSetting($user['id'], 'current_password_change'); $settings['current_password_change'] = time(); if ($lastPasswordChange) { $settings['last_password_change'] = $lastPasswordChange; } // save the password strength $passwordStrength = BackendAuthentication::checkPassword($fields['new_password']->getValue()); $settings['password_strength'] = $passwordStrength; } } // get user groups when allowed to edit if ($this->allowUserRights) { // get selected groups $groups = $fields['groups']->getChecked(); // init var $newSequence = BackendGroupsModel::getSetting($groups[0], 'dashboard_sequence'); // loop through groups and collect all dashboard widget sequences foreach ($groups as $group) { $sequences[] = BackendGroupsModel::getSetting($group, 'dashboard_sequence'); } // loop through sequences foreach ($sequences as $sequence) { // loop through modules inside a sequence foreach ($sequence as $moduleKey => $module) { // loop through widgets inside a module foreach ($module as $widgetKey => $widget) { // if widget present set true if ($widget['present']) { $newSequence[$moduleKey][$widgetKey]['present'] = true; } } } } // add new sequence to settings $settings['dashboard_sequence'] = $newSequence; } // has the user submitted an avatar? if ($fields['avatar']->isFilled()) { // init vars $avatarsPath = FRONTEND_FILES_PATH . '/backend_users/avatars'; // delete old avatar if it isn't the default-image if ($this->record['settings']['avatar'] != 'no-avatar.jpg' && $this->record['settings']['avatar'] != '') { $fs = new Filesystem(); $fs->remove($avatarsPath . '/source/' . $this->record['settings']['avatar']); $fs->remove($avatarsPath . '/128x128/' . $this->record['settings']['avatar']); $fs->remove($avatarsPath . '/64x64/' . $this->record['settings']['avatar']); $fs->remove($avatarsPath . '/32x32/' . $this->record['settings']['avatar']); } // create new filename $filename = rand(0, 3) . '_' . $user['id'] . '.' . $fields['avatar']->getExtension(); // add into settings to update $settings['avatar'] = $filename; // resize (128x128) $fields['avatar']->createThumbnail($avatarsPath . '/128x128/' . $filename, 128, 128, true, false, 100); // resize (64x64) $fields['avatar']->createThumbnail($avatarsPath . '/64x64/' . $filename, 64, 64, true, false, 100); // resize (32x32) $fields['avatar']->createThumbnail($avatarsPath . '/32x32/' . $filename, 32, 32, true, false, 100); } // save changes BackendUsersModel::update($user, $settings); // save groups if ($this->allowUserRights) { BackendGroupsModel::insertMultipleGroups($this->id, $groups); } // trigger event BackendModel::triggerEvent($this->getModule(), 'after_edit', array('item' => $user)); // can only edit own profile if (!BackendAuthentication::isAllowedAction('Index')) { // everything is saved, so redirect to the edit page $this->redirect(BackendModel::createURLForAction('Edit') . '&id=' . $this->id . '&report=edited&var=' . $settings['nickname']); } else { // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('Index') . '&report=edited&var=' . $settings['nickname'] . '&highlight=row-' . $user['id']); } } } }
/** * Update the user password * * @param BackendUser $user An instance of BackendUser. * @param string $password The new password for the user. */ public static function updatePassword(BackendUser $user, $password) { // fetch user info $userId = $user->getUserId(); $key = $user->getSetting('password_key'); // update user BackendModel::getContainer()->get('database')->update('users', array('password' => BackendAuthentication::getEncryptedString((string) $password, $key)), 'id = ?', $userId); // remove the user settings linked to the resetting of passwords self::deleteResetPasswordSettings($userId); }