Skip to content

Commit

Permalink
Add validation when packaging a pass using the PassValidator class
Browse files Browse the repository at this point in the history
  • Loading branch information
g- committed Apr 24, 2016
1 parent 9e52406 commit db4f235
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 75 deletions.
29 changes: 29 additions & 0 deletions src/Passbook/Exception/PassInvalidException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Passbook\Exception;

/**
* Thrown when validation of a pass fails.
*/
class PassInvalidException extends \RuntimeException
{
/**
* Construct a PassInvalidException either with or without an array of errors.
*
* @param string[]|null $errors
*/
public function __construct(array $errors = null)
{
$this->errors = $errors ? $errors : array();
}

/**
* Returns the errors with the pass.
*
* @return string[]
*/
public function getErrors()
{
return $this->errors;
}
}
2 changes: 1 addition & 1 deletion src/Passbook/Pass/Localization.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function getStringsFileOutput()
/**
* {@inheritdoc}
*/
public function addImage(ImageInterface $image)
public function addImage(Image $image)
{
$this->images[] = $image;

Expand Down
6 changes: 3 additions & 3 deletions src/Passbook/Pass/LocalizationInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ public function getStrings();
public function getStringsFileOutput();

/**
* @param ImageInterface $image
* @param Image $image
*
* @return LocalizationInterface
*/
public function addImage(ImageInterface $image);
public function addImage(Image $image);

/**
* @return ImageInterface[]
* @return Image[]
*/
public function getImages();
}
216 changes: 153 additions & 63 deletions src/Passbook/PassFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Passbook\Certificate\P12;
use Passbook\Certificate\WWDR;
use Passbook\Exception\FileException;
use Passbook\Exception\PassInvalidException;
use Passbook\Pass\Image;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
Expand Down Expand Up @@ -83,6 +84,11 @@ class PassFactory
*/
protected $skipSignature;

/**
* @var PassValidatorInterface
*/
private $passValidator;

/**
* Pass file extension
*
Expand All @@ -96,9 +102,13 @@ public function __construct($passTypeIdentifier, $teamIdentifier, $organizationN
$this->passTypeIdentifier = $passTypeIdentifier;
$this->teamIdentifier = $teamIdentifier;
$this->organizationName = $organizationName;

// Create certificate objects
$this->p12 = new P12($p12File, $p12Pass);
$this->wwdr = new WWDR($wwdrFile);

// By default use the PassValidator
$this->passValidator = new PassValidator();
}

/**
Expand All @@ -125,6 +135,16 @@ public function getOutputPath()
return $this->outputPath;
}

/**
* The output path with a directory separator on the end.
*
* @return string
*/
public function getNormalizedOutputPath()
{
return rtrim($this->outputPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
}

/**
* Set overwrite
*
Expand Down Expand Up @@ -156,6 +176,7 @@ public function isOverwrite()
* be used for testing.
*
* @param boolean
*
* @return $this
*/
public function setSkipSignature($skipSignature)
Expand All @@ -166,14 +187,39 @@ public function setSkipSignature($skipSignature)
}

/**
* Get overwrite
* Get skip signature
*
* @return boolean
*/
public function getSkipSignature()
{
return $this->skipSignature;
}

/**
* Set an implementation of PassValidatorInterface to validate the pass
* before packaging. When set to null, no validation is performed when
* packaging the pass.
*
* @param PassValidatorInterface|null $passValidator
*
* @return $this
*/
public function setPassValidator(PassValidatorInterface $passValidator = null)
{
$this->passValidator = $passValidator;

return $this;
}

/**
* @return PassValidatorInterface
*/
public function getPassValidator()
{
return $this->passValidator;
}

/**
* Serialize pass
*
Expand Down Expand Up @@ -203,80 +249,31 @@ public function package(PassInterface $pass, $passName = '')

$this->populateRequiredInformation($pass);

// Serialize pass
$json = self::serialize($pass);

$outputPath = rtrim($this->getOutputPath(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$passDir = $outputPath . $this->getPassName($passName, $pass) . DIRECTORY_SEPARATOR;
$passDirExists = file_exists($passDir);
if ($passDirExists && !$this->isOverwrite()) {
throw new FileException("Temporary pass directory already exists");
} elseif (!$passDirExists && !mkdir($passDir, 0777, true)) {
throw new FileException("Couldn't create temporary pass directory");
if ($this->passValidator) {
if (!$this->passValidator->validate($pass)){
throw new PassInvalidException($this->passValidator->getErrors());
};
}

// Pass.json
$passJSONFile = $passDir . 'pass.json';
file_put_contents($passJSONFile, $json);
$passDir = $this->preparePassDirectory($pass);

// Serialize pass
file_put_contents($passDir . 'pass.json', self::serialize($pass));

// Images
/** @var Image $image */
foreach ($pass->getImages() as $image) {
$fileName = $passDir . $image->getContext();
if ($image->isRetina()) {
$fileName .= '@2x';
}
$fileName .= '.' . $image->getExtension();
copy($image->getPathname(), $fileName);
}
$this->prepareImages($pass, $passDir);

