public function doUploadSnapshot($data, $form) { // Performs canView permission check by limiting visible projects $project = $this->getCurrentProject(); if (!$project) { return new SS_HTTPResponse("Project '" . Convert::raw2xml($this->getRequest()->latestParam('Project')) . "' not found.", 404); } $validEnvs = $project->DNEnvironmentList()->filterByCallback(function ($item) { return $item->canUploadArchive(); }); // Validate $data['EnvironmentID'] by checking against $validEnvs. $environment = $validEnvs->find('ID', $data['EnvironmentID']); if (!$environment) { throw new LogicException('Invalid environment'); } // Validate mode. if (!in_array($data['Mode'], array('all', 'assets', 'db'))) { throw new LogicException('Invalid mode'); } $dataArchive = new DNDataArchive(array('AuthorID' => Member::currentUserID(), 'EnvironmentID' => $data['EnvironmentID'], 'IsManualUpload' => true)); // needs an ID and transfer to determine upload path $dataArchive->write(); $dataTransfer = new DNDataTransfer(array('AuthorID' => Member::currentUserID(), 'Mode' => $data['Mode'], 'Origin' => 'ManualUpload', 'EnvironmentID' => $data['EnvironmentID'])); $dataTransfer->write(); $dataArchive->DataTransfers()->add($dataTransfer); $form->saveInto($dataArchive); $dataArchive->write(); return $this->customise(array('Project' => $project, 'CurrentProject' => $project, 'SnapshotsSection' => 1, 'DataArchive' => $dataArchive, 'DataTransferRestoreForm' => $this->getDataTransferRestoreForm($this->request, $dataArchive), 'BackURL' => $project->Link('snapshots')))->renderWith(array('DNRoot_uploadsnapshot', 'DNRoot')); }
public function testCanMoveTo() { $samantha = $this->objFromFixture('Member', 'project1-samantha'); $sarah = $this->objFromFixture('Member', 'project1-sarah'); $eva = $this->objFromFixture('Member', 'eva'); $uat1 = $this->objFromFixture('DNEnvironment', 'project1-uat'); $live1 = $this->objFromFixture('DNEnvironment', 'project1-live'); $uat2 = $this->objFromFixture('DNEnvironment', 'project2-uat'); $live2 = $this->objFromFixture('DNEnvironment', 'project2-live'); $archive = new DNDataArchive(); $archive->EnvironmentID = $uat1->ID; $archive->write(); // Samantha doesn't have upload permission to live1. $this->assertFalse($archive->canMoveTo($live1, $samantha)); // Cross-project moves are forbidden. $this->assertFalse($archive->canMoveTo($uat2, $samantha)); // Eva has upload permission to live1. $this->assertTrue($archive->canMoveTo($live1, $eva)); // Cross-project moves are forbidden. $this->assertFalse($archive->canMoveTo($uat2, $eva)); // Sarah has upload permission to live1, but not download to uat1. $this->assertFalse($archive->canMoveTo($live1, $sarah)); }
/** * Backs up database and/or assets to a designated folder, * and packs up the files into a single sspak. * * @param DNDataTransfer $dataTransfer * @param DeploynautLogFile $log */ protected function dataTransferBackup(DNDataTransfer $dataTransfer, DeploynautLogFile $log) { $environmentObj = $dataTransfer->Environment(); $project = $environmentObj->Project(); $projectName = $project->Name; $environmentName = $environmentObj->Name; $env = $project->getProcessEnv(); $project = DNProject::get()->filter('Name', $projectName)->first(); $name = $projectName . ':' . $environmentName; // Associate a new archive with the transfer. // Doesn't retrieve a filepath just yet, need to generate the files first. $dataArchive = new DNDataArchive(); $dataArchive->Mode = $dataTransfer->Mode; $dataArchive->AuthorID = $dataTransfer->AuthorID; $dataArchive->OriginalEnvironmentID = $dataTransfer->Environment()->ID; $dataArchive->EnvironmentID = $dataTransfer->Environment()->ID; $dataArchive->IsBackup = $dataTransfer->IsBackupDataTransfer(); // Generate directory structure with strict permissions (contains very sensitive data) $filepathBase = $dataArchive->generateFilepath($dataTransfer); mkdir($filepathBase, 0700, true); $databasePath = $filepathBase . DIRECTORY_SEPARATOR . 'database.sql'; // Backup database if (in_array($dataTransfer->Mode, array('all', 'db'))) { $log->write('Backup of database from "' . $name . '" started'); $args = array('data_path' => $databasePath); $command = $this->getCommand("data:getdb", $name, $args, $env, $log); $command->run(function ($type, $buffer) use($log) { $log->write($buffer); }); if (!$command->isSuccessful()) { throw new RuntimeException($command->getErrorOutput()); } $log->write('Backup of database from "' . $name . '" done'); } // Backup assets if (in_array($dataTransfer->Mode, array('all', 'assets'))) { $log->write('Backup of assets from "' . $name . '" started'); $args = array('data_path' => $filepathBase); $command = $this->getCommand("data:getassets", $name, $args, $env, $log); $command->run(function ($type, $buffer) use($log) { $log->write($buffer); }); if (!$command->isSuccessful()) { throw new RuntimeException($command->getErrorOutput()); } $log->write('Backup of assets from "' . $name . '" done'); } $log->write('Creating *.sspak file'); $sspakFilename = sprintf('%s.sspak', $dataArchive->generateFilename($dataTransfer)); $sspakCmd = sprintf('cd %s && sspak saveexisting %s 2>&1', $filepathBase, $sspakFilename); if ($dataTransfer->Mode == 'db') { $sspakCmd .= sprintf(' --db=%s', $databasePath); } elseif ($dataTransfer->Mode == 'assets') { $sspakCmd .= sprintf(' --assets=%s/assets', $filepathBase); } else { $sspakCmd .= sprintf(' --db=%s --assets=%s/assets', $databasePath, $filepathBase); } $process = new Process($sspakCmd); $process->setTimeout(3600); $process->run(); if (!$process->isSuccessful()) { $log->write('Could not package the backup via sspak'); throw new RuntimeException($process->getErrorOutput()); } // HACK: find_or_make() expects path relative to assets/ $sspakFilepath = ltrim(str_replace(ASSETS_PATH, '', $filepathBase . DIRECTORY_SEPARATOR . $sspakFilename), DIRECTORY_SEPARATOR); try { $folder = Folder::find_or_make(dirname($sspakFilepath)); $file = new File(); $file->Name = $sspakFilename; $file->Filename = $sspakFilepath; $file->ParentID = $folder->ID; $file->write(); // "Status" will be updated by the job execution $dataTransfer->write(); // Get file hash to ensure consistency. // Only do this when first associating the file since hashing large files is expensive. $dataArchive->ArchiveFileHash = md5_file($file->FullPath); $dataArchive->ArchiveFileID = $file->ID; $dataArchive->DataTransfers()->add($dataTransfer); $dataArchive->write(); } catch (Exception $e) { $log->write('Failed to add sspak file: ' . $e->getMessage()); throw new RuntimeException($e->getMessage()); } // Remove any assets and db files lying around, they're not longer needed as they're now part // of the sspak file we just generated. Use --force to avoid errors when files don't exist, // e.g. when just an assets backup has been requested and no database.sql exists. $process = new Process(sprintf('rm -rf %s/assets && rm -f %s', $filepathBase, $databasePath)); $process->run(); if (!$process->isSuccessful()) { $log->write('Could not delete temporary files'); throw new RuntimeException($process->getErrorOutput()); } $log->write(sprintf('Creating *.sspak file done: %s', $file->getAbsoluteURL())); }