protected function body() { if (!$this->userHasPrivileges(User::groupsJoinPrivate, User::groupsJoinPublic, User::groupsRequest)) { return false; } if (!$this->isInputValid(array('id' => 'isIndex'))) { return false; } $groupId = $this->getParams('id'); /** * @var $group \Group */ $group = Repositories::findEntity(Repositories::Group, $groupId); // Calculate privileges of the user $user = User::instance(); $canJoinPrivate = User::instance()->hasPrivileges(User::groupsJoinPrivate); $groupIsPrivate = $group->getType() == \Group::TYPE_PRIVATE; $hasSufficientPrivileges = $groupIsPrivate && ($canJoinPrivate || $user->hasPrivileges(User::groupsRequest)) || !$groupIsPrivate && $user->hasPrivileges(User::groupsJoinPublic); if (!$hasSufficientPrivileges) { return $this->death(StringID::InsufficientPrivileges); } $status = $canJoinPrivate || !$groupIsPrivate ? \Subscription::STATUS_SUBSCRIBED : \Subscription::STATUS_REQUESTED; // Put into database $subscription = new \Subscription(); $subscription->setGroup($group); $subscription->setUser(User::instance()->getEntity()); $subscription->setStatus($status); Repositories::persistAndFlush($subscription); return true; }
protected function body() { if (!$this->isInputSet(array('email'))) { return false; } $email = $this->getParams('email'); $users = Repositories::getRepository(Repositories::User)->findBy(['email' => $email]); foreach ($users as $user) { /** * @var $user \User */ // Generate reset link. $resetLink = StringUtils::randomString(60); $now = new \DateTime(); $expiryDate = $now->add(new \DateInterval('P1D')); // Add in in the database (replacing any older reset links in the process) $user->setResetLink($resetLink); $user->setResetLinkExpiry($expiryDate); Repositories::persistAndFlush($user); // Send the e-mail $body = "A Password Reset Link was requested for your e-mail address on XMLCheck.\n\nYour name: " . $user->getRealName() . "\nYour login: "******"\n\nClick this link to reset your password: \n\n" . Config::get('roots', 'http') . "#resetPassword#" . $resetLink . "\n\nThe link will be valid for the next 24 hours, until " . $expiryDate->format("Y-m-d H:i:s") . "."; if (!Core::sendEmail($user->getEmail(), "[XMLCheck] Password Reset Link for '" . $user->getRealName() . "'", $body)) { return $this->death(StringID::MailError); } } $this->addOutput('count', count($users)); return true; }
protected function body() { if (!$this->isInputValid(['resetLink' => 'isNotEmpty', 'pass' => 'isNotEmpty'])) { return false; } $resetLink = $this->getParams('resetLink'); if (strlen($resetLink) < 1) { // We double-check here. This should not be necessary because the isInputValid function takes care of this. // However, if there is a bug in isInputValid that causes the check to be skipped, // this will allow the user to change the password of the first user with no resetLink active. // This could plausibly be the administrator. return $this->death(StringID::HackerError); } $encryptionType = Security::HASHTYPE_PHPASS; $newPassword = $this->getParams('pass'); $newPasswordHash = Security::hash($newPassword, $encryptionType); $usersWithThisResetLink = Repositories::getRepository(Repositories::User)->findBy(['resetLink' => $resetLink]); if (count($usersWithThisResetLink) !== 1) { return $this->death(StringID::ResetLinkDoesNotExist); } /** * @var $user \User */ $user = $usersWithThisResetLink[0]; if ($user->getResetLinkExpiry() < new \DateTime()) { return $this->death(StringID::ResetLinkExpired); } $user->setResetLink(''); $user->setPass($newPasswordHash); Repositories::persistAndFlush($user); return true; }
protected function body() { $inputs = array('name' => array('isNotEmpty'), 'description' => 'isNotEmpty'); if (!$this->isInputValid($inputs)) { return false; } $name = $this->getParams('name'); $description = $this->getParams('description'); $id = $this->getParams('id'); $isIdSet = $id !== null && $id !== ''; $user = User::instance(); $userId = $user->getId(); if (!$isIdSet) { if (!$this->userHasPrivileges(User::lecturesAdd)) { return false; } $lecture = new \Lecture(); $lecture->setName($name); $lecture->setDescription($description); $lecture->setOwner(User::instance()->getEntity()); Repositories::persistAndFlush($lecture); } else { if ($isIdSet) { $lecture = Repositories::findEntity(Repositories::Lecture, $id); if (!$user->hasPrivileges(User::lecturesManageAll) && (!$user->hasPrivileges(User::lecturesManageOwn) || $lecture->getOwner()->getId() != $userId)) { return $this->death(StringID::InsufficientPrivileges); } $lecture->setDescription($description); Repositories::persistAndFlush($lecture); } } return true; }
protected function body() { if (!$this->userHasPrivileges(User::pluginsAdd)) { return false; } $inputs = array('name' => array('isName', 'isNotEmpty')); if (!$this->isInputValid($inputs)) { return false; } $name = $this->getParams('name'); $existingPluginsWithSameName = Repositories::getRepository(Repositories::Plugin)->findBy(['name' => $name]); if (count($existingPluginsWithSameName) > 0) { return $this->death(StringID::PluginNameAlreadyExists); } $pluginFile = $this->getUploadedFile('plugin'); if (!$pluginFile) { return false; } $pluginFolder = Config::get('paths', 'plugins') . $name; if (file_exists($pluginFolder)) { return $this->death(StringID::PluginFolderAlreadyExists); } if (!Filesystem::createDir($pluginFolder)) { return $this->death(StringID::FileSystemError); } if (!Compression::unzip($pluginFile, $pluginFolder)) { $this->death(StringID::UnzipUnsuccessful); goto cleanup_error; } $manifestFile = $pluginFolder . DIRECTORY_SEPARATOR . 'manifest.xml'; $manifest = null; if (!($manifest = $this->parsePluginManifest($manifestFile))) { $this->death(StringID::BadlyFormedPlugin); goto cleanup_error; } if (!file_exists($pluginFolder . DIRECTORY_SEPARATOR . $manifest['mainFile'])) { $this->death(StringID::BadlyFormedPlugin); goto cleanup_error; } $plugin = new \Plugin(); $plugin->setIdentifier($manifest['identifier']); $plugin->setDescription($manifest['description']); $plugin->setConfig($manifest['arguments']); $plugin->setMainfile($name . '/' . $manifest['mainFile']); $plugin->setName($name); $plugin->setType($manifest['type']); Repositories::persistAndFlush($plugin); Filesystem::removeFile($pluginFile); return true; cleanup_error: Filesystem::removeDir($pluginFolder); Filesystem::removeFile($pluginFile); return false; }
protected function body() { $inputs = array('lecture' => 'isIndex', 'name' => array('isName', 'isNotEmpty'), 'type' => array('isEnum' => array('text', 'code', 'image'))); if (!$this->isInputValid($inputs)) { return false; } $lectureId = $this->getParams('lecture'); /** @var \Lecture $lecture */ $lecture = Repositories::findEntity(Repositories::Lecture, $lectureId); $name = $this->getParams('name'); $type = $this->getParams('type'); $id = $this->getParams('id'); $isIdSet = $id !== null && $id !== ''; $originalName = $this->getUploadedFileName('file'); if (!$originalName) { return false; } $extensionStart = strrpos($originalName, '.'); $extension = $extensionStart === false ? '' : substr($originalName, strrpos($originalName, '.')); $attachmentFolder = Config::get('paths', 'attachments'); $filename = $id . '_' . $name . $extension; if (!$this->checkTestGenerationPrivileges($lecture)) { return $this->death(StringID::InsufficientPrivileges); } /** * @var $attachment \Attachment */ $attachment = null; if (!$this->saveUploadedFile('file', $attachmentFolder . $filename)) { return $this->death(StringID::InsufficientPrivileges); } /** @var \Attachment[] $attachmentsWithThisName */ $attachmentsWithThisName = Repositories::getRepository(Repositories::Attachment)->findBy(['lecture' => $lectureId, 'name' => $name]); if ($isIdSet) { $attachment = Repositories::findEntity(Repositories::Attachment, $id); if (count($attachmentsWithThisName) > 0) { if ($attachmentsWithThisName[0]->getId() !== $attachment->getId()) { return $this->death(StringID::AttachmentExists); } } } else { if (count($attachmentsWithThisName) > 0) { return $this->death(StringID::AttachmentExists); } $attachment = new \Attachment(); } $attachment->setType($type); $attachment->setLecture($lecture); $attachment->setName($name); $attachment->setFile($filename); Repositories::persistAndFlush($attachment); return true; }
protected function body() { $inputs = array('lecture' => 'isIndex', 'text' => 'isNotEmpty', 'type' => array('isEnum' => array('text', 'choice', 'multi'))); if (!$this->isInputValid($inputs)) { return false; } $lectureId = $this->getParams('lecture'); $text = $this->getParams('text'); $type = $this->getParams('type'); $id = $this->getParams('id'); $isIdSet = $id !== null && $id !== ''; $options = $this->getParams('options') . ''; $attachments = $this->getParams('attachments') . ''; if (!$this->checkTestGenerationPrivileges($lectureId)) { return $this->death(StringID::InsufficientPrivileges); } $visibleAttachments = CommonQueries::GetAttachmentsVisibleToActiveUser(); $attTmp = $attachments ? explode(';', $attachments) : array(); foreach ($visibleAttachments as $va) { $aId = $va->getId(); $index = array_search($aId, $attTmp); if ($index !== false) { array_splice($attTmp, $index, 1); if ($va->getLecture()->getId() != $lectureId) { return $this->death(StringID::AttachmentBelongsToAnotherLecture); } } } if (count($attTmp)) { return $this->stop(ErrorCause::invalidInput('Following attachment IDs are invalid or inaccessible: ' . implode(', ', $attTmp) . '.', 'attachments')); } /** @var \Question $question */ $question = null; if (!$isIdSet) { $question = new \Question(); } else { $question = Repositories::findEntity(Repositories::Question, $id); if ($question->getLecture()->getId() != $lectureId) { // This must be a weak comparison, because lectureId comes from user and is text. return $this->death(StringID::HackerError); } } $question->setAttachments($attachments); /** @var \Lecture $lecture */ $lecture = Repositories::findEntity(Repositories::Lecture, $lectureId); $question->setLecture($lecture); $question->setOptions($options); $question->setText($text); $question->setType($type); Repositories::persistAndFlush($question); return true; }
protected function body() { $questions = $this->getParams('questions'); if ($questions === null || $questions === '') { return $this->death(StringID::ChooseAtLeastOneQuestion); // Put this in front to have a more specific, comprehensible error message } $inputs = array('description' => 'isNotEmpty', 'count' => 'isNonNegativeInt', 'questions' => 'isNotEmpty'); if (!$this->isInputValid($inputs)) { return false; } $description = $this->getParams('description'); $count = $this->getParams('count'); $questions = $this->getParams('questions'); $questionsArray = explode(',', $questions); $visibleQuestions = CommonQueries::GetQuestionsVisibleToActiveUser(); /** * @var $lecture \Lecture */ $lecture = null; foreach ($visibleQuestions as $vq) { $qId = $vq->getId(); $index = array_search($qId, $questionsArray); if ($index !== false) { array_splice($questionsArray, $index, 1); if ($lecture === null) { $lecture = $vq->getLecture(); } elseif ($lecture->getId() !== $vq->getLecture()->getId()) { return $this->death(StringID::TestCannotContainQuestionsOfDifferentLectures); } } } if (count($questionsArray)) { return $this->stop(ErrorCause::invalidInput('Following question IDs are invalid or inaccessible: ' . implode(', ', $questionsArray) . '.', 'questions')); } if ($lecture === null) { return $this->death(StringID::ChooseAtLeastOneQuestion); } if (!$this->checkTestGenerationPrivileges($lecture->getId())) { return $this->death(StringID::InsufficientPrivileges); } $randomized = $this->generateTest($questions, $count); $xtest = new \Xtest(); $xtest->setDescription($description); $xtest->setCount($count); $xtest->setLecture($lecture); $xtest->setTemplate($questions); $xtest->setGenerated(implode(',', $randomized)); Repositories::persistAndFlush($xtest); return true; }
protected function body() { if (!$this->isInputValid(array('id' => 'isIndex'))) { return false; } /** * @var $assignment \Assignment */ $assignment = Repositories::findEntity(Repositories::Assignment, $this->getParams('id')); $user = User::instance(); if (!$user->hasPrivileges(User::groupsManageAll) && (!$user->hasPrivileges(User::groupsManageOwn) || $user->getId() != $assignment->getGroup()->getOwner()->getId())) { return $this->death(StringID::InsufficientPrivileges); } $assignment->setDeleted(true); Repositories::persistAndFlush($assignment); return true; }
protected function body() { if (!$this->isInputValid(array('id' => 'isIndex'))) { return false; } $id = $this->getParams('id'); /** * @var $submission \Submission */ $submission = Repositories::findEntity(Repositories::Submission, $id); $userId = User::instance()->getId(); if ($submission->getUser()->getId() != $userId) { return $this->death(StringID::HackerError); } // First, if you handed something off previously, it is no longer handed off /** * @var $yourSubmissions \Submission[] */ $yourSubmissions = Repositories::getRepository(Repositories::Submission)->findBy(['user' => $userId, 'assignment' => $submission->getAssignment()->getId()]); foreach ($yourSubmissions as $previouslyHandedOffSubmission) { if ($previouslyHandedOffSubmission->getStatus() == \Submission::STATUS_REQUESTING_GRADING || $previouslyHandedOffSubmission->getStatus() == \Submission::STATUS_LATEST) { $previouslyHandedOffSubmission->setStatus(\Submission::STATUS_NORMAL); Repositories::persistAndFlush($previouslyHandedOffSubmission); } } // Next, hand off the submission $submission->setStatus(\Submission::STATUS_REQUESTING_GRADING); Repositories::persistAndFlush($submission); $emailText = file_get_contents(Config::get("paths", "newSubmissionEmail")); $emailText = str_replace("%{RealName}", User::instance()->getRealName(), $emailText); $emailText = str_replace("%{Email}", User::instance()->getEmail(), $emailText); $emailText = str_replace("%{Link}", Config::getHttpRoot() . "#correctionAll#submission#" . $submission->getId(), $emailText); $lines = explode("\n", $emailText); $subject = $lines[0]; // The first line is subject. $text = preg_replace('/^.*\\n/', '', $emailText); // Everything except the first line. $to = $submission->getAssignment()->getGroup()->getOwner(); if ($to->getSendEmailOnNewSubmission()) { if (!Core::sendEmail($to->getEmail(), $subject, $text)) { return $this->death(StringID::MailError); } } return true; }
protected function body() { if (!$this->isInputSet(array('code'))) { return false; } $code = $this->getParams('code'); /** * @var $users \User[] */ $users = Repositories::getRepository(Repositories::User)->findBy(['activationCode' => $code]); if (count($users) === 1) { $users[0]->setActivationCode(''); Repositories::persistAndFlush($users[0]); return true; } else { return $this->death(StringID::InvalidActivationCode); } }
protected function body() { $inputs = array('lecture' => 'isIndex', 'name' => 'isNotEmpty', 'description' => array()); if (!$this->isInputValid($inputs)) { return false; } $lectureIndex = $this->getParams('lecture'); $groupName = $this->getParams('name'); $groupDescription = $this->getParams('description'); $public = $this->paramExists('public') ? 'public' : 'private'; $groupId = $this->getParams('id'); $editing = $groupId !== null && $groupId !== ''; $user = User::instance(); /** @var \Lecture $lecture */ $lecture = Repositories::findEntity(Repositories::Lecture, $lectureIndex); if ($editing) { /** * @var $group \Group */ $group = Repositories::findEntity(Repositories::Group, $groupId); $group->setName($groupName); $group->setDescription($groupDescription); $group->setType($public); Repositories::persistAndFlush($group); } else { if (!$this->userHasPrivileges(User::groupsAdd)) { return $this->death(StringID::InsufficientPrivileges); } $group = new \Group(); $group->setDeleted(false); $group->setDescription($groupDescription); $group->setLecture($lecture); $group->setName($groupName); $group->setOwner($user->getEntity()); $group->setType($public); Repositories::persistAndFlush($group); } return true; }
/** * Performs the function of this script. */ protected function body() { if (!$this->userHasPrivileges(User::assignmentsSubmit)) { return; } if (!$this->isInputValid(array('assignmentId' => 'isIndex'))) { return; } $userId = User::instance()->getId(); $assignmentId = $this->getParams('assignmentId'); /** * @var $assignment \Assignment */ $assignment = Repositories::getEntityManager()->find('Assignment', $assignmentId); $query = "SELECT s, a FROM Subscription s, Assignment a WHERE s.group = a.group AND s.user = "******" AND a.id = " . $assignmentId; /** * @var $result \Subscription[] */ $result = Repositories::getEntityManager()->createQuery($query)->getResult(); if (count($result) === 0) { $this->stop(Language::get(StringID::HackerError)); return; } if ($result[0]->getStatus() == \Subscription::STATUS_REQUESTED) { $this->stop(Language::get(StringID::SubscriptionNotYetAccepted)); return; } $submissionsFolder = Config::get('paths', 'submissions'); $file = date('Y-m-d_H-i-s_') . $userId . '_' . StringUtils::randomString(10) . '.zip'; if (!$this->saveUploadedFile('submission', $submissionsFolder . $file)) { return; } // Create submission $newSubmission = new \Submission(); $newSubmission->setAssignment($assignment); $newSubmission->setSubmissionFile($file); $newSubmission->setUser(User::instance()->getEntity()); $newSubmission->setDate(new \DateTime()); // Put into database Repositories::persistAndFlush($newSubmission); // Launch plugin, or set full success if not connected to any plugin if ($assignment->getProblem()->getPlugin() === null) { $newSubmission->setSuccess(100); $newSubmission->setInfo(Language::get(StringID::NoPluginUsed)); $previousSubmissions = Repositories::makeDqlQuery("SELECT s FROM \\Submission s WHERE s.user = :sameUser AND s.assignment = :sameAssignment AND s.status != 'graded' AND s.status != 'deleted'")->setParameter('sameUser', User::instance()->getEntity()->getId())->setParameter('sameAssignment', $assignment->getId())->getResult(); foreach ($previousSubmissions as $previousSubmission) { $previousSubmission->setStatus(\Submission::STATUS_NORMAL); Repositories::getEntityManager()->persist($previousSubmission); } $newSubmission->setStatus(\Submission::STATUS_LATEST); Repositories::getEntityManager()->persist($newSubmission); Repositories::flushAll(); } else { Core::launchPlugin($assignment->getProblem()->getPlugin()->getType(), Config::get('paths', 'plugins') . $assignment->getProblem()->getPlugin()->getMainfile(), $submissionsFolder . $file, false, $newSubmission->getId(), explode(';', $assignment->getProblem()->getConfig())); } // Run checking for plagiarism $similarityJar = Config::get('paths', 'similarity'); if ($similarityJar != null && is_file($similarityJar)) { $arguments = "comparenew"; // Get config file and autoloader file $paths = Config::get('paths'); $vendorAutoload = $paths['composerAutoload']; $java = Config::get('bin', 'java'); $javaArguments = Config::get('bin', 'javaArguments'); $pathToCore = Config::get('paths', 'core'); // This code will be passed, shell-escaped to the PHP CLI $launchCode = <<<LAUNCH_CODE require_once '{$vendorAutoload}'; chdir("{$pathToCore}"); `"{$java}" {$javaArguments} -Dfile.encoding=UTF-8 -jar "{$similarityJar}" {$arguments}`; LAUNCH_CODE; ShellUtils::phpExecInBackground(Config::get('bin', 'phpCli'), $launchCode); } }
/** * Deletes attachment with supplied ID. Removes the attachment from questions that referenced it. This will not delete the attachment file from disk. * * @param int $id attachment ID * @return array error properties provided by removalError() or retrievalError(), * or false in case of success */ public static function deleteAttachmentById($id) { /** * @var $attachment \Attachment */ $attachment = Repositories::findEntity(Repositories::Attachment, $id); $questions = CommonQueries::getQuestionsVisibleToActiveUser(); foreach ($questions as $question) { $modificationMade = false; $attachments = explode(';', $question->getAttachments()); for ($i = 0; $i < count($attachments); $i++) { if ($attachments[$i] === (string) $id) { unset($attachments[$i]); $modificationMade = true; } } if ($modificationMade) { $question->setAttachments(implode(';', $attachments)); Repositories::persistAndFlush($question); } } Repositories::remove($attachment); return false; }
/** * Launches plugin and updates database with results. * @param string $pluginType one of 'php', 'java', or 'exe' * @param string $pluginFile plugin file path * @param string $inputFile input file path * @param boolean $isTest is this a plugin test or a submission? true if plugin test * @param int $rowId ID of row to be updated * @param array $arguments plugin arguments * @return bool true if no error occurred */ public static function launchPlugin($pluginType, $pluginFile, $inputFile, $isTest, $rowId, $arguments = array()) { try { $response = null; if (!is_file($pluginFile) || !is_file($inputFile)) { $error = "plugin file and/or input file don't exist"; } else { array_unshift($arguments, $inputFile); $cwd = getcwd(); chdir(dirname($pluginFile)); switch ($pluginType) { case 'php': $launcher = new PhpLauncher(); ob_start(); $error = $launcher->launch($pluginFile, $arguments, $response); ob_end_clean(); break; case 'java': $launcher = new JavaLauncher(); $error = $launcher->launch($pluginFile, $arguments, $responseString); break; case 'exe': $launcher = new ExeLauncher(); $error = $launcher->launch($pluginFile, $arguments, $responseString); break; default: $error = "unsupported plugin type '{$pluginType}'"; } chdir($cwd); } if (!$error) { if (isset($responseString)) { try { $response = PluginResponse::fromXmlString($responseString); } catch (Exception $ex) { $response = PluginResponse::createError('Internal error. Plugin did not supply valid response XML and this error occurred: ' . $ex->getMessage() . '. Plugin instead supplied this response string: ' . $responseString); } } } else { $response = PluginResponse::createError('Plugin cannot be launched (' . $error . ').'); } $outputFile = $response->getOutput(); if ($outputFile) { $outputFolder = Config::get('paths', 'output'); $newFile = $outputFolder . date('Y-m-d_H-i-s_') . StringUtils::randomString(10) . '.zip'; if (rename($outputFile, $newFile)) { $outputFile = $newFile; } else { $outputFile = 'tmp-file-rename-failed'; } } /** * @var $pluginTest \PluginTest * @var $submission \Submission * @var $previousSubmissions \Submission[] */ if ($isTest) { $pluginTest = Repositories::findEntity(Repositories::PluginTest, $rowId); $pluginTest->setStatus(\PluginTest::STATUS_COMPLETED); $pluginTest->setSuccess($response->getFulfillment()); $pluginTest->setInfo($response->getDetails()); $pluginTest->setOutput($outputFile); Repositories::persistAndFlush($pluginTest); } else { $submission = Repositories::findEntity(Repositories::Submission, $rowId); // There is a sort of a race condition in here. // It is, in theory, possible, that there will be two submissions with the "latest" status after all is done // This should not happen in practice, though, and even if it does, it will have negligible negative effects. $previousSubmissions = Repositories::makeDqlQuery("SELECT s FROM \\Submission s WHERE s.user = :sameUser AND s.assignment = :sameAssignment AND s.status != 'graded' AND s.status != 'deleted'")->setParameter('sameUser', $submission->getUser()->getId())->setParameter('sameAssignment', $submission->getAssignment()->getId())->getResult(); foreach ($previousSubmissions as $previousSubmission) { $previousSubmission->setStatus(\Submission::STATUS_NORMAL); Repositories::getEntityManager()->persist($previousSubmission); } $submission->setStatus(\Submission::STATUS_LATEST); $submission->setInfo($response->getDetails()); $submission->setSuccess($response->getFulfillment()); $submission->setOutputfile($outputFile); Repositories::getEntityManager()->persist($submission); Repositories::flushAll(); } return !$error; } catch (Exception $exception) { $errorInformation = "Internal error. Plugin launcher or plugin failed with an internal error. Exception information: " . $exception->getMessage() . " in " . $exception->getFile() . " at " . $exception->getLine(); if ($isTest) { $pluginTest = Repositories::findEntity(Repositories::PluginTest, $rowId); $pluginTest->setStatus(\PluginTest::STATUS_COMPLETED); $pluginTest->setSuccess(0); $pluginTest->setInfo($errorInformation); Repositories::persistAndFlush($pluginTest); } else { $submission = Repositories::findEntity(Repositories::Submission, $rowId); $submission->setStatus(\Submission::STATUS_NORMAL); $submission->setInfo($errorInformation); Repositories::persistAndFlush($submission); } return false; } }
/** * Runs this script. * @return bool Is it successful? * @throws \Exception Should never occur. */ protected function body() { $inputs = array('name' => array('isAlphaNumeric', 'hasLength' => array('min_length' => Constants::UsernameMinLength, 'max_length' => Constants::UsernameMaxLength)), 'realname' => array('isNotEmpty', 'isName'), 'email' => 'isEmail', 'pass' => array(), 'repass' => array()); if (!$this->isInputValid($inputs)) { return false; } // Extract input data $username = strtolower($this->getParams('name')); $realname = $this->getParams('realname'); $email = $this->getParams('email'); $pass = $this->getParams('pass'); $repass = $this->getParams('repass'); $id = $this->getParams('id'); $type = $this->getParams('type'); $user = null; $isIdSet = $id !== null && $id !== ''; $isTypeSet = $type !== null && $type !== ''; // Extract database data if ($id) { $user = Repositories::findEntity(Repositories::User, $id); } $userExists = $user != null; $sameNameUserExists = count(Repositories::getRepository(Repositories::User)->findBy(['name' => $username])) > 0; // Custom verification of input data if ($pass !== $repass) { return $this->death(StringID::InvalidInput); } if ($userExists) { if ((strlen($pass) < Constants::PasswordMinLength || strlen($pass) > Constants::PasswordMaxLength) && $pass !== "") { return $this->death(StringID::InvalidInput); } } else { // A new user must have full password if (strlen($pass) < Constants::PasswordMinLength || strlen($pass) > Constants::PasswordMaxLength) { return $this->death(StringID::InvalidInput); } } $code = ''; $unhashedPass = $pass; $pass = Security::hash($pass, Security::HASHTYPE_PHPASS); $canAddUsers = User::instance()->hasPrivileges(User::usersAdd); $canEditUsers = User::instance()->hasPrivileges(User::usersManage); $isEditingSelf = $id == User::instance()->getId(); // This must not be a strict comparison. /** * @var $user \User */ if (!$userExists && !$sameNameUserExists) { if ($this->getParams('fromRegistrationForm')) { if ($type != Repositories::StudentUserType) { return $this->death(StringID::InsufficientPrivileges); } $code = md5(uniqid(mt_rand(), true)); $emailText = file_get_contents(Config::get("paths", "registrationEmail")); $emailText = str_replace("%{Username}", $username, $emailText); $emailText = str_replace("%{ActivationCode}", $code, $emailText); $emailText = str_replace("%{Link}", Config::getHttpRoot() . "#activate", $emailText); $lines = explode("\n", $emailText); $subject = $lines[0]; // The first line is subject. $text = preg_replace('/^.*\\n/', '', $emailText); // Everything except the first line. $returnCode = Core::sendEmail($email, $subject, $text); if (!$returnCode) { return $this->stop(ErrorCode::mail, 'user registration failed', 'email could not be sent'); } } else { if (!$canAddUsers) { return $this->death(StringID::InsufficientPrivileges); } } $user = new \User(); /** @var \UserType $typeEntity */ $typeEntity = Repositories::findEntity(Repositories::UserType, $type); $user->setType($typeEntity); $user->setPass($pass); $user->setName($username); $user->setEmail($email); $user->setActivationCode($code); $user->setEncryptionType(Security::HASHTYPE_PHPASS); $user->setRealName($realname); Repositories::persistAndFlush($user); } elseif ($isIdSet) { if (!$canEditUsers && ($isTypeSet || !$isEditingSelf)) { return $this->stop(ErrorCode::lowPrivileges, 'cannot edit data of users other than yourself'); } $type = $isTypeSet ? $type : $user->getType()->getId(); /** @var \UserType $typeEntity */ $typeEntity = Repositories::findEntity(Repositories::UserType, $type); if ($unhashedPass) { $user->setPass($pass); $user->setEncryptionType(Security::HASHTYPE_PHPASS); } $user->setType($typeEntity); $user->setEmail($email); $user->setActivationCode(''); $user->setRealName($realname); Repositories::persistAndFlush($user); } else { return $this->death(StringID::UserNameExists); } return true; }
/** * Tries to log user in with supplied credentials. * @param string $name username * @param string $pass password * @return bool true if login was successful */ public function login($name, $pass) { if ($this->data != null) { $this->logout(); } /// Username is case-insensitive. $name = strtolower($name); $users = Repositories::getRepository(Repositories::User)->findBy(['name' => $name]); if (!empty($users)) { /** * @var $user \User */ $user = $users[0]; if ($user->getActivationCode() !== '') { // Non-empty activation code means the account is not yet activated. return false; } $authenticationSuccess = Security::check($pass, $user->getPass(), $user->getEncryptionType()); if ($authenticationSuccess) { $this->data = array('id' => $user->getId(), 'name' => $user->getName(), 'privileges' => $user->getType()->getPrivileges(), 'realName' => $user->getRealName(), 'email' => $user->getEmail(), 'lastAccess' => $user->getLastAccess()->format("Y-m-d H:i:s"), 'applicationVersion' => implode('.', Config::get('version')), User::sendEmailOnAssignmentAvailableStudent => $user->getSendEmailOnNewAssignment() ? 1 : 0, User::sendEmailOnSubmissionConfirmedTutor => $user->getSendEmailOnNewSubmission() ? 1 : 0, User::sendEmailOnSubmissionRatedStudent => $user->getSendEmailOnSubmissionRated() ? 1 : 0); $this->refresh(); $user->setLastAccess(new \DateTime()); Repositories::persistAndFlush($user); $this->entity = $user; return true; } else { return false; } } return false; }