Ejemplo n.º 1
0
def _handle_old_since_redirect(request):
    """
    In order to limit the number of possible combinations
    of `_since` and `_expected` querystring parameters,
    and thus maximize the effect of caching, we redirect the clients
    that arrive here with a very old `_since` value.

    This simply means that these clients will have to iterate
    and compare the local timestamps of the whole list of changes
    instead of a filtered subset.

    https://searchfox.org/mozilla-central/rev/b58ca450/services/settings/remote-settings.js#299

    See https://bugzilla.mozilla.org/show_bug.cgi?id=1529685
    and https://bugzilla.mozilla.org/show_bug.cgi?id=1665319#c2
    """
    try:
        # request.validated is not populated yet (resource was not instantiated yet,
        # we want to bypass storage).
        qs_since_str = request.GET.get("_since", "")
        qs_since = int(qs_since_str.strip('"'))
    except ValueError:
        # Will fail later during resource querystring validation.
        return

    settings = request.registry.settings
    max_age_since = int(settings.get("changes.since_max_age_days", 21))
    if max_age_since < 0:
        # Redirect is disabled.
        return

    min_since_dt = datetime.now() - timedelta(days=max_age_since)
    min_since = min_since_dt.timestamp() * 1000

    if qs_since >= min_since:
        # Since value is recent. No redirect.
        return

    http_scheme = settings.get("http_scheme") or "https"
    http_host = settings.get("changes.http_host",
                             request.registry.settings.get("http_host"))
    host_uri = f"{http_scheme}://{http_host}"
    redirect = host_uri + request.matched_route.generate(request.matchdict)

    queryparams = request.GET.copy()
    del queryparams["_since"]
    if queryparams:
        redirect += "?" + urlencode(queryparams)

    # Serve a redirection, with optional cache control headers.
    response = httpexceptions.HTTPTemporaryRedirect(redirect)
    cache_seconds = int(
        settings.get("changes.since_max_age_redirect_ttl_seconds", 86400))
    if cache_seconds >= 0:
        response.cache_expires(cache_seconds)
    raise response
Ejemplo n.º 2
0
def get_login(request):
    """Initiates to login dance for the specified scopes and callback URI
    using appropriate redirections."""

    # Settings.
    provider = request.matchdict["provider"]
    settings_prefix = "multiauth.policy.%s." % provider
    issuer = request.registry.settings[settings_prefix + "issuer"]
    client_id = request.registry.settings[settings_prefix + "client_id"]
    userid_field = request.registry.settings.get(settings_prefix +
                                                 "userid_field")
    state_ttl = int(
        request.registry.settings.get(settings_prefix + "state_ttl_seconds",
                                      DEFAULT_STATE_TTL_SECONDS))
    state_length = int(
        request.registry.settings.get(settings_prefix + "state_length",
                                      DEFAULT_STATE_LENGTH))

    # Read OpenID configuration (cached by issuer)
    oid_config = fetch_openid_config(issuer)
    auth_endpoint = oid_config["authorization_endpoint"]

    scope = request.GET["scope"]
    callback = request.GET["callback"]
    prompt = request.GET.get("prompt")

    # Check that email scope is requested if userid field is configured as email.
    if userid_field == "email" and "email" not in scope:
        error_details = {
            "name": "scope",
            "description": "Provider %s requires 'email' scope" % provider,
        }
        raise_invalid(request, **error_details)

    # Generate a random string as state.
    # And save it until code is traded.
    state = random_bytes_hex(state_length)
    request.registry.cache.set("openid:state:" + state,
                               callback,
                               ttl=state_ttl)

    # Redirect the client to the Identity Provider that will eventually redirect
    # to the OpenID token endpoint.
    token_uri = request.route_url("openid_token", provider=provider) + "?"
    params = dict(client_id=client_id,
                  response_type="code",
                  scope=scope,
                  redirect_uri=token_uri,
                  state=state)
    if prompt:
        # The 'prompt' parameter is optional.
        params["prompt"] = prompt
    redirect = "{}?{}".format(auth_endpoint, urllib.parse.urlencode(params))
    raise httpexceptions.HTTPTemporaryRedirect(redirect)
Ejemplo n.º 3
0
def handle_redir(request):
    rlogger = request.registry.get('logger')
    storage = request.registry.get('storage')
    counter = request.registry.get('counter')
    data = storage.resolve(request.matchdict.get('token'))
    if data is None:
        raise http.HTTPNotFound
    counter.redir(data)
    rlogger.log(type='redirect',
                severity=LOG.INFORMATIONAL,
                msg='redirect',
                fields=data)
    raise http.HTTPTemporaryRedirect(location=data['dest_url'])
