/** * Update Cabin configuration * * @route cabins/manage{_string} * @param string $cabinName */ public function manage(string $cabinName = '') { if (!$this->isSuperUser()) { // Admins only! \Airship\redirect($this->airship_cabin_prefix); } if (!\in_array($cabinName, $this->getCabinNamespaces())) { // Invalid cabin name \Airship\redirect($this->airship_cabin_prefix . '/cabins'); } $this->setTemplateExtraData($cabinName); if (!$this->ensureCabinLinkExists($cabinName)) { \Airship\json_response(['error' => 'Could not create symlink']); } $cabin = \Airship\loadJSON(ROOT . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'cabins.json'); // Apply the cabin's input filter: $filterName = '\\Airship\\Cabin\\' . $cabinName . '\\ConfigFilter'; if (\class_exists($filterName)) { $filter = new $filterName(); } else { $filter = (new GeneralFilterContainer())->addFilter('content_security_policy', new ArrayFilter()); } $post = $this->post($filter); if (!empty($post)) { if ($this->saveSettings($cabinName, $cabin, $post)) { \Airship\redirect($this->airship_cabin_prefix . '/cabins/manage/' . $cabinName); } } $settings = []; foreach ($cabin as $path => $data) { if ($data['name'] === $cabinName) { $settings['cabin'] = $data; $settings['cabin']['path'] = $path; break; } } if (empty($settings['cabin'])) { // Cabin not found \Airship\redirect($this->airship_cabin_prefix); } $settings['content_security_policy'] = $this->loadJSONConfigFile($cabinName, 'content_security_policy.json'); $settings['cabin_extra'] = \Airship\loadJSON(ROOT . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'Cabin' . DIRECTORY_SEPARATOR . $cabinName . DIRECTORY_SEPARATOR . 'config.json'); $gadgets = ROOT . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'Cabin' . DIRECTORY_SEPARATOR . $cabinName . DIRECTORY_SEPARATOR . 'gadgets.json'; if (!\file_exists($gadgets)) { \file_put_contents($gadgets, '[]'); } $settings['gadgets'] = \Airship\loadJSON($gadgets); $settings['motifs'] = $this->getCabinsMotifs($cabinName); $settings['twig_vars'] = \Airship\loadJSON(ROOT . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'Cabin' . DIRECTORY_SEPARATOR . $cabinName . DIRECTORY_SEPARATOR . 'twig_vars.json'); $this->lens('cabin_manage', ['name' => $cabinName, 'config' => $settings]); }
/** * @route notary/verify */ public function verify() { // Input validation if (empty($_POST['challenge'])) { \Airship\json_response(['status' => 'error', 'message' => 'Expected a challenge=something HTTP POST parameter.']); } if (!\is_string($_POST['challenge'])) { \Airship\json_response(['status' => 'error', 'message' => 'Challenge must be a string.']); } if (Binary::safeStrlen($_POST['challenge']) < 20) { \Airship\json_response(['status' => 'error', 'message' => 'Challenge is too short. Continuum should be generating a long random nonce.']); } try { list($update, $signature) = $this->chanUp->verifyUpdate($this->sk, $_POST['challenge']); \Airship\json_response(['status' => 'OK', 'response' => $update, 'signature' => $signature]); } catch (\Exception $ex) { \Airship\json_response(['status' => 'error', 'message' => $ex->getMessage()]); } }
/** * Trigger the package install process */ public function updatePackage() { $expected = ['package', 'supplier', 'type', 'version']; if (!\Airship\all_keys_exist($expected, $_POST)) { \Airship\json_response(['status' => 'ERROR', 'message' => \__('Incomplete request.')]); } try { $filter = new SkyportFilter(); $_POST = $filter($_POST); } catch (\TypeError $ex) { $this->log("Input violation", LogLevel::ALERT, \Airship\throwableToArray($ex)); \Airship\json_response(['status' => 'ERROR', 'message' => \__('Invalid input.')]); } /** * @security We need to guarantee RCE isn't possible: */ $args = \implode(' ', [\escapeshellarg(Util::charWhitelist($_POST['type'], Util::PRINTABLE_ASCII)), \escapeshellarg(Util::charWhitelist($_POST['supplier'], Util::PRINTABLE_ASCII) . '/' . Util::charWhitelist($_POST['package'], Util::PRINTABLE_ASCII)), \escapeshellarg(Util::charWhitelist($_POST['version'], Util::PRINTABLE_ASCII))]); $output = \shell_exec('php -dphar.readonly=0 ' . ROOT . '/CommandLine/update_one.sh ' . $args); \Airship\json_response(['status' => 'OK', 'message' => $output]); }
/** * Make sure the secret exists, then get the GoogleAuth object * * @param int $userID * @return GoogleAuth * @throws \Airship\Alerts\Security\UserNotLoggedIn */ protected function twoFactorPreamble(int $userID = 0) : GoogleAuth { if (!$userID) { $userID = $this->getActiveUserId(); } $secret = $this->acct->getTwoFactorSecret($userID); if (empty($secret)) { if (!$this->acct->resetTwoFactorSecret($userID)) { \Airship\json_response(['test2']); \Airship\redirect($this->airship_cabin_prefix); } $secret = $this->acct->getTwoFactorSecret($userID); } return new GoogleAuth($secret, new TOTP(0, (int) ($this->config('two-factor.period') ?? 30), (int) ($this->config('two-factor.length') ?? 6))); }
/** * @route ajax/authors_save_photo */ public function saveAuthorsPhoto() { $auth_bp = $this->blueprint('Author'); if (IDE_HACKS) { $db = \Airship\get_database(); $auth_bp = new Author($db); } $authorId = (int) $_POST['author']; if (!$this->isSuperUser()) { $authors = $auth_bp->getAuthorIdsForUser($this->getActiveUserId()); if (!\in_array($authorId, $authors)) { \Airship\json_response(['status' => 'ERROR', 'message' => \__('You do not have permission to access this author\'s posts.')]); } } if (!\Airship\all_keys_exist(['cabin', 'context', 'author', 'filename'], $_POST)) { \Airship\json_response(['keys' => array_keys($_POST), 'status' => 'ERROR', 'message' => 'Insufficient parameters']); } $result = $auth_bp->savePhotoChoice($authorId, $_POST['context'], $_POST['cabin'], $_POST['filename']); if (!$result) { \Airship\json_response(['status' => 'ERROR', 'message' => 'Could not save photo choice.', 'photo' => null]); } \Airship\json_response(['status' => 'OK', 'message' => 'Saved!']); }
/** * @param CacheInterface $cache * @param string $uniqueID */ protected function fetchComments(CacheInterface $cache, string $uniqueID) { $blog = $this->blog->getBlogPostByUniqueId($uniqueID); $comments = $this->blog->getCommentTree((int) $blog['postid']); $contents = $this->lensRender('blog/comments', ['blogpost' => $blog, 'comments' => $comments, 'config' => $this->config()]); $cache->set($uniqueID, ['status' => 'OK', 'cached' => $contents]); \Airship\json_response(['status' => 'OK', 'cached' => $contents]); }