Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ The `conf.ini` fields are described below. Optional arguments are in brackets
```
create-certificate-template --help

usage: create_v2_certificate_template.py [-h] [-c MY_CONFIG]
usage: create_v2_1_certificate_template.py [-h] [-c MY_CONFIG]
[--data_dir DATA_DIR]
[--issuer_logo_file ISSUER_LOGO_FILE]
[--cert_image_file CERT_IMAGE_FILE]
Expand All @@ -62,7 +62,8 @@ usage: create_v2_certificate_template.py [-h] [-c MY_CONFIG]
[--issuer_signature_lines ISSUER_SIGNATURE_LINES]
[--additional_global_fields ADDITIONAL_GLOBAL_FIELDS]
[--additional_per_recipient_fields ADDITIONAL_PER_RECIPIENT_FIELDS]

[--display_html DISPLAY_HTML]
[--verification_type VERIFICATION_TYPE]

Args that start with '--' (eg. --data_dir) can also be set in a config file (./cert-tools/conf.ini or specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at https://goo.gl/R74nmi). If an arg is specified in more than one place, then commandline values override config file values which override defaults.

Expand Down Expand Up @@ -112,8 +113,10 @@ Argument details:
additional global fields (default: None)
--additional_per_recipient_fields ADDITIONAL_PER_RECIPIENT_FIELDS
additional per-recipient fields (default: None)


--display_html DISPLAY_HTML
html content to display (default: None)
--verification_type VERIFICATION_TYPE
verification type (default: merkle)
```

#### About
Expand Down Expand Up @@ -159,6 +162,9 @@ usage: instantiate_v2_certificate_batch.py [-h] [-c MY_CONFIG]
[--additional_per_recipient_fields ADDITIONAL_PER_RECIPIENT_FIELDS]
[--unsigned_certificates_dir UNSIGNED_CERTIFICATES_DIR]
[--roster ROSTER]
[--filename_format FILENAME_FORMAT]
[--no_clobber]
[--verification_type VERIFICATION_TYPE]

Args that start with '--' (eg. --data_dir) can also be set in a config file (./cert-tools/conf.ini or specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at https://goo.gl/R74nmi). If an arg is specified in more than one place, then commandline values override config file values which override defaults.

Expand All @@ -181,6 +187,13 @@ Argument details:
output directory for unsigned certificates (default:
None)
--roster ROSTER roster file name (default: None)
--filename_format FILENAME_FORMAT
how to format certificate filenames (one of
certname_identity or uuid) (default: None)
--no_clobber whether to overwrite existing certificates (default:
False)
--verification_type VERIFICATION_TYPE
verification type (default: merkle)

```

Expand Down
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'
208 changes: 208 additions & 0 deletions cert_tools/create_v2_1_certificate_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/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',
'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
}
}

if config.verification_type == 'openbadge_hosted':
badge['id'] = config.badge_id
else:
badge['id'] = helpers.URN_UUID_PREFIX + config.badge_id

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

if config.issuer_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|*'
}

if config.verification_type == 'openbadge_hosted':
assertion['id'] = helpers.urljoin_wrapper(config.issuer_certs_url, '*|CERTUID|*')
else:
assertion['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