def get_message(request, global_id):
    """ Respond to an HTTP GET "<global_id>/message" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get the messages to return.  At the same time, remember the messages to
    # delete.

    message_ids = []
    messages    = []

    query = Message.objects.filter(recipient__global_id=global_id)

    for message in query.order_by("timestamp"):
        message_ids.append(message.id)
        messages.append({'sender'  : message.sender.global_id,
                         'message' : json.loads(message.message)})

    # Delete the messages we retrieved.

    for message_id in message_ids:
        Message.objects.filter(id=message_id).delete()

    # Finally, return the messages back to the caller.

    print("Returning messages: {}".format(json.dumps(messages)))

    return JsonResponse(messages, safe=False)
def post_location_session(request, global_id):
    """ Respond to an HTTP POST "<global_id>/location_session" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Create a new location session for this user, re-using the old one if it
    # exists.

    try:
        session = LocationSession.objects.get(global_id__global_id=global_id)
    except LocationSession.DoesNotExist:
        session = None

    if session == None:
        session = LocationSession()
        session.global_id  = access_id.global_id
        session.session_id = uuid.uuid4().hex
        session.save()

    # Finally, return the session ID back to the caller.

    return JsonResponse({'session_id' : session.session_id}, status=201)
Beispiel #3
0
def post_location_session(request, global_id):
    """ Respond to an HTTP POST "<global_id>/location_session" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Create a new location session for this user, re-using the old one if it
    # exists.

    try:
        session = LocationSession.objects.get(global_id__global_id=global_id)
    except LocationSession.DoesNotExist:
        session = None

    if session == None:
        session = LocationSession()
        session.global_id = access_id.global_id
        session.session_id = uuid.uuid4().hex
        session.save()

    # Finally, return the session ID back to the caller.

    return JsonResponse({'session_id': session.session_id}, status=201)
Beispiel #4
0
def post_message(request, global_id):
    """ Respond to an HTTP POST "<global_id>/message" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our parameters from the body of the request.

    if request.META['CONTENT_TYPE'] != "application/json":
        return HttpResponse(status=415)  # Unsupported media type.

    try:
        request_data = json.loads(request.body.decode("utf-8"))
    except ValueError:
        return HttpResponseBadRequest("Invalid JSON request")

    if "recipient" not in request_data:
        return HttpResponseBadRequest("Missing recipient")
    else:
        recipient = request_data['recipient']

    if "message" not in request_data:
        return HttpResponseBadRequest("Missing message")
    else:
        message = request_data['message']

    # Create the message.

    recipient_rec, created = GlobalID.objects.get_or_create(
        global_id=recipient)

    message_rec = Message()
    message_rec.timestamp = utils.current_utc_timestamp()
    message_rec.sender = access_id.global_id
    message_rec.recipient = recipient_rec
    message_rec.message = json.dumps(message)
    message_rec.save()

    # Finally, tell the caller that we created the new message

    print("Created message {} from {} to {}".format(json.dumps(message),
                                                    access_id.global_id,
                                                    recipient_rec.global_id))

    return HttpResponse(status=201)
def post_message(request, global_id):
    """ Respond to an HTTP POST "<global_id>/message" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our parameters from the body of the request.

    if request.META['CONTENT_TYPE'] != "application/json":
        return HttpResponse(status=415) # Unsupported media type.

    try:
        request_data = json.loads(request.body.decode("utf-8"))
    except ValueError:
        return HttpResponseBadRequest("Invalid JSON request")

    if "recipient" not in request_data:
        return HttpResponseBadRequest("Missing recipient")
    else:
        recipient = request_data['recipient']

    if "message" not in request_data:
        return HttpResponseBadRequest("Missing message")
    else:
        message = request_data['message']

    # Create the message.

    recipient_rec,created = GlobalID.objects.get_or_create(global_id=recipient)

    message_rec = Message()
    message_rec.timestamp = utils.current_utc_timestamp()
    message_rec.sender    = access_id.global_id
    message_rec.recipient = recipient_rec
    message_rec.message   = json.dumps(message)
    message_rec.save()

    # Finally, tell the caller that we created the new message

    print("Created message {} from {} to {}".format(json.dumps(message),
                                                    access_id.global_id,
                                                    recipient_rec.global_id))

    return HttpResponse(status=201)
def get_permission(request, global_id):
    """ Respond to the HTTP GET "<global_id>/permission" endpoint.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our query-string parameters.

    if "global_id" in request.GET:
        global_id_param = request.GET['global_id']
    else:
        global_id_param = None

    if "type" in request.GET:
        type_param = request.GET['type']
    else:
        type_param = None

    # Build a database query to retrieve the desired set of permissions.

    query = Permission.objects.filter(issuing_global_id=access_id.global_id)

    if global_id_param != None:
        query = query.filter(recipient_global_id__global_id=global_id_param)

    # Build a list of the matching permissions.

    permissions = []
    for permission in query:
        if type_param != None:
            # Only include the permission if it covers the given type of status
            # update.
            if not permission.matches_status_type(type_param):
                continue

        permissions.append(
            {'access_type' : permission.access_type,
             'global_id'   : permission.recipient_global_id.global_id,
             'status_type' : permission.status_type})

    # Finally, return the results back to the caller.

    return JsonResponse(permissions, safe=False)
