From 4f2d900af7636e6744ae14aeeefdf2d6080ac50a Mon Sep 17 00:00:00 2001 From: George Halkiadakis Date: Thu, 30 Mar 2023 05:03:23 +0300 Subject: several mail options tested; mailhog (fake) mailer is working --- .gitignore | 5 + Dockerfile | 14 ++ core/classes/Database.php | 2 +- core/classes/Request.php | 2 +- core/classes/authentication/Core/UserManager.php | 1 + core/classes/authentication/User.php | 18 --- docker-compose.yml | 9 ++ docker/config/msmtprc.kill | 12 ++ html/app/config/app_constants.php | 29 +++- html/app/controllers/Auth.php | 105 ++++++++++++-- html/app/extends/Classroom_manager.php | 106 ++++++++++++++ html/app/extends/Classroom_user.php | 105 ++++---------- html/app/models/admin/History_model.php | 25 ++-- html/app/models/admin/User_model.php | 52 ++++--- html/app/routes/api.php | 68 ++++++--- html/app/routes/frontend.php | 4 +- html/app/views/basic.php | 50 ------- html/app/views/components/breadcrumbs.php | 30 ++++ html/app/views/components/header_includes.php | 23 +++ html/app/views/components/top-bar.php | 17 +++ html/app/views/components/user-management.php | 35 +++-- html/app/views/error/404.php | 8 +- html/app/views/error/general.php | 8 +- html/app/views/group.php | 5 - html/app/views/user/login.php | 8 +- html/app/views/user/new-password.php | 68 ++++----- html/app/views/user/register.php | 175 ++++++++++++++--------- html/app/views/welcome.php | 21 +++ html/assets/css/class.css | 8 +- html/assets/css/overides.css | 13 +- tests/mail.php | 2 + 31 files changed, 677 insertions(+), 351 deletions(-) create mode 100644 docker/config/msmtprc.kill create mode 100644 html/app/extends/Classroom_manager.php delete mode 100644 html/app/views/basic.php create mode 100644 html/app/views/components/breadcrumbs.php create mode 100644 html/app/views/components/header_includes.php create mode 100644 html/app/views/components/top-bar.php create mode 100644 html/app/views/welcome.php create mode 100644 tests/mail.php diff --git a/.gitignore b/.gitignore index 0b728f0..a103dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,8 @@ vendor/* # -- vscode ide configurations .vscode .vscode/* + + +# docker test and other auto-created folders +.docker-data/* +.dms/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6c92eb8..85e4a32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,20 @@ FROM php:8.1-apache as builder # ARG MODE="PRODUCTION" ARG MODE="debug" + +# mailhog configuration +# ------------------------------------------------------------------------------ +# RUN apt-get update &&\ +# apt-get install --no-install-recommends --assume-yes --quiet ca-certificates curl git &&\ +# rm -rf /var/lib/apt/lists/* +# RUN curl -Lsf 'https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz' | tar -C '/usr/local' -xvzf - +# ENV PATH /usr/local/go/bin:$PATH +# RUN go get github.com/mailhog/mhsendmail +# RUN cp /root/go/bin/mhsendmail /usr/bin/mhsendmail +# RUN echo 'sendmail_path = /usr/bin/mhsendmail --smtp-addr mailhog:1025' > /usr/local/etc/php/php.ini + + + # setup apache # ------------------------------------------------------------------------------ diff --git a/core/classes/Database.php b/core/classes/Database.php index 629753c..8cac479 100644 --- a/core/classes/Database.php +++ b/core/classes/Database.php @@ -74,7 +74,7 @@ class Database { */ public function lastInsertID() { - return $this->connection->insert_id; + return $this->connection->lastInsertId(); } diff --git a/core/classes/Request.php b/core/classes/Request.php index e1148b4..bc322a1 100644 --- a/core/classes/Request.php +++ b/core/classes/Request.php @@ -38,7 +38,7 @@ class Request $this->HOST = $_SERVER['HTTP_HOST']; $this->PORT = $_SERVER['SERVER_PORT']; $this->TIME = $_SERVER['REQUEST_TIME']; - $this->CLI_IP = $_SERVER['REMOTE_ADDR']; + $this->IP = $_SERVER['REMOTE_ADDR']; $this->METHOD = strtolower($_SERVER['REQUEST_METHOD']); diff --git a/core/classes/authentication/Core/UserManager.php b/core/classes/authentication/Core/UserManager.php index a3c1cd5..54cd2cb 100644 --- a/core/classes/authentication/Core/UserManager.php +++ b/core/classes/authentication/Core/UserManager.php @@ -76,6 +76,7 @@ class UserManager implements UserManagerInterface /** createUserToken() * == serializes user and stores it into session + * so $_SESSION[DEFAULT_PREFIX_KEY] has the serialized representaion of user */ public function createUserToken(UserInterface $user): UserTokenInterface { diff --git a/core/classes/authentication/User.php b/core/classes/authentication/User.php index 06f0ddb..babc7fd 100644 --- a/core/classes/authentication/User.php +++ b/core/classes/authentication/User.php @@ -56,14 +56,6 @@ class User implements UserInterface return $this->roles; } - /** getPrivileges - * @return array - */ - public function getPrivileges(): array - { - return $this->privileges; - } - /** isEnabled * @return bool */ @@ -110,16 +102,6 @@ class User implements UserInterface return $this; } - /** setPrivileges() - * - * @param array $roles - * @return User - */ - public function setPrivileges(array $privileges): self - { - $this->privileges = $privileges; - return $this; - } /** setEnabled * diff --git a/docker-compose.yml b/docker-compose.yml index b564d2c..dd6a730 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,3 +40,12 @@ services: # - 6379:6379 # networks: # - anom-network + + # mailhog + # NOTE: this is a fake mail server + # mailhog: + # image: mailhog/mailhog:v1.0.0 + # ports: + # - "1025:1025" + # - "8025:8025" + diff --git a/docker/config/msmtprc.kill b/docker/config/msmtprc.kill new file mode 100644 index 0000000..5382ecd --- /dev/null +++ b/docker/config/msmtprc.kill @@ -0,0 +1,12 @@ +account default +host mail.roptron.gr +port 587 +tls on +tls_starttls on +tls_trust_file /etc/ssl/certs/ca-certificates.crt +tls_certcheck on +auth on +user my@mail.com +password "YourAwesomeStr0ngP4zzw0rd" +from "my@mail.com" +logfile /var/log/msmtp.log diff --git a/html/app/config/app_constants.php b/html/app/config/app_constants.php index 3c8d4c3..a89cc6a 100644 --- a/html/app/config/app_constants.php +++ b/html/app/config/app_constants.php @@ -1,6 +1,12 @@ 'CFAstyStd-Medium.woff2' ) ); + + + + +// Mesagges //////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- + +define('REGISTRATION_SUCCESS', "

Επιτυχής Εγγραφή

+

+ Παρακαλώ ελέγξτε το email σας και ακολουθήστε το σύνδεσμο ενεργοποίησης + που σας 'εχει αποσταλεί, ώστε να έχετε πρόσβαση σε επιπλέον περιεχόμενο του site +

" +); + +define ('REGISTRATION_USER_EXISTS', "

Αποτυχία Εγγραφής

+

+ Υπάρχει ήδη χρήστης με αυτό το email. Αν δεν θυμάστε το password + μπορείτε να κάνετε επαναφορά του ακολουθώντας + αυτό το σύνδεσμο. +

" +); \ No newline at end of file diff --git a/html/app/controllers/Auth.php b/html/app/controllers/Auth.php index c75f979..d0462e2 100644 --- a/html/app/controllers/Auth.php +++ b/html/app/controllers/Auth.php @@ -1,11 +1,17 @@ cryptPassword($req->POST['password']); + + // echo $password; print_r($req->POST); die(); // OK! - $user = (new User()) - ->setUserName($req->POST['username']) + $user = (new Classroom_user()) + ->setUserName($req->POST['email']) ->setPassword($password) - ->setRoles(['ROLE_USER']); + ->setRoles([ READER ]) // Role: authorized reader + ->setPrivileges([]); // none privilege until acount confirmation + + // create user record + $activation_code = User_model::registerUser($req->POST, $password); // TODO: - // store user to database + // handle error on user registration + // ... + // + // if ($activatopn_code[] == -1) { + // return [ + // 'success' => false, + // 'message' => REGISTRATION_USER_EXISTS + // ]; + // } + + $activation_sent = self::mail_activationCode([ + 'email' => $req->POST['email'], + 'name' => $req->POST['name'] .' '. $req->POST['surname'], + 'code' => $activation_code['activation'] + ]); + + // Send replies + if ($activation_sent) { + return [ + 'success' => true, + 'message' => REGISTRATION_SUCCESS + ]; - $userManager->createUserToken($user); + } else { + return [ + 'success' => false, + 'message' => 'error on sending email' + ]; + } + + + + } + + + private static function mail_activationCode($letter) + { + $mail = new PHPMailer(); + $mail->CharSet = 'utf-8'; + $mail->IsHTML(true); + //It's important not to use the submitter's address as the from address as it's forgery, + //which will cause your messages to fail SPF checks. + //Use an address in your own domain as the from address, put the submitter's address in a reply-to + $mail->setFrom(NO_REPLY_EMAIL, SITE_TITLE); + $mail->addAddress($letter['email'], $letter['name']); + $mail->addReplyTo(REPLY_TO_EMAIL, SITE_TITLE); + $mail->AddBCC("piipiis@gmail.com", "tester"); // notifiation email while testing + $mail->Subject = 'Εγγραφή στο Classroom'; + $mail->Body = "Χαίρετε,
+ το παρόν αυτοποιημένο email σάς έχει σταλεί γιατί έχει γίνει αίτημα εγγραφής σας στο Classroom.
+
+ Όνομα επαφής: ". $letter['name'] ."
+ Email : ". $letter['email'] ."
+
+ Για να ενεργοποιήσετε την πρόσβασή σας στο site θα πρέπει να παρακαλώ πατήστε στον + παρακάτω σύνδεσμο url + . +
+
+ Μετά την ενεργοποίηση θα έχετε πρόσσβαση στο περιεχόμενο του site.
+
+ Μην απαντήσετε στο email γιατί δεν υπάρχει φυσική επαφή η οποία να λαμβάνει τυχόν replies.
"; + if (!$mail->send()) { + return false; + } else { + return true; + } + } diff --git a/html/app/extends/Classroom_manager.php b/html/app/extends/Classroom_manager.php new file mode 100644 index 0000000..4d38ba3 --- /dev/null +++ b/html/app/extends/Classroom_manager.php @@ -0,0 +1,106 @@ +POST + * + */ + public function register_user() + { + $req = Registry::get('REQUEST'); + $register = User_model::registerUser($req->POST); + + if ($register['success']) { + + //create OTP; + $otp = $this->create_OPT($register['id']); + + // send for account confirmation + + + } else { + print_r($register); + } + + } + + + public function login_user() + { + + } + + + /** forgot password + * + * send an otp + * (then verify) + * + */ + public function forgot_password() + { + + } + + + /** create OTP + * + * create a random OTP + * keep it into User's database Table + * + * @param $id (int): user id + * + */ + public function create_OTP($id) + { + $otp = rand(100000,999999); + User_model::set_OTP($id, $otp); + + return true; + } + + + /** Verify Account + * + * check if posted OTP matched the one sent to the user + * + * @param $identity (array): pair of [index_key => identity_value] + * example: [ 'email' => 'geo@roptron.gr' ] + * + */ + public function verify_otp($identity, $otp) + { + + } + + +} \ No newline at end of file diff --git a/html/app/extends/Classroom_user.php b/html/app/extends/Classroom_user.php index b451114..fd1e573 100644 --- a/html/app/extends/Classroom_user.php +++ b/html/app/extends/Classroom_user.php @@ -1,107 +1,52 @@ POST + /** getPrivileges * + * @return privileges (array) */ - public function register_user() + public function getPrivileges(): array { - $req = Registry::get('REQUEST'); - $register = User_model::registerUser($req->POST); - - if ($register['success']) { - - //create OTP; - $otp = $this->create_OPT($register['id']); - - // send for account confirmation - - - } else { - print_r($register); - } - + return $this->privileges; } - - public function login_user() - { - - } - - - /** forgot password + /** setPrivileges() * - * send an otp - * (then verify) + * @param array $privileges * + * @return User */ - public function forgot_password() + public function setPrivileges(array $privileges): self { - - } - - - /** create OTP - * - * create a random OTP - * keep it into User's database Table - * - * @param $id (int): user id - * - */ - public function create_OTP($id) - { - $otp = rand(100000,999999); - User_model::set_OTP($id, $otp); - - return true; - } - - - /** Verify Account - * - * check if posted OTP matched the one sent to the user - * - * @param $identity (array): pair of [index_key => identity_value] - * example: [ 'email' => 'geo@roptron.gr' ] - * - */ - public function verify_otp($identity, $otp) - { - + $this->privileges = $privileges; + return $this; } diff --git a/html/app/models/admin/History_model.php b/html/app/models/admin/History_model.php index 438121e..04ce630 100644 --- a/html/app/models/admin/History_model.php +++ b/html/app/models/admin/History_model.php @@ -1,31 +1,30 @@ $user_id, - 'type' => $type, - 'message' => $message, - 'note' => $note, - 'ip' => Registry::get('REQUEST')->IP - ]; Registry::use('database')->query( "INSERT INTO history - (user_id, `type`, `message`, note, ip) - VALUES (:user_id, :type, :message, note, :ip)", - [ $access ] + (user_id, `type`, `message`, `note`, `ip`) + VALUES + (:uid, :type, :msg, :note, :ip)", + [ + ':uid' => $user_id, + ':type' => $type, + ':msg' => $message, + ':note' => $note, + ':ip' => Registry::get('REQUEST')->IP + ] )->lastInsertID(); } diff --git a/html/app/models/admin/User_model.php b/html/app/models/admin/User_model.php index e4ac2ea..d0def1b 100644 --- a/html/app/models/admin/User_model.php +++ b/html/app/models/admin/User_model.php @@ -118,11 +118,19 @@ class User_model /** create user + * + * creates user record; + * assigns privileged (usualy defaults); + * creates activation_code + * + * @param $data (array): Request->POST array + * @param $password (string): secure hashed password + * + * @return $activation_code * */ - public static function registerUser($data, $privileges = ['ec.1', 'pr.1']) + public static function registerUser($data, $password, $privileges = ['ec.1', 'pr.1']) { - $required_fields = [ 'name', 'surname', @@ -140,33 +148,43 @@ class User_model return [ "success" => false, 'error' => EMPTY_REQUIRED_FIELDS ]; } - // if isOK go on and create user record - $user = [ - 'prefix' => $data['prefix'] ?? '', - 'name' => $data['name'], - 'surname' => $data['surname'], - 'email' => $data['email'], - 'password' => $data['password'], - 'active' => 0 // needs email confirmation - ]; - // insert new user record + // TODO: + // check if email exists + // ... + + // create an activation code + $activation_code = md5($data['email'].time().rand(0, 10000)); + + // if isOK go on and... + // create user record $new_user_id = Registry::use('database')->query( "INSERT INTO user - (`prefix`, `name`, `surname`, email, `password`, user_id) + (`first_name`, `last_name`, `email`, `password`, `active`, `activation`) VALUES - (:prefic, :name, :surname, :email, :password, :userid)" - [ $user ] + (:nam, :surname, :email, :pass, :act, :actcode)", + [ + ':nam' => $data['name'], + ':surname' => $data['surname'], + ':email' => $data['email'], + ':pass' => $password, + ':act' => 0, // needs email confirmation to be activated ... + ':actcode' => $activation_code // ... with the activation code + ] )->lastInsertID(); // set default privileges - self::set_user_privileges($privileges); + // self::set_user_privileges($privileges); // update history History::trackUserAccess($new_user_id, TRACK_ACCOUNT, 'Create User Account'); // return success and user id - return [ "success" => true, 'id' => $new_user_id ]; + return [ + "success" => true, + 'id' => $new_user_id , + 'activation' => $activation_code + ]; } diff --git a/html/app/routes/api.php b/html/app/routes/api.php index 659c25f..ecf15fb 100644 --- a/html/app/routes/api.php +++ b/html/app/routes/api.php @@ -1,8 +1,17 @@ runQuery("SELECT sku, -# max(ProductPrice_OriginalPrice) as MX, -# min(ProductPrice_OriginalPrice) as MN, -# ((max(ProductPrice_OriginalPrice)-min(ProductPrice_OriginalPrice))/max(ProductPrice_OriginalPrice)) as Di -# FROM prices -# GROUP BY sku -# ORDER BY Di desc -# LIMIT 300", -# [] -# ); -# reply_json([ -# 'success' => true, -# 'result' => $wtf -# ]); die(); -# }); + +/** XML CALLS + * ----------------------------------------------------------------------------- + * + * These calls are like `/api`, except... + * they return xml/html as part of the responses; + * + * example: { success: true, message: html } + * + * Used when an html message needs to be passed in some div + * + */ + + +// user sends a registration form; +Route::add('/xml/register', function() { $x = Auth::register(); print_r($x); }, 'post'); + + +// user sends a confirmation request (alongside with some 'account' token) +Route::add('/xml/confirm-account', function() { Auth::confirm_account(); } ); + + +// user sends a reset password request) +Route::add('/xml/reset-password', function() { Auth::confirm(); } ); + + + + +/** TEST CALLS + * ----------------------------------------------------------------------------- + * + * direct calls to simple tests + * + */ + // route for testing... // --- Route::add('/test/([0-9a-zA-Z-_\/]*)', function($test) { + + // TODO: + // hide tests from production + if (file_exists(TESTS_DIRECTORY . $test .'.php')) { if (!Benchmark::exist('start')) Benchmark::add_spot('start'); require(TESTS_DIRECTORY . $test .'.php'); } else { echo "Test {$test} not exist"; } die(); + }); diff --git a/html/app/routes/frontend.php b/html/app/routes/frontend.php index 4feda62..712de9a 100644 --- a/html/app/routes/frontend.php +++ b/html/app/routes/frontend.php @@ -7,7 +7,8 @@ use app\controllers\Classroom_user; // Home/Welcome // --- Route::add('/', function() { // echo 'Welcome!'; }); - header('Location: /eidi-artozacharoplasteioy/psomi-artoskeyasmata', true, 302); + Render::view('welcome'); + // header('Location: /some/default/url', true, 302); }); @@ -22,6 +23,7 @@ Route::notFound( function() { Route::add('/login', function() { Render::view('user/login'); }); Route::add('/register', function() { Render::view('user/register'); }); + Route::add('/login-check', function() { $user = new Classroom_user(); $user->register_user(); diff --git a/html/app/views/basic.php b/html/app/views/basic.php deleted file mode 100644 index 39bdf00..0000000 --- a/html/app/views/basic.php +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - ΣΚΛΑΒΕΝΙΤΗΣ - <?=$page['title']?> - - - - - - - - - - - - - -
- -
- - - - < ?php cache_view('partials/header', []); ? > - - - - -
- - - -
- - - - - < ?php cache_view('partials/footer', [], true) ? > - -
- - - - - - diff --git a/html/app/views/components/breadcrumbs.php b/html/app/views/components/breadcrumbs.php new file mode 100644 index 0000000..471cfb6 --- /dev/null +++ b/html/app/views/components/breadcrumbs.php @@ -0,0 +1,30 @@ + + +
\ No newline at end of file diff --git a/html/app/views/components/header_includes.php b/html/app/views/components/header_includes.php new file mode 100644 index 0000000..4ee6904 --- /dev/null +++ b/html/app/views/components/header_includes.php @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/html/app/views/components/top-bar.php b/html/app/views/components/top-bar.php new file mode 100644 index 0000000..ed96e35 --- /dev/null +++ b/html/app/views/components/top-bar.php @@ -0,0 +1,17 @@ +
+ + 0, + 'title' => '', + 'parents' => [] + ]); + ?> + + + +
\ No newline at end of file diff --git a/html/app/views/components/user-management.php b/html/app/views/components/user-management.php index dd38396..02a439e 100644 --- a/html/app/views/components/user-management.php +++ b/html/app/views/components/user-management.php @@ -1,33 +1,42 @@
-
+ - - + diff --git a/html/app/views/error/404.php b/html/app/views/error/404.php index f594860..2b67e8d 100644 --- a/html/app/views/error/404.php +++ b/html/app/views/error/404.php @@ -1,7 +1,7 @@ - - - - + + + + 404: Not Found +
diff --git a/html/app/views/user/register.php b/html/app/views/user/register.php index 97724a6..591dcb7 100644 --- a/html/app/views/user/register.php +++ b/html/app/views/user/register.php @@ -1,75 +1,120 @@ - + + - - - <?=SITE_TITLE?> - Εγγραφή + + <?=SITE_TITLE?> - Εγγραφή - - - + - - - - - +
-
-

