trivago /
chapi
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * @package: chapi |
||
| 4 | * |
||
| 5 | * @author: msiebeneicher |
||
| 6 | * @since: 2015-07-29 |
||
| 7 | * |
||
| 8 | */ |
||
| 9 | |||
| 10 | namespace Chapi\Service\JobRepository; |
||
| 11 | |||
| 12 | use Chapi\Component\Cache\CacheInterface; |
||
| 13 | use Chapi\Entity\Chronos\ChronosJobEntity; |
||
| 14 | use Chapi\Entity\JobEntityInterface; |
||
| 15 | use Chapi\Entity\Marathon\MarathonAppEntity; |
||
| 16 | use Chapi\Exception\JobLoadException; |
||
| 17 | use Symfony\Component\Filesystem\Filesystem; |
||
| 18 | use Webmozart\Glob\Glob; |
||
| 19 | |||
| 20 | class BridgeFileSystem implements BridgeInterface |
||
| 21 | { |
||
| 22 | /** |
||
| 23 | * @var Filesystem |
||
| 24 | */ |
||
| 25 | private $fileSystemService; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @var CacheInterface |
||
| 29 | */ |
||
| 30 | private $cache; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * @var string |
||
| 34 | */ |
||
| 35 | private $repositoryDir = ''; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var string[] |
||
| 39 | */ |
||
| 40 | private $directorySeparators = ['.', ':', '-', '\\']; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @var array |
||
| 44 | */ |
||
| 45 | private $jobFileMap = []; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var array |
||
| 49 | */ |
||
| 50 | private $groupedApps = []; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @param Filesystem $oFileSystemService |
||
| 54 | * @param CacheInterface $cache |
||
| 55 | * @param string $repositoryDir |
||
| 56 | */ |
||
| 57 | 9 | public function __construct( |
|
| 58 | Filesystem $oFileSystemService, |
||
| 59 | CacheInterface $cache, |
||
| 60 | $repositoryDir |
||
| 61 | ) { |
||
| 62 | 9 | $this->fileSystemService = $oFileSystemService; |
|
| 63 | 9 | $this->cache = $cache; |
|
| 64 | 9 | $this->repositoryDir = $repositoryDir; |
|
| 65 | 9 | } |
|
| 66 | |||
| 67 | /** |
||
| 68 | * @return JobEntityInterface[] |
||
| 69 | */ |
||
| 70 | 8 | public function getJobs() |
|
| 71 | { |
||
| 72 | 8 | if (empty($this->jobFileMap)) { |
|
| 73 | 8 | $jobFiles = $this->getJobFilesFromFileSystem($this->repositoryDir); |
|
| 74 | 8 | return $this->loadJobsFromFileContent($jobFiles, true); |
|
| 75 | } |
||
| 76 | 4 | return $this->loadJobsFromFileContent($this->jobFileMap, false); |
|
| 77 | } |
||
| 78 | |||
| 79 | /** |
||
| 80 | * @param ChronosJobEntity|JobEntityInterface $jobEntity |
||
| 81 | * @return bool |
||
| 82 | * @throws JobLoadException |
||
| 83 | */ |
||
| 84 | 2 | public function addJob(JobEntityInterface $jobEntity) |
|
| 85 | { |
||
| 86 | // generate job file path by name |
||
| 87 | 2 | $jobFile = $this->generateJobFilePath($jobEntity); |
|
| 88 | |||
| 89 | 2 | if ($this->hasDumpFile($jobFile, $jobEntity)) { |
|
| 90 | 2 | $this->setJobFileToMap($jobEntity->getKey(), $jobFile); |
|
| 91 | 2 | return true; |
|
| 92 | } |
||
| 93 | |||
| 94 | return false; |
||
| 95 | } |
||
| 96 | |||
| 97 | /** |
||
| 98 | * @param JobEntityInterface $jobEntity |
||
| 99 | * @return bool |
||
| 100 | */ |
||
| 101 | 3 | public function updateJob(JobEntityInterface $jobEntity) |
|
| 102 | { |
||
| 103 | 3 | if (in_array($jobEntity->getKey(), $this->groupedApps)) { |
|
| 104 | // marathon's group case where app belongs to a group file |
||
| 105 | 1 | return $this->dumpFileWithGroup( |
|
| 106 | 1 | $this->getJobFileFromMap($jobEntity->getKey()), |
|
| 107 | 1 | $jobEntity |
|
| 108 | ); |
||
| 109 | } |
||
| 110 | 2 | return $this->hasDumpFile( |
|
| 111 | 2 | $this->getJobFileFromMap($jobEntity->getKey()), |
|
| 112 | 2 | $jobEntity |
|
| 113 | ); |
||
| 114 | } |
||
| 115 | |||
| 116 | /** |
||
| 117 | * @param ChronosJobEntity|JobEntityInterface $jobEntity |
||
| 118 | * @return bool |
||
| 119 | */ |
||
| 120 | 3 | public function removeJob(JobEntityInterface $jobEntity) |
|
| 121 | { |
||
| 122 | 3 | if (in_array($jobEntity->getKey(), $this->groupedApps)) { |
|
| 123 | 1 | $jobFile = $this->getJobFileFromMap($jobEntity->getKey()); |
|
| 124 | 1 | $this->dumpFileWithGroup( |
|
| 125 | 1 | $jobFile, |
|
| 126 | 1 | $jobEntity, |
|
| 127 | 1 | false |
|
| 128 | ); |
||
| 129 | |||
| 130 | 1 | unset($this->jobFileMap[$jobEntity->getKey()]); |
|
| 131 | 1 | return true; |
|
| 132 | } |
||
| 133 | |||
| 134 | 2 | $jobFile = $this->getJobFileFromMap($jobEntity->getKey()); |
|
| 135 | 2 | $this->fileSystemService->remove($jobFile); |
|
| 136 | |||
| 137 | 2 | return $this->hasUnsetJobFileFromMap($jobEntity->getKey(), $jobFile); |
|
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * @param JobEntityInterface $jobEntity |
||
| 142 | * @return string |
||
| 143 | */ |
||
| 144 | 2 | private function generateJobFilePath(JobEntityInterface $jobEntity) |
|
| 145 | { |
||
| 146 | 2 | if ($jobEntity->getEntityType() == JobEntityInterface::CHRONOS_TYPE) { |
|
| 147 | 1 | $jobPath = str_replace( |
|
| 148 | 1 | $this->directorySeparators, |
|
| 149 | 1 | DIRECTORY_SEPARATOR, |
|
| 150 | 1 | $jobEntity->getKey() |
|
| 151 | ); |
||
| 152 | } else { |
||
| 153 | 1 | $jobPath = $jobEntity->getKey(); |
|
| 154 | } |
||
| 155 | |||
| 156 | 2 | return $this->repositoryDir . DIRECTORY_SEPARATOR . $jobPath . '.json'; |
|
| 157 | } |
||
| 158 | |||
| 159 | /** |
||
| 160 | * @param string $path |
||
| 161 | * @param array $jobFiles |
||
| 162 | * @return array |
||
| 163 | */ |
||
| 164 | 8 | private function getJobFilesFromFileSystem($path, array &$jobFiles = []) |
|
| 165 | { |
||
| 166 | 8 | if (!is_dir($path)) { |
|
| 167 | throw new \RuntimeException(sprintf('Path "%s" is not valid', $path)); |
||
| 168 | } |
||
| 169 | |||
| 170 | 8 | $temp = Glob::glob(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*'); |
|
| 171 | |||
| 172 | 8 | foreach ($temp as $path) { |
|
| 173 | 7 | if (is_file($path) && preg_match('~\.json~i', $path)) { |
|
| 174 | 6 | $jobFiles[] = $path; |
|
| 175 | 6 | } elseif (is_dir($path)) { |
|
| 176 | 7 | $this->getJobFilesFromFileSystem($path, $jobFiles); |
|
| 177 | } |
||
| 178 | } |
||
| 179 | |||
| 180 | 8 | return $jobFiles; |
|
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * @param string $jobName |
||
| 185 | * @param string $jobFile |
||
| 186 | * @throws JobLoadException |
||
| 187 | */ |
||
| 188 | 7 | private function setJobFileToMap($jobName, $jobFile) |
|
| 189 | { |
||
| 190 | // set path to job file map |
||
| 191 | 7 | if (isset($this->jobFileMap[$jobName])) { |
|
| 192 | 1 | throw new JobLoadException( |
|
| 193 | 1 | sprintf('The jobname "%s" already exists. Jobnames have to be unique - Please check your local jobfiles for duplicate entries.', $jobName), |
|
| 194 | 1 | JobLoadException::ERROR_CODE_DUPLICATE_JOB_ID |
|
| 195 | ); |
||
| 196 | } |
||
| 197 | |||
| 198 | 7 | $this->jobFileMap[$jobName] = $jobFile; |
|
| 199 | 7 | } |
|
| 200 | |||
| 201 | /** |
||
| 202 | * @param string $jobName |
||
| 203 | * @return string |
||
| 204 | * @throws \RuntimeException |
||
| 205 | */ |
||
| 206 | 4 | private function getJobFileFromMap($jobName) |
|
| 207 | { |
||
| 208 | 4 | if (!isset($this->jobFileMap[$jobName])) { |
|
| 209 | throw new \RuntimeException(sprintf('Can\'t find file for job "%s"', $jobName)); |
||
| 210 | } |
||
| 211 | 4 | return $this->jobFileMap[$jobName]; |
|
| 212 | } |
||
| 213 | |||
| 214 | /** |
||
| 215 | * @param string $jobName |
||
| 216 | * @param string $jobFile |
||
| 217 | * @return bool |
||
| 218 | * @throws \RuntimeException |
||
| 219 | */ |
||
| 220 | 2 | private function hasUnsetJobFileFromMap($jobName, $jobFile = '') |
|
| 221 | { |
||
| 222 | 2 | $jobFile = (!empty($jobFile)) ? $jobFile : $this->getJobFileFromMap($jobName); |
|
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
| 223 | 2 | if (file_exists($jobFile)) { |
|
| 224 | throw new \RuntimeException(sprintf('Job file "%s" for job "%s" still exists.', $jobFile, $jobName)); |
||
| 225 | } |
||
| 226 | |||
| 227 | // unset path from job file map |
||
| 228 | 2 | unset($this->jobFileMap[$jobName]); |
|
| 229 | 2 | return true; |
|
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * @param array $jobFiles |
||
| 234 | * @param bool $setToFileMap |
||
| 235 | * @return JobEntityInterface[] |
||
| 236 | * @throws JobLoadException |
||
| 237 | */ |
||
| 238 | 8 | private function loadJobsFromFileContent(array $jobFiles, $setToFileMap) |
|
| 239 | { |
||
| 240 | 8 | $jobs = []; |
|
| 241 | |||
| 242 | 8 | foreach ($jobFiles as $jobFilePath) { |
|
| 243 | 8 | $jobEntities = []; |
|
| 244 | // remove comment blocks |
||
| 245 | 8 | $temp = json_decode( |
|
| 246 | 8 | preg_replace( |
|
| 247 | 8 | '~\/\*(.*?)\*\/~mis', |
|
| 248 | 8 | '', |
|
| 249 | 8 | file_get_contents($jobFilePath) |
|
| 250 | ) |
||
| 251 | ); |
||
| 252 | |||
| 253 | 8 | if ($temp) { |
|
| 254 | // chronos |
||
| 255 | 7 | if (property_exists($temp, 'name')) { |
|
| 256 | 4 | $jobEntities[] = new ChronosJobEntity($temp); |
|
| 257 | } //marathon |
||
| 258 | 4 | elseif (property_exists($temp, 'id')) { |
|
| 259 | 4 | foreach ($this->getMarathonEntitiesForConfig($temp) as $app) { |
|
| 260 | 4 | $jobEntities[] = $app; |
|
| 261 | } |
||
| 262 | } else { |
||
| 263 | throw new JobLoadException( |
||
| 264 | 'Could not distinguish job as either chronos or marathon', |
||
| 265 | JobLoadException::ERROR_CODE_UNKNOWN_ENTITY_TYPE |
||
| 266 | ); |
||
| 267 | } |
||
| 268 | |||
| 269 | /** @var JobEntityInterface $jobEntity */ |
||
| 270 | 7 | foreach ($jobEntities as $jobEntity) { |
|
| 271 | 7 | if ($setToFileMap) { |
|
| 272 | // set path to job file map |
||
| 273 | 5 | $this->setJobFileToMap($jobEntity->getKey(), $jobFilePath); |
|
| 274 | } |
||
| 275 | |||
| 276 | 7 | $jobs[] = $jobEntity; |
|
| 277 | } |
||
| 278 | } else { |
||
| 279 | 1 | throw new JobLoadException( |
|
| 280 | 1 | sprintf('Unable to load json job data from "%s". Please check if the json is valid.', $jobFilePath), |
|
| 281 | 8 | JobLoadException::ERROR_CODE_NO_VALID_JSON |
|
| 282 | ); |
||
| 283 | } |
||
| 284 | } |
||
| 285 | |||
| 286 | 6 | return $jobs; |
|
| 287 | } |
||
| 288 | |||
| 289 | |||
| 290 | 4 | private function getMarathonEntitiesForConfig($entityData) |
|
| 291 | { |
||
| 292 | 4 | $return = []; |
|
| 293 | 4 | if (property_exists($entityData, 'apps')) { |
|
| 294 | // store individual apps like single apps |
||
| 295 | 2 | foreach ($entityData->apps as $app) { |
|
| 296 | 2 | $groupEntity = new MarathonAppEntity($app); |
|
| 297 | 2 | $this->groupedApps[] = $app->id; |
|
| 298 | 2 | $return[] = $groupEntity; |
|
| 299 | } |
||
| 300 | } else { |
||
| 301 | 2 | $return[] = new MarathonAppEntity($entityData); |
|
| 302 | } |
||
| 303 | 4 | return $return; |
|
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * @param string $jobFile |
||
| 308 | * @param JobEntityInterface $jobEntity |
||
| 309 | * @return bool |
||
| 310 | */ |
||
| 311 | 2 | private function hasDumpFile($jobFile, JobEntityInterface $jobEntity) |
|
| 312 | { |
||
| 313 | 2 | $this->fileSystemService->dumpFile( |
|
| 314 | 2 | $jobFile, |
|
| 315 | 2 | json_encode($jobEntity, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) |
|
| 316 | ); |
||
| 317 | |||
| 318 | 2 | return (file_exists($jobFile)); |
|
| 319 | } |
||
| 320 | |||
| 321 | /** |
||
| 322 | * @param string $jobFile |
||
| 323 | * @param JobEntityInterface $jobEntity |
||
| 324 | * @param bool $add |
||
| 325 | * @return bool |
||
| 326 | */ |
||
| 327 | 2 | private function dumpFileWithGroup($jobFile, JobEntityInterface $jobEntity, $add = true) |
|
| 328 | { |
||
| 329 | 2 | $groupConfig = file_get_contents($jobFile); |
|
| 330 | |||
| 331 | 2 | $decodedConfig = json_decode(preg_replace( |
|
| 332 | 2 | '~\/\*(.*?)\*\/~mis', |
|
| 333 | 2 | '', |
|
| 334 | 2 | $groupConfig |
|
| 335 | )); |
||
| 336 | |||
| 337 | 2 | if (!property_exists($decodedConfig, 'apps')) { |
|
| 338 | throw new \RuntimeException(sprintf( |
||
| 339 | 'Job file %s does not contain group configuration. But, "%s" belongs to group %s', |
||
| 340 | $jobFile, |
||
| 341 | $jobEntity->getKey(), |
||
| 342 | $decodedConfig->id |
||
| 343 | )); |
||
| 344 | } |
||
| 345 | |||
| 346 | 2 | $appFound = false; |
|
| 347 | 2 | foreach ($decodedConfig->apps as $key => $app) { |
|
| 348 | 2 | if ($app->id == $jobEntity->getKey()) { |
|
| 349 | 2 | if (!$add) { |
|
| 350 | 1 | array_splice($decodedConfig->apps, $key, 1); |
|
| 351 | 1 | if (count($decodedConfig->apps) == 0) { |
|
| 352 | $this->fileSystemService->remove($jobFile); |
||
| 353 | $iIndex = array_search($jobEntity->getKey(), $this->groupedApps); |
||
| 354 | if ($iIndex) { |
||
| 355 | unset($this->groupedApps[$iIndex]); |
||
| 356 | } |
||
| 357 | 1 | return false; |
|
| 358 | } |
||
| 359 | } else { |
||
| 360 | 1 | $decodedConfig->apps[$key] = $jobEntity; |
|
| 361 | } |
||
| 362 | 2 | $appFound = true; |
|
| 363 | } |
||
| 364 | } |
||
| 365 | |||
| 366 | 2 | if (!$appFound) { |
|
| 367 | throw new \RuntimeException(sprintf( |
||
| 368 | 'Could update job. job %s could not be found in the group file %s.', |
||
| 369 | $jobEntity->getKey(), |
||
| 370 | $jobFile |
||
| 371 | )); |
||
| 372 | } |
||
| 373 | |||
| 374 | 2 | $updatedConfig = json_encode($decodedConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); |
|
| 375 | |||
| 376 | 2 | $this->fileSystemService->dumpFile( |
|
| 377 | 2 | $jobFile, |
|
| 378 | 2 | $updatedConfig |
|
| 379 | ); |
||
| 380 | |||
| 381 | 2 | return (file_exists($jobFile)); |
|
| 382 | } |
||
| 383 | } |
||
| 384 |