def delete_permission(request, global_id):
    """ Respond to the HTTP DELETE "<global_id>/permission" endpoint.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.  Note that, because Django doesn't parse our
    # query-string parameters, we have to do it manually.

    params = urllib.parse.parse_qs(request.META['QUERY_STRING'])

    if "access_type" not in params or len(params['access_type']) != 1:
        return HttpResponseBadRequest("Missing or invalid access_type")
    else:
        access_type = params['access_type'][0]

    if "global_id" not in params or len(params['global_id']) != 1:
        return HttpResponseBadRequest("Missing or invalid global_id")
    else:
        global_id = params['global_id'][0]

    if "status_type" not in params or len(params['status_type']) != 1:
        return HttpResponseBadRequest("Missing or invalid status_type")
    else:
        status_type = params['status_type'][0]

    # Delete the given permission, if it exists.

    Permission.objects.filter(issuing_global_id=access_id.global_id,
                              access_type=access_type,
                              recipient_global_id__global_id=global_id,
                              status_type=status_type).delete()

    return HttpResponse(status=200)
Beispiel #8
0
def get_message(request, global_id):
    """ Respond to an HTTP GET "<global_id>/message" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get the messages to return.  At the same time, remember the messages to
    # delete.

    message_ids = []
    messages = []

    query = Message.objects.filter(recipient__global_id=global_id)

    for message in query.order_by("timestamp"):
        message_ids.append(message.id)
        messages.append({
            'sender': message.sender.global_id,
            'message': json.loads(message.message)
        })

    # Delete the messages we retrieved.

    for message_id in message_ids:
        Message.objects.filter(id=message_id).delete()

    # Finally, return the messages back to the caller.

    print("Returning messages: {}".format(json.dumps(messages)))

    return JsonResponse(messages, safe=False)
def delete_location_session(request, global_id):
    """ Respond to an HTTP DELETE "<global_id>/location_session" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Delete the location session for this user.

    try:
        session = LocationSession.objects.get(global_id__global_id=global_id)
    except LocationSession.DoesNotExist:
        return HttpResponseNotFound()

    session.delete()

    # Tell the user we succeeded.

    return HttpResponse(status=200)
Beispiel #10
0
def delete_location_session(request, global_id):
    """ Respond to an HTTP DELETE "<global_id>/location_session" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Delete the location session for this user.

    try:
        session = LocationSession.objects.get(global_id__global_id=global_id)
    except LocationSession.DoesNotExist:
        return HttpResponseNotFound()

    session.delete()

    # Tell the user we succeeded.

    return HttpResponse(status=200)
