Пример #1
0
 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())
Пример #2
0
 def test_serializing_json_v2_with_binary(self):
     id = base64.b64decode('AK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc==')
     m = Macaroon(
         location='http://mybank/',
         identifier=id,
         key='this is our super secret key; only we should know it',
         version=MACAROON_V2)
     assert_equal(
         json.loads(m.serialize(serializer=JsonSerializer()))['i64'],
         "AK2o-q0Aq9-bONkXw7ky7HAuhCLO9hhaMMc")
     n = Macaroon.deserialize(m.serialize(serializer=JsonSerializer()),
                              serializer=JsonSerializer())
     assert_equal(m.identifier_bytes, n.identifier_bytes)
Пример #3
0
 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 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)
Пример #5
0
 def test_serializing_max_length_packet(self):
     m = Macaroon(location='test', identifier='blah', key='secret')
     m.add_first_party_caveat('x' * 65526)  # exactly 0xFFFF
     assert_not_equal(
         m.serialize(),
         None
     )
Пример #6
0
 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",
                 }),
         ]))
Пример #7
0
 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(),
         }
Пример #8
0
 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)
Пример #9
0
 def test_serializing_json_v2_with_binary(self):
     id = base64.b64decode('AK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc==')
     m = Macaroon(
         location='http://mybank/',
         identifier=id,
         key='this is our super secret key; only we should know it',
         version=MACAROON_V2
     )
     assert_equal(
         json.loads(m.serialize(serializer=JsonSerializer()))['i64'],
         "AK2o-q0Aq9-bONkXw7ky7HAuhCLO9hhaMMc"
     )
     n = Macaroon.deserialize(
         m.serialize(serializer=JsonSerializer()),
         serializer=JsonSerializer()
     )
     assert_equal(m.identifier_bytes, n.identifier_bytes)
Пример #10
0
 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))
Пример #11
0
 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()}))
Пример #12
0
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()
Пример #13
0
 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")
Пример #14
0
 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))
Пример #15
0
 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")
Пример #16
0
 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))
Пример #17
0
 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 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)
Пример #19
0
 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"
     )
Пример #20
0
    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')
Пример #21
0
 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)
Пример #22
0
 def test_serializing_with_binary_v2(self):
     identifier = base64.b64decode('AK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc==')
     m = Macaroon(
         location='http://mybank/',
         identifier=identifier,
         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)
Пример #23
0
 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"
     )
Пример #24
0
 def test_serializing_with_binary_v2(self):
     identifier = base64.b64decode('AK2o+q0Aq9+bONkXw7ky7HAuhCLO9hhaMMc==')
     m = Macaroon(
         location='http://mybank/',
         identifier=identifier,
         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)
Пример #25
0
 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)
Пример #26
0
 def test_serializing_strips_padding(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 = acaveat')
     assert_equal(
         m.serialize(),
         # In padded base64, this would end with '=='
         ('MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVz'
          'ZWQgb3VyIHNlY3JldCBrZXkKMDAxN2NpZCB0ZXN0ID0gYWNhdmVhdAowMDJmc2ln'
          'bmF0dXJlIJRJ_V3WNJQnqlVq5eez7spnltwU_AXs8NIRY739sHooCg'))
Пример #27
0
def make_token():

    form = forms.MakeToken()

    #Grab image names from uploads/images folder
    names = get_images()

    names.insert(0, "Select Image")

    form.image_name.choices = [(name, name) for name in names]

    form.expiry_time.choices = [(x, '{} minutes'.format(x))
                                for x in range(1, 31)]

    #On valid for submission
    if form.validate_on_submit():
        email = form.user_email.data

        #Create macaroon so entered user email can view image
        m = Macaroon(location='http://localhost:5000/gallery/{}'.format(
            form.image_name.data),
                     identifier='secret-key',
                     key=keys['secret-key'])

        #Get chosen expiry time
        chosen_expiry = int(form.expiry_time.data)

        expiry_time = datetime.now() + timedelta(minutes=chosen_expiry)

        #Add first party caveas to Macaroon
        m.add_first_party_caveat('email = {}'.format(email))
        m.add_first_party_caveat('image_name = {}'.format(
            form.image_name.data))
        m.add_first_party_caveat('time < {}'.format(expiry_time))

        #Determine IP address for creating access link
        try:
            vm_ip = ni.ifaddresses('ens33')[ni.AF_INET][0]['addr']
        except:
            try:
                vm_ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
            except:
                vm_ip = ni.ifaddresses('wlan0')[ni.AF_INET][0]['addr']

        #Create share link to be given to user
        final_link = "http://{}:5000/gallery/{}/{}".format(
            vm_ip, m.serialize(), form.image_name.data)

        flash(
            Markup("Give this user the following link:<br /><a href={}>{}</a>".
                   format(final_link, final_link)))

    return render_template('make_token.html', form=form, images=names)
Пример #28
0
    def test_serializing_macaroon_with_first_and_third_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)

        n = Macaroon.deserialize(m.serialize())

        assert_equal(m.signature, n.signature)