// Localizations
foreach ($pass->getLocalizations() as $localization) {
// Create dir (LANGUAGE.lproj)
$localizationDir = $passDir . $localization->getLanguage() . '.lproj' . DIRECTORY_SEPARATOR;
mkdir($localizationDir, 0777, true);

// pass.strings File (Format: "token" = "value")
$localizationStringsFile = $localizationDir . 'pass.strings';
file_put_contents($localizationStringsFile, $localization->getStringsFileOutput());

// Localization images
foreach ($localization->getImages() as $image) {
$fileName = $localizationDir . $image->getContext();
if ($image->isRetina()) {
$fileName .= '@2x';
}
$fileName .= '.' . $image->getExtension();
copy($image->getPathname(), $fileName);
}
}
$this->prepareLocalizations($pass, $passDir);

// Manifest.json - recursive, also add files in sub directories
$manifestJSONFile = $passDir . 'manifest.json';
$manifest = array();
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($passDir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $file) {
// Ignore "." and ".." folders
if (in_array(substr($file, strrpos($file, '/') + 1), array('.', '..'))) {
continue;
}
//
$filePath = realpath($file);
if (is_file($filePath) === true) {
$relativePathName = str_replace($passDir, '', $file->getPathname());
$manifest[$relativePathName] = sha1_file($filePath);
}
}
file_put_contents($manifestJSONFile, $this->jsonEncode($manifest));
$manifestJSONFile = $this->prepareManifest($passDir);

// Signature
$this->sign($passDir, $manifestJSONFile);

// Zip pass
$zipFile = $outputPath . $this->getPassName($passName, $pass) . self::PASS_EXTENSION;
$zipFile = $this->getNormalizedOutputPath() . $this->getPassName($passName, $pass) . self::PASS_EXTENSION;
$this->zip($passDir, $zipFile);

// Remove temporary pass directory
Expand Down Expand Up @@ -311,7 +308,7 @@ private function sign($passDir, $manifestJSONFile)
$this->wwdr->getRealPath()
);
// Get signature content
$signature = @file_get_contents($signatureFile);
$signature = file_get_contents($signatureFile);
// Check signature content
if (!$signature) {
throw new FileException("Couldn't read signature file.");
Expand Down Expand Up @@ -432,4 +429,97 @@ public function getPassName($passName, PassInterface $pass)
return strlen($passNameSanitised) != 0 ? $passNameSanitised : $pass->getSerialNumber();
}

/**
* @param $passDir
*
* @return string
*/
private function prepareManifest($passDir)
{
$manifestJSONFile = $passDir . 'manifest.json';
$manifest = array();
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($passDir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $file) {
// Ignore "." and ".." folders
if (in_array(substr($file, strrpos($file, '/') + 1), array('.', '..'))) {
continue;
}
//
$filePath = realpath($file);
if (is_file($filePath) === true) {
$relativePathName = str_replace($passDir, '', $file->getPathname());
$manifest[$relativePathName] = sha1_file($filePath);
}
}
file_put_contents($manifestJSONFile, $this->jsonEncode($manifest));

return $manifestJSONFile;
}

/**
* @param PassInterface $pass
*
* @return string
*/
private function preparePassDirectory(PassInterface $pass)
{
$passDir = $this->getNormalizedOutputPath() . $pass->getSerialNumber() . DIRECTORY_SEPARATOR;
$passDirExists = file_exists($passDir);
if ($passDirExists && !$this->isOverwrite()) {
throw new FileException("Temporary pass directory already exists");
} elseif (!$passDirExists && !mkdir($passDir, 0777, true)) {
throw new FileException("Couldn't create temporary pass directory");
}

return $passDir;
}

/**
* @param PassInterface $pass
* @param $passDir
*/
private function prepareImages(PassInterface $pass, $passDir)
{
/** @var Image $image */
foreach ($pass->getImages() as $image) {
$fileName = $passDir . $image->getContext();
if ($image->isRetina()) {
$fileName .= '@2x';
}
$fileName .= '.' . $image->getExtension();
copy($image->getPathname(), $fileName);
}
}

/**
* @param PassInterface $pass
* @param $passDir
*/
private function prepareLocalizations(PassInterface $pass, $passDir)
{
foreach ($pass->getLocalizations() as $localization) {
// Create dir (LANGUAGE.lproj)
$localizationDir = $passDir . $localization->getLanguage() . '.lproj' . DIRECTORY_SEPARATOR;
mkdir($localizationDir, 0777, true);

// pass.strings File (Format: "token" = "value")
$localizationStringsFile = $localizationDir . 'pass.strings';
file_put_contents($localizationStringsFile, $localization->getStringsFileOutput());

// Localization images
foreach ($localization->getImages() as $image) {
$fileName = $localizationDir . $image->getContext();
if ($image->isRetina()) {
$fileName .= '@2x';
}
$fileName .= '.' . $image->getExtension();
copy($image->getPathname(), $fileName);
}
}
}


}
13 changes: 13 additions & 0 deletions src/Passbook/PassInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ public function setBarcode(BarcodeInterface $barcode);
public function getBarcode();

/**
* @param BarcodeInterface $barcode - barcode to add to the pass
*
* @return $this
*/
public function addBarcode(BarcodeInterface $barcode);
Expand Down Expand Up @@ -276,4 +278,15 @@ public function addLocalization(LocalizationInterface $localization);
* @return LocalizationInterface[]
*/
public function getLocalizations();

/**
* {@inheritdoc}
*/
public function setAppLaunchURL($appLaunchURL);

/**
* {@inheritdoc}
*/
public function getAppLaunchURL();

}
Loading

0 comments on commit db4f235

Please sign in to comment.