def test_prepare_for_request(self, rand_nonce): # use a fixed nonce to ensure the same signature rand_nonce.return_value = truncate_or_pad( b'\0', size=crypto_box_NONCEBYTES ) m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat( 'http://auth.mybank/', caveat_key, identifier ) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) assert_equal( protected.signature, '2eb01d0dd2b4475330739140188648cf25dda0425ea9f661f1574ca0a9eac54e' )
def test_logging(self): r = Request() root_key = hashlib.sha256("root").hexdigest() root_macaroon = Macaroon(key=root_key) discharge_key = hashlib.sha256("discharge").hexdigest() discharge_caveat_id = '{"secret": "thing"}' root_macaroon.add_third_party_caveat( "sso.example", discharge_key, discharge_caveat_id) root_macaroon.add_first_party_caveat( "store.example|package_id|{}".format( json.dumps(["example-package"]))) unbound_discharge_macaroon = Macaroon( location="sso.example", key=discharge_key, identifier=discharge_caveat_id) unbound_discharge_macaroon.add_first_party_caveat( "sso.example|account|{}".format( base64.b64encode(json.dumps({ "openid": "1234567", "email": "*****@*****.**", })))) logger = BufferLogger() MacaroonAuth( root_macaroon.serialize(), unbound_discharge_macaroon.serialize(), logger=logger)(r) self.assertEqual( ['DEBUG root macaroon: snap-ids: ["example-package"]', 'DEBUG discharge macaroon: OpenID identifier: 1234567'], logger.getLogBuffer().splitlines())
def test_verify_third_party_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our other secret key', key='this is a different super-secret key; \ never use the same secret twice' ) m.add_first_party_caveat('account = 3735928559') caveat_key = '4; guaranteed random by a fair toss of the dice' identifier = 'this was how we remind auth of key/pred' m.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) discharge = Macaroon( location='http://auth.mybank/', key=caveat_key, identifier=identifier ) discharge.add_first_party_caveat('time < 2015-01-01T00:00') protected = m.prepare_for_request(discharge) v = Verifier() v.satisfy_exact('account = 3735928559') v.satisfy_exact('time < 2015-01-01T00:00') verified = v.verify( m, 'this is a different super-secret key; \ never use the same secret twice', discharge_macaroons=[protected] ) assert_true(verified)
def test_verify_third_party_caveats_multi_level(self): # See https://github.com/ecordell/pymacaroons/issues/37 root = Macaroon(location="", identifier="root-id", key="root-key") root.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") # Create a discharge macaroon that requires a secondary discharge. discharge1 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") discharge1.add_third_party_caveat("barbara", "barbara-caveat-root-key", "barbara-is-great") # Create the secondary discharge macaroon. discharge2 = Macaroon(location="barbara", identifier="barbara-is-great", key="barbara-caveat-root-key") # Prepare the discharge macaroons for request. discharge1 = root.prepare_for_request(discharge1) discharge2 = root.prepare_for_request(discharge2) verified = Verifier( discharge_macaroons=[discharge1, discharge2]).verify( root, "root-key") assert_true(verified)
def test_mutual_discharge(self): m1 = Macaroon(location="", identifier="root-id", key="root-key") m1.add_third_party_caveat("bob", "bob-caveat-root-key", "bob-is-great") m2 = Macaroon(location="bob", identifier="bob-is-great", key="bob-caveat-root-key") m2.add_third_party_caveat("charlie", "bob-caveat-root-key", "bob-is-great") m2 = m1.prepare_for_request(m2) Verifier(discharge_macaroons=[m2]).verify(m1, "root-key")
def test_serializing_deserializing_macaroon(self, key_id, loc, key): assume(key_id and loc and key) macaroon = Macaroon(location=loc, identifier=key_id, key=key) deserialized = Macaroon.deserialize(macaroon.serialize()) assert_equal(macaroon.identifier, deserialized.identifier) assert_equal(macaroon.location, deserialized.location) assert_equal(macaroon.signature, deserialized.signature)
def test_upload_no_discharge(self): root_key = hashlib.sha256(self.factory.getUniqueString()).hexdigest() root_macaroon = Macaroon(key=root_key) snapbuild = self.makeUploadableSnapBuild( store_secrets={"root": root_macaroon.serialize()}) transaction.commit() self._addUnscannedUploadResponse() self._addSnapPushResponse() with dbuser(config.ISnapStoreUploadJobSource.dbuser): self.assertEqual( "http://sca.example/dev/api/snaps/1/builds/1/status", self.client.upload(snapbuild)) requests = [call.request for call in responses.calls] self.assertThat(requests, MatchesListwise([ RequestMatches( url=Equals("http://updown.example/unscanned-upload/"), method=Equals("POST"), form_data={ "binary": MatchesStructure.byEquality( name="binary", filename="test-snap.snap", value="dummy snap content", type="application/octet-stream", )}), RequestMatches( url=Equals("http://sca.example/dev/api/snap-push/"), method=Equals("POST"), headers=ContainsDict( {"Content-Type": Equals("application/json")}), auth=("Macaroon", MacaroonsVerify(root_key)), json_data={ "name": "test-snap", "updown_id": 1, "series": "rolling", }), ]))
def _make_store_secrets(self): self.root_key = hashlib.sha256( self.factory.getUniqueString()).hexdigest() root_macaroon = Macaroon(key=self.root_key) self.discharge_key = hashlib.sha256( self.factory.getUniqueString()).hexdigest() self.discharge_caveat_id = self.factory.getUniqueString() root_macaroon.add_third_party_caveat( "sso.example", self.discharge_key, self.discharge_caveat_id) unbound_discharge_macaroon = Macaroon( location="sso.example", key=self.discharge_key, identifier=self.discharge_caveat_id) return { "root": root_macaroon.serialize(), "discharge": unbound_discharge_macaroon.serialize(), }
def test_triggers_store_uploads(self): # The upload processor triggers store uploads if appropriate. self.pushConfig("snappy", store_url="http://sca.example/", store_upload_url="http://updown.example/") self.switchToAdmin() self.snap.store_series = self.factory.makeSnappySeries( usable_distro_series=[self.snap.distro_series]) self.snap.store_name = self.snap.name self.snap.store_upload = True self.snap.store_secrets = {"root": Macaroon().serialize()} Store.of(self.snap).flush() self.switchToUploader() self.assertFalse(self.build.verifySuccessfulUpload()) upload_dir = os.path.join(self.incoming_folder, "test", str(self.build.id), "ubuntu") write_file(os.path.join(upload_dir, "wget_0_all.snap"), "snap") handler = UploadHandler.forProcessor(self.uploadprocessor, self.incoming_folder, "test", self.build) result = handler.processSnap(self.log) self.assertEqual( UploadStatusEnum.ACCEPTED, result, "Snap upload failed\nGot: %s" % self.log.getLogBuffer()) self.assertEqual(BuildStatus.FULLYBUILT, self.build.status) self.assertTrue(self.build.verifySuccessfulUpload()) self.assertEqual(1, len(list(self.build.store_upload_jobs)))
def test_serializing_too_long_packet(self): m = Macaroon(location='test', identifier='blah', key='secret', version=MACAROON_V1) m.add_first_party_caveat('x' * 65527) # one byte too long assert_raises(MacaroonSerializationException, m.serialize)
def test_serializing_max_length_packet(self): m = Macaroon(location='test', identifier='blah', key='secret', version=MACAROON_V1) m.add_first_party_caveat('x' * 65526) # exactly 0xFFFF assert_not_equal(m.serialize(), None)
def callback(request): new_macaroon = Macaroon( location="sso.example", key=self.discharge_key, identifier=self.discharge_caveat_id) new_macaroon.add_first_party_caveat("sso|expires|tomorrow") return ( 200, {}, json.dumps({"discharge_macaroon": new_macaroon.serialize()}))
def test_serializing_deserializing_json(self): m = Macaroon(location='http://test/', identifier='first', key='secret_key_1') m.add_first_party_caveat('test = caveat') n = Macaroon.deserialize(m.serialize(serializer=JsonSerializer()), serializer=JsonSerializer()) assert_equal(m.signature, n.signature)
def test_basic_signature(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') assert_equal( m.signature, 'e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f')
def test_verify_unknown_issuer(self): macaroon = Macaroon(location=config.vhost.mainsite.hostname, identifier='unknown-issuer', key='test') self.assertEqual( faults.Unauthorized(), self.authserver.verifyMacaroon(macaroon.serialize(), 'LibraryFileAlias', 1))
def setUpStoreUpload(self): self.pushConfig("snappy", store_url="http://sca.example/", store_upload_url="http://updown.example/") self.build.snap.store_series = self.factory.makeSnappySeries( usable_distro_series=[self.build.snap.distro_series]) self.build.snap.store_name = self.factory.getUniqueUnicode() self.build.snap.store_secrets = {"root": Macaroon().serialize()}
def test_updateStatus_failure_does_not_trigger_store_uploads(self): # A failed SnapBuild does not trigger store uploads. self.build.snap.store_series = self.factory.makeSnappySeries() self.build.snap.store_name = self.factory.getUniqueUnicode() self.build.snap.store_upload = True self.build.snap.store_secrets = {"root": Macaroon().serialize()} with dbuser(config.builddmaster.dbuser): self.build.updateStatus(BuildStatus.FAILEDTOBUILD) self.assertContentEqual([], self.build.store_upload_jobs)
def bake(user: User, caveats: List[str]) -> str: m_obj = Macaroon(location=os.environ.get("ENDPOINT"), identifier=user.username, key=user.key) for caveat in caveats: m_obj.add_first_party_caveat(caveat) return m_obj.serialize()
def test_updateStatus_fullybuilt_triggers_store_uploads(self): # A completed SnapBuild triggers store uploads. self.build.snap.store_series = self.factory.makeSnappySeries() self.build.snap.store_name = self.factory.getUniqueUnicode() self.build.snap.store_upload = True self.build.snap.store_secrets = {"root": Macaroon().serialize()} with dbuser(config.builddmaster.dbuser): self.build.updateStatus(BuildStatus.FULLYBUILT) self.assertEqual(1, len(list(self.build.store_upload_jobs)))
def test_serializing_json(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('test = caveat') assert_equal( json.loads(m.serialize(serializer=JsonSerializer()))['signature'], "197bac7a044af33332865b9266e26d493bdd668a660e44d88ce1a998c23dbd67")
def test_first_party_caveat(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('test = caveat') assert_equal( m.signature, '197bac7a044af33332865b9266e26d493bdd668a660e44d88ce1a998c23dbd67')
def issueMacaroon(self, context, **kwargs): """See `IMacaroonIssuer`.""" context = self.checkIssuingContext(context, **kwargs) macaroon = Macaroon(location=config.vhost.mainsite.hostname, identifier=self.identifier, key=self._root_secret) macaroon.add_first_party_caveat("%s %s" % (self._primary_caveat_name, context)) return macaroon
def test_good_no_discharge(self): r = Request() root_key = hashlib.sha256("root").hexdigest() root_macaroon = Macaroon(key=root_key) MacaroonAuth(root_macaroon.serialize())(r) auth_value = r.headers["Authorization"] self.assertThat(auth_value, StartsWith("Macaroon ")) self.assertThat( parse_dict_header(auth_value[len("Macaroon "):]), MacaroonsVerify(root_key))
def test_good(self): r = Request() root_key = hashlib.sha256("root").hexdigest() root_macaroon = Macaroon(key=root_key) discharge_key = hashlib.sha256("discharge").hexdigest() discharge_caveat_id = '{"secret": "thing"}' root_macaroon.add_third_party_caveat( "sso.example", discharge_key, discharge_caveat_id) unbound_discharge_macaroon = Macaroon( location="sso.example", key=discharge_key, identifier=discharge_caveat_id) MacaroonAuth( root_macaroon.serialize(), unbound_discharge_macaroon.serialize())(r) auth_value = r.headers["Authorization"] self.assertThat(auth_value, StartsWith("Macaroon ")) self.assertThat( parse_dict_header(auth_value[len("Macaroon "):]), MacaroonsVerify(root_key))
def test_serializing_json_v2(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it', version=MACAROON_V2) m.add_first_party_caveat('test = caveat') assert_equal( json.loads(m.serialize(serializer=JsonSerializer()))['s64'], "GXusegRK8zMyhluSZuJtSTvdZopmDkTYjOGpmMI9vWc")
def test_serializing_v2(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it', version=MACAROON_V2) m.add_first_party_caveat('test = caveat') n = Macaroon.deserialize(m.serialize()) assert_equal(m.identifier_bytes, n.identifier_bytes) assert_equal(m.version, n.version)
def login_device(macaroon, manufacturer, channel_id): m = Macaroon(location='localhost', identifier=manufacturer, key=keys[manufacturer]) m1 = Macaroon.deserialize(macaroon) channels = [] for caveat in m1.caveats: m.add_first_party_caveat(caveat.to_dict()['cid']) channels.append(caveat.to_dict()['cid']) return m1.signature == m.signature and channel_id in channels[2:]
def test_serializing(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('test = caveat') assert_equal( m.serialize(), 'MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZ\ WQgb3VyIHNlY3JldCBrZXkKMDAxNmNpZCB0ZXN0ID0gY2F2ZWF0CjAwMmZzaWduYXR1cmUgGXusegR\ K8zMyhluSZuJtSTvdZopmDkTYjOGpmMI9vWcK')
def test_verify_failing_first_party_general_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('general caveat') v = Verifier() v.satisfy_general(lambda _: False) with assert_raises(MacaroonInvalidSignatureException) as cm: v.verify(m, 'this is our super secret key; only we should know it')
def test_verify_first_party_exact_caveats(self): m = Macaroon( location='http://mybank/', identifier='we used our secret key', key='this is our super secret key; only we should know it') m.add_first_party_caveat('test = caveat') v = Verifier() v.satisfy_exact('test = caveat') verified = v.verify( m, 'this is our super secret key; only we should know it') assert_true(verified)