likely the last commit had a serious vulnerability. this is just a sync
Deploy vintagecoding.net / deploy (push) Canceled after 0s

This commit is contained in:
2025-06-10 10:46:22 -06:00
parent becfbc3628
commit ae76b11a87
8 changed files with 304 additions and 324 deletions
+131 -276
View File
@@ -1,4 +1,5 @@
<?php
require_once 'relationships/spaceMember.php';
class LoginCredentials
{
@@ -38,9 +39,11 @@ class User
private ?string $bio;
private ?string $website;
private int $role;
private ?string $passwordHash;
public function __construct(int $id = -1, ?array $spaces, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, int $role = 3, ?string $passwordHash)
public function __construct(int $id, ?array $spaces, string $username, ?string $profilePicture = null, ?string $bio = null, ?string $website = null, int $role = 3, ?string $passwordHash)
{
error_log('Constructing a User with id: ' . $id);
// Basic XSS prevention on construction (can be enhanced)
$username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
$bio = htmlspecialchars($bio ?? '', ENT_QUOTES, 'UTF-8');
@@ -58,7 +61,8 @@ class User
$this->website = $website;
$this->isActive = false;
$this->role = $role;
$this->spaces = null;
$this->spaces = $spaces;
$this->passwordHash = $passwordHash;
if ($id < 0) {
$this->id = $id * -1;
@@ -66,7 +70,8 @@ class User
exec_stmt($stmt, 'isssss', $this->id, $this->username, $passwordHash, $this->profilePicture, $this->bio, $this->website);
Space::addUserToSpace($this->id, 2025);
new Space($id, $this->username, $this->username . "'s private Space.", 5, [$this], [], null, true);
$space = new Space($id, $this->username, $this->username . "'s private Space.", 5, [], null, true);
$this->spaces[] = new SpaceMember(randomId(4), $this, $space, 1, new Datetime('now'), true);
} else
$this->id = $id;
}
@@ -98,15 +103,132 @@ class User
return true;
}
public static function retrieveFromDB(int $userId): ?User
public function add(SpaceMember $sm)
{
$stmt = 'SELECT id, username, profilePicture, bio, website, role FROM user WHERE id = ?';
$user = exec_stmt($stmt, 'i', $userId)->fetch_assoc();
$this->spaces[] = $sm;
}
if ($user)
return new User($user['id'], null, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
else
public static function retrieveFromDB($identifier): ?User
{
error_log('Instantiating a user');
if (is_int($identifier)) {
$stmt = 'SELECT id, username, profilePicture, bio, website, role, passwordHash FROM user WHERE id = ?';
$rawUser = exec_stmt($stmt, 'i', $identifier)->fetch_assoc();
if (!$rawUser)
return null;
} else if (is_string($identifier)) {
$stmt = 'SELECT id, username, profilePicture, bio, website, role, passwordHash FROM user WHERE username = ?';
$rawUser = exec_stmt($stmt, 'i', $identifier)->fetch_assoc();
if (!$rawUser)
return null;
} else {
return null;
}
$user = new User($rawUser['id'], null, $rawUser['username'], $rawUser['profilePicture'], $rawUser['bio'], $rawUser['website'], $rawUser['role'], $rawUser['passwordHash']);
$stmt = 'SELECT id, space, role, addedAt, favorited from spaceMembers WHERE user = ?';
$rels = exec_stmt($stmt, 'i', $user->getId());
if (!$rels)
return null;
while ($r = $rels->fetch_assoc())
$user->add(new SpaceMember($r['id'], $user, Space::retrieveFromDB($r['space'], $user->getId()), $r['role'], new DateTime($r['addedAt']), $r['favorited']));
return $user;
}
public static function login(LoginCredentials $credentials): ?User
{
$user = User::retrieveFromDB($credentials->getUsername());
if (!password_verify($credentials->getPassword(), $user->getPasswordHash()))
return null;
else
return $user;
}
public static function logout(): void
{
foreach (array_keys($_SESSION) as $key) {
unset($_SESSION[$key]);
}
// Consider destroying the session cookie as well: session_destroy();
header('Location: .'); // Redirect to the homepage or login page
exit();
}
public function getPasswordHash()
{
return $this->passwordHash;
}
public function getHomeSpace()
{
// TODO: Need to implement. Requires fleshing out additional space types.
return Space::retrieveFromDB(1, 1);
}
public function getSpaces()
{
$baseSpaces = [];
foreach ($this->spaces as $s)
if (!$s->getSpace()->hasParent())
$baseSpaces[] = $s;
return $baseSpaces;
}
public function getAllSpaces()
{
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
$spaces = [];
while ($r = $rawSpaces->fetch_assoc())
$spaces[] = Space::retrieveFromDB($r['space']);
return $spaces;
}
public function getRole(): int
{
return $this->role;
}
public function getId(): int
{
return $this->id;
}
public function getUsername(): string
{
return $this->username;
}
public function isActive(): bool
{
return $this->isActive;
}
public function getProfilePicture(): ?string
{
return $this->profilePicture;
}
public function getBio(): ?string
{
return $this->bio;
}
public function getWebsite(): ?string
{
return $this->website;
}
public function setIsActive(bool $isActive): void
{
$this->isActive = $isActive;
}
public static function getSignupHTML()
@@ -190,271 +312,4 @@ class User
return $html;
}
public static function login(LoginCredentials $credentials): ?User
{
$stmt = 'SELECT id, username, email, passwordHash, role, profilePicture, bio, website FROM user WHERE username = ?';
$user = exec_stmt($stmt, 's', $credentials->getUsername())->fetch_assoc();
if (!password_verify($credentials->getPassword(), $user['passwordHash']))
return null;
else {
$spaces = [];
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $user['id'])->fetch_assoc();
if ($rawSpaces)
foreach ($rawSpaces as $s)
$spaces[] = serialize(Space::retrieveFromDB($s));
return new User($user['id'], $spaces, $user['username'], $user['profilePicture'], $user['bio'], $user['website'], $user['role'], null);
}
}
public static function logout(): void
{
foreach (array_keys($_SESSION) as $key) {
unset($_SESSION[$key]);
}
// Consider destroying the session cookie as well: session_destroy();
header('Location: .'); // Redirect to the homepage or login page
exit();
}
public function getHomeSpace()
{
// TODO: Need to implement. Requires fleshing out additional space types.
return Space::retrieveFromDB(1, 1);
}
public function getSpaces()
{
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
$spaces = [];
while ($r = $rawSpaces->fetch_assoc()) {
$space = Space::retrieveFromDB($r['space']);
if (!$space->getParentSpace())
$spaces[] = $space;
}
return $spaces;
}
public function getAllSpaces()
{
$stmt = 'SELECT space FROM spaceMembers WHERE user = ?';
$rawSpaces = exec_stmt($stmt, 'i', $this->id);
$spaces = [];
while ($r = $rawSpaces->fetch_assoc())
$spaces[] = Space::retrieveFromDB($r['space']);
return $spaces;
}
public function getRole(): int
{
return $this->role;
}
public function getId(): int
{
return $this->id;
}
public function getUsername(): string
{
return $this->username;
}
public function isActive(): bool
{
return $this->isActive;
}
public function getProfilePicture(): ?string
{
return $this->profilePicture;
}
public function getBio(): ?string
{
return $this->bio;
}
public function getWebsite(): ?string
{
return $this->website;
}
public function setIsActive(bool $isActive): void
{
$this->isActive = $isActive;
}
public function toggleActive(?mysqli $db): bool
{
if (!$db) {
error_log('Database connection not provided for toggleActive.');
return false;
}
$this->isActive = !$this->isActive;
$stmt = $db->prepare('UPDATE user SET is_active = ? WHERE user_id = ?');
if ($stmt) {
$stmt->bind_param('ii', (int) $this->isActive, $this->id);
$result = $stmt->execute();
$stmt->close();
return $result;
} else {
error_log('Error preparing statement: ' . $db->error);
return false;
}
}
public function updateUsername(string $newUsername, ?mysqli $db): bool
{
$newUsername = htmlspecialchars($newUsername, ENT_QUOTES, 'UTF-8');
if (preg_match('/[\'";\-\_]/', $newUsername)) {
throw new InvalidArgumentException('Invalid characters in username.');
}
if (!$db) {
error_log('Database connection not provided for updateUsername.');
return false;
}
$stmt = $db->prepare('UPDATE user SET username = ? WHERE user_id = ?');
if ($stmt) {
$stmt->bind_param('si', $newUsername, $this->id);
$result = $stmt->execute();
$stmt->close();
if ($result) {
$this->username = $newUsername;
return true;
}
} else {
error_log('Error preparing statement: ' . $db->error);
}
return false;
}
public function updatePassword(string $newPassword, ?mysqli $db): bool
{
$newPasswordHash = hash('sha256', $newPassword);
if (!$db) {
error_log('Database connection not provided for updatePassword.');
return false;
}
$stmt = $db->prepare('UPDATE user SET password_hash = ? WHERE user_id = ?');
if ($stmt) {
$stmt->bind_param('si', $newPasswordHash, $this->id);
$result = $stmt->execute();
$stmt->close();
return $result;
} else {
error_log('Error preparing statement: ' . $db->error);
}
return false;
}
public function updateProfilePicture(?string $newProfilePicture, ?mysqli $db): bool
{
$newProfilePicture = htmlspecialchars($newProfilePicture ?? 'default-profile.png', ENT_QUOTES, 'UTF-8');
if (preg_match('/[\'";\-\_]/', $newProfilePicture)) {
throw new InvalidArgumentException('Invalid characters in profile picture filename.');
}
if (!$db) {
error_log('Database connection not provided for updateProfilePicture.');
return false;
}
$stmt = $db->prepare('UPDATE user SET profile_picture = ? WHERE user_id = ?');
if ($stmt) {
$stmt->bind_param('si', $newProfilePicture, $this->id);
$result = $stmt->execute();
$stmt->close();
if ($result) {
$this->profilePicture = $newProfilePicture;
return true;
}
} else {
error_log('Error preparing statement: ' . $db->error);
}
return false;
}
public function followUser(int $followingUserId, bool $notify = false, ?mysqli $db): bool
{
if (!$db) {
error_log('Database connection not provided for followUser.');
return false;
}
$stmt = $db->prepare('INSERT INTO follows (follower_user_id, following_user_id, notify_user) VALUES (?, ?, ?)');
if ($stmt) {
$stmt->bind_param('iii', $this->id, $followingUserId, (int) $notify);
$result = $stmt->execute();
$stmt->close();
return $result;
} else {
error_log('Error preparing statement: ' . $db->error);
return false;
}
}
public function getFollowers(?mysqli $db): array
{
if (!$db) {
error_log('Database connection not provided for getFollowers.');
return [];
}
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.follower_user_id = u.user_id WHERE f.following_user_id = ?');
if ($stmt) {
$stmt->bind_param('i', $this->id);
$stmt->execute();
$result = $stmt->get_result();
$followers = [];
while ($row = $result->fetch_assoc()) {
$followers[] = new User(
(int) $row['user_id'],
$row['username'],
$row['profile_picture'],
$row['bio'],
$row['website']
);
}
$stmt->close();
return $followers;
} else {
error_log('Error preparing statement: ' . $db->error);
return [];
}
}
public function getFollowing(?mysqli $db): array
{
if (!$db) {
error_log('Database connection not provided for getFollowing.');
return [];
}
$stmt = $db->prepare('SELECT u.user_id, u.username, u.profile_picture, u.bio, u.website FROM follows f JOIN user u ON f.following_user_id = u.user_id WHERE f.follower_user_id = ?');
if ($stmt) {
$stmt->bind_param('i', $this->id);
$stmt->execute();
$result = $stmt->get_result();
$following = [];
while ($row = $result->fetch_assoc()) {
$following[] = new User(
(int) $row['user_id'],
$row['username'],
$row['profile_picture'],
$row['bio'],
$row['website']
);
}
$stmt->close();
return $following;
} else {
error_log('Error preparing statement: ' . $db->error);
return [];
}
}
}
class Creator extends User {}