# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
import pyrfc3339
from ._auth_context import ContextKey
from ._caveat import parse_caveat
from ._conditions import COND_TIME_BEFORE, STD_NAMESPACE
from ._utils import condition_with_prefix
TIME_KEY = ContextKey('time-key')
def context_with_clock(ctx, clock):
''' Returns a copy of ctx with a key added that associates it with the
given clock implementation, which will be used by the time-before checker
to determine the current time.
The clock should have a utcnow method that returns the current time
as a datetime value in UTC.
'''
if clock is None:
return ctx
return ctx.with_value(TIME_KEY, clock)
def macaroons_expiry_time(ns, ms):
''' Returns the minimum time of any time-before caveats found in the given
macaroons or None if no such caveats were found.
:param ns: a Namespace, used to resolve caveats.
:param ms: a list of pymacaroons.Macaroon
:return: datetime.DateTime or None.
'''
t = None
for m in ms:
et = expiry_time(ns, m.caveats)
if et is not None and (t is None or et < t):
t = et
return t
def expiry_time(ns, cavs):
''' Returns the minimum time of any time-before caveats found
in the given list or None if no such caveats were found.
The ns parameter is
:param ns: used to determine the standard namespace prefix - if
the standard namespace is not found, the empty prefix is assumed.
:param cavs: a list of pymacaroons.Caveat
:return: datetime.DateTime or None.
'''
prefix = ns.resolve(STD_NAMESPACE)
time_before_cond = condition_with_prefix(
prefix, COND_TIME_BEFORE)
t = None
for cav in cavs:
if not cav.first_party():
continue
cav = cav.caveat_id_bytes.decode('utf-8')
name, rest = parse_caveat(cav)
if name != time_before_cond:
continue
try:
et = pyrfc3339.parse(rest, utc=True).replace(tzinfo=None)
if t is None or et < t:
t = et
except ValueError:
continue
return t