Ejemplo n.º 4
0
Archivo: views.py Proyecto: xcffl/kinto
def get_login(request):
    """Initiates to login dance for the specified scopes and callback URI
    using appropriate redirections."""

    # Settings.
    provider = request.matchdict['provider']
    settings_prefix = 'multiauth.policy.%s.' % provider
    issuer = request.registry.settings[settings_prefix + 'issuer']
    client_id = request.registry.settings[settings_prefix + 'client_id']
    userid_field = request.registry.settings.get(settings_prefix +
                                                 'userid_field')
    state_ttl = int(
        request.registry.settings.get(settings_prefix + 'state_ttl_seconds',
                                      DEFAULT_STATE_TTL_SECONDS))
    state_length = int(
        request.registry.settings.get(settings_prefix + 'state_length',
                                      DEFAULT_STATE_LENGTH))

    # Read OpenID configuration (cached by issuer)
    oid_config = fetch_openid_config(issuer)
    auth_endpoint = oid_config['authorization_endpoint']

    scope = request.GET['scope']
    callback = request.GET['callback']

    # Check that email scope is requested if userid field is configured as email.
    if userid_field == 'email' and 'email' not in scope:
        error_details = {
            'name': 'scope',
            'description': "Provider %s requires 'email' scope" % provider,
        }
        raise_invalid(request, **error_details)

    # Generate a random string as state.
    # And save it until code is traded.
    state = random_bytes_hex(state_length)
    request.registry.cache.set('openid:state:' + state,
                               callback,
                               ttl=state_ttl)

    # Redirect the client to the Identity Provider that will eventually redirect
    # to the OpenID token endpoint.
    token_uri = request.route_url('openid_token', provider=provider) + '?'
    params = dict(client_id=client_id,
                  response_type='code',
                  scope=scope,
                  redirect_uri=token_uri,
                  state=state)
    redirect = '{}?{}'.format(auth_endpoint, urllib.parse.urlencode(params))
    raise httpexceptions.HTTPTemporaryRedirect(redirect)
Ejemplo n.º 5
0
def get_token(request):
    """Trades the specified code and state against access and ID tokens.
    The client is redirected to the original ``callback`` URI with the
    result in querystring."""

    # Settings.
    provider = request.matchdict["provider"]
    settings_prefix = "multiauth.policy.%s." % provider
    issuer = request.registry.settings[settings_prefix + "issuer"]
    client_id = request.registry.settings[settings_prefix + "client_id"]
    client_secret = request.registry.settings[settings_prefix +
                                              "client_secret"]

    # Read OpenID configuration (cached by issuer)
    oid_config = fetch_openid_config(issuer)
    token_endpoint = oid_config["token_endpoint"]

    code = request.GET["code"]
    state = request.GET["state"]

    # State can be used only once.
    callback = request.registry.cache.delete("openid:state:" + state)
    if callback is None:
        error_details = {
            "name": "state",
            "description": "Invalid state",
            "errno": ERRORS.INVALID_AUTH_TOKEN.value,
        }
        raise_invalid(request, **error_details)

    # Trade the code for tokens on the Identity Provider.
    # Google Identity requires to specify again redirect_uri.
    redirect_uri = request.route_url("openid_token", provider=provider)
    data = {
        "code": code,
        "client_id": client_id,
        "client_secret": client_secret,
        "redirect_uri": redirect_uri,
        "grant_type": "authorization_code",
    }
    resp = requests.post(token_endpoint, data=data)

    # The IdP response is forwarded to the client in the querystring/location hash.
    # (eg. callback=`http://localhost:3000/#tokens=`)
    token_info = resp.text.encode("utf-8")
    encoded_token = base64.b64encode(token_info)
    redirect = callback + urllib.parse.quote(encoded_token.decode("utf-8"))
    raise httpexceptions.HTTPTemporaryRedirect(redirect)
Ejemplo n.º 6
0
Archivo: views.py Proyecto: xcffl/kinto
def get_token(request):
    """Trades the specified code and state against access and ID tokens.
    The client is redirected to the original ``callback`` URI with the
    result in querystring."""

    # Settings.
    provider = request.matchdict['provider']
    settings_prefix = 'multiauth.policy.%s.' % provider
    issuer = request.registry.settings[settings_prefix + 'issuer']
    client_id = request.registry.settings[settings_prefix + 'client_id']
    client_secret = request.registry.settings[settings_prefix +
                                              'client_secret']

    # Read OpenID configuration (cached by issuer)
    oid_config = fetch_openid_config(issuer)
    token_endpoint = oid_config['token_endpoint']

    code = request.GET['code']
    state = request.GET['state']

    # State can be used only once.
    callback = request.registry.cache.delete('openid:state:' + state)
    if callback is None:
        error_details = {
            'name': 'state',
            'description': 'Invalid state',
            'errno': ERRORS.INVALID_AUTH_TOKEN.value,
        }
        raise_invalid(request, **error_details)

    # Trade the code for tokens on the Identity Provider.
    # Google Identity requires to specify again redirect_uri.
    redirect_uri = request.route_url('openid_token', provider=provider) + '?'
    data = {
        'code': code,
        'client_id': client_id,
        'client_secret': client_secret,
        'redirect_uri': redirect_uri,
        'grant_type': 'authorization_code',
    }
    resp = requests.post(token_endpoint, data=data)

    # The IdP response is forwarded to the client in the querystring/location hash.
    # (eg. callback=`http://localhost:3000/#tokens=`)
    redirect = callback + urllib.parse.quote(resp.text)
    raise httpexceptions.HTTPTemporaryRedirect(redirect)
Ejemplo n.º 7
0
def redirect_to_version(request):
    """Redirect to the current version of the API."""
    raise httpexceptions.HTTPTemporaryRedirect(
        '/%s/%s' % (API_VERSION, request.matchdict['path']))
Ejemplo n.º 8
0
def boot_to_author(request):
    raise http.HTTPTemporaryRedirect(location='/author/%s/' % api_version)
Ejemplo n.º 9
0
def widget(context, request):
    location = request.resource_url(context, 'viewer', query=request.GET)
    return httpexceptions.HTTPTemporaryRedirect(location=location)