From 63f714d5a78b765c117ebf6bbdfdbd748dd45644 Mon Sep 17 00:00:00 2001 From: George Halkiadakis Date: Thu, 4 May 2023 03:02:01 +0300 Subject: authentication; invitation; activation; base form setup --- public/app/controllers/Auth.php | 280 ++++++++-------------- public/app/controllers/Cms.php | 259 --------------------- public/app/controllers/JsonToForm.php | 89 +++++-- public/app/controllers/Office.php | 424 ++++++++++++++++++++++++++++++++++ 4 files changed, 593 insertions(+), 459 deletions(-) delete mode 100644 public/app/controllers/Cms.php create mode 100644 public/app/controllers/Office.php (limited to 'public/app/controllers') diff --git a/public/app/controllers/Auth.php b/public/app/controllers/Auth.php index 4b3dcfb..118b4d0 100644 --- a/public/app/controllers/Auth.php +++ b/public/app/controllers/Auth.php @@ -5,8 +5,9 @@ use Registry; use Render; // user classes and models -use app\extends\Classroom_user; -use app\extends\Classroom_manager; +use app\controllers\JsonToForm; +use app\extends\App_user; +use app\extends\App_manager; use app\models\Access_model; use app\extends\SendMail_service; @@ -37,50 +38,40 @@ class Auth { // user is valid; check user password // create a user object - $user = (new Classroom_user()) + $user = (new App_user()) ->setID($record['id']) + ->setSex( ($record['prefix'] == 'η') ? 2 : 1 ) ->setUserName($record['email']) ->setName($record['first_name'] .' '. $record['last_name']) ->setPassword($record['password']) + ->setRoles([ $record['role_id'] ]) // roles is an array ->setEnabled($record['active']); + // let user manager to validate user credentials - $userManager = new Classroom_manager(); + $userManager = new App_manager(); if ($userManager->isPasswordValid($user, $req->POST['password'])) { - // get user's security attributes - $attributes = Access_model::getUser($record['id']); - $roles = json_decode($attributes['Roles_json']); - $user - ->setRoles($roles) - ->setPrivileges( - array_merge( - json_decode($attributes['RootPrivileges_json']), - self::merge_lists_array( - json_decode($attributes['SubPrivileges_json']) - ) - ) - ); - // regeneration session ID (prevent session fixation) + session_unset(); // unset $_SESSION variable for the run-time + session_destroy(); // destroy session data in storage before continue + session_start(); session_regenerate_id(); + // set cookie for connected user setcookie( - 'cluser', + CONNECTION_COOKIE, 'connected;'. $user->getName(), - time()+60*60*8, // 8 hours + time()+60*60*10, // 10 hours '/' ); - - // check if admin (and redirect differently) - $is_admin = (!empty(array_intersect([1,2,3], $roles))); - + // login OK, set Token in session $userManager->createUserToken($user); return [ 'success' => true, - 'goto' => $is_admin ? '/admin/lessons' : '/user/profile', + 'goto' => '/admin/panel' ]; } else { @@ -89,33 +80,27 @@ class Auth { } - /** - * merges an array of lists to one list - */ - private static function merge_lists_array( $list ) - { - $current = []; - foreach($list as $sublist) { - $current = array_merge($current, $sublist); - } - return $current; - } - - /** activate - * resolves a call like: /account/activate?ticket=ca42d68cfba5fbbafeacc010b8e3a551 + * resolves call POST:/account/activate + * + * @param void : all parametres passed via request->POST */ public static function activate() { $req = Registry::get('REQUEST'); + // print_r($req->POST); die(); - // get the record of the target user - $check = Access_model::activate($req->GET['ticket']); + // create a salted password hash + $userManager = new App_manager(); + $password = $userManager->cryptPassword($req->POST['password']); - if ($check == true) { + // acivate target user and set password + $check = Access_model::activate_set_password($req->POST, $password); + + if ($check != 0) { Render::view('/error/general', [ 'title' => ACCOUNT_ACTIVATED_TITLE, - 'message' => ACCOUNT_ACTIVATED_MESSAGE + 'message' => ACCOUNT_ACTIVATED_MESSAGE . '
(msg code: '. $check .')' ]); } else { @@ -127,64 +112,72 @@ class Auth { } - /** register + + /** invitation + * + * setups and renders the form for a certain invitation + * + * NOTE: + * after form is submited, client calls Auth::activate() * - * Method for new user registration + * @param $id (string|MD5) : invitation code * */ - public static function register() + public static function invitation($id) { - $userManager = new Classroom_manager(); - $req = Registry::get('REQUEST'); + // get user from invitation number + $user_array = Registry::use('database')->query( + "SELECT * FROM user WHERE invitation = :id", + ['id' => $id] + )->getFirst(); + if ($user_array == false) { + Render::view('error/404', ['moto' => 'Δεν βρέθηκε η πρόσκληση']); + die(); + } + $user = json_decode( json_encode($user_array, JSON_UNESCAPED_UNICODE)); // user in json format - // create a salted password hash - $password = $userManager->cryptPassword($req->POST['password']); + // format form_setup to a json array + $form_setup = json_decode(json_encode(REGISTRATION_FORM, JSON_UNESCAPED_UNICODE)); - // echo $password; print_r($req->POST); die(); // OK! - - $user = (new Classroom_user()) - ->setUserName($req->POST['email']) - ->setName($req->POST['name'] .' '. $req->POST['surname']) - ->setPassword($password) - ->setRoles([ READER ]) // Role: authorized reader - ->setPrivileges([]); // none privilege until acount confirmation - - // create user record - $activation_code = Access_model::registerUser($req->POST, $password); - - // TODO: - // handle error on user registration - // ... - // - // if ($activatopn_code[] == -1) { - // return [ - // 'success' => false, - // 'message' => REGISTRATION_USER_EXISTS - // ]; - // } - - $send_mail = SendMail_service::send_activation_code([ - 'email' => $req->POST['email'], - 'name' => $req->POST['name'] .' '. $req->POST['surname'], - 'code' => $activation_code['activation'] - ]); + // get $key of position item inside the form_setup->form array + for($i=0; $i < sizeof($form_setup->form) ; $i++) { + if ($form_setup->form[$i]->name == 'position') { $key = $i; } + } - // Send replies - if ($send_mail) { - return [ - 'success' => true, - 'message' => REGISTRATION_SUCCESS + // prepare gendered options to position field + $positions = ($user->prefix != 'η') + ? [ + 'Διευθυντής', + 'Υποδιευθυντής', + 'Μόνιμος Καθηγητής', + 'Αναπληρωτής Καθηγητής' + ] + : [ + 'Διευθύντρια', + 'Υποδιευθύντρια', + 'Μόνιμη Καθηγήτρια', + 'Αναπληρώτρια Καθηγήτρια' ]; + $gendered_position = json_decode( json_encode( $positions, JSON_UNESCAPED_UNICODE )); - } else { - return [ - 'success' => false, - 'message' => 'error on sending email' - ]; - } + // attach gendered options + $form_setup->form[$key]->options = $gendered_position; + + // attach default values + $form_setup->defaults->values = $user; + + // get Form's HTML and Jsvascript + $form = JsonToForm::json_form($form_setup, [ + // pass invitation identity for security + ['name' => 'id', 'value' => $user->id], + ['name' => 'invitation', 'value' => $user->invitation] + + ]); + Render::view('user/invitation', ['form' => $form]); } + /** is_connected * checks if the user is connected * @@ -192,7 +185,7 @@ class Auth { */ public static function is_connected() { - $manager = new Classroom_manager(); + $manager = new App_manager(); if ($manager->hasUserToken()) { // user is connected; @@ -215,16 +208,19 @@ class Auth { */ public static function logout() { - $userManager = new Classroom_manager(); + $userManager = new App_manager(); $userManager->logout(); // regeneration session ID (prevent session fixation) + session_unset(); // unset $_SESSION variable for the run-time + session_destroy(); // destroy session data in storage before continue + session_start(); session_regenerate_id(); // remove user-conected cookie - if (isset($_COOKIE['cluser'])) { - unset($_COOKIE['cluser']); - setcookie('cluser', '', -1, '/'); + if (isset($_COOKIE[CONNECTION_COOKIE])) { + unset($_COOKIE[CONNECTION_COOKIE]); + setcookie(CONNECTION_COOKIE, '', -1, '/'); return true; } else { @@ -233,97 +229,27 @@ class Auth { } - - /** hasPermition( PERMIT ) - * - * checks if the user owns the specified permition - * to access the source + /** TODO: * */ - public static function hasPermition($permit = [0]) + public static function forgot_password() { - if (in_array(0, $permit)) { // permision 0 means public - return true; // permision 0 is always granted - } - - if ($user = self::is_connected() === false) { // if not connected - return false; // then no other permition is granted - } - - if ($user instanceof UserInterface) { - return ( !empty( array_intersect($permit, $user->getPrivileges()) ) ); - } } - /** isAuthenticated() - * - * chechs if the user's roles and permitions - * satisfy the specified requirements - * to access the source - * - * @param $requirements (array of rules-array) - * - * example: - * [ - * [ - * role => [2, 3] - * permition => ['10', '12', '18'] - * ], - * [ - * role => [1 , 4] - * ], - * [ - * permition => [ 3 ] - * ] - * ] - * - * defines (and parses to) a requirements rule of: - * [ - * user should be creator or editor - * _AND_ have permition 10 or 12 or 18 - * ] - * OR - * [ - * user should be an administrator or developer - * ] - * OR - * [ - * user should have permition #3 - * ] - * + /** TODO: * */ - public static function isAuthorized($requirements) - { - $authorized = false; - foreach($requirements as $required) { - - if (isset($required['role'])) { // if a role is required - if ( (self::isGranted($required['role'])) // authorize both role - && (self::hasPermition($required['permition'] ?? [ 0 ])) ) { // and permition - // $authorized = true; - return true; - } - - } else { // else, if not is not required - if (self::hasPermition($required['permition'] ?? [ 0 ])) { // authorize permition - // $authorized = true; - return true; - } - } - } - return $authorized; - } - - - public static function forgot_pass() + public static function validate_otp() { } - public static function validate_otp() + /** TODO: + * + */ + public static function reset_password() { } @@ -339,7 +265,7 @@ class Auth { */ public static function allowRoles($allowed) { - $manager = new Classroom_manager(); + $manager = new App_manager(); if ($manager->isGranted($allowed)) { // if valid, return true (continue) return true; @@ -361,21 +287,17 @@ class Auth { */ public static function hasValidRole($allowed) { - $manager = new Classroom_manager(); + $manager = new App_manager(); return ($manager->isGranted($allowed)); } public static function in_admin_group() { - $manager = new Classroom_manager(); + $manager = new App_manager(); return ($manager->isGranted([1, 2, 3])); } } - - -// NOTE: -// check: https://netcorecloud.com/tutorials/send-an-email-via-gmail-smtp-server-using-php/ \ No newline at end of file diff --git a/public/app/controllers/Cms.php b/public/app/controllers/Cms.php deleted file mode 100644 index 306bc88..0000000 --- a/public/app/controllers/Cms.php +++ /dev/null @@ -1,259 +0,0 @@ -GET['type']; // get media-type - $real_path = MEDIA_STORAGE_ROOT . $file_path; // construct real path - - if (!file_exists($real_path)) { - Render::view('error/404'); - - } else { - Render::file($real_path, $media_type); - } - - } else { - Render::view('error/404', [ - 'error_code' => 403, - 'moto' => 'Forbidden', - 'message' => '' - ]); - } - } - - - /** get_file_attributes - * - * returns attributes of a file - * (medias are proxied for speed optimization) - * - * @param $path (string) : file path - * @return $file attributes --or-- false - */ - private static function get_file_attributes($path) - { - $medias = Cache_service::files_attributes(); - - foreach($medias as $key => $medi) { - - if ($medi['path'] == $path) { - - return $medi; - } - } - - return false; - } - - - - ## COURSES - ## ------------------------------------------------------------------------- - - - /** course - * prepare and render the course view - * - * @param $id : course id - */ - public static function course($id) - { - $id = intval($id); // course id - - // collect all data needed for course view - // --- -- -- - - - - $cache = Cache_service::courses_struct(); - $tree = $cache['tree']; - $breadcrumbs = $cache['breadcrumbs']; - $lessons = Cms_model::lessons_of_course(intval($id)); - - // find parent_ids of current category (to open the menu tree) - // --- - $course_path = []; - foreach($breadcrumbs[$id]['parents'] as $key => $parent) { - $course_path[] = intval($parent['id']); - } - $course_path[] = $id; - - // then Render - // --- -- -- - - - - Render::view('templates/course', [ - 'id' => $id, - 'title' => $breadcrumbs[ $id ]['rec']['label'], - 'categories' => $tree, - 'breadcrumbs' => $breadcrumbs, - 'lessons' => $lessons, - 'course_path' => $course_path, - // needed by footer - 'entity' => Cache_service::pages_list() - ]); - } - - - - ## LESSONS - ## ------------------------------------------------------------------------- - - - - /** lesson - * - * check if user is authorized to view the content; - * if so, prepare and render the lesson view - * - * @param $id : lesson id - */ - public static function lesson($id) - { - $id = intval($id); // lesson id - - $lesson = Cms_model::lesson($id); // get lesson - $course_id = $lesson['course_id']; // get course id - $pri_id = $lesson['privilege_id']; // privilege needed - - - if (self::is_admin_or_permited($pri_id)) { - - // collect all data needed for course view - // --- -- -- - - - - $cache = Cache_service::courses_struct(); - $tree = $cache['tree']; - $breadcrumbs = $cache['breadcrumbs']; - - // find parent_ids of current category (to open the menu tree) - // --- - $course_path = []; - foreach($breadcrumbs[$course_id]['parents'] as $key => $parent) { - $course_path[] = intval($parent['id']); - } - $course_path[] = intval($course_id); - - // NOTE: CRITICAL: - // do not pass Class::method directly to the eported variables - // $entity = Cache_service::pages_list(); - - // var_dump($entity); die(); - - // then Render - // --- -- -- - - - - Render::view('templates/lesson', [ - // main content - 'id' => $id, - 'course_id' => $course_id, - 'title' => $lesson['title'], - 'lesson' => $lesson, - // needed for side panel and breadcrunbs - 'categories' => $tree, - 'breadcrumbs' => $breadcrumbs, - 'course_path' => $course_path, - // needed by footer - 'entity' => Cache_service::pages_list() - ]); - - } else { // user is not authorized - Render::view('error/general', [ - 'title' => 'Δεν έχετε πρόσβαση', - 'message' => 'Θα πρέπει να αγοράσετε το πακέτο πρόσβασης ' - . $pri_id - .' για να δείτε το περιεχόμενο! -

- Αγόρασε τώρα το ' - ]); - } - - } - - - ## PAGES - ## ------------------------------------------------------------------------- - - - /** lesson - * - * check if user is authorized to view the content; - * if so, prepare and render the lesson view - * - * @param $id : lesson id - */ - public static function page($id) - { - $id = intval($id); // lesson id - - $pages = Cache_service::pages_list(); - $page = $pages[$id]; - - // then Render - // --- -- -- - - - - Render::view('templates/page', [ - // main content - 'id' => $id, - 'page' => $page, - // needed by footer - 'entity' => $pages - ]); - - } - -} diff --git a/public/app/controllers/JsonToForm.php b/public/app/controllers/JsonToForm.php index 1b05af7..5df650d 100644 --- a/public/app/controllers/JsonToForm.php +++ b/public/app/controllers/JsonToForm.php @@ -1,4 +1,5 @@ $len) ? mb_substr($str, 0, $len-1) ."…" : $str; } - - public static function make_a_form($form) + /** json_form + * + * construct a form (html, javasctipt) + * from a json designer + * + */ + public static function json_form($formJson, $require_identity = []) { - $formJson = json_decode(json_encode($form)); + // $formJson = json_decode(json_encode($form)); - $default_outer = $form->defaults->outer_class ?? ''; - $default_inner = $form->defaults->inner_class ?? ''; - $default_type = $form->defaults->type ?? 'text'; + $default_outer = $formJson->defaults->outer_class ?? ''; + $default_inner = $formJson->defaults->inner_class ?? ''; + $default_type = $formJson->defaults->type ?? 'text'; + $default_values = $formJson->defaults->values ?? ((object) ['nothing' => true]); // TODO handle $form->defaults->source; @@ -180,8 +187,15 @@ class JsonToForm $initSelectsJS = ""; // js to initialize select fields (single or multiple) + $identity = ""; // construct required identity hidden fields + foreach ($require_identity as $group) { + $identity .= " + "; + } - $html = " + // now constuct the form body + $html = "{$identity}
"; @@ -191,15 +205,22 @@ class JsonToForm $type = $field->type ?? $default_type; $class = $field->class ?? ""; $label = $field->label; - $name = $field->name ?? "$field->label"; - - // TODO: auto value - + $name = $field->name ?? $field->label; + + // handle value assignment + if (isset($field->value)) { // if value property is set + $set_value = true; // (flag: value seted) + $value = (($field->value == 'auto') && isset($default_values->$name)) + ? $default_values->$name // set auto value + : $field->value; // or other set-value + } else { + $set_value = false; + } + // print_r([ $set_value, $name, ($default_values->$name ?? 'none') ]); + $appendKey = in_array('append-key', $attributes) ? true : false; // append key (for selects) - $required = in_array('required', $attributes) ? 'required' : ''; // required + $required = in_array('required', $attributes) ? '' : ''; // required $asterisk = in_array('required', $attributes) ? '*' : ''; // asterisk in label if required - - $html .= "
@@ -220,6 +241,15 @@ class JsonToForm var {$name} = $('#{$name}'); {$name}.select2({ width: '100%', placeholde: '{$label}' });"; + if ($set_value) { // if default value is set + $initSelectsJS .= " + select2_set_multi_text( {$name} , {$value} );"; + + } else { + $initSelectsJS .= " + {$name}.val(null).trigger('change');"; + } + // prepare check if empty field $checkFilledJS .= " if (getValues({$name}) =='') { empty += '{$label} {$asterisk}
'; } @@ -234,8 +264,16 @@ class JsonToForm // prepare initialization of select2 (single) $initSelectsJS .= " var {$name} = $('#{$name}'); - {$name}.select2({ }); - {$name}.val(null).trigger('change')"; + {$name}.select2();"; + + if ($set_value) { // if default value is set + $initSelectsJS .= " + select2_set_text( {$name} , '{$value}' );"; + + } else { + $initSelectsJS .= " + {$name}.val(null).trigger('change');"; + } // prepare check if empty field $checkFilledJS .= " @@ -259,8 +297,10 @@ class JsonToForm case 'text': + $def_value = ($set_value) ? "value='{$value}'" : "value=''"; + $html .= " - "; + "; $checkFilledJS = " if ($('input[name={$name}]').val() =='') { empty += '{$label} {$asterisk}
'; } @@ -271,6 +311,8 @@ class JsonToForm case 'password': + $def_value = ($set_value) ? "value='{$value}'" : "value=''"; + $html .= " "; @@ -283,8 +325,10 @@ class JsonToForm case 'integer': + $def_value = ($set_value) ? "value='{$value}'" : "value=''"; + $html .= " - "; + "; $checkFilledJS .= " if ($('input[name={$name}]').val() =='') { empty += '{$label} {$asterisk}
'; } @@ -295,8 +339,10 @@ class JsonToForm case 'textarea': + $def_value = ($set_value) ? "{$value}" : ""; + $html .= " - "; + "; $checkFilledJS .= " if ($('textarea[name={$name}]').val() =='') { empty += '{$label} {$asterisk}
'; } @@ -319,8 +365,10 @@ class JsonToForm case 'email': + $def_value = ($set_value) ? "value='{$value}'" : "value=''"; + $html .= " - "; + "; $checkFilledJS .= " if ($('input[name={$name}]').val() =='') { empty += '{$label} {$asterisk}
'; } @@ -332,7 +380,6 @@ class JsonToForm default: // label, acts as common text //$html .= "