Beispiel #1
0
def resolve_start_time(trip, flights, human_times):
    """
    Resolves the correct start time for a trip based on its flights.

    Note that this assumes that our trips start with flights. That isn't always the case.
    This will need to be refactored once we start taking other trip objects into account.
    """
    if not flights:
        logger.debug("Trip: %s", str(trip))
        if "start_date" not in trip.keys():
            logger.warn("Trip %s doesn't have a start time!", trip["id"])
        return retrieve_trip_time_as_unix(trip.get("start_date", "1970-01-01"))

    first_flight_segment_start_time = flights[0]["depart_time"]
    if human_times:
        first_flight_segment_start_time = convert_human_dt_to_ts(
            first_flight_segment_start_time)

    if os.getenv("TRIPIT_INGRESS_TIME_MINUTES"):
        trip_ingress_seconds = int(
            os.getenv("TRIPIT_INGRESS_TIME_MINUTES")) * 60
    else:
        trip_ingress_seconds = 0

    return first_flight_segment_start_time + trip_ingress_seconds
Beispiel #2
0
def get_authn_url(api_gateway_endpoint, host, access_key, reauthorize=False):
    """
    Generates an authentication URL for an access key if no request or access
    tokens exist for it.
    """
    if access_key_has_access_token(access_key) and not reauthorize:
        logger.debug("Access key already has token: %s", access_key)
        return urllib.parse.urlunparse(
            ("https", host, f"{api_gateway_endpoint}/token", "", "", ""))
    if reauthorize:
        delete_existing_access_tokens(access_key)

    token_data = request_request_token()
    if token_data is None:
        logger.error("Unable to get token; see previous logs for why.")
        return None

    callback_url = urllib.parse.urlunparse(
        ("https", host, f"{api_gateway_endpoint}/callback", "", "", ""))
    auth_url = urllib.parse.urlunparse((
        "https",
        "www.tripit.com",
        "/oauth/authorize",
        "",
        f"oauth_token={token_data['token']}&oauth_callback={callback_url}",
        "",
    ))
    associate_request_token_with_access_key(token_data["token"], access_key,
                                            token_data["token_secret"])
    return auth_url
Beispiel #3
0
def delete_existing_access_tokens(access_key):
    """
    Delete access tokens associated with an access key, if any found.
    """
    logger.debug("Deleting existing access tokens for key %s", access_key)
    try:
        TripitAccessToken.delete_tokens_by_access_key(access_key)
    except (GetError, TripitAccessToken.DoesNotExist):
        logger.warning("No request tokens found for key: %s", access_key)
Beispiel #4
0
def fetch_token(token=None, token_secret=None):
    """
    Get a new request token from the TripIt API.
    """
    env_vars = get_missing_client_id_and_secret_env_vars()
    if env_vars:
        raise RuntimeError(
            f"Please define these environment variables: {env_vars}")

    client_id = os.environ.get("TRIPIT_APP_CLIENT_ID")
    client_secret = os.environ.get("TRIPIT_APP_CLIENT_SECRET")
    timestamp = int(datetime.now().timestamp())
    nonce = secrets.token_hex()
    """ If we are trying to request tokens and already have a token
        secret, then that means we already went through the first step
        of the OAuth process and are now trying to get access tokens. """
    if token_secret is not None:
        request_uri = "https://api.tripit.com/oauth/access_token"
    else:
        request_uri = "https://api.tripit.com/oauth/request_token"
    logger.debug("Token: %s, token_secret: %s, URI: %s", token, token_secret,
                 request_uri)
    common_arguments = {
        "uri": request_uri,
        "consumer_key": client_id,
        "nonce": nonce,
        "timestamp": timestamp,
    }
    access_token_arguments = {}
    if token_secret is not None:
        access_token_arguments["token"] = token
        access_token_arguments["token_secret"] = token_secret

    oauth_sig = generate_signature(method="GET",
                                   consumer_secret=client_secret,
                                   **common_arguments,
                                   **access_token_arguments)
    auth_header = generate_sha1_auth_header(signature=oauth_sig,
                                            **common_arguments,
                                            **access_token_arguments)
    logger.debug("Auth header: %s", auth_header)
    response = requests.get(request_uri,
                            headers={"Authorization": auth_header})
    if response.status_code != 200:
        logging.error("Failed to get token data: %s)", response.text)
        return None
    token_data = {}
    for token_part in response.text.split("&"):
        key, value = token_part.split("=")
        token_data[key.replace("oauth_", "")] = value
    return token_data
Beispiel #5
0
def associate_request_token_with_access_key(token, access_key, token_secret):
    """
    Associates tokens with access keys and request secrets so that
    we can retrieve an access key after manually authorizing this application
    on TripIt.

    (This is only used by the callback function. Since Tripit won't attach our
    AWS access key to the GET request to /callback, we need to be able to
    resolve it from the token that Tripit gives us.)
    """
    logger.debug("Inserting a new token for our access key %s", access_key)
    TripitRequestToken.insert(token,
                              access_key=access_key,
                              token_secret=token_secret)