def post_status(request, global_id):
    """ Respond to an HTTP POST "<global_id>/status" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our parameters from the body of the request.

    if request.META['CONTENT_TYPE'] != "application/json":
        return HttpResponse(status=415) # Unsupported media type.

    try:
        request_data = json.loads(request.body.decode("utf-8"))
    except ValueError:
        return HttpResponseBadRequest("Invalid JSON request")

    if "type" in request_data:
        status_type = request_data['type']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "timestamp" in request_data:
        status_timestamp = request_data['timestamp']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "contents" in request_data:
        status_contents = request_data['contents']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    # Check that the parameters are valid.

    try:
        status_update_type = StatusUpdateType.objects.get(type=status_type)
    except StatusUpdateType.DoesNotExist:
        return HttpResponseBadRequest("Invalid type")

    try:
        timestamp = utils.timestamp_to_datetime(status_timestamp)
    except:
        return HttpResponseBadRequest("Invalid timestamp")

    # Based on the type of status update, validate the contents.

    if status_type == "location/latlong":
        try:
            contents = json.loads(status_contents)
        except ValueError:
            return HttpResponseBadRequest("Invalid lat/long contents")

        if not isinstance(contents, dict):
            return HttpResponseBadRequest("Invalid lat/long contents")

        if sorted(contents.keys()) != ["latitude", "longitude", "type"]:
            return HttpResponseBadRequest("Invalid lat/long contents")

        if not isinstance(contents['latitude'], (int, float)):
            return HttpResponseBadRequest("Invalid latitude value")

        if contents['latitude'] < -90 or contents['latitude'] > 90:
            return HttpResponseBadRequest("Invalid latitude value")

        if not isinstance(contents['longitude'], (int, float)):
            return HttpResponseBadRequest("Invalid longitude value")

        if contents['longitude'] < -180 or contents['longitude'] > 180:
            return HttpResponseBadRequest("Invalid longitude value")

    # Create the status update itself.

    utc_datetime,tz_offset = utils.datetime_to_utc_and_timezone(timestamp)

    update = StatusUpdate()
    update.global_id = access_id.global_id
    update.type      = status_update_type
    update.timestamp = utc_datetime
    update.tz_offset = tz_offset
    update.contents  = status_contents
    update.save()

    # Now, based upon the permissions, create a CurrentStatusUpdateView record
    # for each global ID able to view this status update.

    for permission in Permission.objects.filter(
                                    issuing_global_id=access_id.global_id,
                                    access_type=Permission.ACCESS_TYPE_CURRENT):
        if permission.matches_status_type(status_type):
            # Create a CurrentStatusUpdateView record for this recipient,
            # replacing the existing one if it exists.
            try:
                view = CurrentStatusUpdateView.objects.get(
                            issuing_global_id=access_id.global_id,
                            recipient_global_id=permission.recipient_global_id,
                            type=status_update_type)
            except CurrentStatusUpdateView.DoesNotExist:
                view = None

            if view != None:
                view.delete()

            view = CurrentStatusUpdateView()
            view.issuing_global_id   = access_id.global_id
            view.recipient_global_id = permission.recipient_global_id
            view.status_update       = update
            view.type                = status_update_type
            view.timestamp           = utc_datetime
            view.tz_offset           = tz_offset
            view.contents            = status_contents
            view.save()

    # Finally, tell the caller that we created the new status update.

    return HttpResponse(status=201)
Beispiel #12
0
def history(request, global_id):
    """ Respond to the /history endpoint.
    """
    if request.method != "GET":
        return HttpResponseNotAllowed(["GET"])

    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.

    if "global_id" in request.GET:
        global_id_param = request.GET['global_id']
    else:
        return HttpResponseBadRequest("Missing request params")

    if "type" in request.GET:
        type_param = request.GET['type']
    else:
        return HttpResponseBadRequest("Missing request params")

    if "more" in request.GET:
        more_param = request.GET['more']
    else:
        more_param = None

    # Check that the request is valid.

    try:
        status_type = StatusUpdateType.objects.get(type=type_param)
    except StatusUpdateType.DoesNotExist:
        return HttpResponseBadRequest("Invalid type")

    if access_id.global_id.global_id != global_id_param:
        # The user is attempting to access someone else's history.  Make sure
        # the other user has created a Permission record to allow this.
        has_permission = False  # initially.
        for permission in Permission.objects.filter(
                issuing_global_id__global_id=global_id_param,
                access_type=Permission.ACCESS_TYPE_HISTORY,
                recipient_global_id=access_id.global_id):
            if permission.matches_status_type(type_param):
                has_permission = True
                break

        if not has_permission:
            return HttpResponseForbidden()

    if more_param != None:
        try:
            more_param = int(more_param)
        except ValueError:
            return HttpResponseBadRequest("Invalid more parameter")

    # Build the database query to retrieve the desired set of status updates.
    # Note that we use a Paginator to paginate the results.

    query = StatusUpdate.objects.filter(global_id__global_id=global_id_param,
                                        type=status_type)
    query = query.order_by("-timestamp")
    paginator = Paginator(query, MAX_PAGE_SIZE)

    if more_param != None:
        page_num = more_param
    else:
        page_num = 1

    if page_num > paginator.num_pages:
        updates = []
        more = None
    else:
        updates = []
        for update in paginator.page(page_num):
            date_time = utils.utc_and_timezone_to_datetime(
                update.timestamp, update.tz_offset)
            updates.append({
                'global_id': update.global_id.global_id,
                'type': update.type.type,
                'timestamp': utils.datetime_to_timestamp(date_time),
                'contents': update.contents
            })

        if page_num < paginator.num_pages:
            more = str(page_num + 1)
        else:
            more = None

    # Finally, return the results back to the caller.

    return JsonResponse({'updates': updates, 'more': more})
Beispiel #13
0
def get_status(request, global_id):
    """ Respond to an HTTP GET "<global_id>/status" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.

    if "own" in request.GET and request.GET['own'] == "1":
        param_own = True
    else:
        param_own = False

    if "global_id" in request.GET:
        param_global_id = request.GET['global_id']
    else:
        param_global_id = None

    if "type" in request.GET:
        param_type = request.GET['type']
    else:
        param_type = None

    if "since" in request.GET and request.GET['since'] != "ALL":
        try:
            param_since = utils.timestamp_to_datetime(request.GET['since'])
        except ValueError:
            return HttpResponseBadRequest("Invalid 'since' value")
    else:
        param_since = None

    # Build the database query to retrieve the desired set of
    # CurrentStatusUpdateView records.

    query = CurrentStatusUpdateView.objects.all()

    if param_own:
        query = query.filter(issuing_global_id=access_id.global_id)
    else:
        query = query.filter(recipient_global_id=access_id.global_id)

    if param_global_id != None:
        query = query.filter(issuing_global_id__global_id=param_global_id)

    if param_type != None:
        query = query.filter(type__type=param_type)

    if param_since != None:
        query = query.filter(timestamp__gt=param_since)

    # Assemble the list of status updates which match our database query.

    updates = []
    latest = None
    for view in query:
        date_time = utils.utc_and_timezone_to_datetime(view.timestamp,
                                                       view.tz_offset)
        updates.append({
            'global_id': view.issuing_global_id.global_id,
            'type': view.type.type,
            'timestamp': utils.datetime_to_timestamp(date_time),
            'contents': view.contents
        })
        if latest == None or view.timestamp > latest:
            latest = view.timestamp

    # Calculate the 'since' value to use for retrieving only the new updates.

    if latest == None:
        if param_since == None:
            since = "ALL"
        else:
            since = utils.datetime_to_timestamp(param_since)
    else:
        since = utils.datetime_to_timestamp(latest)

    # Finally, return the results back to the caller.

    return JsonResponse({'updates': updates, 'since': since})
