Skip to content

Commit

Permalink
[#30] - WIP, do not merge into master yet. This adds some basic, but …
Browse files Browse the repository at this point in the history
…not entirely functional, support for hosted verification.
  • Loading branch information
fuerve committed Aug 8, 2018
1 parent 6373ddf commit 29a2cbc
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 6 deletions.
2 changes: 1 addition & 1 deletion cert_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.0.11'
__version__ = '2.1.0'
200 changes: 200 additions & 0 deletions cert_tools/create_v2_1_certificate_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#!/usr/bin/env python

'''
Creates a certificate template with merge tags for recipient/assertion-specific data.
'''
import json
import os
import uuid

import configargparse

from cert_tools import helpers
from cert_tools import jsonpath_helpers

from cert_core.cert_model.model import scope_name
from cert_schema import *

OPEN_BADGES_V2_CONTEXT = OPEN_BADGES_V2_CANONICAL_CONTEXT
BLOCKCERTS_V2_1_CONTEXT = BLOCKCERTS_V2_1_CANONICAL_CONTEXT


def create_badge_section(config):
cert_image_path = os.path.join(config.abs_data_dir, config.cert_image_file)
issuer_image_path = os.path.join(config.abs_data_dir, config.issuer_logo_file)
badge = {
'type': 'BadgeClass',
'id': helpers.URN_UUID_PREFIX + config.badge_id,
'name': config.certificate_title,
'description': config.certificate_description,
'image': helpers.encode_image(cert_image_path),
'issuer': {
'id': config.issuer_id,
'type': 'Profile',
'name': config.issuer_name,
'url': config.issuer_url,
'email': config.issuer_email,
'image': helpers.encode_image(issuer_image_path),
'revocationList': config.revocation_list
}
}

badge['criteria'] = {}
badge['criteria']['narrative'] = config.criteria_narrative

if config.issuer_signature_lines:
signature_lines = []
signature_lines = []
for signature_line in config.issuer_signature_lines:
signature_image_path = os.path.join(config.abs_data_dir, signature_line['signature_image'])
signature_lines.append(
{
'type': [
'SignatureLine',
'Extension'
],
'jobTitle': signature_line['job_title'],
'image': helpers.encode_image(signature_image_path),
'name': signature_line['name']
}
)
badge[scope_name('signatureLines')] = signature_lines

return badge

def create_verification_section(config):
if config.verification_type == 'openbadge_hosted':
verification = {
'type': 'hosted'
}
else:
verification = {
'type': ['MerkleProofVerification2017', 'Extension'],
'publicKey': config.issuer_public_key

}
return verification

def create_recipient_section(config):
recipient = {
'type': 'email',
'identity': '*|EMAIL|*',
'hashed': config.hash_emails
}
return recipient


def create_recipient_profile_section():
return {
'type': ['RecipientProfile', 'Extension'],
'name': '*|NAME|*',
'publicKey': 'ecdsa-koblitz-pubkey:*|PUBKEY|*'
}


def create_assertion_section(config):
assertion = {
'@context': [
OPEN_BADGES_V2_CONTEXT,
BLOCKCERTS_V2_1_CONTEXT,
{
"displayHtml": { "@id": "schema:description" }
}
],
'type': 'Assertion',
'displayHtml': config.display_html,
'issuedOn': '*|DATE|*',
'id': helpers.URN_UUID_PREFIX + '*|CERTUID|*'
}
return assertion


def create_certificate_template(config):

if not config.badge_id:
badge_uuid = str(uuid.uuid4())
print('Generated badge id {0}'.format(badge_uuid))
config.badge_id = badge_uuid

badge = create_badge_section(config)
verification = create_verification_section(config)
assertion = create_assertion_section(config)
recipient = create_recipient_section(config)
recipient_profile = create_recipient_profile_section()

template_dir = config.template_dir
if not os.path.isabs(template_dir):
template_dir = os.path.join(config.abs_data_dir, template_dir)
template_file_name = config.template_file_name

assertion['recipient'] = recipient
assertion[scope_name('recipientProfile')] = recipient_profile

assertion['badge'] = badge
assertion['verification'] = verification

if config.additional_global_fields:
for field in config.additional_global_fields:
assertion = jsonpath_helpers.set_field(assertion, field['path'], field['value'])

if config.additional_per_recipient_fields:
for field in config.additional_per_recipient_fields:
assertion = jsonpath_helpers.set_field(assertion, field['path'], field['value'])

template_path = os.path.join(config.abs_data_dir, template_dir, template_file_name)

print('Writing template to ' + template_path)
with open(template_path, 'w') as cert_template:
json.dump(assertion, cert_template)

return assertion


def get_config():
cwd = os.getcwd()
config_file_path = os.path.join(cwd, 'conf.ini')
p = configargparse.getArgumentParser(default_config_files=[config_file_path])

p.add('-c', '--my-config', required=False, is_config_file=True, help='config file path')

p.add_argument('--data_dir', type=str, help='where data files are located')
p.add_argument('--issuer_logo_file', type=str, help='issuer logo image file, png format')
p.add_argument('--cert_image_file', type=str, help='issuer logo image file, png format')
p.add_argument('--issuer_url', type=str, help='issuer URL')
p.add_argument('--issuer_certs_url', type=str, help='issuer certificates URL')
p.add_argument('--issuer_email', required=True, type=str, help='issuer email')
p.add_argument('--issuer_name', required=True, type=str, help='issuer name')
p.add_argument('--issuer_id', required=True, type=str, help='issuer profile')
p.add_argument('--issuer_key', type=str, help='issuer issuing key')
p.add_argument('--certificate_description', type=str, help='the display description of the certificate')
p.add_argument('--certificate_title', required=True, type=str, help='the title of the certificate')
p.add_argument('--criteria_narrative', required=True, type=str, help='criteria narrative')
p.add_argument('--template_dir', type=str, help='the template output directory')
p.add_argument('--template_file_name', type=str, help='the template file name')
p.add_argument('--hash_emails', action='store_true',
help='whether to hash emails in the certificate')
p.add_argument('--revocation_list', type=str, help='issuer revocation list')
p.add_argument('--issuer_public_key', type=str, help='issuer public key')
p.add_argument('--badge_id', required=True, type=str, help='badge id')
p.add_argument('--issuer_signature_lines', action=helpers.make_action('issuer_signature_lines'),
help='issuer signature lines')
p.add_argument('--additional_global_fields', action=helpers.make_action('global_fields'),
help='additional global fields')
p.add_argument('--additional_per_recipient_fields', action=helpers.make_action('per_recipient_fields'),
help='additional per-recipient fields')
p.add_argument('--display_html', type=str, help='html content to display'),
p.add_argument('--verification_type', type=str, default='merkle', help='verification type')

args, _ = p.parse_known_args()
args.abs_data_dir = os.path.abspath(os.path.join(cwd, args.data_dir))
return args


def main():
conf = get_config()
template = create_certificate_template(conf)
print('Created template!')


if __name__ == "__main__":
main()
80 changes: 80 additions & 0 deletions cert_tools/create_v2_1_issuer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python
'''
Generates the issuer file (.json) that represents the issues which is needed for issuing and validating certificates.
Currently, just not check for inputs' validity (e.g. valid address, URLs, etc.)
'''
import os
import sys
from cert_schema import *
import configargparse
import json

from cert_tools import helpers

ISSUER_TYPE = 'Profile'

OPEN_BADGES_V2_CONTEXT_JSON = OPEN_BADGES_V2_CANONICAL_CONTEXT
BLOCKCERTS_V2_1_CONTEXT_JSON = BLOCKCERTS_V2_1_CANONICAL_CONTEXT


def generate_issuer_file(config):

if config.public_key_created:
issued_on = config.public_key_created
else:
issued_on = helpers.create_iso8601_tz()
output_handle = open(config.output_file, 'w') if config.output_file else sys.stdout

context = [OPEN_BADGES_V2_CONTEXT_JSON, BLOCKCERTS_V2_1_CONTEXT_JSON]

issuer_json = {
'@context': context,
'id': config.issuer_id,
'url': config.issuer_url,
'name': config.issuer_name,
'email': config.issuer_email,
'image': helpers.encode_image(os.path.join(config.abs_data_dir, config.issuer_logo_file)),
'publicKey': [{'id': config.issuer_public_key, "created": issued_on}],
'revocationList': config.revocation_list_uri,
'type': ISSUER_TYPE
}

if config.intro_url:
issuer_json['introductionUrl'] = config.intro_url

output_handle.write(json.dumps(issuer_json, indent=2))

if output_handle is not sys.stdout:
output_handle.close()


def get_config():
cwd = os.getcwd()
p = configargparse.getArgumentParser(default_config_files=[os.path.join(cwd, 'conf.ini')])
p.add('-c', '--my-config', required=True, is_config_file=True, help='config file path')
p.add_argument('--data_dir', type=str, help='where data files are located')
p.add_argument('-k', '--issuer_public_key', type=str, required=True, help='The key(s) an issuer uses to sign Assertions. See https://openbadgespec.org/#Profile for more details')
p.add_argument('-k', '--public_key_created', type=str, help='ISO8601-formatted date the issuer public key should be considered active')
p.add_argument('-r', '--revocation_list_uri', type=str, required=True, help='URI of the Revocation List used for marking revocation. See https://openbadgespec.org/#Profile for more details')
p.add_argument('-d', '--issuer_id', type=str, required=True, help='the issuer\'s publicly accessible identification file; i.e. URL of the file generated by this tool')
p.add_argument('-u', '--issuer_url', type=str, help='the issuer\'s main URL address')
p.add_argument('-n', '--issuer_name', type=str, help='the issuer\'s name')
p.add_argument('-e', '--issuer_email', type=str, help='the issuer\'s email')
p.add_argument('-m', '--issuer_logo_file', type=str, help='the issuer\' logo image')
p.add_argument('-i', '--intro_url', required=False, type=str, help='the issuer\'s introduction URL address')
p.add_argument('-o', '--output_file', type=str, help='the output file to save the issuer\'s identification file')
args, _ = p.parse_known_args()
args.abs_data_dir = os.path.abspath(os.path.join(cwd, args.data_dir))

return args


def main():
conf = get_config()
generate_issuer_file(conf)


if __name__ == "__main__":
main()

2 changes: 1 addition & 1 deletion cert_tools/create_v2_issuer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
'''
Generates the issuer file (.json) thar represents the issues which is needed for issuing and validating certificates.
Generates the issuer file (.json) that represents the issues which is needed for issuing and validating certificates.
Currently, just not check for inputs' validity (e.g. valid address, URLs, etc.)
'''
Expand Down
Loading

0 comments on commit 29a2cbc

Please sign in to comment.