Beispiel #6
0
def get_from_tripit_v1(endpoint, token, token_secret, params=None):
    """ GET against authenticated TripIt endpoints. """
    params_string = _join_params_by_slash(params)
    endpoint = _strip_leading_slash_from_endpoint(endpoint)
    clean_endpoint = f"{endpoint}/{params_string}format/json"
    uri = f"https://api.tripit.com/v1/{clean_endpoint}"
    headers = generate_authenticated_headers_for_request(
        "GET",
        uri,
        os.getenv("TRIPIT_APP_CLIENT_ID"),
        os.getenv("TRIPIT_APP_CLIENT_SECRET"),
        token,
        token_secret,
    )
    logger.debug("Sending GET to TripIt at: %s", uri)
    return requests.get(uri, headers={"Authorization": headers})
Beispiel #7
0
def generate_signature(method,
                       uri,
                       consumer_key,
                       consumer_secret,
                       nonce,
                       timestamp,
                       token=None,
                       token_secret=None):
    """
    Generates an OAuth v1 signature. These are used to form authentication headers.

    Unfortunately, because we really require these many arguments while working
    with OAuth v1, we need to tell pylint to disable the, usually correct,
    "too-many-arguments" error. Otherwise, we'll need to resort to using
    **kwargs, which is too unsafe for my liking.
    """
    params = {
        "oauth_consumer_key": consumer_key,
        "oauth_nonce": nonce,
        "oauth_signature_method": "HMAC-SHA1",
        "oauth_timestamp": int(timestamp),
        "oauth_version": "1.0",
    }
    if token:
        params["oauth_token"] = token

    encrypt_key = "&".join(
        [consumer_secret, (token_secret if token_secret is not None else "")])
    param_parts = "&".join(
        [f"{key}={params[key]}" for key, value in sort_dict(params).items()])
    base_string_for_signature = "&".join([
        method,
        urllib.parse.quote_plus(uri),
        urllib.parse.quote_plus(param_parts)
    ])
    signature = hmac.new(bytes(encrypt_key, "utf8"),
                         bytes(base_string_for_signature, "utf8"), sha1)

    # Trying to figure out why access tokens have bad sigs
    for param in params.keys():
        logger.debug("%s: %s", param, params[param])
    logger.debug("Encryption key, if any: %s", encrypt_key)
    logger.debug("Signature base: %s", base_string_for_signature)
    logger.debug("Signature: %s", base64.b64encode(signature.digest()))
    logger.debug("Method: %s, URI: %s", method, uri)
    return base64.b64encode(signature.digest())
Beispiel #8
0
def resolve_trip(trip_reference, token, token_secret, human_times):
    """
    Generates a summarized version of a trip with expanded flight
    information.

    This involves a nested API call and might be time-expensive!
    """
    logger.debug("Fetching trip %s", trip_reference["id"])
    trip_info = get_from_tripit_v1(
        endpoint="".join(["/get/trip/id/", trip_reference["id"]]),
        token=token,
        token_secret=token_secret,
    )
    if trip_info.status_code != 200:
        logger.error("Unable to fetch trip %s, error %d", trip_reference["id"],
                     trip_info.status_code)
    trip_object = trip_info.json()["Trip"]

    if trip_is_empty(trip_object):
        logger.warn("Trip %s is empty", trip_object["id"])
        return {}

    flight_objects = trip_info.json().get("AirObject") or []
    note_objects = trip_info.json().get("NoteObject") or []
    flights = resolve_flights(flight_objects, human_times)
    trip_start_time = resolve_start_time(trip_object, flights, human_times)
    trip_end_time = resolve_end_time(trip_object, flights, human_times)
    primary_location = resolve_primary_location(trip_object)

    summarized_trip = {
        "id": int(trip_object["id"]),
        "name": trip_object["display_name"],
        "city": trip_object["primary_location"],
        "ends_on": trip_end_time,
        "ended": determine_if_trip_ended(trip_end_time, note_objects),
        "link": "https://www.tripit.com" + trip_object["relative_url"],
        "starts_on": trip_start_time,
        "flights": flights,
    }
    if human_times:
        for key in ["starts_on", "ends_on"]:
            summarized_trip[key] = convert_to_human_dt(summarized_trip[key])
    return summarized_trip
Beispiel #9
0
def get_all_trips(token, token_secret, human_times=False):
    """
    Retrieves all trips from TripIt and parses it in a way that's friendly to
    this API.

    We only care about flights and notes. Every other TripIt object is stripped out.
    """
    trip_data = get_from_tripit_v1(endpoint="/list/trip",
                                   token=token,
                                   token_secret=token_secret)
    logger.debug("Response: %d, Text: %s", trip_data.status_code,
                 trip_data.text)
    if trip_data.status_code != 200:
        logger.error("Failed to get trips: %s", trip_data.status_code)
        return None
    trips_json = trip_data.json()
    if "Trip" not in trips_json:
        logger.info("No trips found.")
        return []
    return join_trips(normalize_trip_objects(trips_json["Trip"]), token,
                      token_secret, human_times)