Пример #29
0
    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'
        )
Пример #30
0
 def test_serializing_strips_padding(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 = acaveat')
     assert_equal(
         m.serialize(),
         # In padded base64, this would end with '=='
         ('MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVz'
          'ZWQgb3VyIHNlY3JldCBrZXkKMDAxN2NpZCB0ZXN0ID0gYWNhdmVhdAowMDJmc2ln'
          'bmF0dXJlIJRJ_V3WNJQnqlVq5eez7spnltwU_AXs8NIRY739sHooCg')
     )
def auth_server_login():
    user_name = request.form['username']
    for user in auth_server_users:
        if user["name"] == user_name and user["identifier"] != "":
            dm = Macaroon(location='http://auth-server.example.com/',
                          key=user["caveat_key"],
                          identifier=user["identifier"])
            # dm = dm.add_first_party_caveat('time < 2020-01-01T00:00')
            serialized = dm.serialize()
            resp = make_response(
                render_template("auth_demo.html",
                                discharge_macaroon=dm.inspect().replace(
                                    "\n", "<br/>")))
            resp.set_cookie('macaroonDischargeCookie', serialized)
            return resp
    resp = make_response(
        render_template("auth_demo.html", result="Auth failed"))
    return resp
Пример #32
0
    def test_serializing_macaroon_with_first_and_third_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)

        n = Macaroon.deserialize(m.serialize())

        assert_equal(
            m.signature,
            n.signature
        )
Пример #33
0
    def test_login_handler_redirect(self):
        m = Macaroon()
        m.add_third_party_caveat("login.ubuntu.com", "key", "id")

        serialized_macaroon = m.serialize()

        responses.add(
            responses.Response(
                method="POST",
                url=self.api_url,
                json={"macaroon": serialized_macaroon},
                status=200,
            ))

        response = self.client.get(self.endpoint_url)

        assert len(responses.calls) == 1
        assert response.status_code == 302
Пример #34
0
def generate_macaroons(device, manufacturer, channels):
    # Check if we have key for the existing device
    # If so, revoke it and add the new one
    if check_macaroon(device):
        revoke_macaroon_by_device(device)

    m = Macaroon(location='localhost',
                 identifier=manufacturer,
                 key=keys[manufacturer])
    m.add_first_party_caveat(device)
    validity = "Valid till: " + str(time.time() + 31556952)
    m.add_first_party_caveat(validity)
    for channel in channels:
        m.add_first_party_caveat(channel)
    # Add to a file for blacklisting and whitelisting
    with open('whitelist.csv', 'a') as csvfile:
        writer = csv.writer(csvfile, delimiter=',')
        writer.writerow([device, validity, m.serialize()] + channels)
    return m
