Skip to content

Commit

Permalink
Add auto-generated json schema for every payload type.
Browse files Browse the repository at this point in the history
  • Loading branch information
mosen committed Feb 6, 2017
1 parent b1dd563 commit 7330a5e
Show file tree
Hide file tree
Showing 73 changed files with 7,512 additions and 0 deletions.
16 changes: 16 additions & 0 deletions jsonschema.sh
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
DESTDIR="./source/_static/json-schema"

for filename in ./source/_static/manifests/*.plist; do
echo ${filename}
BASENAME=$(basename "${filename}")

python ./pfm2jsonschema/pfm2jsonschema.py -o "${DESTDIR}" "${filename}"
done

for filename in ./source/payloads/manifests/manual/*.plist; do
echo ${filename}
BASENAME=$(basename "${filename}")

python ./pfm2jsonschema/pfm2jsonschema.py -o "${DESTDIR}" "${filename}"
done
134 changes: 134 additions & 0 deletions pfm2jsonschema/pfm2jsonschema.py
@@ -0,0 +1,134 @@
#!/usr/bin/env python

import plistlib
import json
import os.path
import logging
from optparse import OptionParser

logger = logging.getLogger('pfm2jsonschema')

def generate_pfm_header(data, result):
"""Generate a top-level JSON schema"""
result['title'] = data.get('pfm_domain', 'Untitled')
result['type'] = 'object'

properties = {}
generate(data['pfm_subkeys'], properties)

result['properties'] = properties

def pfm_to_jsontype(pfm_type):
"""Return the JSON Schema form of the given pfm_type"""
translated = {
'integer': 'number',
'real': 'number',
'data': 'string',
'date': 'string',
'dictionary': 'object'
}

if pfm_type in translated:
return translated[pfm_type]
else:
logger.debug("possibly unhandled pfm_type: {}".format(pfm_type))
# Types that are identically named: array, string
return pfm_type

def generate_pfm_item(data, result):
"""Generate a JSON Schema object from a pfm item i.e an item in the schema that has at least a pfm_name."""
logger.debug("generating schema for property: {}".format(data['pfm_name']))

if data['pfm_name'] in result:
raise Exception('Already in properties dict: {}'.format(data['pfm_name']))

prop = {}
prop['type'] = pfm_to_jsontype(data['pfm_type'])

if 'pfm_description' in data:
prop['description'] = data['pfm_description']

if 'pfm_default' in data:
# TODO: need processing of dict defaults
prop['default'] = data['pfm_default']

if 'pfm_range_list' in data:
prop['enum'] = data['pfm_range_list']

if 'pfm_range_min' in data:
prop['minimum'] = data['pfm_range_min']

if 'pfm_range_max' in data:
prop['maximum'] = data['pfm_range_max']

if 'pfm_format' in data:
prop['pattern'] = data['pfm_format']

if data['pfm_type'] == 'date':
prop['format'] = 'date-time' # Use predefined RFC3339 formatting

if 'pfm_subkeys' in data:
if prop['type'] == 'array':
prop['items'] = {}
# Have to discard the pfm_name in the items sub schema

item = {}
generate(data['pfm_subkeys'][0], item)
name, value = item.items()[0]

prop['items'] = value
else:
prop['properties'] = {}
generate(data['pfm_subkeys'], prop['properties'])

result[data['pfm_name']] = prop


def generate(data, result):
"""Generate a JSON Schema object as a dict from a parsed plist manifest given as a dict"""
logger.debug("generate()")

if isinstance(data, dict):
if 'pfm_name' in data:
logging.debug("generate pfm dict: {}".format(data['pfm_name']))
return generate_pfm_item(data, result)
elif 'pfm_version' in data: # probably top level dict
logging.debug("generate pfm header")
generate_pfm_header(data, result)
else:
logger.debug("generate non pfm dict")
elif isinstance(data, list):
logger.debug("generating list")
for item in data:
generate(item, result)
else:
logger.debug("unhandled type")

return None


if __name__ == "__main__":
usage = "usage: %prog [options] manifest.plist"
parser = OptionParser(usage=usage)
parser.add_option("-o", "--output", dest="output",
help="write template to output dir", metavar="DIR")

(options, args) = parser.parse_args()

ch = logging.StreamHandler()
logger.addHandler(ch)
logger.setLevel(logging.DEBUG)

plist = plistlib.readPlist(args[-1])
domain = plist['pfm_domain']

result = {
"$schema": "http://json-schema.org/schema#"
}
generate(plist, result)

if options.output:
with open(os.path.join(options.output, '{}.json'.format(domain)), 'w+') as f:
json.dump(result, f, indent=4)
else:
print(json.dumps(result))
51 changes: 51 additions & 0 deletions source/_static/json-schema/.GlobalPreferences.json
@@ -0,0 +1,51 @@
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"PayloadUUID": {
"default": "",
"pattern": "^[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}$",
"type": "string",
"description": "Unique identifier for the payload (format 01234567-89AB-CDEF-0123-456789ABCDEF)"
},
"PayloadDescription": {
"default": "Loginwindow GlobalPreferences",
"type": "string",
"description": "Description of the payload"
},
"com.apple.autologout.AutoLogOutDelay": {
"default": 0,
"type": "number",
"description": "Automatically log out after this many seconds of inactivity"
},
"PayloadOrganization": {
"type": "string",
"description": "This value describes the issuing organization of the profile, as displayed to the user"
},
"MultipleSessionEnabled": {
"type": "boolean",
"description": "Enable fast user switching"
},
"PayloadIdentifier": {
"default": "GlobalPreferences",
"type": "string",
"description": "A unique identifier for the payload, dot-delimited. Usually root PayloadIdentifier+subidentifier"
},
"PayloadDisplayName": {
"default": "Loginwindow GlobalPreferences",
"type": "string",
"description": "Name of the payload"
},
"PayloadType": {
"default": ".GlobalPreferences",
"type": "string",
"description": "The type of the payload, a reverse dns string"
},
"PayloadVersion": {
"default": 1,
"type": "number",
"description": "The version of the whole configuration profile."
}
},
"title": ".GlobalPreferences"
}
100 changes: 100 additions & 0 deletions source/_static/json-schema/com.apple.ADCertificate.managed.json
@@ -0,0 +1,100 @@
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"PayloadUUID": {
"default": "",
"pattern": "^[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}$",
"type": "string",
"description": "Unique identifier for the payload (format 01234567-89AB-CDEF-0123-456789ABCDEF)"
},
"UserName": {
"type": "string",
"description": "The user name with which to authenticate to the certificate server"
},
"PayloadDescription": {
"default": "Requests an Active Directory certificate",
"type": "string",
"description": "Description of the payload"
},
"KeyIsExtractable": {
"default": false,
"type": "boolean",
"description": "Allow admin to export private key from the keychain"
},
"Description": {
"type": "string",
"description": "The description of the certificate request as shown in the certificate selector of other payloads such as VPN and Network"
},
"CertificateAuthority": {
"type": "string",
"description": "Name of the CA. This value is determined from the Common Name (CN) of the Active Directory entry: CN=(your CA name), CN='Certification Authorities', CN='Public Key Services', CN='Services', or CN='Configuration', (your base Domain Name)."
},
"AllowAllAppsAccess": {
"default": false,
"type": "boolean",
"description": "Allow all apps to access the certificate in the keychain"
},
"PayloadOrganization": {
"type": "string",
"description": "This value describes the issuing organization of the profile, as displayed to the user"
},
"PayloadIdentifier": {
"default": "com.apple.ADCertificate.managed",
"type": "string",
"description": "A unique identifier for the payload, dot-delimited. Usually root PayloadIdentifier+subidentifier"
},
"PayloadDisplayName": {
"default": "AD Certificate",
"type": "string",
"description": "Name of the payload"
},
"CertTemplate": {
"default": "User",
"type": "string",
"description": "Template Name as it appears in the General tab of the template\u2019s object in the Certificate Templates\u2019 Microsoft Management Console snap-in component."
},
"PayloadType": {
"default": "com.apple.ADCertificate.managed",
"type": "string",
"description": "The type of the payload, a reverse dns string"
},
"CertificateAcquisitionMechanism": {
"enum": [
"RPC",
"HTTP"
],
"type": "string",
"description": "Most commonly RPC. If using Web enrollment, HTTP."
},
"Keysize": {
"default": 2048,
"type": "number",
"description": "The RSA key size for the Certificate Signing Request (CSR)."
},
"PromptForCredentials": {
"default": false,
"type": "boolean",
"description": "Prompt the user for credentials. This setting is not supported for pushed profiles"
},
"PayloadVersion": {
"default": 1,
"type": "number",
"description": "The version of the whole configuration profile."
},
"CertificateRenewalTimeInterval": {
"default": 14,
"type": "number",
"description": "The number of days before the certificate expires at which to start showing the expiration notification"
},
"Password": {
"type": "string",
"description": "The password with which to authenticate to the certificate server"
},
"CertServer": {
"type": "string",
"description": "Fully qualified host name of the Active Directory issuing CA."
}
},
"title": "com.apple.ADCertificate.managed"
}
46 changes: 46 additions & 0 deletions source/_static/json-schema/com.apple.Dictionary.json
@@ -0,0 +1,46 @@
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"PayloadUUID": {
"default": "",
"pattern": "^[0-9A-Za-z]{8}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{4}-[0-9A-Za-z]{12}$",
"type": "string",
"description": "Unique identifier for the payload (format 01234567-89AB-CDEF-0123-456789ABCDEF)"
},
"PayloadDescription": {
"default": "Parental Controls: Dictionary",
"type": "string",
"description": "Description of the payload"
},
"PayloadOrganization": {
"type": "string",
"description": "This value describes the issuing organization of the profile, as displayed to the user"
},
"PayloadIdentifier": {
"default": "com.apple.Dictionary",
"type": "string",
"description": "A unique identifier for the payload, dot-delimited. Usually root PayloadIdentifier+subidentifier"
},
"PayloadDisplayName": {
"default": "Parental Controls: Dictionary",
"type": "string",
"description": "Name of the payload"
},
"PayloadType": {
"default": "com.apple.Dictionary",
"type": "string",
"description": "The type of the payload, a reverse dns string"
},
"PayloadVersion": {
"default": 1,
"type": "number",
"description": "The version of the whole configuration profile."
},
"parentalControl": {
"type": "boolean",
"description": "Required. Set to true to enable parental controls dictionary restrictions."
}
},
"title": "com.apple.Dictionary"
}

0 comments on commit 7330a5e

Please sign in to comment.