/** * Load user account. * * Always creates user object. To check if user exists, use $this->exists(). * * @param string $username * @return User */ public static function load($username) { // FIXME: validate directory name $blueprints = new Blueprints('blueprints://user'); $blueprint = $blueprints->get('account'); $file = CompiledYamlFile::instance(ACCOUNTS_DIR . $username . YAML_EXT); $content = $file->content(); if (!isset($content['username'])) { $content['username'] = $username; } $user = new User($content, $blueprint); $user->file($file); return $user; }
/** * Load user account. * * Always creates user object. To check if user exists, use $this->exists(). * * @param string $username * @return User */ public static function load($username) { $locator = self::getGrav()['locator']; $blueprints = new Blueprints('blueprints://'); $blueprint = $blueprints->get('user/account'); $file_path = $locator->findResource('account://' . $username . YAML_EXT); $file = CompiledYamlFile::instance($file_path); $content = $file->content(); if (!isset($content['username'])) { $content['username'] = $username; } $user = new User($content, $blueprint); $user->file($file); return $user; }
/** * Authenticate user. * * @param array $form Form fields. * @return bool */ protected function authenticate($form) { /** @var User $user */ $user = $this->grav['user']; if (!$user->authenticated) { $username = isset($form['username']) ? $form['username'] : $this->rememberMe->login(); // Normal login process $user = User::load($username); if ($user->exists()) { if (!empty($form['username']) && !empty($form['password'])) { // Authenticate user. $result = $user->authenticate($form['password']); if ($result) { $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; // If the user wants to be remembered, create // Rememberme cookie if (!empty($form['rememberme'])) { $this->rememberMe->createCookie($form['username']); } else { $this->rememberMe->clearCookie(); $this->rememberMe->getStorage()->cleanAllTriplets($user->get('username')); } } } } } // Authorize against user ACL $user->authenticated = $user->authorize('site.login'); return $user->authenticated; }
/** * @return int|null|void */ protected function serve() { $this->options = ['user' => $this->input->getOption('user'), 'password1' => $this->input->getOption('password')]; $this->validateOptions(); $helper = $this->getHelper('question'); $data = []; $this->output->writeln('<green>Changing User Password</green>'); $this->output->writeln(''); if (!$this->options['user']) { // Get username and validate $question = new Question('Enter a <yellow>username</yellow>: '); $question->setValidator(function ($value) { return $this->validate('user', $value); }); $username = $helper->ask($this->input, $this->output, $question); } else { $username = $this->options['user']; } if (!$this->options['password1']) { // Get password and validate $password = $this->askForPassword($helper, 'Enter a <yellow>new password</yellow>: ', function ($password1) use($helper) { $this->validate('password1', $password1); // Since input is hidden when prompting for passwords, the user is asked to repeat the password return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use($password1) { return $this->validate('password2', $password2, $password1); }); }); $data['password'] = $password; } else { $data['password'] = $this->options['password1']; } // Lowercase the username for the filename $username = strtolower($username); // Grab the account file and read in the information before setting the file (prevent setting erase) $oldUserFile = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('account://' . $username . YAML_EXT, true, true)); $oldData = $oldUserFile->content(); //Set the password feild to new password $oldData['password'] = $data['password']; // Create user object and save it using oldData (with updated password) $user = new User($oldData); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('account://' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $this->output->writeln(''); $this->output->writeln('<green>Success!</green> User <cyan>' . $username . '\'s</cyan> password changed.'); }
/** * @return int|null|void */ protected function serve() { $this->options = ['user' => $this->input->getOption('user'), 'state' => $this->input->getOption('state')]; $this->validateOptions(); $helper = $this->getHelper('question'); $data = []; $this->output->writeln('<green>Setting User State</green>'); $this->output->writeln(''); if (!$this->options['user']) { // Get username and validate $question = new Question('Enter a <yellow>username</yellow>: '); $question->setValidator(function ($value) { return $this->validate('user', $value); }); $username = $helper->ask($this->input, $this->output, $question); } else { $username = $this->options['user']; } if (!$this->options['state'] && !count(array_filter($this->options))) { // Choose State $question = new ChoiceQuestion('Please choose the <yellow>state</yellow> for the account:', array('enabled' => 'Enabled', 'disabled' => 'Disabled'), 'enabled'); $question->setErrorMessage('State %s is invalid.'); $data['state'] = $helper->ask($this->input, $this->output, $question); } else { $data['state'] = $this->options['state'] ?: 'enabled'; } // Lowercase the username for the filename $username = strtolower($username); // Grab the account file and read in the information before setting the file (prevent setting erase) $oldUserFile = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $oldData = $oldUserFile->content(); //Set the state feild to new state $oldData['state'] = $data['state']; // Create user object and save it using oldData (with updated state) $user = new User($oldData); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $this->output->writeln(''); $this->output->writeln('<green>Success!</green> User <cyan>' . $username . '</cyan> state set to .' . $data['state']); }
/** * Connect and redirect user to admin panel * * @param string $username */ protected function authenticateAndRedirectToAdminPanel($username) { // Auth $user = User::load($username); $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; $this->grav['log']->info('user exists ? ' . ($user->exists() ? 'true' : 'false')); // Redirect $route = $this->config->get('plugins.admin.route'); $base = '/' . trim($route, '/'); $this->grav->redirect($base); }
/** * Load user account. * * Always creates user object. To check if user exists, use $this->exists(). * * @param string $username * * @return User */ public static function load($username) { $grav = Grav::instance(); $locator = $grav['locator']; $config = $grav['config']; // force lowercase of username $username = strtolower($username); $blueprints = new Blueprints(); $blueprint = $blueprints->get('user/account'); $file_path = $locator->findResource('account://' . $username . YAML_EXT); $file = CompiledYamlFile::instance($file_path); $content = $file->content(); if (!isset($content['username'])) { $content['username'] = $username; } if (!isset($content['state'])) { $content['state'] = 'enabled'; } $user = new User($content, $blueprint); $user->file($file); // add user to config $config->set("user", $user); return $user; }
/** * Matches an email to a user * * @return User */ public static function findUserByEmail($email) { $account_dir = Grav::instance()['locator']->findResource('account://'); $files = array_diff(scandir($account_dir), ['.', '..']); foreach ($files as $file) { if (strpos($file, '.yaml') !== false) { $user = User::load(trim(substr($file, 0, -5))); if ($user['email'] == $email) { return $user; } } } // If a User with the provided email cannot be found, then load user with that email as the username return User::load($email); }
/** * Authenticate user * * @param string $username * @param string $password * @return bool */ protected function authenticate($username, $password) { if (empty($username) || empty($password)) { return false; } // Normal login process $user = User::load($username); if (!$user->exists()) { return false; } // Failed authentication if (!($result = $user->authenticate($password))) { return false; } // Success $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; return $user->authenticated = $user->authorize('site.login'); }
/** * Handles form and saves the input data if its valid. * * @return bool True if the action was performed. */ public function taskSave() { if (!$this->authorizeTask('save', $this->dataPermissions())) { return; } $reorder = false; $data = $this->post; // Special handler for pages data. if ($this->view == 'pages') { /** @var Page\Pages $pages */ $pages = $this->grav['pages']; // Find new parent page in order to build the path. $route = !isset($data['route']) ? dirname($this->admin->route) : $data['route']; $parent = $route && $route != '/' ? $pages->dispatch($route, true) : $pages->root(); $obj = $this->admin->page(true); $original_slug = $obj->slug(); $original_order = intval(trim($obj->order(), '.')); // Change parent if needed and initialize move (might be needed also on ordering/folder change). $obj = $obj->move($parent); $this->preparePage($obj, false, $obj->language()); // Reset slug and route. For now we do not support slug twig variable on save. $obj->slug($original_slug); $obj->validate(); $obj->filter(); // rename folder based on visible if ($original_order == 1000) { // increment order to force reshuffle $obj->order($original_order + 1); } // add or remove numeric prefix based on ordering value if (isset($data['ordering'])) { if ($data['ordering'] && !$obj->order()) { $obj->order(1001); } elseif (!$data['ordering'] && $obj->order()) { $obj->folder($obj->slug()); } } } else { // Handle standard data types. $obj = $this->prepareData(); $obj->validate(); $obj->filter(); } if ($obj) { $obj->save(true); $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info'); } if ($this->view != 'pages') { // Force configuration reload. /** @var Config $config */ $config = $this->grav['config']; $config->reload(); if ($this->view === 'users') { $this->grav['user']->merge(User::load($this->admin->route)->toArray()); } } // Always redirect if a page route was changed, to refresh it if ($obj instanceof Page\Page) { if (method_exists($obj, 'unsetRouteSlug')) { $obj->unsetRouteSlug(); } $multilang = $this->isMultilang(); if ($multilang) { if (!$obj->language()) { $obj->language($this->grav['session']->admin_lang); } } $admin_route = $this->grav['config']->get('plugins.admin.route'); $redirect_url = '/' . ($multilang ? $obj->language() : '') . $admin_route . '/' . $this->view . $obj->route(); $this->setRedirect($redirect_url); } return true; }
/** * Gets configuration data. * * @param string $type * @param array $post * @return Data\Data|null * @throws \RuntimeException */ public function data($type, $post = array()) { static $data = []; if (isset($data[$type])) { return $data[$type]; } if (!$post) { $post = isset($_POST) ? $_POST : []; } switch ($type) { case 'configuration': case 'system': $type = 'system'; $blueprints = $this->blueprints("config/{$type}"); $config = $this->grav['config']; $obj = new Data\Data($config->get('system'), $blueprints); $obj->merge($post); $file = CompiledYamlFile::instance($this->grav['locator']->findResource("config://{$type}.yaml")); $obj->file($file); $data[$type] = $obj; break; case 'settings': case 'site': $type = 'site'; $blueprints = $this->blueprints("config/{$type}"); $config = $this->grav['config']; $obj = new Data\Data($config->get('site'), $blueprints); $obj->merge($post); $file = CompiledYamlFile::instance($this->grav['locator']->findResource("config://{$type}.yaml")); $obj->file($file); $data[$type] = $obj; break; case 'login': $data[$type] = null; break; default: /** @var UniformResourceLocator $locator */ $locator = $this->grav['locator']; $filename = $locator->findResource("config://{$type}.yaml", true, true); $file = CompiledYamlFile::instance($filename); if (preg_match('|plugins/|', $type)) { /** @var Plugins $plugins */ $plugins = $this->grav['plugins']; $obj = $plugins->get(preg_replace('|plugins/|', '', $type)); $obj->merge($post); $obj->file($file); $data[$type] = $obj; } elseif (preg_match('|themes/|', $type)) { /** @var Themes $themes */ $themes = $this->grav['themes']; $obj = $themes->get(preg_replace('|themes/|', '', $type)); $obj->merge($post); $obj->file($file); $data[$type] = $obj; } elseif (preg_match('|users/|', $type)) { $obj = User::load(preg_replace('|users/|', '', $type)); $obj->merge($post); $data[$type] = $obj; } else { throw new \RuntimeException("Data type '{$type}' doesn't exist!"); } } return $data[$type]; }
/** * Handle the email password recovery procedure. * * @return bool True if the action was performed. */ protected function taskForgot() { $param_sep = $this->grav['config']->get('system.param_sep', ':'); $post = $this->post; $data = $this->data; $username = isset($data['username']) ? strip_tags(strtolower($data['username'])) : ''; $user = !empty($username) ? User::load($username) : null; if (!isset($this->grav['Email'])) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_EMAIL_NOT_CONFIGURED'), 'error'); $this->setRedirect($post['redirect']); return true; } if (!$user || !$user->exists()) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'), 'info'); $this->setRedirect($post['redirect']); return true; } if (empty($user->email)) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'), 'info'); $this->setRedirect($post['redirect']); return true; } $token = md5(uniqid(mt_rand(), true)); $expire = time() + 604800; // next week $user->reset = $token . '::' . $expire; $user->save(); $author = $this->grav['config']->get('site.author.name', ''); $fullname = $user->fullname ?: $username; $reset_link = rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->admin->base, '/') . '/reset/task' . $param_sep . 'reset/user' . $param_sep . $username . '/token' . $param_sep . $token . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form'); $sitename = $this->grav['config']->get('site.title', 'Website'); $from = $this->grav['config']->get('plugins.email.from'); if (empty($from)) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_EMAIL_NOT_CONFIGURED'), 'error'); $this->setRedirect($post['redirect']); return true; } $to = $user->email; $subject = $this->admin->translate(['PLUGIN_ADMIN.FORGOT_EMAIL_SUBJECT', $sitename]); $content = $this->admin->translate(['PLUGIN_ADMIN.FORGOT_EMAIL_BODY', $fullname, $reset_link, $author, $sitename]); $body = $this->grav['twig']->processTemplate('email/base.html.twig', ['content' => $content]); $message = $this->grav['Email']->message($subject, $body, 'text/html')->setFrom($from)->setTo($to); $sent = $this->grav['Email']->send($message); if ($sent < 1) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_FAILED_TO_EMAIL'), 'error'); } else { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.FORGOT_INSTRUCTIONS_SENT_VIA_EMAIL'), 'info'); } $this->setRedirect('/'); return true; }
/** * Authorize Page */ public function authorizePage() { /** @var User $user */ $user = $this->grav['user']; if (!$user->get('access')) { $user = User::load($user->get('username')); } /** @var Page $page */ $page = $this->grav['page']; if (!$page) { return; } $header = $page->header(); $rules = isset($header->access) ? (array) $header->access : []; $config = $this->mergeConfig($page); if ($config->get('parent_acl')) { // If page has no ACL rules, use its parent's rules if (!$rules) { $parent = $page->parent(); while (!$rules and $parent) { $header = $parent->header(); $rules = isset($header->access) ? (array) $header->access : []; $parent = $parent->parent(); } } } // Continue to the page if it has no ACL rules. if (!$rules) { return; } // Continue to the page if user is authorized to access the page. foreach ($rules as $rule => $value) { if ($user->authorize($rule) == $value) { return; } } // User is not logged in; redirect to login page. if ($this->route && !$user->authenticated) { $this->grav->redirect($this->route, 302); } /** @var Language $l */ $l = $this->grav['language']; // Reset page with login page. if (!$user->authenticated) { $page = new Page(); $this->grav['session']->redirect_after_login = $this->grav['uri']->path(); // Get the admin Login page is needed, else teh default if ($this->isAdmin()) { $login_file = $this->grav['locator']->findResource("plugins://admin/pages/admin/login.md"); $page->init(new \SplFileInfo($login_file)); } else { $page->init(new \SplFileInfo(__DIR__ . "/pages/login.md")); } $page->slug(basename($this->route)); $this->authenticated = false; unset($this->grav['page']); $this->grav['page'] = $page; } else { $this->grav['messages']->add($l->translate('PLUGIN_LOGIN.ACCESS_DENIED'), 'error'); $this->authenticated = false; $twig = $this->grav['twig']; $twig->twig_vars['notAuthorized'] = true; } }
/** * Authenticate user. * * @param string $data ['fullname'] The user name of the OAuth user * @param string $data ['id'] The id of the OAuth user * @param string $data ['email'] The email of the OAuth user * @param string $language Language * * @return bool True if user was authenticated */ protected function authenticateOAuth($data, $language = '') { $username = $this->getUsername($data['id']); $user = User::load($username); $password = md5($data['id']); if (!$user->exists()) { // Create the user $user = $this->createUser(['id' => $data['id'], 'fullname' => $data['fullname'], 'username' => $username, 'email' => $data['email'], 'lang' => $language]); $authenticated = true; $user->authenticated = true; $user->save(); } else { $authenticated = $user->authenticate($password); } // Store user in session if ($authenticated) { $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; } return $authenticated; }
/** * Authenticate user. * * @param string $username The username of the OAuth user * @param string $email The email of the OAuth user * @param string $language Language * * @return bool True if user was authenticated */ protected function authenticate($username, $id, $email, $language = '') { $accountFile = Inflector::underscorize($username); $user = User::load(strtolower("{$accountFile}.{$this->action}")); if ($user->exists()) { // Update username (hide OAuth from user) $user->set('username', $username); $password = md5($id); $authenticated = $user->authenticate($password); } else { /** @var User $user */ $user = $this->grav['user']; // Check user rights if (!$user->authenticated) { $oauthUser = $this->grav['config']->get('plugins.login.oauth.user', []); // Create new user from OAuth request $user = $this->createUser(['id' => $id, 'username' => $username, 'email' => $email, 'lang' => $language, 'access' => $oauthUser['access']], $oauthUser['autocreate']); } // Authenticate user against oAuth rules $authenticated = $user->authenticated; } // Store user in session if ($authenticated) { $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; } return $authenticated; }
/** * Handles form and saves the input data if its valid. * * @return bool True if the action was performed. */ public function taskSave() { if (!$this->authorizeTask('save', $this->dataPermissions())) { return false; } $data = $this->post; $config = $this->grav['config']; // Special handler for pages data. if ($this->view == 'pages') { /** @var Pages $pages */ $pages = $this->grav['pages']; // Find new parent page in order to build the path. $route = !isset($data['route']) ? dirname($this->admin->route) : $data['route']; $obj = $this->admin->page(true); if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'), 'error'); return false; } //Handle system.home.hide_in_urls $hide_home_route = $config->get('system.home.hide_in_urls', false); if ($hide_home_route) { $home_route = $config->get('system.home.alias'); $topParent = $obj->topParent(); if (isset($topParent)) { if ($topParent->route() == $home_route) { $baseRoute = (string) $topParent->route(); if ($obj->parent() != $topParent) { $baseRoute .= $obj->parent()->route(); } $route = isset($baseRoute) ? $baseRoute : null; } } } $parent = $route && $route != '/' && $route != '.' ? $pages->dispatch($route, true) : $pages->root(); $original_slug = $obj->slug(); $original_order = intval(trim($obj->order(), '.')); // Change parent if needed and initialize move (might be needed also on ordering/folder change). $obj = $obj->move($parent); $this->preparePage($obj, false, $obj->language()); // Reset slug and route. For now we do not support slug twig variable on save. $obj->slug($original_slug); $obj->validate(); $obj->filter(); // rename folder based on visible if ($original_order == 1000) { // increment order to force reshuffle $obj->order($original_order + 1); } // add or remove numeric prefix based on ordering value if (isset($data['ordering'])) { if ($data['ordering'] && !$obj->order()) { $obj->order(1001); } elseif (!$data['ordering'] && $obj->order()) { $obj->folder($obj->slug()); } } } else { // Handle standard data types. $obj = $this->prepareData(); $obj = $this->processFiles($obj); $obj->validate(); $obj->filter(); } if ($obj) { // Event to manipulate data before saving the object $this->grav->fireEvent('onAdminSave', new Event(['object' => &$obj])); $obj->save(true); $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info'); } if ($this->view != 'pages') { // Force configuration reload. /** @var Config $config */ $config = $this->grav['config']; $config->reload(); if ($this->view === 'users') { $this->grav['user']->merge(User::load($this->admin->route)->toArray()); } } // Always redirect if a page route was changed, to refresh it if ($obj instanceof Page) { if (method_exists($obj, 'unsetRouteSlug')) { $obj->unsetRouteSlug(); } $multilang = $this->isMultilang(); if ($multilang) { if (!$obj->language()) { $obj->language($this->grav['session']->admin_lang); } } $admin_route = $this->grav['config']->get('plugins.admin.route'); //Handle system.home.hide_in_urls $route = $obj->route(); $hide_home_route = $config->get('system.home.hide_in_urls', false); if ($hide_home_route) { $home_route = $config->get('system.home.alias'); $topParent = $obj->topParent(); if (isset($topParent)) { if ($topParent->route() == $home_route) { $route = (string) $topParent->route() . $route; } } } $redirect_url = '/' . ($multilang ? $obj->language() : '') . $admin_route . '/' . $this->view . $route; $this->setRedirect($redirect_url); } return true; }
/** * Handle the email to activate the user account. * * @param User $user * * @return bool True if the action was performed. */ public function sendActivationEmail($user) { if (empty($user->email)) { throw new \RuntimeException($this->grav['language']->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD')); } $token = md5(uniqid(mt_rand(), true)); $expire = time() + 604800; // next week $user->activation_token = $token . '::' . $expire; $user->save(); $param_sep = $this->grav['config']->get('system.param_sep', ':'); $activation_link = $this->grav['base_url_absolute'] . $this->config->get('plugins.login.route_activate') . '/token' . $param_sep . $token . '/username' . $param_sep . $user->username . '/nonce' . $param_sep . Utils::getNonce('user-activation'); $site_name = $this->grav['config']->get('site.title', 'Website'); $subject = $this->grav['language']->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_SUBJECT', $site_name]); $content = $this->grav['language']->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_BODY', $user->username, $activation_link, $site_name]); $to = $user->email; $sent = EmailUtils::sendEmail($subject, $content, $to); if ($sent < 1) { throw new \RuntimeException($this->grav['language']->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); } return true; }
/** * Process a registration form. Handles the following actions: * * - validate_password: validates a password * - register_user: registers a user * * @param Event $event */ public function onFormProcessed(Event $event) { $form = $event['form']; $action = $event['action']; $params = $event['params']; if (!$this->config->get('plugins.login.enabled')) { throw new \RuntimeException($this->grav['language']->translate('LOGIN_PLUGIN.LOGIN_PLUGIN_DISABLED')); } if (!$this->config->get('plugins.login.user_registration.enabled')) { throw new \RuntimeException($this->grav['language']->translate('LOGIN_PLUGIN.USER_REGISTRATION_DISABLED')); } switch ($action) { case 'register_user': $data = []; $username = $form->value('username'); $this->validate('user', $username); if (isset($params['options']['validate_password1_and_password2']) && $params['options']['validate_password1_and_password2']) { $this->validate('password1', $form->value('password1')); $this->validate('password2', $form->value('password2'), $form->value('password1')); $data['password'] = $form->value('password1'); } if (isset($params['options']['validate_password']) && $params['options']['validate_password']) { $this->validate('password1', $form->value('password')); } $fields = $this->config->get('plugins.login.user_registration.fields', []); foreach ($fields as $field) { // Process value of field if set in the page process.register_user if (isset($params['fields'])) { foreach ($params['fields'] as $key => $param) { if ($key == $field) { $data[$field] = $param; } } } if (!isset($data[$field]) && $form->value($field)) { $data[$field] = $form->value($field); } } if (isset($params['options']['validate_password1_and_password2']) && $params['options']['validate_password1_and_password2']) { unset($data['password1']); unset($data['password2']); } // Don't store the username: that is part of the filename unset($data['username']); // Create user object and save it $user = new User($data); $file = CompiledYamlFile::instance($this->grav['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); if (isset($params['options']['login_after_registration']) && $params['options']['login_after_registration']) { //Login user $user = User::load($username); $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; $user->authenticated = $user->authorize('site.login'); } break; } }
/** * @return int|null|void */ protected function serve() { $this->options = ['user' => $this->input->getOption('user'), 'password1' => $this->input->getOption('password'), 'email' => $this->input->getOption('email'), 'permissions' => $this->input->getOption('permissions'), 'fullname' => $this->input->getOption('fullname'), 'title' => $this->input->getOption('title'), 'state' => $this->input->getOption('state')]; $this->validateOptions(); $helper = $this->getHelper('question'); $data = []; $this->output->writeln('<green>Creating new user</green>'); $this->output->writeln(''); if (!$this->options['user']) { // Get username and validate $question = new Question('Enter a <yellow>username</yellow>: ', 'admin'); $question->setValidator(function ($value) { return $this->validate('user', $value); }); $username = $helper->ask($this->input, $this->output, $question); } else { $username = $this->options['user']; } if (!$this->options['password1']) { // Get password and validate $password = $this->askForPassword($helper, 'Enter a <yellow>password</yellow>: ', function ($password1) use($helper) { $this->validate('password1', $password1); // Since input is hidden when prompting for passwords, the user is asked to repeat the password return $this->askForPassword($helper, 'Repeat the <yellow>password</yellow>: ', function ($password2) use($password1) { return $this->validate('password2', $password2, $password1); }); }); $data['password'] = $password; } else { $data['password'] = $this->options['password1']; } if (!$this->options['email']) { // Get email and validate $question = new Question('Enter an <yellow>email</yellow>: '); $question->setValidator(function ($value) { return $this->validate('email', $value); }); $data['email'] = $helper->ask($this->input, $this->output, $question); } else { $data['email'] = $this->options['email']; } if (!$this->options['permissions']) { // Choose permissions $question = new ChoiceQuestion('Please choose a set of <yellow>permissions</yellow>:', array('a' => 'Admin Access', 's' => 'Site Access', 'b' => 'Admin and Site Access'), 'a'); $question->setErrorMessage('Permissions %s is invalid.'); $permissions_choice = $helper->ask($this->input, $this->output, $question); } else { $permissions_choice = $this->options['permissions']; } switch ($permissions_choice) { case 'a': $data['access']['admin'] = ['login' => true, 'super' => true]; break; case 's': $data['access']['site'] = ['login' => true]; break; case 'b': $data['access']['admin'] = ['login' => true, 'super' => true]; $data['access']['site'] = ['login' => true]; } if (!$this->options['fullname']) { // Get fullname $question = new Question('Enter a <yellow>fullname</yellow>: '); $question->setValidator(function ($value) { return $this->validate('fullname', $value); }); $data['fullname'] = $helper->ask($this->input, $this->output, $question); } else { $data['fullname'] = $this->options['fullname']; } if (!$this->options['title'] && !count(array_filter($this->options))) { // Get title $question = new Question('Enter a <yellow>title</yellow>: '); $data['title'] = $helper->ask($this->input, $this->output, $question); } else { $data['title'] = $this->options['title']; } if (!$this->options['state'] && !count(array_filter($this->options))) { // Choose State $question = new ChoiceQuestion('Please choose the <yellow>state</yellow> for the account:', array('enabled' => 'Enabled', 'disabled' => 'Disabled'), 'enabled'); $question->setErrorMessage('State %s is invalid.'); $data['state'] = $helper->ask($this->input, $this->output, $question); } else { $data['state'] = $this->options['state'] ?: 'enabled'; } // Create user object and save it $user = new User($data); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $this->output->writeln(''); $this->output->writeln('<green>Success!</green> User <cyan>' . $username . '</cyan> created.'); }
/** * Handles form and saves the input data if its valid. * * @return bool True if the action was performed. */ public function taskSave() { if (!$this->authorizeTask('save', $this->dataPermissions())) { return false; } $reorder = true; $data = (array) $this->data; $config = $this->grav['config']; // Special handler for pages data. if ($this->view == 'pages') { /** @var Pages $pages */ $pages = $this->grav['pages']; // Find new parent page in order to build the path. $route = !isset($data['route']) ? dirname($this->admin->route) : $data['route']; /** @var Page $obj */ $obj = $this->admin->page(true); // Ensure route is prefixed with a forward slash. $route = '/' . ltrim($route, '/'); if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) { $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'), 'error'); return false; } //Handle system.home.hide_in_urls $hide_home_route = $config->get('system.home.hide_in_urls', false); if ($hide_home_route) { $home_route = $config->get('system.home.alias'); $topParent = $obj->topParent(); if (isset($topParent)) { if ($topParent->route() == $home_route) { $baseRoute = (string) $topParent->route(); if ($obj->parent() != $topParent) { $baseRoute .= $obj->parent()->route(); } $route = isset($baseRoute) ? $baseRoute : null; } } } $parent = $route && $route != '/' && $route != '.' ? $pages->dispatch($route, true) : $pages->root(); $original_slug = $obj->slug(); $original_order = intval(trim($obj->order(), '.')); // Change parent if needed and initialize move (might be needed also on ordering/folder change). $obj = $obj->move($parent); $this->preparePage($obj, false, $obj->language()); // Reset slug and route. For now we do not support slug twig variable on save. $obj->slug($original_slug); try { $obj->validate(); } catch (\Exception $e) { $this->admin->setMessage($e->getMessage(), 'error'); return false; } $obj->filter(); // rename folder based on visible if ($original_order == 1000) { // increment order to force reshuffle $obj->order($original_order + 1); } // add or remove numeric prefix based on ordering value if (isset($data['ordering'])) { if ($data['ordering'] && !$obj->order()) { $obj->order($this->getNextOrderInFolder($obj->parent()->path())); $reorder = false; } elseif (!$data['ordering'] && $obj->order()) { $obj->folder($obj->slug()); } } } else { // Handle standard data types. $obj = $this->prepareData($data); try { $obj->validate(); } catch (\Exception $e) { $this->admin->setMessage($e->getMessage(), 'error'); return false; } $obj->filter(); } // Process previously uploaded files for the current URI // and finally store them. Everything else will get discarded $queue = $this->admin->session()->getFlashObject('files-upload'); $queue = $queue[base64_encode($this->uri)]; if (is_array($queue)) { foreach ($queue as $key => $files) { foreach ($files as $destination => $file) { if (!rename($file['tmp_name'], $destination)) { throw new \RuntimeException(sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_MOVE', null, true), '"' . $file['tmp_name'] . '"', $destination)); } unset($files[$destination]['tmp_name']); } if ($this->view == 'pages') { $keys = explode('.', preg_replace('/^header./', '', $key)); $init_key = array_shift($keys); if (count($keys) > 0) { $new_data = isset($obj->header()->{$init_key}) ? $obj->header()->{$init_key} : []; Utils::setDotNotation($new_data, implode('.', $keys), $files, true); } else { $new_data = $files; } if (isset($data['header'][$init_key])) { $obj->modifyHeader($init_key, array_replace_recursive([], $data['header'][$init_key], $new_data)); } else { $obj->modifyHeader($init_key, $new_data); } } else { // TODO: [this is JS handled] if it's single file, remove existing and use set, if it's multiple, use join $obj->join($key, $files); // stores } } } if ($obj) { // Event to manipulate data before saving the object $this->grav->fireEvent('onAdminSave', new Event(['object' => &$obj])); $obj->save($reorder); $this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info'); } if ($this->view != 'pages') { // Force configuration reload. /** @var Config $config */ $config = $this->grav['config']; $config->reload(); if ($this->view === 'user') { $this->grav['user']->merge(User::load($this->admin->route)->toArray()); } } // Always redirect if a page route was changed, to refresh it if ($obj instanceof Page) { if (method_exists($obj, 'unsetRouteSlug')) { $obj->unsetRouteSlug(); } $multilang = $this->isMultilang(); if ($multilang) { if (!$obj->language()) { $obj->language($this->grav['session']->admin_lang); } } $admin_route = $this->admin->base; //Handle system.home.hide_in_urls $route = $obj->route(); $hide_home_route = $config->get('system.home.hide_in_urls', false); if ($hide_home_route) { $home_route = $config->get('system.home.alias'); $topParent = $obj->topParent(); if (isset($topParent)) { $top_parent_route = (string) $topParent->route(); if ($top_parent_route == $home_route && substr($route, 0, strlen($top_parent_route) + 1) != $top_parent_route . '/') { $route = $top_parent_route . $route; } } } $redirect_url = ($multilang ? '/' . $obj->language() : '') . $admin_route . '/' . $this->view . $route; $this->setRedirect($redirect_url); } return true; }
/** * Process the admin registration form. * * @param Event $event */ public function onFormProcessed(Event $event) { $form = $event['form']; $action = $event['action']; switch ($action) { case 'register_admin_user': if (!$this->config->get('plugins.login.enabled')) { throw new \RuntimeException($this->grav['language']->translate('PLUGIN_LOGIN.PLUGIN_LOGIN_DISABLED')); } $data = []; $username = $form->value('username'); if ($form->value('password1') != $form->value('password2')) { $this->grav->fireEvent('onFormValidationError', new Event(['form' => $form, 'message' => $this->grav['language']->translate('PLUGIN_LOGIN.PASSWORDS_DO_NOT_MATCH')])); $event->stopPropagation(); return; } $data['password'] = $form->value('password1'); $fields = ['email', 'fullname', 'title']; foreach ($fields as $field) { // Process value of field if set in the page process.register_user if (!isset($data[$field]) && $form->value($field)) { $data[$field] = $form->value($field); } } unset($data['password1']); unset($data['password2']); // Don't store the username: that is part of the filename unset($data['username']); // Extra lowercase to ensure file is saved lowercase $username = strtolower($username); $inflector = new Inflector(); $data['fullname'] = isset($data['fullname']) ? $data['fullname'] : $inflector->titleize($username); $data['title'] = isset($data['title']) ? $data['title'] : 'Administrator'; $data['state'] = 'enabled'; $data['access'] = ['admin' => ['login' => true, 'super' => true], 'site' => ['login' => true]]; // Create user object and save it $user = new User($data); $file = CompiledYamlFile::instance($this->grav['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $user = User::load($username); //Login user $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; $user->authenticated = $user->authorize('site.login'); $messages = $this->grav['messages']; $messages->add($this->grav['language']->translate('PLUGIN_ADMIN.LOGIN_LOGGED_IN'), 'info'); $this->grav->redirect($this->admin_route); break; } }
/** * @param InputInterface $input * @param OutputInterface $output * * @return int|null|void */ protected function execute(InputInterface $input, OutputInterface $output) { $this->setupConsole($input, $output); $helper = $this->getHelper('question'); $data = []; $this->output->writeln('<green>Create new user</green>'); $this->output->writeln(''); // Get username and validate $question = new Question('Enter a <yellow>username</yellow>: ', 'admin'); $question->setValidator(function ($value) { if (!preg_match('/^[a-z0-9_-]{3,16}$/', $value)) { throw new RuntimeException('Username should be between 3 and 16 comprised of lowercase letters, numbers, underscores and hyphens'); } if (file_exists(self::getGrav()['locator']->findResource('user://accounts/' . $value . YAML_EXT))) { throw new RuntimeException('Username "' . $value . '" already exists, please pick another username'); } return $value; }); $username = $helper->ask($this->input, $this->output, $question); // Get password and validate $question = new Question('Enter a <yellow>password</yellow>: '); $question->setValidator(function ($value) { if (!preg_match('/(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/', $value)) { throw new RuntimeException('Password must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters'); } return $value; }); $data['password'] = $helper->ask($this->input, $this->output, $question); // Get email and validate $question = new Question('Enter an <yellow>email</yellow>: '); $question->setValidator(function ($value) { if (!preg_match('/^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$/', $value)) { throw new RuntimeException('Not a valid email address'); } return $value; }); $data['email'] = $helper->ask($this->input, $this->output, $question); // Choose permissions $question = new ChoiceQuestion('Please choose a set of <yellow>permissions</yellow>:', array('a' => 'admin access', 's' => 'site access', 'b' => 'admin and site access'), 'a'); $question->setErrorMessage('permissions %s is invalid.'); $permissions_choice = $helper->ask($this->input, $this->output, $question); switch ($permissions_choice) { case 'a': $data['access']['admin'] = ['login' => true, 'super' => true]; break; case 's': $data['access']['site'] = ['login' => true]; break; case 'b': $data['access']['admin'] = ['login' => true, 'super' => true]; $data['access']['site'] = ['login' => true]; } // Get fullname $question = new Question('Enter a <yellow>fullname</yellow>: '); $question->setValidator(function ($value) { if ($value === null or trim($value) == '') { throw new RuntimeException('Fullname is required'); } return $value; }); $data['fullname'] = $helper->ask($this->input, $this->output, $question); // Get title $question = new Question('Enter a <yellow>title</yellow>: '); $data['title'] = $helper->ask($this->input, $this->output, $question); // Create user object and save it $user = new User($data); $file = CompiledYamlFile::instance(self::getGrav()['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $this->output->writeln(''); $this->output->writeln('<green>Success!</green> User <cyan>' . $username . '</cyan> created.'); }
/** * Handles form and saves the input data if its valid. * * @return bool True if the action was performed. */ public function taskSave() { if (!$this->authoriseTask('save', $this->dataPermissions())) { return; } $reorder = false; $data = $this->post; // Special handler for pages data. if ($this->view == 'pages') { /** @var Page\Pages $pages */ $pages = $this->grav['pages']; // Find new parent page in order to build the path. $route = !isset($data['route']) ? dirname($this->admin->route) : $data['route']; $parent = $route && $route != '/' ? $pages->dispatch($route, true) : $pages->root(); $obj = $this->admin->page(true); $original_slug = $obj->slug(); // Change parent if needed and initialize move (might be needed also on ordering/folder change). $obj = $obj->move($parent); $this->preparePage($obj); // Reset slug and route. For now we do not support slug twig variable on save. $obj->slug($original_slug); $obj->validate(); $obj->filter(); $visible_after = $obj->visible(); // force reordering $reorder = true; // rename folder based on visible if ($visible_after && !$obj->order()) { // needs to have order set $obj->order(1000); } elseif (!$visible_after && $obj->order()) { // needs to have order removed $obj->folder($obj->slug()); } } else { // Handle standard data types. $obj = $this->prepareData(); $obj->validate(); $obj->filter(); } if ($obj) { $obj->save($reorder); $this->admin->setMessage('Successfully saved', 'info'); } if ($this->view != 'pages') { // Force configuration reload. /** @var Config $config */ $config = $this->grav['config']; $config->reload(); if ($this->view === 'users') { $this->grav['user']->merge(User::load($this->admin->route)->toArray()); } } // Always redirect if a page route was changed, to refresh it if ($obj instanceof Page\Page) { if (method_exists($obj, 'unsetRoute')) { $obj->unsetRoute(); } $this->setRedirect($this->view . $obj->route()); } return true; }
/** * Process a registration form. Handles the following actions: * * - register_user: registers a user * * @param Event $event */ public function onFormProcessed(Event $event) { $form = $event['form']; $action = $event['action']; $params = $event['params']; switch ($action) { case 'register_user': if (!$this->config->get('plugins.login.enabled')) { throw new \RuntimeException($this->grav['language']->translate('PLUGIN_LOGIN.PLUGIN_LOGIN_DISABLED')); } if (!$this->config->get('plugins.login.user_registration.enabled')) { throw new \RuntimeException($this->grav['language']->translate('PLUGIN_LOGIN.USER_REGISTRATION_DISABLED')); } $data = []; $username = $form->value('username'); if (file_exists($this->grav['locator']->findResource('user://accounts/' . $username . YAML_EXT))) { $this->grav->fireEvent('onFormValidationError', new Event(['form' => $form, 'message' => $this->grav['language']->translate(['PLUGIN_LOGIN.USERNAME_NOT_AVAILABLE', $username])])); $event->stopPropagation(); return; } if ($this->config->get('plugins.login.user_registration.options.validate_password1_and_password2', false)) { if ($form->value('password1') != $form->value('password2')) { $this->grav->fireEvent('onFormValidationError', new Event(['form' => $form, 'message' => $this->grav['language']->translate('PLUGIN_LOGIN.PASSWORDS_DO_NOT_MATCH')])); $event->stopPropagation(); return; } $data['password'] = $form->value('password1'); } $fields = $this->config->get('plugins.login.user_registration.fields', []); foreach ($fields as $field) { // Process value of field if set in the page process.register_user $default_values = $this->config->get('plugins.login.user_registration.default_values'); if ($default_values) { foreach ($default_values as $key => $param) { $values = explode(',', $param); if ($key == $field) { $data[$field] = $values; } } } if (!isset($data[$field]) && $form->value($field)) { $data[$field] = $form->value($field); } } if ($this->config->get('plugins.login.user_registration.options.validate_password1_and_password2', false)) { unset($data['password1']); unset($data['password2']); } // Don't store the username: that is part of the filename unset($data['username']); if ($this->config->get('plugins.login.user_registration.options.set_user_disabled', false)) { $data['state'] = 'disabled'; } else { $data['state'] = 'enabled'; } // Create user object and save it $user = new User($data); $file = CompiledYamlFile::instance($this->grav['locator']->findResource('user://accounts/' . $username . YAML_EXT, true, true)); $user->file($file); $user->save(); $user = User::load($username); if ($data['state'] == 'enabled' && $this->config->get('plugins.login.user_registration.options.login_after_registration', false)) { //Login user $this->grav['session']->user = $user; unset($this->grav['user']); $this->grav['user'] = $user; $user->authenticated = $user->authorize('site.login'); } if ($this->config->get('plugins.login.user_registration.options.send_activation_email', false)) { $this->sendActivationEmail($user); } else { if ($this->config->get('plugins.login.user_registration.options.send_welcome_email', false)) { $this->sendWelcomeEmail($user); } if ($this->config->get('plugins.login.user_registration.options.send_notification_email', false)) { $this->sendNotificationEmail($user); } } if ($redirect = $this->config->get('plugins.login.user_registration.redirect_after_registration', false)) { $this->grav->redirect($redirect); } break; } }
/** * Gets configuration data. * * @param string $type * @param array $post * * @return mixed * @throws \RuntimeException */ public function data($type, array $post = []) { static $data = []; if (isset($data[$type])) { return $data[$type]; } if (!$post) { $post = isset($_POST['data']) ? $_POST['data'] : []; } // Check to see if a data type is plugin-provided, before looking into core ones $event = $this->grav->fireEvent('onAdminData', new Event(['type' => &$type])); if ($event && isset($event['data_type'])) { return $event['data_type']; } /** @var UniformResourceLocator $locator */ $locator = $this->grav['locator']; $filename = $locator->findResource("config://{$type}.yaml", true, true); $file = CompiledYamlFile::instance($filename); if (preg_match('|plugins/|', $type)) { /** @var Plugins $plugins */ $plugins = $this->grav['plugins']; $obj = $plugins->get(preg_replace('|plugins/|', '', $type)); if (!$obj) { return []; } $obj->merge($post); $obj->file($file); $data[$type] = $obj; } elseif (preg_match('|themes/|', $type)) { /** @var Themes $themes */ $themes = $this->grav['themes']; $obj = $themes->get(preg_replace('|themes/|', '', $type)); if (!$obj) { return []; } $obj->merge($post); $obj->file($file); $data[$type] = $obj; } elseif (preg_match('|users/|', $type)) { $obj = User::load(preg_replace('|users/|', '', $type)); $obj->merge($post); $data[$type] = $obj; } elseif (preg_match('|user/|', $type)) { $obj = User::load(preg_replace('|user/|', '', $type)); $obj->merge($post); $data[$type] = $obj; } elseif (preg_match('|config/|', $type)) { $type = preg_replace('|config/|', '', $type); $blueprints = $this->blueprints("config/{$type}"); $config = $this->grav['config']; $obj = new Data\Data($config->get($type, []), $blueprints); $obj->merge($post); // FIXME: We shouldn't allow user to change configuration files in system folder! $filename = $this->grav['locator']->findResource("config://{$type}.yaml") ?: $this->grav['locator']->findResource("config://{$type}.yaml", true, true); $file = CompiledYamlFile::instance($filename); $obj->file($file); $data[$type] = $obj; } else { throw new \RuntimeException("Data type '{$type}' doesn't exist!"); } return $data[$type]; }
/** * Initialize login plugin if path matches. */ public function initialize() { /** @var Uri $uri */ $uri = $this->grav['uri']; /** @var Grav\Common\Session */ $session = $this->grav['session']; // Autoload classes $autoload = __DIR__ . '/vendor/autoload.php'; if (!is_file($autoload)) { throw new \Exception('Login Plugin failed to load. Composer dependencies not met.'); } require_once $autoload; // Define session message service. $this->grav['messages'] = function ($c) { $session = $c['session']; if (!isset($session->messages)) { $session->messages = new Message(); } return $session->messages; }; // Define current user service. $this->grav['user'] = function ($c) { $session = $c['session']; if (!isset($session->user)) { $session->user = new User(); if ($c['config']->get('plugins.login.rememberme.enabled')) { $controller = new Login\Controller($this->grav, ''); $rememberMe = $controller->rememberMe(); // If we can present the correct tokens from the cookie, we are logged in $username = $rememberMe->login(); if ($username) { // Normal login process $user = User::load($username); if ($user->exists()) { // There is a chance that an attacker has stolen // the login token, so we store the fact that // the user was logged in via RememberMe // (instead of login form) $session->remember_me = $rememberMe; $session->user = $user; } } // Check if the token was invalid if ($rememberMe->loginTokenWasInvalid()) { $controller->setMessage($t->translate('LOGIN_PLUGIN.REMEMBER_ME_STOLEN_COOKIE')); } } } return $session->user; }; // Manage OAuth login $task = !empty($_POST['task']) ? $_POST['task'] : $uri->param('task'); if (!$task && isset($_POST['oauth']) || !empty($_GET) && $session->oauth) { $this->oauthController(); } // Aborted OAuth authentication (invalidate it) unset($session->oauth); // Register route to login page if it has been set. $this->route = $this->config->get('plugins.login.route'); if ($this->route) { $this->enable(['onPagesInitialized' => ['addLoginPage', 0]]); } }
/** * Authenticate user. * * @param array $form Form fields. * @return bool */ protected function authenticate($form) { /** @var User $user */ $user = $this->grav['user']; if (!$user->authenticated && isset($form['username']) && isset($form['password'])) { $user = User::load($form['username']); if ($user->exists()) { // Authenticate user. $result = $user->authenticate($form['password']); if ($result) { $this->grav['session']->user = $user; } } } $user->authenticated = $user->authorise('site.login'); return $user->authenticated; }