-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#30] - WIP, do not merge into master yet. This adds some basic, but …
…not entirely functional, support for hosted verification.
- Loading branch information
Showing
9 changed files
with
428 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = '2.0.11' | ||
__version__ = '2.1.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.