def verify_baked_image(uploaded_image): try: unbake(uploaded_image) except Exception: return False return True
def test_can_rebake_assertion(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) import issuer.utils # issue badge that gets baked with 1_1, while current version is 2_0 issuer.utils.CURRENT_OBI_VERSION = '2_0' issuer.utils.UNVERSIONED_BAKED_VERSION = '1_1' test_assertion = test_badgeclass.issue(recipient_id='*****@*****.**') v1_data = json.loads(str(unbake(test_assertion.image))) self.assertDictContainsSubset( {'@context': u'https://w3id.org/openbadges/v1'}, v1_data) original_image_url = test_assertion.image_url() test_assertion.rebake() self.assertEqual(original_image_url, test_assertion.image_url()) v2_datastr = unbake(test_assertion.image) self.assertTrue(v2_datastr) v2_data = json.loads(v2_datastr) self.assertDictContainsSubset( {'@context': u'https://w3id.org/openbadges/v2'}, v2_data)
def test_can_rebake_assertion(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) import issuer.utils # issue badge that gets baked with 1_1, while current version is 2_0 issuer.utils.CURRENT_OBI_VERSION = '2_0' issuer.utils.UNVERSIONED_BAKED_VERSION = '1_1' test_assertion = test_badgeclass.issue(recipient_id='*****@*****.**') v1_data = json.loads(str(unbake(test_assertion.image))) self.assertDictContainsSubset({ '@context': u'https://w3id.org/openbadges/v1' }, v1_data) original_image_url = test_assertion.image_url() test_assertion.rebake() self.assertEqual(original_image_url, test_assertion.image_url()) v2_datastr = unbake(test_assertion.image) self.assertTrue(v2_datastr) v2_data = json.loads(v2_datastr) self.assertDictContainsSubset({ '@context': u'https://w3id.org/openbadges/v2' }, v2_data)
def test_put_rebakes_assertion(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) test_assertion = test_badgeclass.issue(recipient_id='*****@*****.**') # v1 api v1_backdate = datetime.datetime(year=2021, month=3, day=3, tzinfo=pytz.UTC) updated_data = dict( expires=v1_backdate.isoformat() ) response = self.client.put('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions/{assertion}'.format( issuer=test_assertion.cached_issuer.entity_id, badge=test_assertion.cached_badgeclass.entity_id, assertion=test_assertion.entity_id ), updated_data) self.assertEqual(response.status_code, 200) updated_assertion = BadgeInstance.objects.get(entity_id=test_assertion.entity_id) updated_obo = json.loads(str(unbake(updated_assertion.image))) self.assertEqual(updated_obo.get('expires', None), updated_data.get('expires')) # v2 api v2_backdate = datetime.datetime(year=2002, month=3, day=3, tzinfo=pytz.UTC) updated_data = dict( issuedOn=v2_backdate.isoformat() ) response = self.client.put('/v2/assertions/{assertion}'.format( assertion=test_assertion.entity_id ), updated_data) self.assertEqual(response.status_code, 200) updated_assertion = BadgeInstance.objects.get(entity_id=test_assertion.entity_id) updated_obo = json.loads(str(unbake(updated_assertion.image))) self.assertEqual(updated_obo.get('issuedOn', None), updated_data.get('issuedOn'))
def test_resized_png_image_baked_properly(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion = { "email": "*****@*****.**" } response = self.client.post('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id ), assertion) self.assertIn('slug', response.data) assertion_slug = response.data.get('slug') instance = BadgeInstance.objects.get(entity_id=assertion_slug) instance.image.open() self.assertIsNotNone(unbake(instance.image)) instance.image.close() instance.image.open() image_data_present = False badge_data_present = False reader = png.Reader(file=instance.image) for chunk in reader.chunks(): if chunk[0] == 'IDAT': image_data_present = True elif chunk[0] == 'iTXt' and chunk[1].startswith('openbadges\x00\x00\x00\x00\x00'): badge_data_present = True self.assertTrue(image_data_present and badge_data_present)
def process_baked_resource(state, task_meta, **options): try: node_id = task_meta['node_id'] resource_b64 = state['input']['original_json'][node_id] except KeyError: raise TaskPrerequisitesError() try: search_string = resource_b64.decode('utf-8') match = re.search(r'^data:(image\/png|image\/svg\+xml);base64,(.+)$', search_string) content_type = match.group(1) suffix = '.png' if content_type == 'image/png' else '.svg' b64_data = match.group(2) except (AttributeError, IndexError): return task_result(False, "Cannot determine image type or content from dataURI {}".format(abv(resource_b64))) baked_file = NamedTemporaryFile(suffix=suffix) baked_file.write(base64.b64decode(b64_data)) baked_file.seek(0) assertion_data = unbake(baked_file) if assertion_data: actions = [ store_input(assertion_data), add_task(DETECT_INPUT_TYPE, is_potential_baked_input=False) ] return task_result(True, "Retrieved baked data from image resource {}".format(abv(node_id)), actions) else: return task_result( False, "Resource {} was an image of known type but no baked Open Badges data was available".format( abv(node_id) ))
def test_resized_png_image_baked_properly(self): test_eduid = "urn:mace:eduid.nl:1.0:d57b4355-c7c6-4924-a944-6172e31e9bbc:27871c14-b952-4d7e-85fd-6329ac5c6f18" test_recipient = self.setup_user(authenticate=True, eduid=test_eduid) test_user = self.setup_user(authenticate=True, teacher=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion_post_data = self.enroll_user(test_recipient, test_badgeclass) response = self.client.post('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id ), json.dumps(assertion_post_data), content_type='application/json') self.assertIn('slug', response.data) assertion_slug = response.data.get('slug') instance = BadgeInstance.objects.get(entity_id=assertion_slug) instance.image.open() self.assertIsNotNone(unbake(instance.image)) instance.image.close() instance.image.open() image_data_present = False badge_data_present = False reader = png.Reader(file=instance.image) for chunk in reader.chunks(): if chunk[0] == 'IDAT': image_data_present = True elif chunk[0] == 'iTXt' and chunk[1].startswith('openbadges\x00\x00\x00\x00\x00'): badge_data_present = True self.assertTrue(image_data_present and badge_data_present)
def verify_baked_image_response(self, assertion, response, obi_version, **kwargs): self.assertEqual(response.status_code, 200) baked_image = io.BytesIO(b"".join(response.streaming_content)) baked_json = unbake(baked_image) baked_metadata = json.loads(baked_json) assertion_metadata = assertion.get_json(obi_version=obi_version, **kwargs) self.assertDictEqual(baked_metadata, assertion_metadata)
def test_resized_png_image_baked_properly(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion = {"email": "*****@*****.**"} response = self.client.post( '/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id), assertion) self.assertIn('slug', response.data) assertion_slug = response.data.get('slug') instance = BadgeInstance.objects.get(entity_id=assertion_slug) instance.image.open() self.assertIsNotNone(unbake(instance.image)) instance.image.close() instance.image.open() image_data_present = False badge_data_present = False reader = png.Reader(file=instance.image) for chunk in reader.chunks(): if chunk[0] == 'IDAT': image_data_present = True elif chunk[0] == 'iTXt' and chunk[1].startswith( 'openbadges\x00\x00\x00\x00\x00'): badge_data_present = True self.assertTrue(image_data_present and badge_data_present)
def test_can_update_assertion(self): test_eduid = "urn:mace:eduid.nl:1.0:d57b4355-c7c6-4924-a944-6172e31e9bbc:27871c14-b952-4d7e-85fd-6329ac5c6f18" test_recipient = self.setup_user(authenticate=True, eduid=test_eduid) test_user = self.setup_user(authenticate=True, teacher=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion_post_data = self.enroll_user(test_recipient, test_badgeclass) response = self.client.post('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id ), json.dumps(assertion_post_data), content_type='application/json') self.assertEqual(response.status_code, 201) original_assertion = response.data new_assertion_data = { "recipient_type": "id", "recipient_identifier": "urn:mace:eduid.nl:1.0:d57b4355-c7c6-4924-a944-6172e31e9bbc:27871c14-b952-4d7e-85fd-6329ac5c6f18", "narrative": "test narrative", "evidence_items": [{ "narrative": "This is the evidence item narrative AGAIN!.", "evidence_url": "" }], } response = self.client.put('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions/{assertion}'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id, assertion=original_assertion.get('slug'), ), json.dumps(new_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) updated_assertion = response.data self.assertDictContainsSubset(new_assertion_data, updated_assertion) # verify v2 api v2_assertion_data = { "evidence": [ { "narrative": "remove and add new narrative", } ] } response = self.client.put('/v2/assertions/{assertion}'.format( assertion=original_assertion.get('slug') ), json.dumps(v2_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) data = json.loads(response.content) v2_assertion = data.get('result', [None])[0] self.assertEqual(len(v2_assertion_data['evidence']), 1) self.assertEqual(v2_assertion['evidence'][0]['narrative'], v2_assertion_data['evidence'][0]['narrative']) instance = BadgeInstance.objects.get(entity_id=original_assertion['slug']) image = instance.image image_data = json.loads(unbake(image)) self.assertEqual(image_data.get('evidence', {})[0].get('narrative'), v2_assertion_data['evidence'][0]['narrative'])
def get_instance_url_from_image(imageFile): """ unbake an open file, and return the assertion URL contained within """ image_contents = unbake(imageFile) if image_contents is None: raise ValidationError("No assertion found in image") return get_instance_url_from_unknown_string(image_contents)
def test_can_update_assertion(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion_data = { "email": "*****@*****.**", "create_notification": False, } response = self.client.post('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id ), assertion_data) self.assertEqual(response.status_code, 201) original_assertion = response.data new_assertion_data = { "recipient_type": "email", "recipient_identifier": "*****@*****.**", "narrative": "test narrative", "evidence_items": [{ "narrative": "This is the evidence item narrative AGAIN!.", "evidence_url": "" }], } response = self.client.put('/v1/issuer/issuers/{issuer}/badges/{badge}/assertions/{assertion}'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id, assertion=original_assertion.get('slug'), ), json.dumps(new_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) updated_assertion = response.data self.assertDictContainsSubset(new_assertion_data, updated_assertion) # verify v2 api v2_assertion_data = { "evidence": [ { "narrative": "remove and add new narrative", } ] } response = self.client.put('/v2/assertions/{assertion}'.format( assertion=original_assertion.get('slug') ), json.dumps(v2_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) data = json.loads(response.content) v2_assertion = data.get('result', [None])[0] self.assertEqual(len(v2_assertion_data['evidence']), 1) self.assertEqual(v2_assertion['evidence'][0]['narrative'], v2_assertion_data['evidence'][0]['narrative']) instance = BadgeInstance.objects.get(entity_id=original_assertion['slug']) image = instance.image image_data = json.loads(unbake(image)) self.assertEqual(image_data.get('evidence', {})[0].get('narrative'), v2_assertion_data['evidence'][0]['narrative'])
def verification_store(badge_input, recipient_profile=None, store=None, options=DEFAULT_OPTIONS): if store is None: store = create_store(main_reducer, INITIAL_STATE) try: if hasattr(badge_input, 'read') and hasattr(badge_input, 'seek'): badge_input.seek(0) badge_data = unbake(badge_input) if not badge_data: raise ValueError( "Could not find Open Badges metadata in file.") else: badge_data = badge_input except ValueError as e: # Could not obtain badge data from input. Set the result as a failed DETECT_INPUT_TYPE task. store.dispatch(store_input(badge_input.name)) store.dispatch(add_task(tasks.DETECT_INPUT_TYPE)) store.dispatch(set_input_type('file')) task = store.get_state()['tasks'][0] store.dispatch( resolve_task(task.get('task_id'), success=False, result=e.message)) else: store.dispatch(store_input(badge_data)) store.dispatch( add_task(tasks.DETECT_INPUT_TYPE, expected_class=OBClasses.Assertion)) if recipient_profile: profile_id = recipient_profile.get('id') recipient_profile['@context'] = recipient_profile.get( '@context', OPENBADGES_CONTEXT_V2_URI) task = add_task(JSONLD_COMPACT_DATA, data=json.dumps(recipient_profile), expected_class=OBClasses.ExpectedRecipientProfile) if profile_id: task['node_id'] = profile_id store.dispatch(task) last_task_id = 0 while len(filter_active_tasks(store.get_state())): active_tasks = filter_active_tasks(store.get_state()) task_meta = active_tasks[0] task_func = tasks.task_named(task_meta['name']) if task_meta['task_id'] == last_task_id: break last_task_id = task_meta['task_id'] call_task(task_func, task_meta, store, options) return store
def get_badge_instance_from_baked_image(baked_image): image_data = unbake(baked_image) if not image_data: raise ValidationError("No badge data found within the provided image.") badge_instance_type = _get_reference_type(image_data) if badge_instance_type == "json": return get_badge_instance_from_json(image_data) if badge_instance_type == "url": return get_badge_component_from_url(image_data) if badge_instance_type == "jwt": return get_badge_instance_from_jwt(image_data) raise ValidationError( "Unable to determine badge instance format from baked image.")
def get_badge_instance_from_baked_image(baked_image): image_data = unbake(baked_image) if not image_data: raise ValidationError( "No badge data found within the provided image.") badge_instance_type = _get_reference_type(image_data) if badge_instance_type == "json": return get_badge_instance_from_json(image_data) if badge_instance_type == "url": return get_badge_component_from_url(image_data) if badge_instance_type == "jwt": return get_badge_instance_from_jwt(image_data) raise ValidationError( "Unable to determine badge instance format from baked image.")
def test_resized_png_image_baked_properly(self): current_user = get_user_model().objects.get(pk=1) with open( os.path.join(os.path.dirname(__file__), 'testfiles', 'guinea_pig_testing_badge.png'), 'r') as badge_image: badgeclass_props = { 'name': 'Badge of Awesome', 'description': "An awesome badge only awarded to awesome people or non-existent test entities", 'image': badge_image, 'criteria': 'The earner of this badge must be truly, truly awesome.', } self.client.force_authenticate(user=current_user) response_bc = self.client.post( '/v1/issuer/issuers/test-issuer/badges', badgeclass_props) assertion = {"email": "*****@*****.**"} self.client.force_authenticate(user=current_user) response = self.client.post( '/v1/issuer/issuers/test-issuer/badges/badge-of-awesome/assertions', assertion) instance = BadgeInstance.objects.get(slug=response.data.get('slug')) instance.image.open() self.assertIsNotNone(unbake(instance.image)) instance.image.close() instance.image.open() reader = png.Reader(file=instance.image) for chunk in reader.chunks(): if chunk[0] == 'IDAT': image_data_present = True elif chunk[0] == 'iTXt' and chunk[1].startswith( 'openbadges\x00\x00\x00\x00\x00'): badge_data_present = True self.assertTrue(image_data_present and badge_data_present)
def test_resized_png_image_baked_properly(self): current_user = get_user_model().objects.get(pk=1) with open( os.path.join(os.path.dirname(__file__), 'testfiles', 'guinea_pig_testing_badge.png'), 'r' ) as badge_image: badgeclass_props = { 'name': 'Badge of Awesome', 'description': "An awesome badge only awarded to awesome people or non-existent test entities", 'image': badge_image, 'criteria': 'The earner of this badge must be truly, truly awesome.', } self.client.force_authenticate(user=current_user) response_bc = self.client.post( '/v1/issuer/issuers/test-issuer/badges', badgeclass_props ) assertion = { "email": "*****@*****.**" } self.client.force_authenticate(user=current_user) response = self.client.post('/v1/issuer/issuers/test-issuer/badges/badge-of-awesome/assertions', assertion) instance = BadgeInstance.objects.get(slug=response.data.get('slug')) instance.image.open() self.assertIsNotNone(unbake(instance.image)) instance.image.close() instance.image.open() reader = png.Reader(file=instance.image) for chunk in reader.chunks(): if chunk[0] == 'IDAT': image_data_present = True elif chunk[0] == 'iTXt' and chunk[1].startswith('openbadges\x00\x00\x00\x00\x00'): badge_data_present = True self.assertTrue(image_data_present and badge_data_present)
def test_can_update_assertion(self): test_user = self.setup_user(authenticate=True) test_issuer = self.setup_issuer(owner=test_user) test_badgeclass = self.setup_badgeclass(issuer=test_issuer) assertion_data = { "email": "*****@*****.**", "create_notification": False, } response = self.client.post( '/v1/issuer/issuers/{issuer}/badges/{badge}/assertions'.format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id), assertion_data) self.assertEqual(response.status_code, 201) original_assertion = response.data new_assertion_data = { "recipient_type": "email", "recipient_identifier": "*****@*****.**", "narrative": "test narrative", "evidence_items": [{ "narrative": "This is the evidence item narrative AGAIN!.", "evidence_url": "" }], } response = self.client.put( '/v1/issuer/issuers/{issuer}/badges/{badge}/assertions/{assertion}' .format( issuer=test_issuer.entity_id, badge=test_badgeclass.entity_id, assertion=original_assertion.get('slug'), ), json.dumps(new_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) updated_assertion = response.data self.assertDictContainsSubset(new_assertion_data, updated_assertion) # verify v2 api v2_assertion_data = { "evidence": [{ "narrative": "remove and add new narrative", }] } response = self.client.put('/v2/assertions/{assertion}'.format( assertion=original_assertion.get('slug')), json.dumps(v2_assertion_data), content_type='application/json') self.assertEqual(response.status_code, 200) data = json.loads(response.content) v2_assertion = data.get('result', [None])[0] self.assertEqual(len(v2_assertion_data['evidence']), 1) self.assertEqual(v2_assertion['evidence'][0]['narrative'], v2_assertion_data['evidence'][0]['narrative']) instance = BadgeInstance.objects.get( entity_id=original_assertion['slug']) image = instance.image image_data = json.loads(unbake(image)) self.assertEqual( image_data.get('evidence', {})[0].get('narrative'), v2_assertion_data['evidence'][0]['narrative'])
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))