namespace Ice\Auth\Driver; use Ice\Exception; use Ice\Auth\Social; use Ice\Auth\Driver; use Ice\Auth\Driver\DriverInterface; use Ice\Auth\Driver\Model\Roles; use Ice\Auth\Driver\Model\Users; use Ice\Auth\Driver\Model\Users\Tokens; use Ice\Auth\Driver\Model\Users\Social as UserSocial; /** * Auth database driver. * * @package Ice/Auth * @category Driver * @author Ice Team * @copyright (c) 2014-2023 Ice Team * @license http://iceframework.org/license */ class Db extends Driver implements DriverInterface { /** * Logs a user in, based on the autologin cookie. * * @return mixed */ public function autoLogin() { var token, user, userRoles, roles, role; let token = this->cookies->get("auth_autologin"); if token { // Find the token let token = Tokens::findOne(["token": token]); if token { // Get user for the token let user = token->{"getUser"}(); if user && token->{"useragent"} === sha1(this->request->getUserAgent()) { // Get user's roles let userRoles = user->{"getRoles"}(), roles = []; for role in iterator(userRoles) { let role =role->{"getRole"}(), roles[] = role->get("name"); } if in_array("login", roles) { // Update the token to create a new unique token token->update(); // Set the new token this->cookies->set("auth_autologin", token->{"token"}, token->{"expires"}); // Complete the login with the found data this->completeLogin(user, roles); // Automatic login was successful return user; } } // Token is invalid token->delete(); } } return false; } /** * Complete the login for a user by setting session data and eg. incrementing the logins. * * @param object user User Model object * @param array roles User's roles * @return void */ protected function completeLogin(var user, array roles = []) { if user instanceof Users { user->completeLogin(); parent::completeLogin(serialize(user), roles); } } /** * Gets the currently logged in user from the session. Returns NULL if no user is currently logged in. * * @param mixed defaultValue Default value to return if the user is currently not logged in * @return mixed */ public function getUser(var defaultValue = null) { var data; if !this->user { let data = parent::getUser(defaultValue); if data === defaultValue { // User isn't currently logged in let this->user = defaultValue; } elseif typeof data == "string" { var user; let user = unserialize(data); if user instanceof Users { let this->user = user; } } } if !this->user { let this->user = this->autoLogin(); } return this->user; } /** * Check if user has the role. * * @param object user User Model object * @param string role Role name * @return boolean */ public function hasRole(var user, string role = "login") -> boolean { if typeof user == "object" && (user instanceof Users) { return user->getRole(role) ? true : false; } else { throw new Exception("User must be an object"); } } /** * Logs a user in. * * @param mixed username * @param string password * @param boolean remember enable autologin * @param boolean force login without password * @return boolean */ public function login(var username, string password, boolean remember = false, boolean force = false) -> boolean | null { var user, users, roles, userRoles, role, token, lifetime; let user = null; if username { if typeof username == "object" { let user = username; } else { let users = this->getOption("users", "Ice\\Auth\\Driver\\Model\\Users"), user = {users}::findOne(["username": username]); } } else { // Username not specified return null; } if typeof user == "object" && (user instanceof Users) { if empty password && !force { return false; } // Check if password match if parent::checkHash(password, user->get("password")) || force { let userRoles = user->{"getRoles"}(), roles = []; for role in iterator(userRoles) { let role = role->{"getRole"}(), roles[] = role->get("name"); } // Check if user has `login` role if in_array("login", roles) { if remember { // Create a new autologin token let lifetime = this->getOption("lifetime"), token = new Tokens(), token->{"user_id"} = user->getId(), token->{"useragent"} = sha1(this->request->getUserAgent()), token->{"created"} = time(), token->{"expires"} = time() + lifetime; if token->create() === true { // Set the autologin cookie this->cookies->set("auth_autologin", token->get("token"), token->get("expires")); } } this->completeLogin(user, roles); return true; } } return false; } else { // User not found return null; } } /** * Logs a user in through social network. * * @param mixed social * @param boolean remember enable autologin * @return boolean */ public function loginBy( social, boolean remember = false) { var user, userSocial, roles, userRoles, role, token, lifetime; // Try to find record for this social response let userSocial = UserSocial::findOne(["social_id": social->get("socialId"), "type": social->getAdapter()->getProvider()]); // Check if record exist if typeof userSocial == "object" && (userSocial instanceof UserSocial) { let user = userSocial->{"getUser"}(); // Check if user is valid if typeof user == "object" && (user instanceof Users) { let userRoles = user->{"getRoles"}(), roles = []; for role in iterator(userRoles) { let role = role->{"getRole"}(), roles[] = role->get("name"); } // Check if user has `login` role if in_array("login", roles) { if remember { // Create a new autologin token let lifetime = this->getOption("lifetime"), token = new Tokens(), token->{"user_id"} = user->getId(), token->{"useragent"} = sha1(this->request->getUserAgent()), token->{"created"} = time(), token->{"expires"} = time() + lifetime; if token->create() === true { // Set the autologin cookie this->cookies->set("auth_autologin", token->get("token"), token->get("expires")); } } this->completeLogin(user, roles); return true; } return false; } } // Social or user not found return null; } /** * Log a user out and remove any autologin cookies. * * @param boolean destroy Completely destroy the session * @param boolean logoutAll Remove all tokens for user * @return boolean */ public function logout(boolean destroy = false, boolean logoutAll = false) -> boolean { var token, tokens, user; let token = this->cookies->get("auth_autologin"); if token { // Delete the autologin cookie to prevent re-login this->cookies->remove("auth_autologin"); // Clear the autologin token from the database let token = Tokens::findOne(["token": token]); if token { token->delete(); } } if logoutAll { // Delete all user tokens let tokens = new Tokens(), user = this->getUser(); if user { tokens->delete(["user_id": user->getId()]); } } let this->user = null; return parent::logout(destroy); } /** * Refresh user data stored in the session. * Returns null if no user is currently logged in. * * @return mixed */ public function refreshUser() { var user, refreshed, userRoles, userRole, roles, role; let user = this->getUser(); if !user { this->logout(); return null; } else { if typeof user == "object" && (user instanceof Users) && this->getOption("session_roles") { // Get user's data from db let refreshed = user->loadOne(user->get(user->getPrimary())), user = refreshed, userRoles = user->{"getRoles"}(), roles = []; for userRole in iterator(userRoles) { let role = userRole->{"getRole"}(), roles[] = role->get("name"); } parent::completeLogin(serialize(user), roles); let this->user = user; } } return user; } }