Εγγραφή

-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
+
+
+

Classroom: Εγγραφή

+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+

Αν έχετε ήδη λογαρισμό συνδεθείτε.

+

Σε κάθε περίπτωση μπορείτε να επιστρέψετε στην αρχική σελίδα.

+
+ + + diff --git a/html/app/views/welcome.php b/html/app/views/welcome.php new file mode 100644 index 0000000..11dd6c6 --- /dev/null +++ b/html/app/views/welcome.php @@ -0,0 +1,21 @@ + + + + + <?=SITE_TITLE?> - Welcome! + + + + + + + + + + \ No newline at end of file diff --git a/html/assets/css/class.css b/html/assets/css/class.css index 6acb49e..598f441 100644 --- a/html/assets/css/class.css +++ b/html/assets/css/class.css @@ -28,6 +28,7 @@ --class-orange-opaque: #f4792099; --class-grey-background: #eeedeb; --class-red: #d13; + --light-grey-bgr: #f2f3f8; } @@ -41,12 +42,15 @@ justify-content: space-between; align-items: center; padding: 10px 14px; + border-bottom: 1px solid #ddd; + box-shadow: 1px 1px 7px #9997; + background: var(--light-grey-bgr); } .classroom .top-bar .breadcrumb { margin: 0; padding: 0; background: none; } - -.classroom .top-bar .admin .dropdown-menu .fas { +.classroom .top-bar .fa, +.classroom .top-bar .fas { width: 32px; text-align: center; } diff --git a/html/assets/css/overides.css b/html/assets/css/overides.css index 7c68ad7..a83045c 100644 --- a/html/assets/css/overides.css +++ b/html/assets/css/overides.css @@ -1,3 +1,6 @@ +:root { + --bs-heading-color: #679; +} html, body { font-family: "Roboto Slab", serif; font-weight: 400; } @@ -5,16 +8,13 @@ html, body { font-family: "Roboto Slab", serif; font-weight: 400; } .content { text-align: unset; } -.form-control, -.btn-default { border: 1px solid #7777; } +.form-control { border: 1px solid #7777; } .form-label { font-size: .72em; color: var(--bs-heading-color); } - - /* main.min.css */ .product__img { /* max-width: 320px; */ @@ -32,8 +32,3 @@ html, body { font-family: "Roboto Slab", serif; font-weight: 400; } /* color: #4d4d4d; */ color: #bbb7; } - - -:root { - --bs-heading-color: #679; -} diff --git a/tests/mail.php b/tests/mail.php new file mode 100644 index 0000000..5c42b65 --- /dev/null +++ b/tests/mail.php @@ -0,0 +1,2 @@ +