Updated Schemas

This commit is contained in:
Alexander Cerutti
2021-09-28 00:47:50 +02:00
parent e8a40c4554
commit 770a5e780b
3 changed files with 129 additions and 100 deletions

View File

@@ -32,9 +32,9 @@ type TransitTypes = `PKTransitType${
const LOCALIZED_FILE_REGEX_BASE = "(?<lang>[a-zA-Z-]{2,}).lproj/"; const LOCALIZED_FILE_REGEX_BASE = "(?<lang>[a-zA-Z-]{2,}).lproj/";
export default class PKPass extends Bundle { export default class PKPass extends Bundle {
private certificates: Schemas.Certificates; private certificates: Schemas.CertificatesSchema;
private [fieldKeysPoolSymbol] = new Set<string>(); private [fieldKeysPoolSymbol] = new Set<string>();
private [propsSymbol]: Schemas.ValidPass = {}; private [propsSymbol]: Schemas.PassProps = {};
private [localizationSymbol]: { private [localizationSymbol]: {
[lang: string]: { [lang: string]: {
[placeholder: string]: string; [placeholder: string]: string;
@@ -129,7 +129,11 @@ export default class PKPass extends Bundle {
// *** INSTANCE *** // // *** INSTANCE *** //
// **************** // // **************** //
constructor(buffers: NamedBuffers, certificates: Certificates) { constructor(
buffers: NamedBuffers,
certificates: Schemas.CertificatesSchema,
overrides: Schemas.OverridablePassProps,
) {
super("application/vnd.apple.pkpass"); super("application/vnd.apple.pkpass");
/** /**
@@ -153,7 +157,7 @@ export default class PKPass extends Bundle {
* that are composing your pass instance. * that are composing your pass instance.
*/ */
public get props(): Readonly<Schemas.ValidPass> { public get props(): Readonly<Schemas.PassProps> {
return freezeRecusive(this[propsSymbol]); return freezeRecusive(this[propsSymbol]);
} }
@@ -322,7 +326,7 @@ export default class PKPass extends Bundle {
return super.addBuffer(pathName, buffer); return super.addBuffer(pathName, buffer);
} }
private [importMetadataSymbol](data: Schemas.ValidPass) { private [importMetadataSymbol](data: Schemas.PassProps) {
const possibleTypes = [ const possibleTypes = [
"boardingPass", "boardingPass",
"coupon", "coupon",
@@ -768,11 +772,11 @@ function readPassMetadata(buffer: Buffer) {
try { try {
const contentAsJSON = JSON.parse( const contentAsJSON = JSON.parse(
buffer.toString("utf8"), buffer.toString("utf8"),
) as Schemas.ValidPass; ) as Schemas.PassProps;
const validation = Schemas.getValidated( const validation = Schemas.getValidated(
contentAsJSON, contentAsJSON,
Schemas.ValidPass, Schemas.PassProps,
); );
/** /**

View File

@@ -25,7 +25,7 @@ export interface Manifest {
[key: string]: string; [key: string]: string;
} }
export interface Certificates { /* export interface Certificates {
wwdr?: string; wwdr?: string;
signerCert?: string; signerCert?: string;
signerKey?: signerKey?:
@@ -34,54 +34,28 @@ export interface Certificates {
passphrase?: string; passphrase?: string;
} }
| string; | string;
} }*/
export interface FactoryOptions {
model: BundleUnit | string;
certificates: Certificates;
overrides?: OverridesSupportedOptions;
}
export interface BundleUnit {
[key: string]: Buffer;
}
export interface PartitionedBundle {
bundle: BundleUnit;
l10nBundle: {
[key: string]: BundleUnit;
};
}
export interface CertificatesSchema { export interface CertificatesSchema {
wwdr: string; wwdr: string | Buffer;
signerCert: string; signerCert: string | Buffer;
signerKey: string; signerKey: string | Buffer;
signerKeyPassphrase?: string;
} }
export const CertificatesSchema = Joi.object<CertificatesSchema>() export const CertificatesSchema = Joi.object<CertificatesSchema>()
.keys({ .keys({
wwdr: Joi.alternatives(Joi.binary(), Joi.string()).required(), wwdr: Joi.alternatives(Joi.binary(), Joi.string()).required(),
signerCert: Joi.alternatives(Joi.binary(), Joi.string()).required(), signerCert: Joi.alternatives(Joi.binary(), Joi.string()).required(),
signerKey: Joi.alternatives() signerKey: Joi.alternatives(Joi.binary(), Joi.string()).required(),
.try( signerKeyPassphrase: Joi.string(),
Joi.object().keys({
keyFile: Joi.alternatives(
Joi.binary(),
Joi.string(),
).required(),
passphrase: Joi.string().required(),
}),
Joi.alternatives(Joi.binary(), Joi.string()),
)
.required(),
}) })
.required(); .required();
export interface Template { export interface Template {
model: string; model: string;
certificates: CertificatesSchema; certificates: CertificatesSchema;
overrides?: OverridesSupportedOptions; overrides?: OverridablePassProps;
} }
export const Template = Joi.object<Template>({ export const Template = Joi.object<Template>({
@@ -90,56 +64,37 @@ export const Template = Joi.object<Template>({
overrides: Joi.object(), overrides: Joi.object(),
}); });
export interface OverridesSupportedOptions { export interface PassProps {
serialNumber?: string; serialNumber?: string;
description?: string; description?: string;
organizationName?: string; organizationName?: string;
passTypeIdentifier?: string; passTypeIdentifier?: string;
teamIdentifier?: string; teamIdentifier?: string;
appLaunchURL?: string; appLaunchURL?: string;
associatedStoreIdentifiers?: Array<number>; voided?: boolean;
userInfo?: { [key: string]: any }; userInfo?: { [key: string]: any };
webServiceURL?: string;
authenticationToken?: string;
sharingProhibited?: boolean; sharingProhibited?: boolean;
backgroundColor?: string;
foregroundColor?: string;
labelColor?: string;
groupingIdentifier?: string; groupingIdentifier?: string;
suppressStripShine?: boolean; suppressStripShine?: boolean;
logoText?: string; logoText?: string;
maxDistance?: number; maxDistance?: number;
semantics?: Semantics; semantics?: Semantics;
}
export const OverridesSupportedOptions = Joi.object<OverridesSupportedOptions>() webServiceURL?: string;
.keys({ associatedStoreIdentifiers?: Array<number>;
serialNumber: Joi.string(), authenticationToken?: string;
description: Joi.string(),
organizationName: Joi.string(), backgroundColor?: string;
passTypeIdentifier: Joi.string(), foregroundColor?: string;
teamIdentifier: Joi.string(), labelColor?: string;
appLaunchURL: Joi.string(),
associatedStoreIdentifiers: Joi.array().items(Joi.number()), nfc?: NFC;
userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()), beacons?: Beacon[];
// parsing url as set of words and nums followed by dots, optional port and any possible path after barcodes?: Barcode[];
webServiceURL: Joi.string().regex( relevantDate?: string;
/https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/, expirationDate?: string;
), locations?: Location[];
authenticationToken: Joi.string().min(16),
sharingProhibited: Joi.boolean(),
backgroundColor: Joi.string().min(10).max(16),
foregroundColor: Joi.string().min(10).max(16),
labelColor: Joi.string().min(10).max(16),
groupingIdentifier: Joi.string(),
suppressStripShine: Joi.boolean(),
logoText: Joi.string(),
maxDistance: Joi.number().positive(),
semantics: Semantics,
})
.with("webServiceURL", "authenticationToken");
export interface ValidPassType {
boardingPass?: PassFields & { transitType: TransitType }; boardingPass?: PassFields & { transitType: TransitType };
eventTicket?: PassFields; eventTicket?: PassFields;
coupon?: PassFields; coupon?: PassFields;
@@ -147,29 +102,89 @@ export interface ValidPassType {
storeCard?: PassFields; storeCard?: PassFields;
} }
interface PassInterfacesProps { /**
barcode?: Barcode; * These are the properties passkit-generator will
barcodes?: Barcode[]; * handle through its methods
beacons?: Beacon[]; */
locations?: Location[];
maxDistance?: number; type PassMethodsProps =
relevantDate?: string; | "nfc"
nfc?: NFC; | "beacons"
expirationDate?: string; | "barcodes"
voided?: boolean; | "relevantDate"
} | "expirationDate"
| "locations";
export type PassTypesProps =
| "boardingPass"
| "eventTicket"
| "coupon"
| "generic"
| "storeCard";
export type OverridablePassProps = Omit<
PassProps,
PassMethodsProps | PassTypesProps
>;
export type PassPropsFromMethods = { [K in PassMethodsProps]: PassProps[K] };
export type PassKindsProps = { [K in PassTypesProps]: PassProps[K] };
type AllPassProps = PassInterfacesProps &
ValidPassType &
OverridesSupportedOptions;
export type ValidPass = {
[K in keyof AllPassProps]: AllPassProps[K];
};
export type PassColors = Pick< export type PassColors = Pick<
OverridesSupportedOptions, OverridablePassProps,
"backgroundColor" | "foregroundColor" | "labelColor" "backgroundColor" | "foregroundColor" | "labelColor"
>; >;
export const PassPropsFromMethods = Joi.object<PassPropsFromMethods>({
nfc: NFC,
beacons: Joi.array().items(Beacon),
barcodes: Joi.array().items(Barcode),
relevantDate: Joi.string().isoDate(),
expirationDate: Joi.string().isoDate(),
locations: Joi.array().items(Location),
});
export const PassKindsProps = Joi.object<PassKindsProps>({
coupon: Joi.array().items(Field),
generic: Joi.array().items(Field),
storeCard: Joi.array().items(Field),
eventTicket: Joi.array().items(Field),
boardingPass: Joi.array().items(
Field.concat(Joi.object({ transitType: TransitType })),
),
});
export const OverridablePassProps = Joi.object<OverridablePassProps>({
semantics: Semantics,
voided: Joi.boolean(),
logoText: Joi.string(),
description: Joi.string(),
serialNumber: Joi.string(),
appLaunchURL: Joi.string(),
teamIdentifier: Joi.string(),
organizationName: Joi.string(),
passTypeIdentifier: Joi.string(),
sharingProhibited: Joi.boolean(),
groupingIdentifier: Joi.string(),
suppressStripShine: Joi.boolean(),
maxDistance: Joi.number().positive(),
labelColor: Joi.string().min(10).max(16),
authenticationToken: Joi.string().min(16),
backgroundColor: Joi.string().min(10).max(16),
foregroundColor: Joi.string().min(10).max(16),
associatedStoreIdentifiers: Joi.array().items(Joi.number()),
userInfo: Joi.alternatives(Joi.object().unknown(), Joi.array()),
// parsing url as set of words and nums followed by dots, optional port and any possible path after
webServiceURL: Joi.string().regex(
/https?:\/\/(?:[a-z0-9]+\.?)+(?::\d{2,})?(?:\/[\S]+)*/,
),
}).with("webServiceURL", "authenticationToken");
export const PassProps = Joi.object({
...OverridablePassProps,
...PassKindsProps,
...PassPropsFromMethods,
});
// --------- UTILITIES ---------- // // --------- UTILITIES ---------- //
type AvailableSchemas = type AvailableSchemas =
@@ -183,7 +198,7 @@ type AvailableSchemas =
| typeof TransitType | typeof TransitType
| typeof Template | typeof Template
| typeof CertificatesSchema | typeof CertificatesSchema
| typeof OverridesSupportedOptions; | typeof OverridablePassProps;
export type ArrayPassSchema = Beacon | Location | Barcode; export type ArrayPassSchema = Beacon | Location | Barcode;

View File

@@ -20,8 +20,18 @@ export function create(
"utf8", "utf8",
); );
signature.addCertificate(certificates.wwdr); const { wwdr, signerCert, signerKey, signerKeyPassphrase } = certificates;
signature.addCertificate(certificates.signerCert);
const wwdrString = wwdr instanceof Buffer ? wwdr.toString("utf-8") : wwdr;
const signerCertString =
signerCert instanceof Buffer
? signerCert.toString("utf-8")
: signerCert;
const signerKeyString =
signerKey instanceof Buffer ? signerKey.toString("utf-8") : signerKey;
signature.addCertificate(wwdrString);
signature.addCertificate(signerCertString);
/** /**
* authenticatedAttributes belong to PKCS#9 standard. * authenticatedAttributes belong to PKCS#9 standard.
@@ -33,8 +43,8 @@ export function create(
*/ */
signature.addSigner({ signature.addSigner({
key: certificates.signerKey, key: signerKeyString,
certificate: certificates.signerCert, certificate: signerCertString,
digestAlgorithm: forge.pki.oids.sha1, digestAlgorithm: forge.pki.oids.sha1,
authenticatedAttributes: [ authenticatedAttributes: [
{ {