Beispiel #14
0
def post_status(request, global_id):
    """ Respond to an HTTP POST "<global_id>/status" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our parameters from the body of the request.

    if request.META['CONTENT_TYPE'] != "application/json":
        return HttpResponse(status=415)  # Unsupported media type.

    try:
        request_data = json.loads(request.body.decode("utf-8"))
    except ValueError:
        return HttpResponseBadRequest("Invalid JSON request")

    if "type" in request_data:
        status_type = request_data['type']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "timestamp" in request_data:
        status_timestamp = request_data['timestamp']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "contents" in request_data:
        status_contents = request_data['contents']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    # Check that the parameters are valid.

    try:
        status_update_type = StatusUpdateType.objects.get(type=status_type)
    except StatusUpdateType.DoesNotExist:
        return HttpResponseBadRequest("Invalid type")

    try:
        timestamp = utils.timestamp_to_datetime(status_timestamp)
    except:
        return HttpResponseBadRequest("Invalid timestamp")

    # Based on the type of status update, validate the contents.

    if status_type == "location/latlong":
        try:
            contents = json.loads(status_contents)
        except ValueError:
            return HttpResponseBadRequest("Invalid lat/long contents")

        if not isinstance(contents, dict):
            return HttpResponseBadRequest("Invalid lat/long contents")

        if sorted(contents.keys()) != ["latitude", "longitude", "type"]:
            return HttpResponseBadRequest("Invalid lat/long contents")

        if not isinstance(contents['latitude'], (int, float)):
            return HttpResponseBadRequest("Invalid latitude value")

        if contents['latitude'] < -90 or contents['latitude'] > 90:
            return HttpResponseBadRequest("Invalid latitude value")

        if not isinstance(contents['longitude'], (int, float)):
            return HttpResponseBadRequest("Invalid longitude value")

        if contents['longitude'] < -180 or contents['longitude'] > 180:
            return HttpResponseBadRequest("Invalid longitude value")

    # Create the status update itself.

    utc_datetime, tz_offset = utils.datetime_to_utc_and_timezone(timestamp)

    update = StatusUpdate()
    update.global_id = access_id.global_id
    update.type = status_update_type
    update.timestamp = utc_datetime
    update.tz_offset = tz_offset
    update.contents = status_contents
    update.save()

    # Now, based upon the permissions, create a CurrentStatusUpdateView record
    # for each global ID able to view this status update.

    for permission in Permission.objects.filter(
            issuing_global_id=access_id.global_id,
            access_type=Permission.ACCESS_TYPE_CURRENT):
        if permission.matches_status_type(status_type):
            # Create a CurrentStatusUpdateView record for this recipient,
            # replacing the existing one if it exists.
            try:
                view = CurrentStatusUpdateView.objects.get(
                    issuing_global_id=access_id.global_id,
                    recipient_global_id=permission.recipient_global_id,
                    type=status_update_type)
            except CurrentStatusUpdateView.DoesNotExist:
                view = None

            if view != None:
                view.delete()

            view = CurrentStatusUpdateView()
            view.issuing_global_id = access_id.global_id
            view.recipient_global_id = permission.recipient_global_id
            view.status_update = update
            view.type = status_update_type
            view.timestamp = utc_datetime
            view.tz_offset = tz_offset
            view.contents = status_contents
            view.save()

    # Finally, tell the caller that we created the new status update.

    return HttpResponse(status=201)
def history(request, global_id):
    """ Respond to the /history endpoint.
    """
    if request.method != "GET":
        return HttpResponseNotAllowed(["GET"])

    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.

    if "global_id" in request.GET:
        global_id_param = request.GET['global_id']
    else:
        return HttpResponseBadRequest("Missing request params")

    if "type" in request.GET:
        type_param = request.GET['type']
    else:
        return HttpResponseBadRequest("Missing request params")

    if "more" in request.GET:
        more_param = request.GET['more']
    else:
        more_param = None

    # Check that the request is valid.

    try:
        status_type = StatusUpdateType.objects.get(type=type_param)
    except StatusUpdateType.DoesNotExist:
        return HttpResponseBadRequest("Invalid type")

    if access_id.global_id.global_id != global_id_param:
        # The user is attempting to access someone else's history.  Make sure
        # the other user has created a Permission record to allow this.
        has_permission = False # initially.
        for permission in Permission.objects.filter(
                                issuing_global_id__global_id=global_id_param,
                                access_type=Permission.ACCESS_TYPE_HISTORY,
                                recipient_global_id=access_id.global_id):
            if permission.matches_status_type(type_param):
                has_permission = True
                break

        if not has_permission:
            return HttpResponseForbidden()

    if more_param != None:
        try:
            more_param = int(more_param)
        except ValueError:
            return HttpResponseBadRequest("Invalid more parameter")

    # Build the database query to retrieve the desired set of status updates.
    # Note that we use a Paginator to paginate the results.

    query = StatusUpdate.objects.filter(global_id__global_id=global_id_param,
                                        type=status_type)
    query = query.order_by("-timestamp")
    paginator = Paginator(query, MAX_PAGE_SIZE)

    if more_param != None:
        page_num = more_param
    else:
        page_num = 1

    if page_num > paginator.num_pages:
        updates = []
        more    = None
    else:
        updates = []
        for update in paginator.page(page_num):
            date_time = utils.utc_and_timezone_to_datetime(update.timestamp,
                                                           update.tz_offset)
            updates.append(
                {'global_id' : update.global_id.global_id,
                 'type'      : update.type.type,
                 'timestamp' : utils.datetime_to_timestamp(date_time),
                 'contents'  : update.contents})

        if page_num < paginator.num_pages:
            more = str(page_num + 1)
        else:
            more = None

    # Finally, return the results back to the caller.

    return JsonResponse({'updates' : updates,
                         'more'    : more})
def get_status(request, global_id):
    """ Respond to an HTTP GET "<global_id>/status" API call.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.

    if "own" in request.GET and request.GET['own'] == "1":
        param_own = True
    else:
        param_own = False

    if "global_id" in request.GET:
        param_global_id = request.GET['global_id']
    else:
        param_global_id = None

    if "type" in request.GET:
        param_type = request.GET['type']
    else:
        param_type = None

    if "since" in request.GET and request.GET['since'] != "ALL":
        try:
            param_since = utils.timestamp_to_datetime(request.GET['since'])
        except ValueError:
            return HttpResponseBadRequest("Invalid 'since' value")
    else:
        param_since = None

    # Build the database query to retrieve the desired set of
    # CurrentStatusUpdateView records.

    query = CurrentStatusUpdateView.objects.all()

    if param_own:
        query = query.filter(issuing_global_id=access_id.global_id)
    else:
        query = query.filter(recipient_global_id=access_id.global_id)

    if param_global_id != None:
        query = query.filter(issuing_global_id__global_id=param_global_id)

    if param_type != None:
        query = query.filter(type__type=param_type)

    if param_since != None:
        query = query.filter(timestamp__gt=param_since)

    # Assemble the list of status updates which match our database query.

    updates = []
    latest  = None
    for view in query:
        date_time = utils.utc_and_timezone_to_datetime(view.timestamp,
                                                       view.tz_offset)
        updates.append(
                {'global_id' : view.issuing_global_id.global_id,
                 'type'      : view.type.type,
                 'timestamp' : utils.datetime_to_timestamp(date_time),
                 'contents'  : view.contents})
        if latest == None or view.timestamp > latest:
            latest = view.timestamp

    # Calculate the 'since' value to use for retrieving only the new updates.

    if latest == None:
        if param_since == None:
            since = "ALL"
        else:
            since = utils.datetime_to_timestamp(param_since)
    else:
        since = utils.datetime_to_timestamp(latest)

    # Finally, return the results back to the caller.

    return JsonResponse({'updates' : updates,
                         'since'   : since})
