def save(self, *args, **kwargs): if self.pk is None: self.salt = uuid.uuid4().hex self.created_at = datetime.datetime.now() # do this now instead of in AbstractVersionedEntity.save() so we can use it for image name if self.entity_id is None: self.entity_id = generate_entity_uri() if not self.image: badgeclass_name, ext = os.path.splitext(self.badgeclass.image.file.name) new_image = StringIO.StringIO() bake(image_file=self.cached_badgeclass.image.file, assertion_json_string=json_dumps(self.get_json(obi_version=UNVERSIONED_BAKED_VERSION), indent=2), output_file=new_image) self.image.save(name='assertion-{id}{ext}'.format(id=self.entity_id, ext=ext), content=ContentFile(new_image.read()), save=False) try: from badgeuser.models import CachedEmailAddress existing_email = CachedEmailAddress.cached.get(email=self.recipient_identifier) if self.recipient_identifier != existing_email.email and \ self.recipient_identifier not in [e.email for e in existing_email.cached_variants()]: existing_email.add_variant(self.recipient_identifier) except CachedEmailAddress.DoesNotExist: pass if self.revoked is False: self.revocation_reason = None super(BadgeInstance, self).save(*args, **kwargs)
def get_baked_image_url(self, obi_version=CURRENT_OBI_VERSION): if obi_version == UNVERSIONED_BAKED_VERSION: # requested version is the one referenced in assertion.image return self.image.url try: baked_image = BadgeInstanceBakedImage.cached.get(badgeinstance=self, obi_version=obi_version) except BadgeInstanceBakedImage.DoesNotExist: # rebake baked_image = BadgeInstanceBakedImage(badgeinstance=self, obi_version=obi_version) json_to_bake = self.get_json( obi_version=obi_version, expand_issuer=True, expand_badgeclass=True, include_extra=True ) badgeclass_name, ext = os.path.splitext(self.badgeclass.image.file.name) new_image = StringIO.StringIO() bake(image_file=self.cached_badgeclass.image.file, assertion_json_string=json_dumps(json_to_bake, indent=2), output_file=new_image) baked_image.image.save( name='assertion-{id}-{version}{ext}'.format(id=self.entity_id, ext=ext, version=obi_version), content=ContentFile(new_image.read()), save=False ) baked_image.save() return baked_image.image.url
def get_baked_image_url(self, obi_version=CURRENT_OBI_VERSION): if obi_version == UNVERSIONED_BAKED_VERSION: # requested version is the one referenced in assertion.image return self.image.url try: baked_image = BadgeInstanceBakedImage.cached.get( badgeinstance=self, obi_version=obi_version) except BadgeInstanceBakedImage.DoesNotExist: # rebake baked_image = BadgeInstanceBakedImage(badgeinstance=self, obi_version=obi_version) json_to_bake = self.get_json(obi_version=obi_version, expand_issuer=True, expand_badgeclass=True, include_extra=True) badgeclass_name, ext = os.path.splitext( self.badgeclass.image.file.name) new_image = StringIO.StringIO() bake(image_file=self.cached_badgeclass.image.file, assertion_json_string=json_dumps(json_to_bake, indent=2), output_file=new_image) baked_image.image.save(name='assertion-{id}-{version}{ext}'.format( id=self.entity_id, ext=ext, version=obi_version), content=ContentFile(new_image.read()), save=False) baked_image.save() return baked_image.image.url
def rebake(self, obi_version=CURRENT_OBI_VERSION, save=True): if self.source_url: # dont rebake imported assertions return new_image = StringIO.StringIO() bake( image_file=self.cached_badgeclass.image.file, assertion_json_string=json_dumps(self.get_json(obi_version=obi_version), indent=2), output_file=new_image ) new_name = default_storage.save(self.image.name, ContentFile(new_image.read())) self.image.name = new_name if save: self.save()
def rebake(self, obi_version=CURRENT_OBI_VERSION, save=True): if self.source_url: # dont rebake imported assertions return new_image = StringIO.StringIO() bake(image_file=self.cached_badgeclass.image.file, assertion_json_string=json_dumps( self.get_json(obi_version=obi_version), indent=2), output_file=new_image) new_name = default_storage.save(self.image.name, ContentFile(new_image.read())) self.image.name = new_name if save: self.save()
def test_subject_set_from_badge_image(self): with open(os.path.join(os.path.dirname(__file__), 'testfiles', 'public_domain_heart.png'), 'rb') as f: image = bake(f, test_components['2_0_basic_assertion']) self.set_response_mocks() store = verification_store(image) report = generate_report(store) self.assertEqual(report['report']['validationSubject'], 'https://example.org/beths-robotics-badge.json')
def bakery(self, award: Award): assertion = self.openbadges.badge_assertion(award) image = self.badge_service.open_image(award) image = openbadges_bakery.bake(image, json.dumps(assertion)) image.seek(0) image_bytes = BytesIO(image.read()) return BadgeImage(data=image_bytes, path=None)
def bake_badge_instance(badge_instance, badge_class_image_url): try: unbaked_image = ContentFile( requests.get(badge_class_image_url)._content, "unbaked_image.png") unbaked_image.open() baked_image = bake(unbaked_image, json.dumps(badge_instance, indent=2)) except requests.exceptions.RequestException as e: raise ValidationError("Error retrieving image {}: {}".format( badge_class_image_url, e.message)) return baked_image
def bake_badge_instance(badge_instance, badge_class_image_url): try: unbaked_image = ContentFile( requests.get(badge_class_image_url)._content, "unbaked_image.png") unbaked_image.open() baked_image = bake(unbaked_image, json.dumps(badge_instance, indent=2)) except requests.exceptions.RequestException as e: raise ValidationError( "Error retrieving image {}: {}".format( badge_class_image_url, e.message)) return baked_image
def test_verify_of_baked_image(self): url = 'https://example.org/beths-robotics-badge.json' png_badge = os.path.join(os.path.dirname(__file__), 'testfiles', 'public_domain_heart.png') responses.add(responses.GET, url, body=test_components['2_0_basic_assertion'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/beths-robot-badge.png') responses.add(responses.GET, 'https://w3id.org/openbadges/v2', body=test_components['openbadges_context'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/robotics-badge.json', body=test_components['2_0_basic_badgeclass'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/robotics-badge.png') responses.add(responses.GET, 'https://example.org/organization.json', body=test_components['2_0_basic_issuer'], status=200, content_type='application/ld+json') with open(png_badge, 'rb') as image: baked_image = bake(image, test_components['2_0_basic_assertion']) responses.add(responses.GET, 'https://example.org/baked', body=baked_image.read(), content_type='image/png') results = verify(baked_image) # verify gets the JSON out of the baked image, and then detect_input_type # will reach out to the assertion URL to fetch the canonical assertion (thus, # we expect this to become an URL input type for the verifier). self.assertNotEqual(results, None) self.assertEqual(results.get('input').get('value'), url) self.assertEqual(results.get('input').get('input_type'), 'url') self.assertEqual(len(results['report']['messages']), 0, "There should be no failing tasks.") # Verify that the same result occurs when passing in the baked image url. another_result = verify('https://example.org/baked') self.assertTrue(another_result['report']['valid']) self.assertEqual(another_result['report']['validationSubject'], results['report']['validationSubject'])
def save(self, *args, **kwargs): if self.pk is None: self.json['recipient']['salt'] = salt = self.get_new_slug() self.json['recipient']['identity'] = \ generate_sha256_hashstring(self.recipient_identifier, salt) self.created_at = datetime.datetime.now() self.json['issuedOn'] = self.created_at.isoformat() imageFile = default_storage.open(self.badgeclass.image) self.image = bake(imageFile, json.dumps(self.json, indent=2)) self.image.open() if self.revoked is False: self.revocation_reason = None # TODO: If we don't want AutoSlugField to ensure uniqueness, configure it super(BadgeInstance, self).save(*args, **kwargs)
def save(self, *args, **kwargs): if self.pk is None: self.json['recipient']['salt'] = salt = self.get_new_slug() self.json['recipient']['identity'] = \ generate_sha256_hashstring(self.recipient_identifier, salt) self.created_at = datetime.datetime.now() self.json['issuedOn'] = self.created_at.isoformat() imageFile = default_storage.open(self.badgeclass.image.file.name) self.image = bake(imageFile, json.dumps(self.json, indent=2)) self.image.open() if self.revoked is False: self.revocation_reason = None # TODO: If we don't want AutoSlugField to ensure uniqueness, configure it super(BadgeInstance, self).save(*args, **kwargs)
def test_fetch_task_handles_potential_baked_input(self): set_up_context_mock() assertion_url = 'http://example.org/assertion/1' image_url = 'http://example.org/image' with open( os.path.join(os.path.dirname(__file__), 'testfiles', 'public_domain_heart.png'), 'rb') as f: baked_file = bake(f, assertion_url) responses.add(responses.GET, image_url, body=baked_file.read(), status=200, content_type='image/png') task = add_task(FETCH_HTTP_NODE, url=image_url, is_potential_baked_input=True) result, message, actions = run_task({}, task) self.assertTrue(result) store_resource_action = [ a for a in actions if a.get('type') == STORE_ORIGINAL_RESOURCE ][0] process_baked_input_action = [ a for a in actions if a.get('name') == PROCESS_BAKED_RESOURCE ][0] self.assertEqual(store_resource_action.get('node_id'), image_url) self.assertEqual(process_baked_input_action.get('node_id'), image_url) task = add_task(FETCH_HTTP_NODE, url=image_url, is_potential_baked_input=False) result, message, actions = run_task({}, task) self.assertTrue(result)
def test_submit_baked_1_1_badge_preserves_metadata_roundtrip(self): assertion_metadata = { "@context": "https://w3id.org/openbadges/v1", "type": "Assertion", "id": "http://a.com/instance2", "recipient": {"identity": "*****@*****.**", "hashed": False, "type": "email"}, "badge": "http://a.com/badgeclass", "issuedOn": "2015-04-30T00:00+00:00", "verify": {"type": "hosted", "url": "http://a.com/instance2"}, "extensions:ExampleExtension": { "@context": "https://openbadgespec.org/extensions/exampleExtension/context.json", "type": ["Extension", "extensions:ExampleExtension"], "exampleProperty": "some extended text" }, "schema:unknownMetadata": 55 } badgeclass_metadata = { "@context": "https://w3id.org/openbadges/v1", "type": "BadgeClass", "id": "http://a.com/badgeclass", "name": "Basic Badge", "description": "Basic as it gets. v1.1", "image": "http://a.com/badgeclass_image", "criteria": "http://a.com/badgeclass_criteria", "issuer": "http://a.com/issuer" } issuer_metadata = { "@context": "https://w3id.org/openbadges/v1", "type": "Issuer", "id": "http://a.com/issuer", "name": "Basic Issuer", "url": "http://a.com/issuer/website" } with open(os.path.join(dir, 'testfiles/baked_image.png')) as image_file: original_image = bake(image_file, json.dumps(assertion_metadata)) original_image.seek(0) responses.add( responses.GET, 'http://a.com/badgeclass_image', body=open(os.path.join(dir, 'testfiles/unbaked_image.png')).read(), status=200, content_type='image/png' ) setup_resources([ {'url': OPENBADGES_CONTEXT_V1_URI, 'filename': 'v1_context.json'}, {'url': OPENBADGES_CONTEXT_V2_URI, 'response_body': json.dumps(OPENBADGES_CONTEXT_V2_DICT)}, {'url': "https://openbadgespec.org/extensions/exampleExtension/context.json", 'response_body': json.dumps( { "@context": { "obi": "https://w3id.org/openbadges#", "extensions": "https://w3id.org/openbadges/extensions#", "exampleProperty": "http://schema.org/text" }, "obi:validation": [ { "obi:validatesType": "extensions:ExampleExtension", "obi:validationSchema": "https://openbadgespec.org/extensions/exampleExtension/schema.json" } ] } )}, {'url': "https://openbadgespec.org/extensions/exampleExtension/schema.json", 'response_body': json.dumps( { "$schema": "http://json-schema.org/draft-04/schema#", "title": "1.1 Open Badge Example Extension", "description": "An extension that allows you to add a single string exampleProperty to an extension object to represent some of your favorite text.", "type": "object", "properties": { "exampleProperty": { "type": "string" } }, "required": [ "exampleProperty" ] } )}, {'url': 'http://a.com/instance2', 'response_body': json.dumps(assertion_metadata)}, {'url': 'http://a.com/badgeclass', 'response_body': json.dumps(badgeclass_metadata)}, {'url': 'http://a.com/issuer', 'response_body': json.dumps(issuer_metadata)} ]) self.setup_user(email='*****@*****.**', authenticate=True) self.assertDictEqual(json.loads(unbake(original_image)), assertion_metadata) original_image.seek(0) response = self.client.post('/v1/earner/badges', {'image': original_image}) self.assertEqual(response.status_code, 201) public_url = response.data.get('shareUrl') self.assertIsNotNone(public_url) response = self.client.get(public_url, Accept="application/json") for key in ['issuedOn']: fetched_ts = dateutil.parser.parse(response.data.get(key)) metadata_ts = dateutil.parser.parse(assertion_metadata.get(key)) self.assertEqual(fetched_ts, metadata_ts) for key in ['recipient', 'extensions:ExampleExtension']: fetched_dict = response.data.get(key) self.assertIsNotNone(fetched_dict, "Field '{}' is missing".format(key)) metadata_dict = assertion_metadata.get(key) self.assertDictContainsSubset(metadata_dict, fetched_dict) for key in ['schema:unknownMetadata']: self.assertEqual(response.data.get(key), assertion_metadata.get(key))