diff --git a/include/class.auth.php b/include/class.auth.php index d984cb3cb1296051bf0cf4bbe9190984017ca70a..a0abe4ed4468455e9867730cd1d46a9d20fd3405 100644 --- a/include/class.auth.php +++ b/include/class.auth.php @@ -1315,7 +1315,35 @@ abstract class PasswordPolicy { static function register($policy) { static::$registry[] = $policy; } + + static function cleanSessions($model, $user=null) { + $criteria = array(); + + switch (true) { + case ($model instanceof Staff): + $criteria['user_id'] = $model->getId(); + + if ($user && ($model->getId() == $user->getId())) + array_push($criteria, + Q::not(array('session_id' => $user->session->session_id))); + break; + case ($model instanceof User): + $regexp = '_auth\|.*"user";[a-z]+:[0-9]+:{[a-z]+:[0-9]+:"id";[a-z]+:'.$model->getId(); + $criteria['user_id'] = 0; + $criteria['session_data__regex'] = $regexp; + + if ($user) + array_push($criteria, + Q::not(array('session_id' => $user->session->session_id))); + break; + default: + return false; + } + + return SessionData::objects()->filter($criteria)->delete(); + } } +Signal::connect('auth.clean', array('PasswordPolicy', 'cleanSessions')); class osTicketPasswordPolicy extends PasswordPolicy { diff --git a/include/class.client.php b/include/class.client.php index 83684cae3e51155a81f6c39a4ae95821429e399f..11b500e4aef370bf60846024ada0cf8cf645df55 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -416,7 +416,7 @@ class ClientAccount extends UserAccount { global $cfg; // FIXME: Updates by agents should go through UserAccount::update() - global $thisstaff; + global $thisstaff, $thisclient; if ($thisstaff) return parent::update($vars, $errors); @@ -474,6 +474,8 @@ class ClientAccount extends UserAccount { Signal::send('auth.pwchange', $this->getUser(), $info); $this->cancelResetTokens(); $this->clearStatus(UserAccountStatus::REQUIRE_PASSWD_RESET); + // Clean sessions + Signal::send('auth.clean', $this->getUser(), $thisclient); } return $this->save(); diff --git a/include/class.config.php b/include/class.config.php index 66a3232b2518fd13954b7ef924d8daa19461344d..004a1e8e8a44d2be9fdcc1cde54acd0a03300d73 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -176,6 +176,20 @@ extends VerySimpleModel { $this->updated = SqlFunction::NOW(); return parent::save($this->dirty || $refetch); } + + // Clean password reset tokens that have expired + static function cleanPwResets() { + global $cfg; + + if (!$cfg || !($period = $cfg->getPwResetWindow())) // In seconds + return false; + + return ConfigItem::objects() + ->filter(array( + 'namespace' => 'pwreset', + 'updated__lt' => SqlFunction::NOW()->minus(SqlInterval::SECOND($period)), + ))->delete(); + } } class OsticketConfig extends Config { diff --git a/include/class.cron.php b/include/class.cron.php index ee67f7f31aac6a742a411a61358726636d22eba3..f259d60b3a5fb87c825faeb371daaccfe92b9884 100644 --- a/include/class.cron.php +++ b/include/class.cron.php @@ -56,6 +56,11 @@ class Cron { DbSessionBackend::cleanup(); } + function CleanPwResets() { + require_once(INCLUDE_DIR.'class.config.php'); + ConfigItem::cleanPwResets(); + } + function MaybeOptimizeTables() { // Once a week on a 5-minute cron $chance = rand(1,2000); @@ -106,6 +111,7 @@ class Cron { self::TicketMonitor(); self::PurgeLogs(); self::CleanExpiredSessions(); + self::CleanPwResets(); // Run file purging about every 10 cron runs if (mt_rand(1, 9) == 4) self::CleanOrphanedFiles(); diff --git a/include/class.forms.php b/include/class.forms.php index 1915257b7faf6d3c38480545781f7aca7ffbe821..c6183e30163e0ba995dceae22da76027007c722f 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -3166,6 +3166,7 @@ class FileUploadField extends FormField { static $widget = 'FileUploadWidget'; protected $attachments; + protected $files; static function getFileTypes() { static $filetypes; @@ -3333,7 +3334,7 @@ class FileUploadField extends FormField { return ($ext && is_array($allowed) && in_array(".$ext", $allowed)); } - function getFiles() { + function getAttachments() { if (!isset($this->attachments) && ($a = $this->getAnswer()) && ($e = $a->getEntry()) && ($e->get('id')) ) { @@ -3349,6 +3350,18 @@ class FileUploadField extends FormField { $this->attachments = $att; } + function getFiles() { + if (!isset($this->files)) { + $files = array(); + foreach ($this->getAttachments() as $a) { + if ($a && ($f=$a->getFile())) + $files[] = $f; + } + $this->files = $files; + } + return $this->files; + } + function getConfiguration() { $config = parent::getConfiguration(); $_types = self::getFileTypes(); @@ -3406,8 +3419,8 @@ class FileUploadField extends FormField { // array. Then, inspect the difference between the files actually // attached to this field function to_database($value) { - $this->getFiles(); - if (isset($this->attachments)) { + $this->getAttachments(); + if (isset($this->attachments) && $this->attachments) { $this->attachments->keepOnlyFileIds($value); } return JsonDataEncoder::encode($value); @@ -3436,21 +3449,22 @@ class FileUploadField extends FormField { function toString($value) { $files = array(); foreach ($this->getFiles() as $f) { - $files[] = $f->file->name; + $files[] = $f->name; } return implode(', ', $files); } function db_cleanup($field=false) { - // Delete associated attachments from the database, if any - $this->getFiles(); - if (isset($this->attachments)) { + if ($this->getAttachments()) { $this->attachments->deleteAll(); } } function asVar($value, $id=false) { - return new FileFieldAttachments($this->getFiles()); + if (($attachments = $this->getAttachments())) + $attachments = $attachments->all(); + + return new FileFieldAttachments($attachments ?: array()); } function asVarType() { return 'FileFieldAttachments'; @@ -3489,26 +3503,30 @@ class FileUploadField extends FormField { } class FileFieldAttachments { - var $files; + var $attachments; - function __construct($files) { - $this->files = $files; + function __construct($attachments) { + $this->attachments = $attachments; } function __toString() { $files = array(); - foreach ($this->files as $f) { - $files[] = $f->file->name; + foreach ($this->getAttachments() as $a) { + $files[] = $a->getFilename(); } return implode(', ', $files); } + function getAttachments() { + return $this->attachments ?: array(); + } + function getVar($tag) { switch ($tag) { case 'names': return $this->__toString(); case 'files': - throw new OOBContent(OOBContent::FILES, $this->files->all()); + throw new OOBContent(OOBContent::FILES, $this->getAttachments()); } } @@ -4377,7 +4395,7 @@ class FileUploadWidget extends Widget { $config = $this->field->getConfiguration(); $name = $this->field->getFormName(); $id = substr(md5(spl_object_hash($this)), 10); - $attachments = $this->field->getFiles(); + $attachments = $this->field->getAttachments(); $mimetypes = array_filter($config['__mimetypes'], function($t) { return strpos($t, '/') !== false; } ); @@ -4546,21 +4564,22 @@ class FreeTextField extends FormField { } function getAttachments() { - if (!isset($this->attachments)) $this->attachments = GenericAttachments::forIdAndType($this->get('id'), 'I'); - return $this->attachments; + return $this->attachments ?: array(); } function getFiles() { - - if (!($attachments = $this->getAttachments())) - return array(); - - return $attachments->all(); + if (!isset($this->files)) { + $files = array(); + if (($attachments=$this->getAttachments())) + foreach ($attachments->all() as $a) + $files[] = $a->getFile(); + $this->files = $files; + } + return $this->files; } - } class FreeTextWidget extends Widget { @@ -4582,12 +4601,10 @@ class FreeTextWidget extends Widget { echo Format::viewableImages($config['content']); ?></div> </div> <?php - if (($attachments = $this->field->getFiles()) && count($attachments)) { ?> + if (($attachments = $this->field->getAttachments()) && count($attachments)) { ?> <section class="freetext-files"> <div class="title"><?php echo __('Related Resources'); ?></div> - <?php foreach ($attachments as $attach) { - $filename = Format::htmlchars($attach->getFilename()); - ?> + <?php foreach ($attachments->all() as $attach) { ?> <div class="file"> <a href="<?php echo $attach->file->getDownloadUrl(); ?>" target="_blank" download="<?php echo $filename; ?>" diff --git a/include/class.mailer.php b/include/class.mailer.php index db18b71d3288e6bc253233f62fcd216b1ed43436..dfd65b2ab254fabbb7005ef55e30add8921ac85b 100644 --- a/include/class.mailer.php +++ b/include/class.mailer.php @@ -445,10 +445,10 @@ class Mailer { // Add in extra attachments, if any from template variables if ($message instanceof TextWithExtras - && ($files = $message->getFiles()) + && ($attachments = $message->getAttachments()) ) { - foreach ($files as $F) { - $file = $F->getFile(); + foreach ($attachments as $a) { + $file = $a->getFile(); $mime->addAttachment($file->getData(), $file->getType(), $file->getName(), false); } diff --git a/include/class.staff.php b/include/class.staff.php index 608b6b7c0c6e843dc711d33d6b5179d0bbec70a3..a9024227332507d9dce728d05ed8bf0512886f08 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -230,6 +230,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable { } function setPassword($new, $current=false) { + global $thisstaff; + // Allow the backend to update the password. This is the preferred // method as it allows for integration with password policies and // also allows for remotely updating the password where possible and @@ -254,6 +256,9 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable { $this->cancelResetTokens(); $this->passwdreset = SqlFunction::NOW(); + // Clean sessions + Signal::send('auth.clean', $this, $thisstaff); + return $rv; } diff --git a/include/class.user.php b/include/class.user.php index f6660d16f0123397d884c64b4b676a55af411f70..53a8627c3bd4d95e319d86e26d9e1f8498f5a4d4 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -1143,6 +1143,8 @@ class UserAccount extends VerySimpleModel { function setPassword($new) { $this->set('passwd', Passwd::hash($new)); + // Clean sessions + Signal::send('auth.clean', $this->getUser()); } protected function sendUnlockEmail($template) { diff --git a/include/class.variable.php b/include/class.variable.php index 8b346e0cd387483494dc2f245211a16ac75a5784..6a1517b9426a551afe416cb8441f713f37aff421 100644 --- a/include/class.variable.php +++ b/include/class.variable.php @@ -383,8 +383,8 @@ class TextWithExtras { return $this->text; } - function getFiles() { - return $this->extras[OOBContent::FILES]; + function getAttachments() { + return $this->extras[OOBContent::FILES] ?: array(); } } diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php index c8983aca53190d4b300f0894590ae3978c51d224..8fc2b0534807a8a48386a33cd45f7f4de266dc48 100644 --- a/include/staff/tasks.inc.php +++ b/include/staff/tasks.inc.php @@ -119,19 +119,19 @@ if ($filters) // Impose visibility constraints // ------------------------------------------------------------ // -- Open and assigned to me -$visibility = array( +$visibility = Q::any( new Q(array('flags__hasbit' => TaskModel::ISOPEN, 'staff_id' => $thisstaff->getId())) ); // -- Routed to a department of mine if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) - $visibility[] = new Q(array('dept_id__in' => $depts)); + $visibility->add(new Q(array('dept_id__in' => $depts))); // -- Open and assigned to a team of mine if (($teams = $thisstaff->getTeams()) && count(array_filter($teams))) - $visibility[] = new Q(array( + $visibility->add(new Q(array( 'team_id__in' => array_filter($teams), 'flags__hasbit' => TaskModel::ISOPEN - )); -$tasks->filter(Q::any($visibility)); + ))); +$tasks->filter(new Q($visibility)); // Add in annotations $tasks->annotate(array(