Ejemplo n.º 1
0
 /**
  * @route motif-config/{string}
  *
  * @param string $motifName
  */
 public function configure(string $motifName)
 {
     $motifs = $this->getAllMotifs();
     if (!\array_key_exists($motifName, $motifs)) {
         \Airship\redirect($this->airship_cabin_prefix . '/motifs');
     }
     if (!$this->can('update')) {
         \Airship\redirect($this->airship_cabin_prefix . '/motifs');
     }
     $selected = $motifs[$motifName];
     $path = ROOT . '/Motifs/' . $selected['supplier'] . '/' . $selected['name'];
     // Should we load overload the configuration lens?
     if (\file_exists($path . '/lens/config.twig')) {
         Gadgets::loadCargo('bridge_motifs_config_overloaded', 'motif/' . $motifName . '/config.twig');
     }
     $inputFilter = null;
     if (\file_exists($path . '/config_filter.php')) {
         $inputFilter = $this->getConfigFilter($path . '/config_filter.php');
     }
     try {
         $motifConfig = \Airship\loadJSON(ROOT . '/config/motifs/' . $motifName . '.json');
     } catch (\Throwable $ex) {
         $motifConfig = [];
     }
     // Handle POST data
     if ($inputFilter instanceof InputFilterContainer) {
         $post = $this->post($inputFilter);
     } else {
         $post = $this->post();
         if (\is_string($post['motif_config'])) {
             $post['motif_config'] = \Airship\parseJSON($post['motif_config'], true);
         }
     }
     if ($post) {
         if (empty($post['motif_config'])) {
             $post['motif_config'] = [];
         }
         if ($this->saveMotifConfig($motifName, $post['motif_config'])) {
             \Airship\redirect($this->airship_cabin_prefix . '/motif_config/' . $motifName);
         }
     }
     $this->lens('motif_configure', ['cabin_name' => $motifName, 'motifs' => $motifs, 'motif_config' => $motifConfig, 'title' => \__('Configuring %s/%s', 'default', Util::noHTML($selected['supplier']), Util::noHTML($selected['name']))]);
 }
Ejemplo n.º 2
0
 /**
  * Was the checksum of this update stored in Keyggdrasil?
  *
  * Dear future security auditors: This is important.
  *
  * @param UpdateInfo $info
  * @param UpdateFile $file
  * @return bool
  */
 public function checkKeyggdrasil(UpdateInfo $info, UpdateFile $file) : bool
 {
     $debugArgs = ['supplier' => $info->getSupplierName(), 'name' => $info->getPackageName(), 'root' => $info->getMerkleRoot()];
     $this->log('Checking Keyggdrasil', LogLevel::DEBUG, $debugArgs);
     $db = \Airship\get_database();
     $merkle = $db->row('SELECT * FROM airship_tree_updates WHERE merkleroot = ?', $info->getMerkleRoot());
     if (empty($merkle)) {
         $this->log('Merkle root not found in tree', LogLevel::DEBUG, $debugArgs);
         // Not found in Keyggdrasil
         return false;
     }
     $data = \Airship\parseJSON($merkle['data'], true);
     if ($data['action'] !== 'CORE') {
         if (!\hash_equals($this->type, $data['pkg_type'])) {
             $this->log('Wrong package type', LogLevel::DEBUG, $debugArgs);
             // Wrong package type
             return false;
         }
         if (!\hash_equals($info->getSupplierName(), $data['supplier'])) {
             $this->log('Wrong supplier', LogLevel::DEBUG, $debugArgs);
             // Wrong supplier
             return false;
         }
         if (!\hash_equals($info->getPackageName(), $data['name'])) {
             $this->log('Wrong package', LogLevel::DEBUG, $debugArgs);
             // Wrong package
             return false;
         }
     }
     $data = \Airship\parseJSON($merkle['data'], true);
     // Finally, we verify that the checksum matches the entry in our Merkle tree:
     return \hash_equals($file->getHash(), $data['checksum']);
 }
Ejemplo n.º 3
0
/**
 * Load a JSON file and parses it
 *
 * @param string $file - The absolute path of the file name
 * @return mixed
 * @throws AccessDenied
 * @throws FileNotFound
 */
function loadJSON(string $file)
{
    // Very specific checks
    if (!\file_exists($file)) {
        throw new FileNotFound($file);
    }
    if (!\is_readable($file)) {
        throw new AccessDenied($file);
    }
    // The meat of this function is kind of boring:
    return \Airship\parseJSON(\file_get_contents($file), true);
}
Ejemplo n.º 4
0
 /**
  * Verifies that the Merkle root exists, matches this package and version,
  * and has the same checksum as the one we calculated.
  *
  * @param InstallFile $file
  * @return bool
  */
 public function verifyMerkleRoot(InstallFile $file) : bool
 {
     $debugArgs = ['supplier' => $this->supplier->getName(), 'name' => $this->package];
     $db = \Airship\get_database();
     $merkle = $db->row('SELECT * FROM airship_tree_updates WHERE merkleroot = ?', $file->getMerkleRoot());
     if (empty($merkle)) {
         $this->log('Merkle root not found in tree', LogLevel::DEBUG, $debugArgs);
         // Not found in Keyggdrasil
         return false;
     }
     $data = \Airship\parseJSON($merkle['data'], true);
     $instType = \strtolower($this->type);
     $keyggdrasilType = \strtolower($data['pkg_type']);
     if (!\hash_equals($instType, $keyggdrasilType)) {
         $this->log('Wrong package type', LogLevel::DEBUG, $debugArgs);
         // Wrong package type
         return false;
     }
     if (!\hash_equals($this->supplier->getName(), $data['supplier'])) {
         $this->log('Wrong supplier', LogLevel::DEBUG, $debugArgs);
         // Wrong supplier
         return false;
     }
     if (!\hash_equals($this->package, $data['name'])) {
         $this->log('Wrong package', LogLevel::DEBUG, $debugArgs);
         // Wrong package
         return false;
     }
     // Finally, we verify that the checksum matches the entry in our Merkle tree:
     return \hash_equals($file->getHash(), $data['checksum']);
 }