Пример #35
0
    def test_login_handler_redirect(self):
        m = Macaroon()
        m.add_third_party_caveat("login.ubuntu.com", "key", "id")

        serialized_macaroon = m.serialize()

        responses.add(
            responses.Response(
                method="POST",
                url=self.api_url,
                json={"macaroon": serialized_macaroon},
                status=200,
            )
        )

        response = self.client.get(self.endpoint_url)

        assert len(responses.calls) == 1
        assert response.status_code == 302
Пример #36
0
async def r_prepay_wallet(*_, amount: int, memo: str):
    if amount < 10000:
        return Error(
            "InsufficientFunds",
            "Prepaid Wallets must have a value of at least 10000 sats",
        )

    expiry_time = 3600 * 24
    preimage = token_bytes(32)
    macaroon_key = token_bytes(32)

    request = ln.Invoice(
        value=amount, memo=memo, expiry=expiry_time, r_preimage=preimage
    )

    inv = await LND.stub.AddInvoice(request)

    macaroon = Macaroon(
        location=os.environ.get("ENDPOINT"),
        identifier=b64encode(inv.r_hash).decode(),
        key=macaroon_key,
    )

    # add macaroon caveats

    macaroon.add_first_party_caveat("uses = 1")
    macaroon.add_first_party_caveat("action = REDEEM_WALLET")
    # caveat is unecessary for validation and is only included so the token holder may know the amount being redeemed
    macaroon.add_first_party_caveat(f"amount = {amount}")

    lsat = await LSAT.create(
        key=macaroon_key,
        payment_hash=b64encode(inv.r_hash).decode(),
        preimage=b64encode(preimage).decode(),
        used=0,
        uses=1,
    )

    lsat.macaroon = macaroon.serialize()
    lsat.payment_request = inv.payment_request
    lsat.amount = amount

    return lsat
def photo_album_login():
    login_successful = True
    if login_successful:
        m = Macaroon(location='cool-picture-service.example.com',
                     identifier='key-for-bob',
                     key=keys['key-for-bob'])
        # Add a caveat for the target service
        m.add_first_party_caveat('view_pictures = True')
        serialized = m.serialize()
        images = [True, True, True]
        resp = make_response(
            render_template("home.html",
                            showimages=True,
                            images=images,
                            macaroon=m.inspect().replace("\n", "<br/>")))
        resp.set_cookie('macaroonCookie', serialized)
        return resp
    else:
        return redirect(url_for("login"))
def alice_server_get_macaroon():
    m = Macaroon(location='alices-server.example.com',
                 identifier='key-for-bob',
                 key=alice_server_keys['key-for-bob'])
    # should be random
    caveat_key = 'randomKey'
    predicate = 'Bob'
    identifier = auth_server_get_identifier(caveat_key, predicate)
    # location is unused
    m.add_third_party_caveat('http://auth-server.example.com/', caveat_key,
                             identifier)
    serialized = m.serialize()
    resp = make_response(
        render_template("auth_demo.html",
                        macaroon=m.inspect().replace("\n", "<br/>"),
                        caveat_key=caveat_key,
                        identifier=identifier))
    resp.set_cookie('macaroonCookie', serialized)
    return resp
Пример #39
0
 def test_release_no_discharge(self):
     root_key = hashlib.sha256(self.factory.getUniqueString()).hexdigest()
     root_macaroon = Macaroon(key=root_key)
     snap = self.factory.makeSnap(
         store_upload=True,
         store_series=self.factory.makeSnappySeries(name="rolling"),
         store_name="test-snap",
         store_secrets={"root": root_macaroon.serialize()},
         store_channels=["stable", "edge"])
     snapbuild = self.factory.makeSnapBuild(snap=snap)
     self._addSnapReleaseResponse()
     self.client.release(snapbuild, 1)
     self.assertThat(responses.calls[-1].request, RequestMatches(
         url=Equals("http://sca.example/dev/api/snap-release/"),
         method=Equals("POST"),
         headers=ContainsDict({"Content-Type": Equals("application/json")}),
         auth=("Macaroon", MacaroonsVerify(root_key)),
         json_data={
             "name": "test-snap", "revision": 1,
             "channels": ["stable", "edge"], "series": "rolling",
             }))