Example #1
0
 /**
  * @route file_manager/{string}/move
  * @param string $cabin
  */
 public function moveFile(string $cabin = '')
 {
     $dir = $this->determinePath($cabin);
     if (!\in_array($cabin, $this->getCabinNamespaces())) {
         \Airship\redirect($this->airship_cabin_prefix);
     }
     $this->setTemplateExtraData($cabin);
     if (empty($_GET['file'])) {
         $this->commonMoveDir($dir, $cabin);
         return;
     }
     $this->commonMoveFile($_GET['file'], $dir, $cabin);
 }
Example #2
0
 /**
  * @route gadgets/universal
  */
 public function manageUniversal()
 {
     $cabins = $this->getCabinNamespaces();
     $gadgets = \Airship\loadJSON(ROOT . '/config/gadgets.json');
     if (!$this->can('update')) {
         \Airship\redirect($this->airship_cabin_prefix . '/gadgets');
     }
     $post = $this->post(GadgetsFilter::fromConfig(\array_keys($gadgets)));
     if ($post) {
         if ($this->updateUniversalGadgets($gadgets, $post)) {
             \Airship\clear_cache();
             \Airship\redirect($this->airship_cabin_prefix . '/gadgets/universal');
         }
     }
     $this->lens('gadget_manage', ['cabins' => $cabins, 'gadgets' => $gadgets, 'title' => \__('Manage Universal Gadgets')]);
 }
Example #3
0
 /**
  * @route motifs/{string}
  *
  * @param string $cabinName
  */
 public function manage(string $cabinName = '')
 {
     $cabins = $this->getCabinNamespaces();
     if (!\in_array($cabinName, $cabins)) {
         \Airship\redirect($this->airship_cabin_prefix . '/motifs');
     }
     if (!$this->can('update')) {
         \Airship\redirect($this->airship_cabin_prefix . '/motifs');
     }
     $motifs = \Airship\loadJSON(ROOT . '/Cabin/' . $cabinName . '/config/motifs.json');
     $post = $this->post(MotifsFilter::fromConfig(\array_keys($motifs)));
     if ($post) {
         if ($this->updateMotifs($motifs, $post, $cabinName)) {
             \Airship\clear_cache();
             \Airship\redirect($this->airship_cabin_prefix . '/motifs/cabin/' . $cabinName);
         }
     }
     $this->lens('motif_manage', ['cabin_name' => $cabinName, 'cabins' => $cabins, 'motifs' => $motifs, 'title' => \__('Motifs for %s', 'default', Util::noHTML($cabinName))]);
 }
Example #4
0
 /**
  * @route author/files/{id}/{string}/move
  * @param string $authorId
  * @param string $cabin
  */
 public function moveFile(string $authorId, string $cabin = '')
 {
     $this->loadAuthorInfo((int) $authorId);
     $this->files->ensureDirExists($this->root_dir, $cabin);
     $this->files->ensureDirExists($this->root_dir . '/photos', $cabin);
     $dir = $this->determinePath($cabin);
     if (!\in_array($cabin, $this->getCabinNamespaces())) {
         \Airship\redirect($this->airship_cabin_prefix);
     }
     if (empty($_GET['file'])) {
         return $this->commonMoveDir($dir, $cabin);
     }
     return $this->commonMoveFile($_GET['file'], $dir, $cabin);
 }
Example #5
0
if (@\is_readable(dirname(__DIR__) . '/tmp/installing.json') || !\file_exists(dirname(__DIR__) . '/config/databases.json')) {
    include dirname(__DIR__) . '/Installer/launch.php';
    exit;
}
/**
 * Load the bare minimum:
 */
