/** * Delete this group from the database, along with any linked user and authorization rules * * @see DatabaseInterface */ public function delete() { // Can only delete an object where `id` is set if (!$this->_id) { return false; } $result = parent::delete(); // Get connection $db = static::connection(); $link_table = static::getTable('group_user')->name; $auth_table = static::getTable('authorize_group')->name; $sqlVars[":id"] = $this->_id; $query = "\n DELETE FROM `{$link_table}`\n WHERE group_id = :id"; $stmt = $db->prepare($query); $stmt->execute($sqlVars); $query = "\n DELETE FROM `{$auth_table}`\n WHERE group_id = :id"; $stmt = $db->prepare($query); $stmt->execute($sqlVars); // Reassign any primary users to the current default primary group $default_primary_group = GroupLoader::fetch(GROUP_DEFAULT_PRIMARY, 'is_default'); $user_table = static::getTable('user')->name; $query = "\n UPDATE `{$user_table}` \n SET primary_group_id = :primary_group_id\n WHERE primary_group_id = :current_id;"; $sqlVars = [":primary_group_id" => $default_primary_group->id, ":current_id" => $this->_id]; $stmt = $db->prepare($query); $stmt->execute($sqlVars); // TODO: assign user to the default primary group as well? return $result; }
/** * Processes the request to create a new user (from the admin controls). * * Processes the request from the user creation form, checking that: * 1. The username and email are not already in use; * 2. The logged-in user has the necessary permissions to update the posted field(s); * 3. The submitted data is valid. * This route requires authentication. * Request type: POST * @see formUserCreate */ public function createUser() { $post = $this->_app->request->post(); // Load the request schema $requestSchema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/user-create.json"); // Get the alert message stream $ms = $this->_app->alerts; // Access-controlled resource if (!$this->_app->user->checkAccess('create_account')) { $ms->addMessageTranslated("danger", "ACCESS_DENIED"); $this->_app->halt(403); } // Set up Fortress to process the request $rf = new \Fortress\HTTPRequestFortress($ms, $requestSchema, $post); // Sanitize data $rf->sanitize(); // Validate, and halt on validation errors. $error = !$rf->validate(true); // Get the filtered data $data = $rf->data(); // Remove csrf_token from object data $rf->removeFields(['csrf_token']); // Perform desired data transformations on required fields. Is this a feature we could add to Fortress? $data['display_name'] = trim($data['display_name']); $data['email'] = strtolower(trim($data['email'])); $data['flag_verified'] = 1; // Set password as empty on initial creation. We will then send email so new user can set it themselves via secret token $data['password'] = ""; // Check if username or email already exists if (UserLoader::exists($data['user_name'], 'user_name')) { $ms->addMessageTranslated("danger", "ACCOUNT_USERNAME_IN_USE", $data); $error = true; } if (UserLoader::exists($data['email'], 'email')) { $ms->addMessageTranslated("danger", "ACCOUNT_EMAIL_IN_USE", $data); $error = true; } // Halt on any validation errors if ($error) { $this->_app->halt(400); } // Get default primary group (is_default = GROUP_DEFAULT_PRIMARY) $primaryGroup = GroupLoader::fetch(GROUP_DEFAULT_PRIMARY, "is_default"); // Set default values if not specified or not authorized if (!isset($data['locale']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "locale"])) { $data['locale'] = $this->_app->site->default_locale; } if (!isset($data['title']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "title"])) { // Set default title for new users $data['title'] = $primaryGroup->new_user_title; } if (!isset($data['primary_group_id']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "primary_group_id"])) { $data['primary_group_id'] = $primaryGroup->id; } // Set groups to default groups if not specified or not authorized to set groups if (!isset($data['groups']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "groups"])) { $default_groups = GroupLoader::fetchAll(GROUP_DEFAULT, "is_default"); $data['groups'] = []; foreach ($default_groups as $group_id => $group) { $data['groups'][$group_id] = "1"; } } // Create the user $user = new User($data); // Add user to groups, including selected primary group $user->addGroup($data['primary_group_id']); foreach ($data['groups'] as $group_id => $is_member) { if ($is_member == "1") { $user->addGroup($group_id); } } // Create events - account creation and password reset $user->newEventSignUp($this->_app->user); $user->newEventPasswordReset(); // Save user again after creating events $user->save(); // Send an email to the user's email address to set up password $twig = $this->_app->view()->getEnvironment(); $template = $twig->loadTemplate("mail/password-create.twig"); $notification = new Notification($template); $notification->fromWebsite(); // Automatically sets sender and reply-to $notification->addEmailRecipient($user->email, $user->display_name, ['user' => $user, 'create_password_expiration' => $this->_app->site->create_password_expiration / 3600 . " hours"]); try { $notification->send(); } catch (\Exception\phpmailerException $e) { $ms->addMessageTranslated("danger", "MAIL_ERROR"); error_log('Mailer Error: ' . $e->errorMessage()); $this->_app->halt(500); } // Success message $ms->addMessageTranslated("success", "ACCOUNT_CREATION_COMPLETE", $data); }
public function register() { // POST: user_name, display_name, email, title, password, passwordc, captcha, spiderbro, csrf_token $post = $this->_app->request->post(); // Get the alert message stream $ms = $this->_app->alerts; // Check the honeypot. 'spiderbro' is not a real field, it is hidden on the main page and must be submitted with its default value for this to be processed. if (!$post['spiderbro'] || $post['spiderbro'] != "http://") { error_log("Possible spam received:" . print_r($this->_app->request->post(), true)); $ms->addMessage("danger", "Aww hellllls no!"); $this->_app->halt(500); // Don't let on about why the request failed ;-) } // Load the request schema $requestSchema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/register.json"); // Set up Fortress to process the request $rf = new \Fortress\HTTPRequestFortress($ms, $requestSchema, $post); // Security measure: do not allow registering new users until the master account has been created. if (!UserLoader::exists($this->_app->config('user_id_master'))) { $ms->addMessageTranslated("danger", "MASTER_ACCOUNT_NOT_EXISTS"); $this->_app->halt(403); } // Check if registration is currently enabled if (!$this->_app->site->can_register) { $ms->addMessageTranslated("danger", "ACCOUNT_REGISTRATION_DISABLED"); $this->_app->halt(403); } // Prevent the user from registering if he/she is already logged in if (!$this->_app->user->isGuest()) { $ms->addMessageTranslated("danger", "ACCOUNT_REGISTRATION_LOGOUT"); $this->_app->halt(200); } // Sanitize data $rf->sanitize(); // Validate, and halt on validation errors. $error = !$rf->validate(true); // Get the filtered data $data = $rf->data(); // Check captcha, if required if ($this->_app->site->enable_captcha == "1") { if (!$data['captcha'] || md5($data['captcha']) != $_SESSION['userfrosting']['captcha']) { $ms->addMessageTranslated("danger", "CAPTCHA_FAIL"); $error = true; } } // Remove captcha, password confirmation from object data $rf->removeFields(['captcha', 'passwordc']); // Perform desired data transformations. Is this a feature we could add to Fortress? $data['user_name'] = strtolower(trim($data['user_name'])); $data['display_name'] = trim($data['display_name']); $data['email'] = strtolower(trim($data['email'])); $data['locale'] = $this->_app->site->default_locale; if ($this->_app->site->require_activation) { $data['active'] = 0; } else { $data['active'] = 1; } // Check if username or email already exists if (UserLoader::exists($data['user_name'], 'user_name')) { $ms->addMessageTranslated("danger", "ACCOUNT_USERNAME_IN_USE", $data); $error = true; } if (UserLoader::exists($data['email'], 'email')) { $ms->addMessageTranslated("danger", "ACCOUNT_EMAIL_IN_USE", $data); $error = true; } // Halt on any validation errors if ($error) { $this->_app->halt(400); } // Get default primary group (is_default = GROUP_DEFAULT_PRIMARY) $primaryGroup = GroupLoader::fetch(GROUP_DEFAULT_PRIMARY, "is_default"); $data['primary_group_id'] = $primaryGroup->id; // Set default title for new users $data['title'] = $primaryGroup->new_user_title; // Hash password $data['password'] = Authentication::hashPassword($data['password']); // Create the user $user = new User($data); // Add user to default groups, including default primary group $defaultGroups = GroupLoader::fetchAll(GROUP_DEFAULT, "is_default"); $user->addGroup($primaryGroup->id); foreach ($defaultGroups as $group_id => $group) { $user->addGroup($group_id); } // Store new user to database $user->store(); if ($this->_app->site->require_activation) { // Create and send activation email $mail = new \PHPMailer(); $mail->From = $this->_app->site->admin_email; $mail->FromName = $this->_app->site->site_title; $mail->addAddress($user->email); // Add a recipient $mail->addReplyTo($this->_app->site->admin_email, $this->_app->site->site_title); $mail->Subject = $this->_app->site->site_title . " - please activate your account"; $mail->Body = $this->_app->view()->render("common/mail/activate-new.html", ["user" => $user]); $mail->isHTML(true); // Set email format to HTML if (!$mail->send()) { $ms->addMessageTranslated("danger", "MAIL_ERROR"); error_log('Mailer Error: ' . $mail->ErrorInfo); $this->_app->halt(500); } // Activation required $ms->addMessageTranslated("success", "ACCOUNT_REGISTRATION_COMPLETE_TYPE2"); } else { // No activation required $ms->addMessageTranslated("success", "ACCOUNT_REGISTRATION_COMPLETE_TYPE1"); } }
/** * Processes the request to create a new user (from the admin controls). * * Processes the request from the user creation form, checking that: * 1. The username and email are not already in use; * 2. The logged-in user has the necessary permissions to update the posted field(s); * 3. The submitted data is valid. * This route requires authentication. * Request type: POST * @see formUserCreate */ public function createUser() { $post = $this->_app->request->post(); // Load the request schema $requestSchema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/user-create.json"); // Get the alert message stream $ms = $this->_app->alerts; // Access-controlled resource if (!$this->_app->user->checkAccess('create_account')) { $ms->addMessageTranslated("danger", "ACCESS_DENIED"); $this->_app->halt(403); } // Set up Fortress to process the request $rf = new \Fortress\HTTPRequestFortress($ms, $requestSchema, $post); // Sanitize data $rf->sanitize(); // Validate, and halt on validation errors. $error = !$rf->validate(true); // Get the filtered data $data = $rf->data(); // Remove csrf_token, password confirmation from object data $rf->removeFields(['csrf_token, passwordc']); // Perform desired data transformations on required fields. Is this a feature we could add to Fortress? $data['user_name'] = strtolower(trim($data['user_name'])); $data['display_name'] = trim($data['display_name']); $data['email'] = strtolower(trim($data['email'])); $data['active'] = 1; // Check if username or email already exists if (UserLoader::exists($data['user_name'], 'user_name')) { $ms->addMessageTranslated("danger", "ACCOUNT_USERNAME_IN_USE", $data); $error = true; } if (UserLoader::exists($data['email'], 'email')) { $ms->addMessageTranslated("danger", "ACCOUNT_EMAIL_IN_USE", $data); $error = true; } // Halt on any validation errors if ($error) { $this->_app->halt(400); } // Get default primary group (is_default = GROUP_DEFAULT_PRIMARY) $primaryGroup = GroupLoader::fetch(GROUP_DEFAULT_PRIMARY, "is_default"); // Set default values if not specified or not authorized if (!isset($data['locale']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "locale"])) { $data['locale'] = $this->_app->site->default_locale; } if (!isset($data['title']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "title"])) { // Set default title for new users $data['title'] = $primaryGroup->new_user_title; } if (!isset($data['primary_group_id']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "primary_group_id"])) { $data['primary_group_id'] = $primaryGroup->id; } // Set groups to default groups if not specified or not authorized to set groups if (!isset($data['groups']) || !$this->_app->user->checkAccess("update_account_setting", ["property" => "groups"])) { $default_groups = GroupLoader::fetchAll(GROUP_DEFAULT, "is_default"); $data['groups'] = []; foreach ($default_groups as $group_id => $group) { $data['groups'][$group_id] = "1"; } } // Hash password $data['password'] = Authentication::hashPassword($data['password']); // Create the user $user = new User($data); // Add user to groups, including selected primary group $user->addGroup($data['primary_group_id']); foreach ($data['groups'] as $group_id => $is_member) { if ($is_member == "1") { $user->addGroup($group_id); } } // Store new user to database $user->store(); // Success message $ms->addMessageTranslated("success", "ACCOUNT_CREATION_COMPLETE", $data); }
public function addGroup($group_id) { // First, load current groups for user $this->getGroups(); // Return if user already in group if (isset($this->_groups[$group_id])) { return $this; } // Next, check that the requested group actually exists if (!GroupLoader::exists($group_id)) { throw new \Exception("The specified group_id ({$group_id}) does not exist."); } // Ok, add to the list of groups $this->_groups[$group_id] = GroupLoader::fetch($group_id); return $this; }
public function setupMasterAccount() { $post = $this->_app->request->post(); // Get the alert message stream $ms = $this->_app->alerts; // Check the honeypot. 'spiderbro' is not a real field, it is hidden on the main page and must be submitted with its default value for this to be processed. if (!$post['spiderbro'] || $post['spiderbro'] != "http://") { error_log("Possible spam received:" . print_r($this->_app->request->post(), true)); $ms->addMessage("danger", "Aww hellllls no!"); $this->_app->halt(500); // Don't let on about why the request failed ;-) } // Do not allow registering a master account if one has already been created if (UserLoader::exists($this->_app->config('user_id_master'))) { $ms->addMessageTranslated("danger", "MASTER_ACCOUNT_EXISTS"); $this->_app->halt(403); } // Check the configuration token if ($post['root_account_config_token'] != $this->_app->site->root_account_config_token) { $ms->addMessageTranslated("danger", "CONFIG_TOKEN_MISMATCH"); $this->_app->halt(403); } // Load the request schema $requestSchema = new \Fortress\RequestSchema($this->_app->config('schema.path') . "/forms/register.json"); // Set up Fortress to process the request $rf = new \Fortress\HTTPRequestFortress($ms, $requestSchema, $post); // Sanitize data $rf->sanitize(); // Validate, and halt on validation errors. $error = !$rf->validate(true); // Get the filtered data $data = $rf->data(); // Remove configuration token, password confirmation from object data $rf->removeFields(['root_account_config_token', 'passwordc']); // Perform desired data transformations. Is this a feature we could add to Fortress? $data['user_name'] = strtolower(trim($data['user_name'])); $data['display_name'] = trim($data['display_name']); $data['email'] = strtolower(trim($data['email'])); $data['active'] = 1; $data['locale'] = $this->_app->site->default_locale; // Halt on any validation errors if ($error) { $this->_app->halt(400); } // Get default primary group (is_default = GROUP_DEFAULT_PRIMARY) $primaryGroup = GroupLoader::fetch(GROUP_DEFAULT_PRIMARY, "is_default"); $data['primary_group_id'] = $primaryGroup->id; // Set default title for new users $data['title'] = $primaryGroup->new_user_title; // Hash password $data['password'] = Authentication::hashPassword($data['password']); // Create the user $user = new User($data, $this->_app->config('user_id_master')); // Add user to default groups, including default primary group $defaultGroups = GroupLoader::fetchAll(GROUP_DEFAULT, "is_default"); $user->addGroup($primaryGroup->id); foreach ($defaultGroups as $group_id => $group) { $user->addGroup($group_id); } // Store new user to database, forcing it to insert the new user $user->store(true); // No activation required $ms->addMessageTranslated("success", "ACCOUNT_REGISTRATION_COMPLETE_TYPE1"); // Update install status $this->_app->site->install_status = "new"; $this->_app->site->root_account_config_token = ""; $this->_app->site->store(); }
/** * Processes the request to delete an existing group. * * Deletes the specified group, removing associations with any users and any group-specific authorization rules. * Before doing so, checks that: * 1. The group is deleteable (as specified in the `can_delete` column in the database); * 2. The group is not currently set as the default primary group; * 3. The submitted data is valid. * This route requires authentication (and should generally be limited to admins or the root user). * Request type: POST * @param int $group_id the id of the group to delete. */ public function deleteGroup($group_id) { $post = $this->_app->request->post(); // Get the target group $group = GroupLoader::fetch($group_id); // Get the alert message stream $ms = $this->_app->alerts; // Check authorization if (!$this->_app->user->checkAccess('delete_group', ['group' => $group])) { $ms->addMessageTranslated("danger", "ACCESS_DENIED"); $this->_app->halt(403); } // Check that we are allowed to delete this group if ($group->can_delete == "0") { $ms->addMessageTranslated("danger", "CANNOT_DELETE_GROUP", ["name" => $group->name]); $this->_app->halt(403); } // Do not allow deletion if this group is currently set as the default primary group if ($group->is_default == GROUP_DEFAULT_PRIMARY) { $ms->addMessageTranslated("danger", "GROUP_CANNOT_DELETE_DEFAULT_PRIMARY", ["name" => $group->name]); $this->_app->halt(403); } $ms->addMessageTranslated("success", "GROUP_DELETION_SUCCESSFUL", ["name" => $group->name]); $group->delete(); // TODO: implement Group function unset($group); }