summaryrefslogtreecommitdiffstats
path: root/app/class/user.class.php
diff options
context:
space:
mode:
authorMalf Furious <m@lfurio.us>2018-10-27 18:28:22 -0400
committerMalf Furious <m@lfurio.us>2018-10-27 18:28:22 -0400
commit0421aa1b60f4fe6bf140888159c58059c1013588 (patch)
treec3285276f6c53b6789e2f6dc82cb3b0fd17b38a4 /app/class/user.class.php
parent495157341d60522084dcc9f6219877b6ba497312 (diff)
parent6512655aee73d3d295daa4de0e4ef25c08cfec9e (diff)
downloadscrott-0421aa1b60f4fe6bf140888159c58059c1013588.tar.gz
scrott-0421aa1b60f4fe6bf140888159c58059c1013588.zip
Merge branch 'rel/v0.1'v0.1
Diffstat (limited to 'app/class/user.class.php')
-rw-r--r--app/class/user.class.php336
1 files changed, 336 insertions, 0 deletions
diff --git a/app/class/user.class.php b/app/class/user.class.php
new file mode 100644
index 0000000..90aac44
--- /dev/null
+++ b/app/class/user.class.php
@@ -0,0 +1,336 @@
+<?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();
+ }
+}
+
+?>