mirror of
https://github.com/ThomasGsp/HyperProxmox.git
synced 2025-03-09 15:40:18 +00:00
first commit
This commit is contained in:
commit
5352a2b94a
396 changed files with 10008 additions and 0 deletions
254
code/web/backend/application/model/AvatarModel.php
Normal file
254
code/web/backend/application/model/AvatarModel.php
Normal file
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
class AvatarModel
|
||||
{
|
||||
/**
|
||||
* Gets a gravatar image link from given email address
|
||||
*
|
||||
* Gravatar is the #1 (free) provider for email address based global avatar hosting.
|
||||
* The URL (or image) returns always a .jpg file ! For deeper info on the different parameter possibilities:
|
||||
* @see http://gravatar.com/site/implement/images/
|
||||
* @source http://gravatar.com/site/implement/images/php/
|
||||
*
|
||||
* This method will return something like http://www.gravatar.com/avatar/79e2e5b48aec07710c08d50?s=80&d=mm&r=g
|
||||
* Note: the url does NOT have something like ".jpg" ! It works without.
|
||||
*
|
||||
* Set the configs inside the application/config/ files.
|
||||
*
|
||||
* @param string $email The email address
|
||||
* @return string
|
||||
*/
|
||||
public static function getGravatarLinkByEmail($email)
|
||||
{
|
||||
return 'http://www.gravatar.com/avatar/' .
|
||||
md5( strtolower( trim( $email ) ) ) .
|
||||
'?s=' . Config::get('AVATAR_SIZE') . '&d=' . Config::get('GRAVATAR_DEFAULT_IMAGESET') . '&r=' . Config::get('GRAVATAR_RATING');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's avatar file path
|
||||
* @param int $user_has_avatar Marker from database
|
||||
* @param int $user_id User's id
|
||||
* @return string Avatar file path
|
||||
*/
|
||||
public static function getPublicAvatarFilePathOfUser($user_has_avatar, $user_id)
|
||||
{
|
||||
if ($user_has_avatar) {
|
||||
return Config::get('URL') . Config::get('PATH_AVATARS_PUBLIC') . $user_id . '.jpg';
|
||||
}
|
||||
|
||||
return Config::get('URL') . Config::get('PATH_AVATARS_PUBLIC') . Config::get('AVATAR_DEFAULT_IMAGE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's avatar file path
|
||||
* @param $user_id integer The user's id
|
||||
* @return string avatar picture path
|
||||
*/
|
||||
public static function getPublicUserAvatarFilePathByUserId($user_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("SELECT user_has_avatar FROM users WHERE user_id = :user_id LIMIT 1");
|
||||
$query->execute(array(':user_id' => $user_id));
|
||||
|
||||
if ($query->fetch()->user_has_avatar) {
|
||||
return Config::get('URL') . Config::get('PATH_AVATARS_PUBLIC') . $user_id . '.jpg';
|
||||
}
|
||||
|
||||
return Config::get('URL') . Config::get('PATH_AVATARS_PUBLIC') . Config::get('AVATAR_DEFAULT_IMAGE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an avatar picture (and checks all necessary things too)
|
||||
* TODO decouple
|
||||
* TODO total rebuild
|
||||
*/
|
||||
public static function createAvatar()
|
||||
{
|
||||
// check avatar folder writing rights, check if upload fits all rules
|
||||
if (AvatarModel::isAvatarFolderWritable() AND AvatarModel::validateImageFile()) {
|
||||
|
||||
// create a jpg file in the avatar folder, write marker to database
|
||||
$target_file_path = Config::get('PATH_AVATARS') . Session::get('user_id');
|
||||
AvatarModel::resizeAvatarImage($_FILES['avatar_file']['tmp_name'], $target_file_path, Config::get('AVATAR_SIZE'), Config::get('AVATAR_SIZE'), Config::get('AVATAR_JPEG_QUALITY'));
|
||||
AvatarModel::writeAvatarToDatabase(Session::get('user_id'));
|
||||
Session::set('user_avatar_file', AvatarModel::getPublicUserAvatarFilePathByUserId(Session::get('user_id')));
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_AVATAR_UPLOAD_SUCCESSFUL'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the avatar folder exists and is writable
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function isAvatarFolderWritable()
|
||||
{
|
||||
if (is_dir(Config::get('PATH_AVATARS')) AND is_writable(Config::get('PATH_AVATARS'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_AVATAR_FOLDER_DOES_NOT_EXIST_OR_NOT_WRITABLE'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the image
|
||||
* TODO totally decouple
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateImageFile()
|
||||
{
|
||||
if (!isset($_FILES['avatar_file'])) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_AVATAR_IMAGE_UPLOAD_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($_FILES['avatar_file']['size'] > 5000000) {
|
||||
// if input file too big (>5MB)
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_AVATAR_UPLOAD_TOO_BIG'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the image width, height and mime type
|
||||
$image_proportions = getimagesize($_FILES['avatar_file']['tmp_name']);
|
||||
|
||||
// if input file too small
|
||||
if ($image_proportions[0] < Config::get('AVATAR_SIZE') OR $image_proportions[1] < Config::get('AVATAR_SIZE')) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_AVATAR_UPLOAD_TOO_SMALL'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!($image_proportions['mime'] == 'image/jpeg')) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_AVATAR_UPLOAD_WRONG_TYPE'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes marker to database, saying user has an avatar now
|
||||
*
|
||||
* @param $user_id
|
||||
*/
|
||||
public static function writeAvatarToDatabase($user_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("UPDATE users SET user_has_avatar = TRUE WHERE user_id = :user_id LIMIT 1");
|
||||
$query->execute(array(':user_id' => $user_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize avatar image (while keeping aspect ratio and cropping it off sexy)
|
||||
*
|
||||
* TROUBLESHOOTING: You don't see the new image ? Press F5 or CTRL-F5 to refresh browser cache.
|
||||
*
|
||||
* @param string $source_image The location to the original raw image.
|
||||
* @param string $destination The location to save the new image.
|
||||
* @param int $final_width The desired width of the new image
|
||||
* @param int $final_height The desired height of the new image.
|
||||
* @param int $quality The quality of the JPG to produce 1 - 100
|
||||
*
|
||||
* TODO currently we just allow .jpg
|
||||
*
|
||||
* @return bool success state
|
||||
*/
|
||||
public static function resizeAvatarImage($source_image, $destination, $final_width = 44, $final_height = 44, $quality = 85)
|
||||
{
|
||||
list($width, $height) = getimagesize($source_image);
|
||||
|
||||
if (!$width || !$height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//saving the image into memory (for manipulation with GD Library)
|
||||
$myImage = imagecreatefromjpeg($source_image);
|
||||
|
||||
// calculating the part of the image to use for thumbnail
|
||||
if ($width > $height) {
|
||||
$y = 0;
|
||||
$x = ($width - $height) / 2;
|
||||
$smallestSide = $height;
|
||||
} else {
|
||||
$x = 0;
|
||||
$y = ($height - $width) / 2;
|
||||
$smallestSide = $width;
|
||||
}
|
||||
|
||||
// copying the part into thumbnail, maybe edit this for square avatars
|
||||
$thumb = imagecreatetruecolor($final_width, $final_height);
|
||||
imagecopyresampled($thumb, $myImage, 0, 0, $x, $y, $final_width, $final_height, $smallestSide, $smallestSide);
|
||||
|
||||
// add '.jpg' to file path, save it as a .jpg file with our $destination_filename parameter
|
||||
$destination .= '.jpg';
|
||||
imagejpeg($thumb, $destination, $quality);
|
||||
|
||||
// delete "working copy"
|
||||
imagedestroy($thumb);
|
||||
|
||||
if (file_exists($destination)) {
|
||||
return true;
|
||||
}
|
||||
// default return
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user's avatar
|
||||
*
|
||||
* @param int $userId
|
||||
* @return bool success
|
||||
*/
|
||||
public static function deleteAvatar($userId)
|
||||
{
|
||||
if (!ctype_digit($userId)) {
|
||||
Session::add("feedback_negative", Text::get("FEEDBACK_AVATAR_IMAGE_DELETE_FAILED"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to delete image, but still go on regardless of file deletion result
|
||||
self::deleteAvatarImageFile($userId);
|
||||
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sth = $database->prepare("UPDATE users SET user_has_avatar = 0 WHERE user_id = :user_id LIMIT 1");
|
||||
$sth->bindValue(":user_id", (int)$userId, PDO::PARAM_INT);
|
||||
$sth->execute();
|
||||
|
||||
if ($sth->rowCount() == 1) {
|
||||
Session::set('user_avatar_file', self::getPublicUserAvatarFilePathByUserId($userId));
|
||||
Session::add("feedback_positive", Text::get("FEEDBACK_AVATAR_IMAGE_DELETE_SUCCESSFUL"));
|
||||
return true;
|
||||
} else {
|
||||
Session::add("feedback_negative", Text::get("FEEDBACK_AVATAR_IMAGE_DELETE_FAILED"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the avatar image file from the filesystem
|
||||
*
|
||||
* @param $userId
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteAvatarImageFile($userId)
|
||||
{
|
||||
// Check if file exists
|
||||
if (!file_exists(Config::get('PATH_AVATARS') . $userId . ".jpg")) {
|
||||
Session::add("feedback_negative", Text::get("FEEDBACK_AVATAR_IMAGE_DELETE_NO_FILE"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete avatar file
|
||||
if (!unlink(Config::get('PATH_AVATARS') . $userId . ".jpg")) {
|
||||
Session::add("feedback_negative", Text::get("FEEDBACK_AVATAR_IMAGE_DELETE_FAILED"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
46
code/web/backend/application/model/CaptchaModel.php
Normal file
46
code/web/backend/application/model/CaptchaModel.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class CaptchaModel
|
||||
*
|
||||
* This model class handles all the captcha stuff.
|
||||
* Currently this uses the excellent Captcha generator lib from https://github.com/Gregwar/Captcha
|
||||
* Have a look there for more options etc.
|
||||
*/
|
||||
class CaptchaModel
|
||||
{
|
||||
/**
|
||||
* Generates the captcha, "returns" a real image, this is why there is header('Content-type: image/jpeg')
|
||||
* Note: This is a very special method, as this is echoes out binary data.
|
||||
*/
|
||||
public static function generateAndShowCaptcha()
|
||||
{
|
||||
// create a captcha with the CaptchaBuilder lib (loaded via Composer)
|
||||
$captcha = new Gregwar\Captcha\CaptchaBuilder;
|
||||
$captcha->build(
|
||||
Config::get('CAPTCHA_WIDTH'),
|
||||
Config::get('CAPTCHA_HEIGHT')
|
||||
);
|
||||
|
||||
// write the captcha character into session
|
||||
Session::set('captcha', $captcha->getPhrase());
|
||||
|
||||
// render an image showing the characters (=the captcha)
|
||||
header('Content-type: image/jpeg');
|
||||
$captcha->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the entered captcha is the same like the one from the rendered image which has been saved in session
|
||||
* @param $captcha string The captcha characters
|
||||
* @return bool success of captcha check
|
||||
*/
|
||||
public static function checkCaptcha($captcha)
|
||||
{
|
||||
if ($captcha == Session::get('captcha')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
270
code/web/backend/application/model/LoginModel.php
Normal file
270
code/web/backend/application/model/LoginModel.php
Normal file
|
@ -0,0 +1,270 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* LoginModel
|
||||
*
|
||||
* The login part of the model: Handles the login / logout stuff
|
||||
*/
|
||||
class LoginModel
|
||||
{
|
||||
/**
|
||||
* Login process (for DEFAULT user accounts).
|
||||
*
|
||||
* @param $user_name string The user's name
|
||||
* @param $user_password string The user's password
|
||||
* @param $set_remember_me_cookie mixed Marker for usage of remember-me cookie feature
|
||||
*
|
||||
* @return bool success state
|
||||
*/
|
||||
public static function login($user_name, $user_password, $set_remember_me_cookie = null)
|
||||
{
|
||||
// we do negative-first checks here, for simplicity empty username and empty password in one line
|
||||
if (empty($user_name) OR empty($user_password)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_OR_PASSWORD_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if user exists, if login is not blocked (due to failed logins) and if password fits the hash
|
||||
$result = self::validateAndGetUser($user_name, $user_password);
|
||||
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// reset the failed login counter for that user (if necessary)
|
||||
if ($result->user_last_failed_login > 0) {
|
||||
self::resetFailedLoginCounterOfUser($result->user_name);
|
||||
}
|
||||
|
||||
// save timestamp of this login in the database line of that user
|
||||
self::saveTimestampOfLoginOfUser($result->user_name);
|
||||
|
||||
// if user has checked the "remember me" checkbox, then write token into database and into cookie
|
||||
if ($set_remember_me_cookie) {
|
||||
self::setRememberMeInDatabaseAndCookie($result->user_id);
|
||||
}
|
||||
|
||||
// successfully logged in, so we write all necessary data into the session and set "user_logged_in" to true
|
||||
self::setSuccessfulLoginIntoSession(
|
||||
$result->user_id, $result->user_name, $result->user_email, $result->user_account_type
|
||||
);
|
||||
|
||||
// return true to make clear the login was successful
|
||||
// maybe do this in dependence of setSuccessfulLoginIntoSession ?
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the inputs of the users, checks if password is correct etc.
|
||||
* If successful, user is returned
|
||||
*
|
||||
* @param $user_name
|
||||
* @param $user_password
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
private static function validateAndGetUser($user_name, $user_password)
|
||||
{
|
||||
// get all data of that user (to later check if password and password_hash fit)
|
||||
$result = UserModel::getUserDataByUsername($user_name);
|
||||
|
||||
// Check if that user exists. We don't give back a cause in the feedback to avoid giving an attacker details.
|
||||
if (!$result) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_LOGIN_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// block login attempt if somebody has already failed 3 times and the last login attempt is less than 30sec ago
|
||||
if (($result->user_failed_logins >= 3) AND ($result->user_last_failed_login > (time() - 30))) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_WRONG_3_TIMES'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// if hash of provided password does NOT match the hash in the database: +1 failed-login counter
|
||||
if (!password_verify($user_password, $result->user_password_hash)) {
|
||||
self::incrementFailedLoginCounterOfUser($result->user_name);
|
||||
// we say "password wrong" here, but less details like "login failed" would be better (= less information)
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_WRONG'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// if user is not active (= has not verified account by verification mail)
|
||||
if ($result->user_active != 1) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_ACCOUNT_NOT_ACTIVATED_YET'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* performs the login via cookie (for DEFAULT user account, FACEBOOK-accounts are handled differently)
|
||||
* TODO add throttling here ?
|
||||
*
|
||||
* @param $cookie string The cookie "remember_me"
|
||||
*
|
||||
* @return bool success state
|
||||
*/
|
||||
public static function loginWithCookie($cookie)
|
||||
{
|
||||
if (!$cookie) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_COOKIE_INVALID'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// check cookie's contents, check if cookie contents belong together or token is empty
|
||||
list ($user_id, $token, $hash) = explode(':', $cookie);
|
||||
if ($hash !== hash('sha256', $user_id . ':' . $token) OR empty($token)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_COOKIE_INVALID'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// get data of user that has this id and this token
|
||||
$result = UserModel::getUserDataByUserIdAndToken($user_id, $token);
|
||||
if ($result) {
|
||||
// successfully logged in, so we write all necessary data into the session and set "user_logged_in" to true
|
||||
self::setSuccessfulLoginIntoSession($result->user_id, $result->user_name, $result->user_email, $result->user_account_type);
|
||||
// save timestamp of this login in the database line of that user
|
||||
self::saveTimestampOfLoginOfUser($result->user_name);
|
||||
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_COOKIE_LOGIN_SUCCESSFUL'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_COOKIE_INVALID'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out process: delete cookie, delete session
|
||||
*/
|
||||
public static function logout()
|
||||
{
|
||||
self::deleteCookie();
|
||||
Session::destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* The real login process: The user's data is written into the session.
|
||||
* Cheesy name, maybe rename. Also maybe refactoring this, using an array.
|
||||
*
|
||||
* @param $user_id
|
||||
* @param $user_name
|
||||
* @param $user_email
|
||||
* @param $user_account_type
|
||||
*/
|
||||
public static function setSuccessfulLoginIntoSession($user_id, $user_name, $user_email, $user_account_type)
|
||||
{
|
||||
Session::init();
|
||||
Session::set('user_id', $user_id);
|
||||
Session::set('user_name', $user_name);
|
||||
Session::set('user_email', $user_email);
|
||||
Session::set('user_account_type', $user_account_type);
|
||||
Session::set('user_provider_type', 'DEFAULT');
|
||||
|
||||
// get and set avatars
|
||||
Session::set('user_avatar_file', AvatarModel::getPublicUserAvatarFilePathByUserId($user_id));
|
||||
Session::set('user_gravatar_image_url', AvatarModel::getGravatarLinkByEmail($user_email));
|
||||
|
||||
// finally, set user as logged-in
|
||||
Session::set('user_logged_in', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the failed-login counter of a user
|
||||
*
|
||||
* @param $user_name
|
||||
*/
|
||||
public static function incrementFailedLoginCounterOfUser($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users
|
||||
SET user_failed_logins = user_failed_logins+1, user_last_failed_login = :user_last_failed_login
|
||||
WHERE user_name = :user_name OR user_email = :user_name
|
||||
LIMIT 1";
|
||||
$sth = $database->prepare($sql);
|
||||
$sth->execute(array(':user_name' => $user_name, ':user_last_failed_login' => time() ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the failed-login counter of a user back to 0
|
||||
*
|
||||
* @param $user_name
|
||||
*/
|
||||
public static function resetFailedLoginCounterOfUser($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users
|
||||
SET user_failed_logins = 0, user_last_failed_login = NULL
|
||||
WHERE user_name = :user_name AND user_failed_logins != 0
|
||||
LIMIT 1";
|
||||
$sth = $database->prepare($sql);
|
||||
$sth->execute(array(':user_name' => $user_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write timestamp of this login into database (we only write a "real" login via login form into the database,
|
||||
* not the session-login on every page request
|
||||
*
|
||||
* @param $user_name
|
||||
*/
|
||||
public static function saveTimestampOfLoginOfUser($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users SET user_last_login_timestamp = :user_last_login_timestamp
|
||||
WHERE user_name = :user_name LIMIT 1";
|
||||
$sth = $database->prepare($sql);
|
||||
$sth->execute(array(':user_name' => $user_name, ':user_last_login_timestamp' => time()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write remember-me token into database and into cookie
|
||||
* Maybe splitting this into database and cookie part ?
|
||||
*
|
||||
* @param $user_id
|
||||
*/
|
||||
public static function setRememberMeInDatabaseAndCookie($user_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
// generate 64 char random string
|
||||
$random_token_string = hash('sha256', mt_rand());
|
||||
|
||||
// write that token into database
|
||||
$sql = "UPDATE users SET user_remember_me_token = :user_remember_me_token WHERE user_id = :user_id LIMIT 1";
|
||||
$sth = $database->prepare($sql);
|
||||
$sth->execute(array(':user_remember_me_token' => $random_token_string, ':user_id' => $user_id));
|
||||
|
||||
// generate cookie string that consists of user id, random string and combined hash of both
|
||||
$cookie_string_first_part = $user_id . ':' . $random_token_string;
|
||||
$cookie_string_hash = hash('sha256', $cookie_string_first_part);
|
||||
$cookie_string = $cookie_string_first_part . ':' . $cookie_string_hash;
|
||||
|
||||
// set cookie
|
||||
setcookie('remember_me', $cookie_string, time() + Config::get('COOKIE_RUNTIME'), Config::get('COOKIE_PATH'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the cookie
|
||||
* It's necessary to split deleteCookie() and logout() as cookies are deleted without logging out too!
|
||||
* Sets the remember-me-cookie to ten years ago (3600sec * 24 hours * 365 days * 10).
|
||||
* that's obviously the best practice to kill a cookie @see http://stackoverflow.com/a/686166/1114320
|
||||
*/
|
||||
public static function deleteCookie()
|
||||
{
|
||||
setcookie('remember_me', false, time() - (3600 * 24 * 3650), Config::get('COOKIE_PATH'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the user's login
|
||||
*
|
||||
* @return bool user's login status
|
||||
*/
|
||||
public static function isUserLoggedIn()
|
||||
{
|
||||
return Session::userIsLoggedIn();
|
||||
}
|
||||
}
|
120
code/web/backend/application/model/NoteModel.php
Normal file
120
code/web/backend/application/model/NoteModel.php
Normal file
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* NoteModel
|
||||
* This is basically a simple CRUD (Create/Read/Update/Delete) demonstration.
|
||||
*/
|
||||
class NoteModel
|
||||
{
|
||||
/**
|
||||
* Get all notes (notes are just example data that the user has created)
|
||||
* @return array an array with several objects (the results)
|
||||
*/
|
||||
public static function getAllNotes()
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id, note_id, note_text FROM notes WHERE user_id = :user_id";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':user_id' => Session::get('user_id')));
|
||||
|
||||
// fetchAll() is the PDO method that gets all result rows
|
||||
return $query->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single note
|
||||
* @param int $note_id id of the specific note
|
||||
* @return object a single object (the result)
|
||||
*/
|
||||
public static function getNote($note_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id, note_id, note_text FROM notes WHERE user_id = :user_id AND note_id = :note_id LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':user_id' => Session::get('user_id'), ':note_id' => $note_id));
|
||||
|
||||
// fetch() is the PDO method that gets a single result
|
||||
return $query->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a note (create a new one)
|
||||
* @param string $note_text note text that will be created
|
||||
* @return bool feedback (was the note created properly ?)
|
||||
*/
|
||||
public static function createNote($note_text)
|
||||
{
|
||||
if (!$note_text || strlen($note_text) == 0) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_NOTE_CREATION_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "INSERT INTO notes (note_text, user_id) VALUES (:note_text, :user_id)";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':note_text' => $note_text, ':user_id' => Session::get('user_id')));
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// default return
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_NOTE_CREATION_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing note
|
||||
* @param int $note_id id of the specific note
|
||||
* @param string $note_text new text of the specific note
|
||||
* @return bool feedback (was the update successful ?)
|
||||
*/
|
||||
public static function updateNote($note_id, $note_text)
|
||||
{
|
||||
if (!$note_id || !$note_text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE notes SET note_text = :note_text WHERE note_id = :note_id AND user_id = :user_id LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':note_id' => $note_id, ':note_text' => $note_text, ':user_id' => Session::get('user_id')));
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_NOTE_EDITING_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a specific note
|
||||
* @param int $note_id id of the note
|
||||
* @return bool feedback (was the note deleted properly ?)
|
||||
*/
|
||||
public static function deleteNote($note_id)
|
||||
{
|
||||
if (!$note_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "DELETE FROM notes WHERE note_id = :note_id AND user_id = :user_id LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':note_id' => $note_id, ':user_id' => Session::get('user_id')));
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// default return
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_NOTE_DELETION_FAILED'));
|
||||
return false;
|
||||
}
|
||||
}
|
251
code/web/backend/application/model/PasswordResetModel.php
Normal file
251
code/web/backend/application/model/PasswordResetModel.php
Normal file
|
@ -0,0 +1,251 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class PasswordResetModel
|
||||
*
|
||||
* Handles all the stuff that is related to the password-reset process
|
||||
*/
|
||||
class PasswordResetModel
|
||||
{
|
||||
/**
|
||||
* Perform the necessary actions to send a password reset mail
|
||||
*
|
||||
* @param $user_name_or_email string Username or user's email
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function requestPasswordReset($user_name_or_email)
|
||||
{
|
||||
if (empty($user_name_or_email)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_EMAIL_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if that username exists
|
||||
$result = UserModel::getUserDataByUserNameOrEmail($user_name_or_email);
|
||||
if (!$result) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USER_DOES_NOT_EXIST'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// generate integer-timestamp (to see when exactly the user (or an attacker) requested the password reset mail)
|
||||
// generate random hash for email password reset verification (40 char string)
|
||||
$temporary_timestamp = time();
|
||||
$user_password_reset_hash = sha1(uniqid(mt_rand(), true));
|
||||
|
||||
// set token (= a random hash string and a timestamp) into database ...
|
||||
$token_set = PasswordResetModel::setPasswordResetDatabaseToken($result->user_name, $user_password_reset_hash, $temporary_timestamp);
|
||||
if (!$token_set) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... and send a mail to the user, containing a link with username and token hash string
|
||||
$mail_sent = PasswordResetModel::sendPasswordResetMail($result->user_name, $user_password_reset_hash, $result->user_email);
|
||||
if ($mail_sent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// default return
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password reset token in database (for DEFAULT user accounts)
|
||||
*
|
||||
* @param string $user_name username
|
||||
* @param string $user_password_reset_hash password reset hash
|
||||
* @param int $temporary_timestamp timestamp
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function setPasswordResetDatabaseToken($user_name, $user_password_reset_hash, $temporary_timestamp)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users
|
||||
SET user_password_reset_hash = :user_password_reset_hash, user_password_reset_timestamp = :user_password_reset_timestamp
|
||||
WHERE user_name = :user_name AND user_provider_type = :provider_type LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(
|
||||
':user_password_reset_hash' => $user_password_reset_hash, ':user_name' => $user_name,
|
||||
':user_password_reset_timestamp' => $temporary_timestamp, ':provider_type' => 'DEFAULT'
|
||||
));
|
||||
|
||||
// check if exactly one row was successfully changed
|
||||
if ($query->rowCount() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_RESET_TOKEN_FAIL'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the password reset mail
|
||||
*
|
||||
* @param string $user_name username
|
||||
* @param string $user_password_reset_hash password reset hash
|
||||
* @param string $user_email user email
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function sendPasswordResetMail($user_name, $user_password_reset_hash, $user_email)
|
||||
{
|
||||
// create email body
|
||||
$body = Config::get('EMAIL_PASSWORD_RESET_CONTENT') . ' ' . Config::get('URL') .
|
||||
Config::get('EMAIL_PASSWORD_RESET_URL') . '/' . urlencode($user_name) . '/' . urlencode($user_password_reset_hash);
|
||||
|
||||
// create instance of Mail class, try sending and check
|
||||
$mail = new Mail;
|
||||
$mail_sent = $mail->sendMail($user_email, Config::get('EMAIL_PASSWORD_RESET_FROM_EMAIL'),
|
||||
Config::get('EMAIL_PASSWORD_RESET_FROM_NAME'), Config::get('EMAIL_PASSWORD_RESET_SUBJECT'), $body
|
||||
);
|
||||
|
||||
if ($mail_sent) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_PASSWORD_RESET_MAIL_SENDING_SUCCESSFUL'));
|
||||
return true;
|
||||
}
|
||||
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_RESET_MAIL_SENDING_ERROR') . $mail->getError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the password reset request via the verification hash token (that's only valid for one hour)
|
||||
* @param string $user_name Username
|
||||
* @param string $verification_code Hash token
|
||||
* @return bool Success status
|
||||
*/
|
||||
public static function verifyPasswordReset($user_name, $verification_code)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
// check if user-provided username + verification code combination exists
|
||||
$sql = "SELECT user_id, user_password_reset_timestamp
|
||||
FROM users
|
||||
WHERE user_name = :user_name
|
||||
AND user_password_reset_hash = :user_password_reset_hash
|
||||
AND user_provider_type = :user_provider_type
|
||||
LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(
|
||||
':user_password_reset_hash' => $verification_code, ':user_name' => $user_name,
|
||||
':user_provider_type' => 'DEFAULT'
|
||||
));
|
||||
|
||||
// if this user with exactly this verification hash code does NOT exist
|
||||
if ($query->rowCount() != 1) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_RESET_COMBINATION_DOES_NOT_EXIST'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// get result row (as an object)
|
||||
$result_user_row = $query->fetch();
|
||||
|
||||
// 3600 seconds are 1 hour
|
||||
$timestamp_one_hour_ago = time() - 3600;
|
||||
|
||||
// if password reset request was sent within the last hour (this timeout is for security reasons)
|
||||
if ($result_user_row->user_password_reset_timestamp > $timestamp_one_hour_ago) {
|
||||
// verification was successful
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_PASSWORD_RESET_LINK_VALID'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_RESET_LINK_EXPIRED'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the new password to the database
|
||||
*
|
||||
* @param string $user_name username
|
||||
* @param string $user_password_hash
|
||||
* @param string $user_password_reset_hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function saveNewUserPassword($user_name, $user_password_hash, $user_password_reset_hash)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users SET user_password_hash = :user_password_hash, user_password_reset_hash = NULL,
|
||||
user_password_reset_timestamp = NULL
|
||||
WHERE user_name = :user_name AND user_password_reset_hash = :user_password_reset_hash
|
||||
AND user_provider_type = :user_provider_type LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(
|
||||
':user_password_hash' => $user_password_hash, ':user_name' => $user_name,
|
||||
':user_password_reset_hash' => $user_password_reset_hash, ':user_provider_type' => 'DEFAULT'
|
||||
));
|
||||
|
||||
// if one result exists, return true, else false. Could be written even shorter btw.
|
||||
return ($query->rowCount() == 1 ? true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new password (for DEFAULT user, FACEBOOK-users don't have a password)
|
||||
* Please note: At this point the user has already pre-verified via verifyPasswordReset() (within one hour),
|
||||
* so we don't need to check again for the 60min-limit here. In this method we authenticate
|
||||
* via username & password-reset-hash from (hidden) form fields.
|
||||
*
|
||||
* @param string $user_name
|
||||
* @param string $user_password_reset_hash
|
||||
* @param string $user_password_new
|
||||
* @param string $user_password_repeat
|
||||
*
|
||||
* @return bool success state of the password reset
|
||||
*/
|
||||
public static function setNewPassword($user_name, $user_password_reset_hash, $user_password_new, $user_password_repeat)
|
||||
{
|
||||
// validate the password
|
||||
if (!self::validateNewPassword($user_name, $user_password_reset_hash, $user_password_new, $user_password_repeat)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// crypt the password (with the PHP 5.5+'s password_hash() function, result is a 60 character hash string)
|
||||
$user_password_hash = password_hash($user_password_new, PASSWORD_DEFAULT);
|
||||
|
||||
// write the password to database (as hashed and salted string), reset user_password_reset_hash
|
||||
if (PasswordResetModel::saveNewUserPassword($user_name, $user_password_hash, $user_password_reset_hash)) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_PASSWORD_CHANGE_SUCCESSFUL'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_CHANGE_FAILED'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the password submission
|
||||
*
|
||||
* @param $user_name
|
||||
* @param $user_password_reset_hash
|
||||
* @param $user_password_new
|
||||
* @param $user_password_repeat
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateNewPassword($user_name, $user_password_reset_hash, $user_password_new, $user_password_repeat)
|
||||
{
|
||||
if (empty($user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_FIELD_EMPTY'));
|
||||
return false;
|
||||
} else if (empty($user_password_reset_hash)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_RESET_TOKEN_MISSING'));
|
||||
return false;
|
||||
} else if (empty($user_password_new) || empty($user_password_repeat)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_FIELD_EMPTY'));
|
||||
return false;
|
||||
} else if ($user_password_new !== $user_password_repeat) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_REPEAT_WRONG'));
|
||||
return false;
|
||||
} else if (strlen($user_password_new) < 6) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_TOO_SHORT'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
278
code/web/backend/application/model/RegistrationModel.php
Normal file
278
code/web/backend/application/model/RegistrationModel.php
Normal file
|
@ -0,0 +1,278 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class RegistrationModel
|
||||
*
|
||||
* Everything registration-related happens here.
|
||||
*/
|
||||
class RegistrationModel
|
||||
{
|
||||
/**
|
||||
* Handles the entire registration process for DEFAULT users (not for people who register with
|
||||
* 3rd party services, like facebook) and creates a new user in the database if everything is fine
|
||||
*
|
||||
* @return boolean Gives back the success status of the registration
|
||||
*/
|
||||
public static function registerNewUser()
|
||||
{
|
||||
// TODO this could be written simpler and cleaner
|
||||
|
||||
// clean the input
|
||||
$user_name = strip_tags(Request::post('user_name'));
|
||||
$user_email = strip_tags(Request::post('user_email'));
|
||||
$user_password_new = Request::post('user_password_new');
|
||||
$user_password_repeat = Request::post('user_password_repeat');
|
||||
|
||||
// stop registration flow if registrationInputValidation() returns false (= anything breaks the input check rules)
|
||||
$validation_result = RegistrationModel::registrationInputValidation(Request::post('captcha'), $user_name, $user_password_new, $user_password_repeat, $user_email);
|
||||
if (!$validation_result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// crypt the password with the PHP 5.5's password_hash() function, results in a 60 character hash string.
|
||||
// @see php.net/manual/en/function.password-hash.php for more, especially for potential options
|
||||
$user_password_hash = password_hash($user_password_new, PASSWORD_DEFAULT);
|
||||
|
||||
// check if username already exists
|
||||
if (UserModel::doesUsernameAlreadyExist($user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_ALREADY_TAKEN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if email already exists
|
||||
if (UserModel::doesEmailAlreadyExist($user_email)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USER_EMAIL_ALREADY_TAKEN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// generate random hash for email verification (40 char string)
|
||||
$user_activation_hash = sha1(uniqid(mt_rand(), true));
|
||||
|
||||
// write user data to database
|
||||
if (!RegistrationModel::writeNewUserToDatabase($user_name, $user_password_hash, $user_email, time(), $user_activation_hash)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_ACCOUNT_CREATION_FAILED'));
|
||||
}
|
||||
|
||||
// get user_id of the user that has been created, to keep things clean we DON'T use lastInsertId() here
|
||||
$user_id = UserModel::getUserIdByUsername($user_name);
|
||||
|
||||
if (!$user_id) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// send verification email
|
||||
if (RegistrationModel::sendVerificationEmail($user_id, $user_email, $user_activation_hash)) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_ACCOUNT_SUCCESSFULLY_CREATED'));
|
||||
return true;
|
||||
}
|
||||
|
||||
// if verification email sending failed: instantly delete the user
|
||||
RegistrationModel::rollbackRegistrationByUserId($user_id);
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_VERIFICATION_MAIL_SENDING_FAILED'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the registration input
|
||||
*
|
||||
* @param $captcha
|
||||
* @param $user_name
|
||||
* @param $user_password_new
|
||||
* @param $user_password_repeat
|
||||
* @param $user_email
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function registrationInputValidation($captcha, $user_name, $user_password_new, $user_password_repeat, $user_email)
|
||||
{
|
||||
// perform all necessary checks
|
||||
if (!CaptchaModel::checkCaptcha($captcha)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_CAPTCHA_WRONG'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// if username, email and password are all correctly validated
|
||||
if (self::validateUserName($user_name) AND self::validateUserEmail($user_email) AND self::validateUserPassword($user_password_new, $user_password_repeat)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the username
|
||||
*
|
||||
* @param $user_name
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUserName($user_name)
|
||||
{
|
||||
if (empty($user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// if username is too short (2), too long (64) or does not fit the pattern (aZ09)
|
||||
if (!preg_match('/^[a-zA-Z0-9]{2,64}$/', $user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the email
|
||||
*
|
||||
* @param $user_email
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUserEmail($user_email)
|
||||
{
|
||||
if (empty($user_email)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the email with PHP's internal filter
|
||||
// side-fact: Max length seems to be 254 chars
|
||||
// @see http://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
|
||||
if (!filter_var($user_email, FILTER_VALIDATE_EMAIL)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_DOES_NOT_FIT_PATTERN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the password
|
||||
*
|
||||
* @param $user_password_new
|
||||
* @param $user_password_repeat
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUserPassword($user_password_new, $user_password_repeat)
|
||||
{
|
||||
if (empty($user_password_new) OR empty($user_password_repeat)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user_password_new !== $user_password_repeat) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_REPEAT_WRONG'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen($user_password_new) < 6) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_PASSWORD_TOO_SHORT'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the new user's data to the database
|
||||
*
|
||||
* @param $user_name
|
||||
* @param $user_password_hash
|
||||
* @param $user_email
|
||||
* @param $user_creation_timestamp
|
||||
* @param $user_activation_hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function writeNewUserToDatabase($user_name, $user_password_hash, $user_email, $user_creation_timestamp, $user_activation_hash)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
// write new users data into database
|
||||
$sql = "INSERT INTO users (user_name, user_password_hash, user_email, user_creation_timestamp, user_activation_hash, user_provider_type)
|
||||
VALUES (:user_name, :user_password_hash, :user_email, :user_creation_timestamp, :user_activation_hash, :user_provider_type)";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':user_name' => $user_name,
|
||||
':user_password_hash' => $user_password_hash,
|
||||
':user_email' => $user_email,
|
||||
':user_creation_timestamp' => $user_creation_timestamp,
|
||||
':user_activation_hash' => $user_activation_hash,
|
||||
':user_provider_type' => 'DEFAULT'));
|
||||
$count = $query->rowCount();
|
||||
if ($count == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the user from users table. Currently used to rollback a registration when verification mail sending
|
||||
* was not successful.
|
||||
*
|
||||
* @param $user_id
|
||||
*/
|
||||
public static function rollbackRegistrationByUserId($user_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("DELETE FROM users WHERE user_id = :user_id");
|
||||
$query->execute(array(':user_id' => $user_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the verification email (to confirm the account).
|
||||
* The construction of the mail $body looks weird at first, but it's really just a simple string.
|
||||
*
|
||||
* @param int $user_id user's id
|
||||
* @param string $user_email user's email
|
||||
* @param string $user_activation_hash user's mail verification hash string
|
||||
*
|
||||
* @return boolean gives back true if mail has been sent, gives back false if no mail could been sent
|
||||
*/
|
||||
public static function sendVerificationEmail($user_id, $user_email, $user_activation_hash)
|
||||
{
|
||||
$body = Config::get('EMAIL_VERIFICATION_CONTENT') . Config::get('URL') . Config::get('EMAIL_VERIFICATION_URL')
|
||||
. '/' . urlencode($user_id) . '/' . urlencode($user_activation_hash);
|
||||
|
||||
$mail = new Mail;
|
||||
$mail_sent = $mail->sendMail($user_email, Config::get('EMAIL_VERIFICATION_FROM_EMAIL'),
|
||||
Config::get('EMAIL_VERIFICATION_FROM_NAME'), Config::get('EMAIL_VERIFICATION_SUBJECT'), $body
|
||||
);
|
||||
|
||||
if ($mail_sent) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_VERIFICATION_MAIL_SENDING_SUCCESSFUL'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_VERIFICATION_MAIL_SENDING_ERROR') . $mail->getError() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the email/verification code combination and set the user's activation status to true in the database
|
||||
*
|
||||
* @param int $user_id user id
|
||||
* @param string $user_activation_verification_code verification token
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function verifyNewUser($user_id, $user_activation_verification_code)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "UPDATE users SET user_active = 1, user_activation_hash = NULL
|
||||
WHERE user_id = :user_id AND user_activation_hash = :user_activation_hash LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':user_id' => $user_id, ':user_activation_hash' => $user_activation_verification_code));
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_ACCOUNT_ACTIVATION_SUCCESSFUL'));
|
||||
return true;
|
||||
}
|
||||
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_ACCOUNT_ACTIVATION_FAILED'));
|
||||
return false;
|
||||
}
|
||||
}
|
331
code/web/backend/application/model/UserModel.php
Normal file
331
code/web/backend/application/model/UserModel.php
Normal file
|
@ -0,0 +1,331 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* UserModel
|
||||
* Handles all the PUBLIC profile stuff. This is not for getting data of the logged in user, it's more for handling
|
||||
* data of all the other users. Useful for display profile information, creating user lists etc.
|
||||
*/
|
||||
class UserModel
|
||||
{
|
||||
/**
|
||||
* Gets an array that contains all the users in the database. The array's keys are the user ids.
|
||||
* Each array element is an object, containing a specific user's data.
|
||||
* The avatar line is built using Ternary Operators, have a look here for more:
|
||||
* @see http://davidwalsh.name/php-shorthand-if-else-ternary-operators
|
||||
*
|
||||
* @return array The profiles of all users
|
||||
*/
|
||||
public static function getPublicProfilesOfAllUsers()
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id, user_name, user_email, user_active, user_has_avatar FROM users";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute();
|
||||
|
||||
$all_users_profiles = array();
|
||||
|
||||
foreach ($query->fetchAll() as $user) {
|
||||
$all_users_profiles[$user->user_id] = new stdClass();
|
||||
$all_users_profiles[$user->user_id]->user_id = $user->user_id;
|
||||
$all_users_profiles[$user->user_id]->user_name = $user->user_name;
|
||||
$all_users_profiles[$user->user_id]->user_email = $user->user_email;
|
||||
$all_users_profiles[$user->user_id]->user_active = $user->user_active;
|
||||
$all_users_profiles[$user->user_id]->user_avatar_link = (Config::get('USE_GRAVATAR') ? AvatarModel::getGravatarLinkByEmail($user->user_email) : AvatarModel::getPublicAvatarFilePathOfUser($user->user_has_avatar, $user->user_id));
|
||||
}
|
||||
|
||||
return $all_users_profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user's profile data, according to the given $user_id
|
||||
* @param int $user_id The user's id
|
||||
* @return mixed The selected user's profile
|
||||
*/
|
||||
public static function getPublicProfileOfUser($user_id)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id, user_name, user_email, user_active, user_has_avatar
|
||||
FROM users WHERE user_id = :user_id LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
$query->execute(array(':user_id' => $user_id));
|
||||
|
||||
$user = $query->fetch();
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
if (Config::get('USE_GRAVATAR')) {
|
||||
$user->user_avatar_link = AvatarModel::getGravatarLinkByEmail($user->user_email);
|
||||
} else {
|
||||
$user->user_avatar_link = AvatarModel::getPublicAvatarFilePathOfUser($user->user_has_avatar, $user->user_id);
|
||||
}
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USER_DOES_NOT_EXIST'));
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user_name_or_email
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getUserDataByUserNameOrEmail($user_name_or_email)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("SELECT user_id, user_name, user_email FROM users
|
||||
WHERE (user_name = :user_name_or_email OR user_email = :user_name_or_email)
|
||||
AND user_provider_type = :provider_type LIMIT 1");
|
||||
$query->execute(array(':user_name_or_email' => $user_name_or_email, ':provider_type' => 'DEFAULT'));
|
||||
|
||||
return $query->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a username is already taken
|
||||
*
|
||||
* @param $user_name string username
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function doesUsernameAlreadyExist($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("SELECT user_id FROM users WHERE user_name = :user_name LIMIT 1");
|
||||
$query->execute(array(':user_name' => $user_name));
|
||||
if ($query->rowCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a email is already used
|
||||
*
|
||||
* @param $user_email string email
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function doesEmailAlreadyExist($user_email)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("SELECT user_id FROM users WHERE user_email = :user_email LIMIT 1");
|
||||
$query->execute(array(':user_email' => $user_email));
|
||||
if ($query->rowCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes new username to database
|
||||
*
|
||||
* @param $user_id int user id
|
||||
* @param $new_user_name string new username
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function saveNewUserName($user_id, $new_user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("UPDATE users SET user_name = :user_name WHERE user_id = :user_id LIMIT 1");
|
||||
$query->execute(array(':user_name' => $new_user_name, ':user_id' => $user_id));
|
||||
if ($query->rowCount() == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes new email address to database
|
||||
*
|
||||
* @param $user_id int user id
|
||||
* @param $new_user_email string new email address
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function saveNewEmailAddress($user_id, $new_user_email)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("UPDATE users SET user_email = :user_email WHERE user_id = :user_id LIMIT 1");
|
||||
$query->execute(array(':user_email' => $new_user_email, ':user_id' => $user_id));
|
||||
$count = $query->rowCount();
|
||||
if ($count == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the user's name, provided in the editing form
|
||||
*
|
||||
* @param $new_user_name string The new username
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function editUserName($new_user_name)
|
||||
{
|
||||
// new username same as old one ?
|
||||
if ($new_user_name == Session::get('user_name')) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_SAME_AS_OLD_ONE'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// username cannot be empty and must be azAZ09 and 2-64 characters
|
||||
if (!preg_match("/^[a-zA-Z0-9]{2,64}$/", $new_user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_DOES_NOT_FIT_PATTERN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// clean the input, strip usernames longer than 64 chars (maybe fix this ?)
|
||||
$new_user_name = substr(strip_tags($new_user_name), 0, 64);
|
||||
|
||||
// check if new username already exists
|
||||
if (UserModel::doesUsernameAlreadyExist($new_user_name)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USERNAME_ALREADY_TAKEN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$status_of_action = UserModel::saveNewUserName(Session::get('user_id'), $new_user_name);
|
||||
if ($status_of_action) {
|
||||
Session::set('user_name', $new_user_name);
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_USERNAME_CHANGE_SUCCESSFUL'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the user's email
|
||||
*
|
||||
* @param $new_user_email
|
||||
*
|
||||
* @return bool success status
|
||||
*/
|
||||
public static function editUserEmail($new_user_email)
|
||||
{
|
||||
// email provided ?
|
||||
if (empty($new_user_email)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_FIELD_EMPTY'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if new email is same like the old one
|
||||
if ($new_user_email == Session::get('user_email')) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_SAME_AS_OLD_ONE'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// user's email must be in valid email format, also checks the length
|
||||
// @see http://stackoverflow.com/questions/21631366/php-filter-validate-email-max-length
|
||||
// @see http://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
|
||||
if (!filter_var($new_user_email, FILTER_VALIDATE_EMAIL)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_EMAIL_DOES_NOT_FIT_PATTERN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// strip tags, just to be sure
|
||||
$new_user_email = substr(strip_tags($new_user_email), 0, 254);
|
||||
|
||||
// check if user's email already exists
|
||||
if (UserModel::doesEmailAlreadyExist($new_user_email)) {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_USER_EMAIL_ALREADY_TAKEN'));
|
||||
return false;
|
||||
}
|
||||
|
||||
// write to database, if successful ...
|
||||
// ... then write new email to session, Gravatar too (as this relies to the user's email address)
|
||||
if (UserModel::saveNewEmailAddress(Session::get('user_id'), $new_user_email)) {
|
||||
Session::set('user_email', $new_user_email);
|
||||
Session::set('user_gravatar_image_url', AvatarModel::getGravatarLinkByEmail($new_user_email));
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_EMAIL_CHANGE_SUCCESSFUL'));
|
||||
return true;
|
||||
}
|
||||
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's id
|
||||
*
|
||||
* @param $user_name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getUserIdByUsername($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id FROM users WHERE user_name = :user_name AND user_provider_type = :provider_type LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
|
||||
// DEFAULT is the marker for "normal" accounts (that have a password etc.)
|
||||
// There are other types of accounts that don't have passwords etc. (FACEBOOK)
|
||||
$query->execute(array(':user_name' => $user_name, ':provider_type' => 'DEFAULT'));
|
||||
|
||||
// return one row (we only have one result or nothing)
|
||||
return $query->fetch()->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's data
|
||||
*
|
||||
* @param $user_name string User's name
|
||||
*
|
||||
* @return mixed Returns false if user does not exist, returns object with user's data when user exists
|
||||
*/
|
||||
public static function getUserDataByUsername($user_name)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$sql = "SELECT user_id, user_name, user_email, user_password_hash, user_active, user_account_type,
|
||||
user_failed_logins, user_last_failed_login
|
||||
FROM users
|
||||
WHERE (user_name = :user_name OR user_email = :user_name)
|
||||
AND user_provider_type = :provider_type
|
||||
LIMIT 1";
|
||||
$query = $database->prepare($sql);
|
||||
|
||||
// DEFAULT is the marker for "normal" accounts (that have a password etc.)
|
||||
// There are other types of accounts that don't have passwords etc. (FACEBOOK)
|
||||
$query->execute(array(':user_name' => $user_name, ':provider_type' => 'DEFAULT'));
|
||||
|
||||
// return one row (we only have one result or nothing)
|
||||
return $query->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user's data by user's id and a token (used by login-via-cookie process)
|
||||
*
|
||||
* @param $user_id
|
||||
* @param $token
|
||||
*
|
||||
* @return mixed Returns false if user does not exist, returns object with user's data when user exists
|
||||
*/
|
||||
public static function getUserDataByUserIdAndToken($user_id, $token)
|
||||
{
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
// get real token from database (and all other data)
|
||||
$query = $database->prepare("SELECT user_id, user_name, user_email, user_password_hash, user_active,
|
||||
user_account_type, user_has_avatar, user_failed_logins, user_last_failed_login
|
||||
FROM users
|
||||
WHERE user_id = :user_id
|
||||
AND user_remember_me_token = :user_remember_me_token
|
||||
AND user_remember_me_token IS NOT NULL
|
||||
AND user_provider_type = :provider_type LIMIT 1");
|
||||
$query->execute(array(':user_id' => $user_id, ':user_remember_me_token' => $token, ':provider_type' => 'DEFAULT'));
|
||||
|
||||
// return one row (we only have one result or nothing)
|
||||
return $query->fetch();
|
||||
}
|
||||
}
|
65
code/web/backend/application/model/UserRoleModel.php
Normal file
65
code/web/backend/application/model/UserRoleModel.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class UserRoleModel
|
||||
*
|
||||
* This class contains everything that is related to up- and downgrading accounts.
|
||||
*/
|
||||
class UserRoleModel
|
||||
{
|
||||
/**
|
||||
* Upgrades / downgrades the user's account. Currently it's just the field user_account_type in the database that
|
||||
* can be 1 or 2 (maybe "basic" or "premium"). Put some more complex stuff in here, maybe a pay-process or whatever
|
||||
* you like.
|
||||
*
|
||||
* @param $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function changeUserRole($type)
|
||||
{
|
||||
if (!$type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// save new role to database
|
||||
if (self::saveRoleToDatabase($type)) {
|
||||
Session::add('feedback_positive', Text::get('FEEDBACK_ACCOUNT_TYPE_CHANGE_SUCCESSFUL'));
|
||||
return true;
|
||||
} else {
|
||||
Session::add('feedback_negative', Text::get('FEEDBACK_ACCOUNT_TYPE_CHANGE_FAILED'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the new account type marker to the database and to the session
|
||||
*
|
||||
* @param $type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function saveRoleToDatabase($type)
|
||||
{
|
||||
// if $type is not 1 or 2
|
||||
if (!in_array($type, [1, 2])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database = DatabaseFactory::getFactory()->getConnection();
|
||||
|
||||
$query = $database->prepare("UPDATE users SET user_account_type = :new_type WHERE user_id = :user_id LIMIT 1");
|
||||
$query->execute(array(
|
||||
':new_type' => $type,
|
||||
':user_id' => Session::get('user_id')
|
||||
));
|
||||
|
||||
if ($query->rowCount() == 1) {
|
||||
// set account type in session
|
||||
Session::set('user_account_type', $type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue