summaryrefslogblamecommitdiffstats
path: root/app/class/user.class.php
blob: 90aac4402ccc1e9875996b0465687f3377fd286a (plain) (tree)







































                                                                         
                                  








                                                                      
                                       

                                                                                               
                                       

























                                                                                 
                                       
















                                                                                  
                                       















                                                                                  
                                       
















                                                                     
                                                                         









                                                                      
                          


                        










                                                   










                                                                       
                                                                         














                                                                  



















                                                                         
                         




                     





                                                                          


















                                                                             
                         












                                                                           
                         











                                                                                
                         
     


                                                                               
                                                                              























                                                                                                                       




























                                                                    
                                                                           








                                                                 


  
<?php

/*
 * SCROTT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * For more information, please refer to UNLICENSE
 */

require_once "class/agent.class.php";

/*
 * This class models Scrott users, including users without valid logins
 * (external-users).
 */
class user extends agent
{
    /*
     * Constructor
     */
    public function __construct(?string $guid = NULL)
    {
        $this->fields['users'] = array(
            "guid",
            "auth",
            "salt",
            "alias",
            "email",
            "emailVer",
            "admin",
            "reg",
            "emailConf",
        );

        parent::__construct($guid);
        $this->expectType("user");
    }

    /*
     * Get the GUID of a user object from a given username, or NULL if
     * the username is not in use.  Therefore, this function can be
     * used to test the existence of a user with the given username.
     */
    public static function getGuidByUname(string $uname) : ?string
    {
        $uname = database::esc($uname);

        $query = "SELECT guid FROM objects WHERE objtype = 'user' AND name = '" . $uname . "'";
        $res = database::query($query);

        if (count($res) == 0)
            return NULL;

        return $res[0]['guid'];
    }

    /*
     * Get a user object from a given username, or NULL if the username
     * is not in use.  This function can be used to test the existence
     * of a user with the given username.
     */
    public static function getByUname(string $uname) : ?user
    {
        if (($guid = self::getGuidByUname($uname)))
            return new user($guid);

        return NULL;
    }

    /*
     * Get an array of all users, sorted by username
     */
    public static function getAll_ordByUname() : array
    {
        $query = "SELECT guid FROM objects WHERE objtype = 'user' ORDER BY name";
        $res = database::query($query);

        $users = array();

        foreach ($res as $u)
            $users[] = new user($u['guid']);

        return $users;
    }

    /*
     * Get an array of all users, sorted by admin (descending, admins
     * first), then by username.
     */
    public static function getAll_ordByAdminByUname() : array
    {
        $query = "SELECT o.guid FROM objects o JOIN users u ON o.guid = u.guid " .
                 "WHERE o.objtype = 'user' ORDER BY u.admin DESC, o.name";
        $res = database::query($query);

        $users = array();

        foreach ($res as $u)
            $users[] = new user($u['guid']);

        return $users;
    }

    /*
     * Get an array of all admins, sorted by username
     */
    public static function getAllAdmin_ordByUname() : array
    {
        $query = "SELECT o.guid FROM objects o JOIN users u ON o.guid = u.guid " .
                 "WHERE o.objtype = 'user' AND u.admin = 1 ORDER BY o.name";
        $res = database::query($query);

        $users = array();

        foreach ($res as $u)
            $users[] = new user($u['guid']);

        return $users;
    }

    /*
     * Get the currently logged in user, or NULL if logged out.  This
     * function will throw if unable to aquire a PHP session.  This
     * function will also forcibly log the current user out if it
     * detects any changes in the user-agent or remote IP address.
     */
    public static function getCurrent() : ?user
    {
        if ((session_status() != PHP_SESSION_ACTIVE) && !session_start())
            throw new Exception("Unable to aquire a PHP session");

        if (!isset($_SESSION['userguid']))
            return NULL;

        /* detect session hijacking */
        if (($_SESSION['useragent'] != $_SERVER['HTTP_USER_AGENT']) ||
            ($_SESSION['userip'] != $_SERVER['REMOTE_ADDR']))
        {
            self::setCurrent();
            location("/");
            return NULL;
        }

        try
        {
            return new user($_SESSION['userguid']);
        }
        catch (Exception $e)
        {
            /* invalid user */
            self::setCurrent();
            location("/");
            return NULL;
        }
    }

