/** * Permission ensuring function, and session keep-alive point. * This function should be called on the initialization stage of every page load. * * CAUTION: When $token is specified, extended security is performed on the current session. * Current session can expire with constant Session::ERR_EXPIRED after 30 minutes of inactivity. * * @param $token Optional, decided as a one-time key to have advanced security over AJAX calls. * This token string should be get from function requestToken. * * @return true on access permitted, false otherwise. */ static function ensure($sid, $token = null, $fingerprint = null) { if (!$sid) { return static::ERR_INVALID; } $res = Node::getOne(array(Node::FIELD_COLLECTION => FRAMEWORK_COLLECTION_SESSION, 'sid' => util::packUuid($sid), 'fingerprint' => $fingerprint)); if (!$res) { return static::ERR_INVALID; } // One-time token mismatch if (($token || $res['token']) && util::packUuid($token) != $res['token']) { return false; } // Session expired if (strtotime($res['timestamp']) < strtotime(static::EXPIRE_TIME)) { return static::ERR_EXPIRED; } unset($res['timestamp'], $res['token']); // Update timestamp Node::set($res); static::$currentSession = $res; return true; }
/** * @protected * * Pack UUID for delete filters. */ function beforeDelete(array &$filter = array()) { if (isset($filter[$this->primaryKey()])) { $filter[$this->primaryKey()] = util::packUuid($filter[$this->primaryKey()]); } return $this; }
/** * Process next task with POST data. */ public function process() { // note: Some tasks can work without post data, but request method must be POST. if (!$this->__isSuperUser && $this->__request->method() != 'post') { $this->__response->status(405); // Method not allowed return; } // WorkInstance if (!$this->identity()) { $this->__response->status(404); // WorkInstance not found return; } // TaskInstance $instance = $this->nextTask(); if (!$instance) { $this->__response->status(404); // TaskInstance not foudn return; } // release mutable lock for work instance initialization. $this->_immutable = false; // remove tasks to prevent unwanted change. $tasks = $this->tasks; unset($this->tasks); // creates $this->dataStore if not yet. if (empty($this->dataStore)) { $this->dataStore = array(); } unset($this->lastError); // immutable marker to prevent direct modifications to the internal data. $this->_immutable = true; // note: Send bayeux message to groups with permission to this task. $userGroups = $instance->userGroups(); try { // Note: Since $this->dataStore is an array, it is mutable itself. $promise = $instance->process(); } catch (\Exception $e) { Log::warning('Task process exception.', array_filter(array('message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTrace()))); $lastError = array('message' => $this->__response->__($e->getMessage()), 'code' => $e->getCode()); // note: Failure on Headless tasks will revert to previous task. if (@$instance->type == 'Headless') { $deferred = new Deferred(); $deferred->reject($lastError['message'], $lastError['code']); $promise = $deferred->promise(); unset($deferred); } else { $this->_immutable = false; $this->lastError = $lastError; } unset($lastError); } $this->_immutable = false; $result = array(); $saveFunc = function () use(&$result) { unset($this->timestamp); $this->save($result); }; if (isset($promise)) { // note: rejection here means revert to previous task $promise->fail(function ($error, $code = null) use($instance, $tasks) { $this->lastError = array_filter(array('message' => $error, 'code' => $code)); // revert to previous task $prevTask = array_search($instance->identity(), array_map(invokes('identity'), $tasks)); $prevTask = @$tasks[$prevTask - 1]; // fallback to the first task if (!$prevTask) { $prevTask = reset($tasks); } $this->nextTask = util::packUuid($prevTask->identity()); }); // note: resolution always advances to next task $promise->done(function () use($instance, $tasks) { $nextTask = array_search($instance->identity(), array_map(invokes('identity'), $tasks)); $nextTask = @$tasks[$nextTask + 1]; if ($nextTask) { $this->nextTask = util::packUuid($nextTask->identity()); } else { $this->state = static::STATE_CLOSE; $this->nextTask = null; } }); // note: controller script must call resolve() or reject() to make this happen. $promise->always($saveFunc); } else { $saveFunc(); } unset($saveFunc); // note: Merge user groups before and after task processing if ($this->nextTask) { $userGroups = array_unique(array_merge($userGroups, $this->nextTask()->userGroups())); } foreach ($userGroups as $userGroup) { Bayeux::sendMessage("/group/{$userGroup}", array('action' => 'update', '@collection' => 'WorkInstance', 'timestamp' => $this->timestamp)); } if (@$result['error']) { $this->__response->status(500); return $result; } else { // note; User might no longer has access to the updated work instance. if ($this->data()) { $this->__response->status(200); return $this; } else { $this->__response->status(204); } } }