require_once \dirname(__DIR__) . '/preload.php';
$start = \microtime(true);
if (empty($_POST)) {
    /**
     * Let's get rid of trailing slashes in URLs without POST data
     */
    $sliceAt = Binary::safeStrlen($_SERVER['REQUEST_URI']) - 1;
    if ($sliceAt > 0 && $_SERVER['REQUEST_URI'][$sliceAt] === '/') {
        \Airship\redirect('/' . \trim($_SERVER['REQUEST_URI'], '/'));
    }
    /**
     * Let's handle static content caching
     */
    if (\extension_loaded('apcu')) {
        $staticCache = (new MemoryCache())->personalize('staticPage:');
        $cspCache = (new MemoryCache())->personalize('contentSecurityPolicy:');
    } else {
        if (!\is_dir(ROOT . '/tmp/cache/static')) {
            require_once ROOT . '/tmp_dirs.php';
        }
        $staticCache = new FileCache(ROOT . '/tmp/cache/static');
        $cspCache = new FileCache(ROOT . '/tmp/cache/csp_static');
    }
    $port = $_SERVER['HTTP_PORT'] ?? '';
Example #6
0
 /**
  * Do not allow insecure HTTP request to proceed
  *
  * @param string $scheme
  * @return bool
  */
 protected static function forceHTTPS(string $scheme = '') : bool
 {
     if (!self::isHTTPSConnection($scheme)) {
         // Should we redirect to an HTTPS endpoint?
         \Airship\redirect('https://' . $_SERVER['HTTP_HOST'] . '/' . $_SERVER['REQUEST_URI'], $_GET ?? []);
     }
     return true;
 }
Example #7
0
 /**
  * The last phase of the installer
  */
 protected function finalShutdown()
 {
     $this->autoSave = false;
     $this->finalDefaultPages();
     \unlink(ROOT . '/tmp/installing.json');
     foreach (\glob(ROOT . '/tmp/cache/*.json') as $f) {
         \unlink($f);
     }
     \Airship\redirect('/');
 }
Example #8
0
 /**
  * Manage the users that have access to this author
  *
  * @route author/users/{id}
  * @param string $authorId
  */
 public function users(string $authorId = '')
 {
     $authorId = (int) $authorId;
     if ($this->isSuperUser()) {
         $inCharge = true;
     } else {
         $authorsForUser = $this->author->getAuthorIdsForUser($this->getActiveUserId());
         // Check
         if (!\in_array($authorId, $authorsForUser)) {
             \Airship\redirect($this->airship_cabin_prefix . '/author');
         }
         $inCharge = $this->author->userIsOwner($authorId);
     }
     // Only someone in charge can add/remove users:
     if ($inCharge) {
         $post = $this->post(new UsersFilter());
         if ($post) {
             if ($this->manageAuthorUsers($authorId, $post)) {
                 \Airship\redirect($this->airship_cabin_prefix . '/author/users/' . $authorId);
             }
         }
     }
     $this->lens('author/users', ['author' => $this->author->getById($authorId), 'inCharge' => $inCharge, 'users' => $this->author->getUsersForAuthor($authorId)]);
 }
Example #9
0
 /**
  * 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]);
 }
Example #10
0
 /**
  * Edit a user's information
  *
  * @route crew/users/edit/{id}
  * @param string $userId
  */
 public function editUser(string $userId = '')
 {
     $userId = (int) $userId;
     $user = $this->account->getUserAccount($userId, true);
     $post = $this->post(new EditUserFilter());
     if ($post) {
         if ($this->account->editUser($userId, $post)) {
             \Airship\redirect($this->airship_cabin_prefix . '/crew/users');
         }
     }
     $this->lens('crew/user_edit', ['active_link' => 'bridge-link-admin-crew-users', 'user' => $user, 'groups' => $this->account->getGroupTree()]);
 }
Example #11
0
 /**
  * @route admin/settings
  */
 public function manageSettings()
 {
     $state = State::instance();
     $settings = ['universal' => $state->universal];
     $post = $this->post(new SettingsFilter());
     if (!empty($post)) {
         if ($this->saveSettings($post)) {
             \Airship\clear_cache();
             \Airship\redirect($this->airship_cabin_prefix . '/admin/settings', ['msg' => 'saved']);
         } else {
             $this->log('Could not save new settings', LogLevel::ALERT);
         }
     }
     // Load individual files...
     $settings['cabins'] = $this->loadJSONConfigFile('cabins.json');
     $settings['content_security_policy'] = $this->loadJSONConfigFile('content_security_policy.json');
     $settings['keyring'] = $this->loadJSONConfigFile('keyring.json');
     foreach (\Airship\list_all_files(ROOT . '/config/supplier_keys/', 'json') as $supplier) {
         $name = \Airship\path_to_filename($supplier, true);
         $settings['suppliers'][$name] = \Airship\loadJSON($supplier);
     }
     $this->lens('admin_settings', ['active_link' => 'bridge-link-admin-settings', 'config' => $settings, 'groups' => $this->acct->getGroupTree()]);
 }
Example #12
0
 /**
  * Create a new page in the current directory
  *
  * @param string $cabin
  * @param string $path
  * @param array $post
  * @return mixed
  */
 protected function processNewPage(string $cabin, string $path, array $post = []) : bool
 {
     $expected = ['url', 'format', 'page_body', 'save_btn', 'metadata'];
     if (!\Airship\all_keys_exist($expected, $post)) {
         return false;
     }
     $url = $path . '/' . \str_replace('/', '_', $post['url']);
     if (!empty($post['ignore_collisions']) && $this->detectCollisions($url, $cabin)) {
         $this->storeLensVar('post_response', ['message' => \__('The given filename might conflict with another route in this Airship.'), 'status' => 'error']);
         return false;
     }
     $raw = $this->isSuperUser() ? !empty($post['raw']) : false;
     if ($this->can('publish')) {
         $publish = $post['save_btn'] === 'publish';
     } elseif ($this->can('create')) {
         $publish = false;
     } else {
         $this->storeLensVar('post_response', ['message' => \__('You do not have permission to create new pages.'), 'status' => 'error']);
         return false;
     }
     if ($this->pg->createPage($cabin, $path, $post, $publish, $raw)) {
         \Airship\redirect($this->airship_cabin_prefix . '/pages/' . $cabin, ['dir' => $path]);
     }
     return true;
 }
Example #13
0
 /**
  * Handle short URLs
  *
  * @param string $uniqueID
  * @route b/(.*)
  */
 public function shortURL(string $uniqueID = '')
 {
     if (empty($uniqueID)) {
         \Airship\redirect($this->airship_cabin_prefix . '/blog');
     }
     $long = $this->blog->longURL($uniqueID);
     \Airship\redirect($this->airship_cabin_prefix . '/blog' . $long);
 }
Example #14
0
 /**
  * If a redirect exists at this path, serve it.
  *
  * @param string $uri
  * @return bool
  */
 public function serveRedirect(string $uri) : bool
 {
     $lookup = $this->db->row('SELECT * FROM airship_custom_redirect WHERE oldpath = ?', $uri);
     if (empty($lookup)) {
         return false;
     }
     if ($lookup['same_cabin']) {
         // Internal redirects only. Don't create open redirect vulnerabilities.
         \Airship\redirect(\Airship\LensFunctions\cabin_url($lookup['cabin']) . \trim($lookup['newpath'], '/'));
     }
     // Cross-cabin redirects can point to other domains.
     \Airship\redirect(\rtrim($lookup['newpath'], '/'));
     return true;
 }
Example #15
0
 /**
  * Reduce code duplication
  *
  * @param string $path
  * @param string $cabin
  * @return array (array $publicPath, int|null $root)
  */
 protected function loadCommonData(string $path, string $cabin) : array
 {
     if (!$this->permCheck()) {
         \Airship\redirect($this->airship_cabin_prefix);
     }
     $root = null;
     $publicPath = \Airship\chunk($path);
     $pathInfo = $this->getPath($path);
     if (!empty($pathInfo)) {
         try {
             $root = $this->files->getDirectoryId($pathInfo, $cabin);
         } catch (FileNotFound $ex) {
             \Airship\redirect($this->airship_cabin_prefix);
         }
     }
     // return [$pathInfo, $publicPath, $root];
     return [$publicPath, $root];
 }
Example #16
0
 /**
  * Create a new redirect
  *
  * @param string $cabin
  * @route redirects/{string}/new
  */
 public function newRedirect(string $cabin)
 {
     $cabins = $this->getCabinNamespaces();
     if (!\in_array($cabin, $cabins) && !$this->can('create')) {
         \Airship\redirect($this->airship_cabin_prefix . '/redirects');
     }
     $this->setTemplateExtraData($cabin);
     $post = $this->post(new RedirectFilter());
     if ($post) {
         if (\Airship\all_keys_exist(['old_url', 'new_url'], $post)) {
             if (\preg_match('#^https?://#', $post['new_url'])) {
                 // Less restrictions:
                 $result = $this->pg->createDifferentCabinRedirect(\trim($post['old_url'], '/'), \trim($post['new_url'], '/'), $cabin);
             } else {
                 $result = $this->pg->createSameCabinRedirect(\trim($post['old_url'], '/'), \trim($post['new_url'], '/'), $cabin);
             }
             if ($result) {
                 \Airship\redirect($this->airship_cabin_prefix . '/redirects/' . $cabin);
             }
         }
     }
     $this->lens('redirect/new', ['cabin' => $cabin]);
 }
Example #17
0
 /**
  * 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)));
 }
Example #18
0
 /**
  * @route help
  */
 public function helpPage()
 {
     if ($this->isLoggedIn()) {
         $this->storeLensVar('showmenu', true);
         //
         $cabins = $this->getCabinNamespaces();
         // Get debug information.
         $helpInfo = ['cabins' => [], 'cabin_names' => \array_values($cabins), 'gears' => [], 'universal' => []];
         /**
          * This might reveal "sensitive" information. By default, it's
          * locked out of non-administrator users. You can grant access to
          * other users/groups via the Permissions menu.
          */
         if ($this->can('read')) {
             $state = State::instance();
             if (\is_readable(ROOT . '/config/gadgets.json')) {
                 $helpInfo['universal']['gadgets'] = \Airship\loadJSON(ROOT . '/config/gadgets.json');
             }
             if (\is_readable(ROOT . '/config/content_security_policy.json')) {
                 $helpInfo['universal']['content_security_policy'] = \Airship\loadJSON(ROOT . '/config/content_security_policy.json');
             }
             foreach ($cabins as $cabin) {
                 $cabinData = ['config' => \Airship\loadJSON(ROOT . '/Cabin/' . $cabin . '/manifest.json'), 'content_security_policy' => [], 'gadgets' => [], 'motifs' => [], 'user_motifs' => \Airship\LensFunctions\user_motif($this->getActiveUserId(), $cabin)];
                 $prefix = ROOT . '/Cabin/' . $cabin . '/config/';
                 if (\is_readable($prefix . 'gadgets.json')) {
                     $cabinData['gadgets'] = \Airship\loadJSON($prefix . 'gadgets.json');
                 }
                 if (\is_readable($prefix . 'motifs.json')) {
                     $cabinData['motifs'] = \Airship\loadJSON($prefix . 'motifs.json');
                 }
                 if (\is_readable($prefix . 'content_security_policy.json')) {
                     $cabinData['content_security_policy'] = \Airship\loadJSON($prefix . 'content_security_policy.json');
                 }
                 $helpInfo['cabins'][$cabin] = $cabinData;
             }
             $helpInfo['gears'] = [];
             foreach ($state->gears as $gear => $latestGear) {
                 $helpInfo['gears'][$gear] = \Airship\get_ancestors($latestGear);
             }
             // Only grab data likely to be pertinent to common issues:
             $keys = ['airship', 'auto-update', 'debug', 'guzzle', 'notary', 'rate-limiting', 'session_config', 'tor-only', 'twig_cache'];
             $helpInfo['universal']['config'] = \Airship\keySlice($state->universal, $keys);
             $helpInfo['php'] = ['halite' => Halite::VERSION, 'libsodium' => ['major' => \Sodium\library_version_major(), 'minor' => \Sodium\library_version_minor(), 'version' => \Sodium\version_string()], 'version' => \PHP_VERSION, 'versionid' => \PHP_VERSION_ID];
         }
         $this->lens('help', ['active_link' => 'bridge-link-help', 'airship' => \AIRSHIP_VERSION, 'helpInfo' => $helpInfo]);
     } else {
         // Not a registered user? Go read the docs. No info leaks for you!
         \Airship\redirect('https://github.com/paragonie/airship-docs');
     }
 }
Example #19
0
 /**
  * @route crew/permissions/{string}/context/{id}
  *
  * @param string $cabin
  * @param string $contextId
  */
 public function editContext(string $cabin, string $contextId)
 {
     $contextId = (int) $contextId;
     if (!\in_array($cabin, $this->getCabinNamespaces())) {
         \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions');
     }
     $context = $this->perms->getContext($contextId, $cabin);
     if (empty($context)) {
         \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions' . $cabin);
     }
     // Handle post data
     $post = $this->post(new SaveContextFilter());
     if (!empty($post)) {
         if ($this->perms->saveContext($cabin, $contextId, $post)) {
             \Airship\redirect($this->airship_cabin_prefix . '/crew/permissions/' . $cabin . '/context/' . $contextId, ['msg' => 'saved']);
         }
     }
     // Okay,
     $actions = $this->perms->getActionNames($cabin);
     $groupPerms = $this->perms->buildGroupTree($cabin, $contextId, $actions);
     $userPerms = $this->perms->buildUserList($cabin, $contextId, $actions);
     $users = [];
     foreach ($userPerms as $userid => $userPerm) {
         $userid = (int) $userid;
         $users[$userid] = $this->users->getUserAccount($userid, true);
         unset($users[$userid]['password']);
     }
     if (!empty($_GET['msg'])) {
         if ($_GET['msg'] === 'saved') {
             $this->storeLensVar('message', \__('Your changes have been saved.'));
         }
     }
     $this->lens('perms/context', ['actions' => $actions, 'cabin' => $cabin, 'context' => $context, 'permissions' => $groupPerms, 'userperms' => $userPerms, 'users' => $users]);
 }
Example #20
0
 /**
  * View a comment
  *
  * @param string $commentId
  * @route blog/comments/view/{id}
  */
 public function viewComment(string $commentId = '')
 {
     $commentId = (int) $commentId;
     $post = $this->post(new CommentFilter());
     if (!empty($post)) {
         switch ($post['comment_btn']) {
             case 'publish':
                 if ($this->can('publish')) {
                     $this->blog->publishComment($commentId);
                 }
                 break;
             case 'hide':
                 if ($this->can('publish')) {
                     $this->blog->hideComment($commentId);
                 }
                 break;
             case 'delete':
                 if ($this->can('delete')) {
                     if ($this->blog->deleteComment($commentId)) {
                         \Airship\redirect($this->airship_cabin_prefix . '/blog/comments');
                     }
                 }
                 break;
         }
     }
     $this->lens('blog/comments_view', ['active_link' => 'bridge-link-blog-comments', 'comment' => $this->blog->getCommentById((int) $commentId)]);
 }
Example #21
0
 /**
  * This function is called after the dependencies have been injected by
  * AutoPilot. Think of it as a user-land constructor.
  */
 public function airshipLand()
 {
     parent::airshipLand();
     if (!$this->isSuperUser()) {
         \Airship\redirect($this->airship_cabin_prefix);
     }
 }