    /*
     * Set the currently logged in user.  Using NULL will logout any
     * current user.  This function will throw if unable to aquire a
     * PHP session.  This function will also cache the user-agent and
     * remote IP address of the current request to help validate future
     * requests made under the same session.
     */
    public static function setCurrent(?user $user = NULL) : void
    {
        if ((session_status() != PHP_SESSION_ACTIVE) && !session_start())
            throw new Exception("Unable to aquire a PHP session");

        unset($_SESSION['userguid']);
        unset($_SESSION['useragent']);
        unset($_SESSION['userip']);

        if ($user)
        {
            $_SESSION['userguid'] = $user->guid;
            $_SESSION['useragent'] = $_SERVER['HTTP_USER_AGENT'];
            $_SESSION['userip'] = $_SERVER['REMOTE_ADDR'];
        }
    }

    /*
     * Initialize a new user object with the given username and plain
     * text password.  This function returns NULL if $uname is already
     * being used.
     */
    public static function initNew(string $uname, string $passwd) : ?user
    {
        if (self::getByUname($uname))
            return NULL;

        $user = new user();

        /* if there exist no users already, make this new one an admin */
        if (count(self::getAll_ordByUname()) == 0)
            $user->admin = 1;

        $user->name = $uname;
        $user->objtype = "user";
        $user->setPasswd($passwd);
        $user->setEmail("");
        $user->reg = 1;
        $user->saveObj();

        return $user;
    }

    /*
     * Get the salted and hashed form of a password
     */
    private static function getAuth(string $passwd, string $salt) : string
    {
        return hash("sha256", $passwd . $salt);
    }

    /*
     * Validate the given plain-text password for this user.  Returns true if
     * correct, false otherwise.
     */
    public function validatePasswd(string $passwd) : bool
    {
        $auth = self::getAuth($passwd, $this->salt);
        return $auth == $this->auth;
    }

    /*
     * Update the auth and salt for this user, given a new plain-text
     * password.
     */
    public function setPasswd(string $passwd) : void
    {
        $this->salt = self::getBlob();
        $this->auth = self::getAuth($passwd, $this->salt);
        $this->saveObj();
    }

    /*
     * Validate the email confirmation code for this user.  Returns true if
     * correct, false otherwise.  On success, $this->emailConf is also set
     * to 1
     */
    public function verifyEmail(string $ver) : bool
    {
        if ($ver != $this->emailVer)
            return false;

        $this->emailConf = 1;
        $this->saveObj();
        return true;
    }

    /*
     * Update the email address for this user.  This function will automatically
     * reset the emailConf flag and confirmation code for this user as well.
     */
    public function setEmail(string $email) : void
    {
        $this->email = $email;
        $this->emailVer = substr(self::getBlob(), 0, 8);
        $this->emailConf = 0;
        $this->saveObj();
    }

    /*
     * Get all groups this user owns or is a member of.  This isn't necessarily
     * all groups this user has access permissions for.  Results are sorted by
     * ownership, then by name.
     */
    public function getGroups_ordByOwnByName() : array
    {
        $groups = array();

        /* owner */
        $query = "SELECT guid FROM objects WHERE objtype = 'group' AND owner = '" . database::esc($this->guid) . "' " .
            "ORDER BY name";
        $res = database::query($query);

        foreach ($res as $g)
            $groups[] = new group($g['guid']);

        /* member */
        $query = "SELECT o.guid FROM objects o JOIN members m ON o.guid = m.guid WHERE o.objtype = 'group' AND " .
            "m.member = '" . database::esc($this->guid) . "' ORDER BY o.name";
        $res = database::query($query);

        foreach ($res as $g)
            $groups[] = new group($g['guid']);

        return $groups;
    }

    /*
     * Send an email message to this user using stored configuration
     * parameters.  If config is not established, delivery is not
     * attempted.  Return status.
     */
    public function sendEmail(string $subj, string $mesg,
        ?string $attachPath = NULL, ?string $attachName = NULL,
        bool $ignoreEmailConf = false) : bool
    {
        if (settings::smtpServer() == "")
            return false;

        if (!$ignoreEmailConf && !$this->emailConf)
            return true;

        if ($this->email == "")
            return true;

        $mail = new PHPMailer();
        $mail->isSMTP();
        $mail->SMTPAuth = true;

        $mail->Host = settings::smtpServer();
        $mail->Port = settings::smtpPort();
        $mail->Username = settings::smtpUname();
        $mail->Password = settings::smtpPasswd();
        $mail->SMTPSecure = settings::smtpSecurity();

        $mail->setFrom(settings::smtpEmailAddress(), settings::smtpFrom());
        $mail->addAddress($this->email, $this->getDisplayName());
        $mail->Subject = $subj;
        $mail->Body = $mesg;

        if ($attachPath && $attachName)
            $mail->addAttachment($attachPath, $attachName);

        return $mail->send();
    }
}

?>