grav = $grav; $this->config = $this->grav['config']; $this->language = $this->grav['language']; $this->session = $this->grav['session']; $this->uri = $this->grav['uri']; } /** * Login user. * * @param array $credentials * @param array $options * @param array $extra Example: ['authorize' => 'site.login', 'user' => null], undefined variables gets set. * @return User */ public function login(array $credentials, array $options = [], array $extra = []) { $grav = Grav::instance(); $eventOptions = [ 'credentials' => $credentials, 'options' => $options ] + $extra; // Attempt to authenticate the user. $event = new UserLoginEvent($eventOptions); $grav->fireEvent('onUserLoginAuthenticate', $event); if ($event->isSuccess()) { // Make sure that event didn't mess up with the user authorization. $user = $event->getUser(); $user->authenticated = true; $user->authorized = false; // Allow plugins to prevent login after successful authentication. $event = new UserLoginEvent($event->toArray()); $grav->fireEvent('onUserLoginAuthorize', $event); } if ($event->isSuccess()) { // User has been logged in, let plugins know. $event = new UserLoginEvent($event->toArray()); $grav->fireEvent('onUserLogin', $event); // Make sure that event didn't mess up with the user authorization. $user = $event->getUser(); $user->authenticated = true; $user->authorized = $event->isDelayed(); } else { // Allow plugins to log errors or do other tasks on failure. $event = new UserLoginEvent($event->toArray()); $grav->fireEvent('onUserLoginFailure', $event); // Make sure that event didn't mess up with the user authorization. $user = $event->getUser(); $user->authenticated = false; $user->authorized = false; } $user = $event->getUser(); $user->def('language', 'en'); return $user; } /** * Logout user. * * @param array $options * @param User $user * @return User */ public function logout(array $options = [], User $user = null) { $grav = Grav::instance(); $eventOptions = [ 'user' => $user ?: $grav['user'], 'options' => $options ]; $event = new UserLoginEvent($eventOptions); // Logout the user. $grav->fireEvent('onUserLogout', $event); $user = $event->getUser(); $user->authenticated = false; return $user; } /** * Authenticate user. * * @param array $credentials Form fields. * @param array $options * * @return bool */ public function authenticate($credentials, $options = ['remember_me' => true]) { $user = $this->login($credentials, $options); if ($user->authenticated) { $this->grav['messages']->add($this->language->translate('PLUGIN_LOGIN.LOGIN_SUCCESSFUL', [$user->language]), 'info'); $redirect_route = $this->uri->route(); $this->grav->redirect($redirect_route); } return $user->authenticated; } /** * Create a new user file * * @param array $data * * @return User */ public function register($data) { //Add new user ACL settings $groups = $this->config->get('plugins.login.user_registration.groups', []); if (count($groups) > 0) { $data['groups'] = $groups; } $access = $this->config->get('plugins.login.user_registration.access.site', []); if (count($access) > 0) { $data['access']['site'] = $access; } $username = $data['username']; $file = CompiledYamlFile::instance($this->grav['locator']->findResource('account://' . $username . YAML_EXT, true, true)); // Create user object and save it $user = new User($data); $user->file($file); $user->save(); return $user; } /** * Handle the email to notify the user account creation to the site admin. * * @param User $user * * @return bool True if the action was performed. * @throws \RuntimeException */ public function sendNotificationEmail(User $user) { if (empty($user->email)) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD')); } $site_name = $this->config->get('site.title', 'Website'); $subject = $this->language->translate(['PLUGIN_LOGIN.NOTIFICATION_EMAIL_SUBJECT', $site_name]); $content = $this->language->translate([ 'PLUGIN_LOGIN.NOTIFICATION_EMAIL_BODY', $site_name, $user->username, $user->email, $this->grav['base_url_absolute'], ]); $to = $this->config->get('plugins.email.from'); if (empty($to)) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_NOT_CONFIGURED')); } $sent = EmailUtils::sendEmail($subject, $content, $to); if ($sent < 1) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); } return true; } /** * Handle the email to welcome the new user * * @param User $user * * @return bool True if the action was performed. * @throws \RuntimeException */ public function sendWelcomeEmail(User $user) { if (empty($user->email)) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.USER_NEEDS_EMAIL_FIELD')); } $site_name = $this->config->get('site.title', 'Website'); $author = $this->grav['config']->get('site.author.name', ''); $fullname = $user->fullname ?: $user->username; $subject = $this->language->translate(['PLUGIN_LOGIN.WELCOME_EMAIL_SUBJECT', $site_name]); $content = $this->language->translate(['PLUGIN_LOGIN.WELCOME_EMAIL_BODY', $fullname, $this->grav['base_url_absolute'], $site_name, $author ]); $to = $user->email; $sent = EmailUtils::sendEmail($subject, $content, $to); if ($sent < 1) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); } return true; } /** * Handle the email to activate the user account. * * @param User $user * * @return bool True if the action was performed. * @throws \RuntimeException */ public function sendActivationEmail(User $user) { if (empty($user->email)) { throw new \RuntimeException($this->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->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; $site_name = $this->config->get('site.title', 'Website'); $author = $this->grav['config']->get('site.author.name', ''); $fullname = $user->fullname ?: $user->username; $subject = $this->language->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_SUBJECT', $site_name]); $content = $this->language->translate(['PLUGIN_LOGIN.ACTIVATION_EMAIL_BODY', $fullname, $activation_link, $site_name, $author ]); $to = $user->email; $sent = EmailUtils::sendEmail($subject, $content, $to); if ($sent < 1) { throw new \RuntimeException($this->language->translate('PLUGIN_LOGIN.EMAIL_SENDING_FAILURE')); } return true; } /** * Gets and sets the RememberMe class * * @param mixed $var A rememberMe instance to set * * @return RememberMe Returns the current rememberMe instance * @throws \InvalidArgumentException */ public function rememberMe($var = null) { if ($var !== null) { $this->rememberMe = $var; } if (!$this->rememberMe) { /** @var Config $config */ $config = $this->grav['config']; // Setup storage for RememberMe cookies $storage = new TokenStorage; $this->rememberMe = new RememberMe($storage); $this->rememberMe->setCookieName($config->get('plugins.login.rememberme.name')); $this->rememberMe->setExpireTime($config->get('plugins.login.rememberme.timeout')); // Hardening cookies with user-agent and random salt or // fallback to use system based cache key $server_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown'; $data = $server_agent . $config->get('security.salt', $this->grav['cache']->getKey()); $this->rememberMe->setSalt(hash('sha512', $data)); // Set cookie with correct base path of Grav install $cookie = new Cookie; $cookie->setPath($this->grav['base_url_relative'] ?: '/'); $this->rememberMe->setCookie($cookie); } return $this->rememberMe; } /** * @param string $context * @param int $maxCount * @param int $interval * @return RateLimiter */ public function getRateLimiter($context, $maxCount = null, $interval = null) { if (!isset($this->rateLimiters[$context])) { switch ($context) { case 'login_attempts': $maxCount = $this->grav['config']->get('plugins.login.max_login_count', 5); $interval = $this->grav['config']->get('plugins.login.max_login_interval', 10); break; case 'pw_resets': $maxCount = $this->grav['config']->get('plugins.login.max_pw_resets_count', 0); $interval = $this->grav['config']->get('plugins.login.max_pw_resets_interval', 2); break; } $this->rateLimiters[$context] = new RateLimiter($context, $maxCount, $interval); } return $this->rateLimiters[$context]; } /** * @param User $user * @param Page $page * @param Data|null $config * @return bool */ public function isUserAuthorizedForPage(User $user, Page $page, $config = null) { $header = $page->header(); $rules = isset($header->access) ? (array)$header->access : []; if ($config !== null && $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 true; } // Continue to the page if user is authorized to access the page. foreach ($rules as $rule => $value) { if (is_array($value)) { foreach ($value as $nested_rule => $nested_value) { if ($user->authorize($rule . '.' . $nested_rule) == $nested_value) { return true; } } } else { if ($user->authorize($rule) == $value) { return true; } } } return false; } /** * Check if user may use password reset functionality. * * @param User $user * @param string $field * @param int $count * @param int $interval * @return bool * @deprecated 3.0 Use $grav['login']->getRateLimiter($context) instead. See Grav\Plugin\Login\RateLimiter class. */ public function isUserRateLimited(User $user, $field, $count, $interval) { if ($count > 0) { if (!isset($user->{$field})) { $user->{$field} = array(); } //remove older than 1 hour attempts $actual_resets = array(); foreach ($user->{$field} as $reset) { if ($reset > (time() - $interval * 60)) { $actual_resets[] = $reset; } } if (count($actual_resets) >= $count) { return true; } $actual_resets[] = time(); // current reset $user->{$field} = $actual_resets; } return false; } /** * Reset the rate limit counter. * * @param User $user * @param string $field * @deprecated 3.0 Use $grav['login']->getRateLimiter($context) instead. See Grav\Plugin\Login\RateLimiter class. */ public function resetRateLimit(User $user, $field) { $user->{$field} = []; } /** * Get Current logged in user * * @return User * @deprecated 3.0 Use $grav['user'] instead. */ public function getUser() { /** @var User $user */ return $this->grav['user']; } }