Ejemplo n.º 5
0
 /**
  * @covers \Airship\parseJSON()
  */
 public function testParseJSON()
 {
     $this->assertEquals((object) ['a' => true], \Airship\parseJSON('{"a":true}'));
     $this->assertSame(['a' => true], \Airship\parseJSON('{"a":true}', true));
     $this->assertSame(['a' => true], \Airship\parseJSON('{"a":true/*, "b": false */}', true));
     $this->assertSame(['a' => true], \Airship\parseJSON('
             {
                 "a":true
                 /*, "b": false */
             }', true));
     $this->assertSame(['a' => true], \Airship\parseJSON('
             {
                 "a":true
                 //, "b": false
             }', true));
     $this->assertSame(['a' => true, 'b' => false], \Airship\parseJSON('
             {
                 "a":true,
                 "b": false
                 //, "c": false
             }', true));
 }
Ejemplo n.º 6
0
/**
 * Get the user's selected Motif
 *
 * @param int|null $userId
 * @param string $cabin
 * @return array
 */
function user_motif(int $userId = null, string $cabin = \CABIN_NAME) : array
{
    static $userCache = [];
    $state = State::instance();
    if (\count($state->motifs) === 0) {
        return [];
    }
    if (empty($userId)) {
        $userId = \Airship\LensFunctions\userid();
        if (empty($userId)) {
            $k = \array_keys($state->motifs)[0];
            return $state->motifs[$k] ?? [];
        }
    }
    // Did we cache these preferences?
    if (isset($userCache[$userId])) {
        return $state->motifs[$userCache[$userId]];
    }
    $db = \Airship\get_database();
    $userPrefs = $db->cell('SELECT preferences FROM airship_user_preferences WHERE userid = ?', $userId);
    if (empty($userPrefs)) {
        // Default
        $k = \array_keys($state->motifs)[0];
        $userCache[$userId] = $k;
        return $state->motifs[$k] ?? [];
    }
    $userPrefs = \Airship\parseJSON($userPrefs, true);
    if (isset($userPrefs['motif'][$cabin])) {
        $split = \explode('/', $userPrefs['motif'][$cabin]);
        foreach ($state->motifs as $k => $motif) {
            if (empty($motif['config'])) {
                continue;
            }
            if ($motif['supplier'] === $split[0] && $motif['name'] === $split[1]) {
                // We've found a match:
                $userCache[$userId] = $k;
                return $state->motifs[$k];
            }
        }
    }
    // When all else fails, go with the first one
    $k = \array_keys($state->motifs)[0];
    $userCache[$userId] = $k;
    return $state->motifs[$k] ?? [];
}
Ejemplo n.º 7
0
 /**
  * Perform a POST request, get a decoded JSON response.
  *
  * @param string $url
  * @param array $params
  * @return mixed
  */
 public function postJSON(string $url, array $params = [])
 {
     return \Airship\parseJSON($this->postReturnBody($url, $params), true);
 }
Ejemplo n.º 8
0
 /**
  * Motif install process.
  *
  * 1. Extract files to the appropriate directory.
  * 2. If this is a cabin-specific motif, update motifs.json.
  *    Otherwise, it's a global Motif. Enable for all cabins.
  * 3. Create symbolic links.
  * 4. Clear cache files.
  *
  * @param InstallFile $fileInfo
  * @return bool
  */
 public function install(InstallFile $fileInfo) : bool
 {
     $path = $fileInfo->getPath();
     $zip = new \ZipArchive();
     $res = $zip->open($path);
     if ($res !== true) {
         $this->log('Could not open the ZipArchive.', LogLevel::ERROR);
         return false;
     }
     // Extraction destination directory
     $dir = \implode(DIRECTORY_SEPARATOR, [ROOT, 'Motifs', $this->supplier->getName(), $this->package]);
     if (!\is_dir($dir)) {
         \mkdir($dir, 0775, true);
     }
     // Grab metadata
     $metadata = \Airship\parseJSON($zip->getArchiveComment(\ZipArchive::FL_UNCHANGED), true);
     if (isset($metadata['cabin'])) {
         $cabin = $this->expandCabinName($metadata['cabin']);
         if (!\is_dir(ROOT . '/Cabin/' . $cabin)) {
             $this->log('Could not install; cabin "' . $cabin . '" is not installed.', LogLevel::ERROR);
             return false;
         }
     } else {
         $cabin = null;
     }
     // Extract the new files to the current directory
     if (!$zip->extractTo($dir)) {
         $this->log('Could not extract Motif to its destination.', LogLevel::ERROR);
         return false;
     }
     // Add to the relevant motifs.json files
     if ($cabin) {
         $this->addMotifToCabin($cabin);
     } else {
         foreach (\glob(ROOT . '/Cabin/') as $cabin) {
             if (\is_dir($cabin)) {
                 $this->addMotifToCabin(\Airship\path_to_filename($cabin));
             }
         }
     }
     self::$continuumLogger->store(LogLevel::INFO, 'Motif install successful', $this->getLogContext($fileInfo));
     // Finally, nuke the cache:
     return $this->clearCache();
 }