def post_permission(request, global_id):
    """ Respond to the HTTP POST "<global_id>/permission" endpoint.
    """
    # Check the HMAC authentication.

    access_id = utils.get_access_id(global_id)
    if access_id == None:
        return HttpResponseForbidden()

    if not hmac.check_hmac_authentication(request, access_id.access_secret):
        return HttpResponseForbidden()

    # Get our request parameters.

    if request.META['CONTENT_TYPE'] != "application/json":
        return HttpResponse(status=415) # Unsupported media type.

    try:
        request_data = json.loads(request.body.decode("utf-8"))
    except ValueError:
        return HttpResponseBadRequest("Invalid JSON request")

    if "access_type" in request_data:
        access_type = request_data['access_type']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "global_id" in request_data:
        global_id = request_data['global_id']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    if "status_type" in request_data:
        status_type = request_data['status_type']
    else:
        return HttpResponseBadRequest("Invalid JSON request")

    # Check that the parameters are valid.

    if access_type not in ["CURRENT", "HISTORY"]:
        return HttpResponseBadRequest("Invalid access_type")

    if "*" in status_type and not status_type.endswith("*"):
        return HttpResponseBadRequest("Invalid status_type")

    # If necessary, create a GlobalID record for the recipient.

    global_id_rec,created = GlobalID.objects.get_or_create(global_id=global_id)

    # Create the new permission.

    permission = Permission()
    permission.issuing_global_id   = access_id.global_id
    permission.access_type         = access_type
    permission.recipient_global_id = global_id_rec
    permission.status_type         = status_type
    permission.save()

    # Finally, tell the caller the good news.

    return HttpResponse(status=201)