Beispiel #1
0
def add_subscription(request):
    if request.method == "POST":
        sub = Subscription(email=request.POST.get("email"))
        sub.ip = get_request_ip(request) or ""
        sub.save()
        messages.success(request, _("A subscription for '%s' was added.") % request.POST.get("email"))
    return HttpResponseRedirect(reverse("homepage"))
Beispiel #2
0
def add_subscription(request):
    if request.method == "POST":
        sub = Subscription(email=request.POST.get("email"))
        sub.ip = get_request_ip(request) or ""
        sub.save()
        messages.success(
            request,
            _("A subscription for '%s' was added.") %
            request.POST.get("email"))
    return HttpResponseRedirect(reverse("homepage"))
Beispiel #3
0
def register_device(request):
    """Receives the client device info from the distributed server.
    Tries to register either because the device has been pre-registered,
    or because it has a valid INSTALL_CERTIFICATE."""
    # attempt to load the client device data from the request data
    data = simplejson.loads(request.body or "{}")
    if "client_device" not in data:
        return JsonResponseMessageError(
            "Serialized client device must be provided.", status=400)
    try:
        # When hand-shaking on the device models, since we don't yet know the version,
        #   we have to just TRY with our own version.
        #
        # This is currently "central server" code, so
        #   this will only fail (currently) if the central server version
        #   is less than the version of a client--something that should never happen
        try:
            local_version = Device.get_own_device().get_version()
            models = deserialize(data["client_device"],
                                 src_version=local_version,
                                 dest_version=local_version)
        except db_models.FieldDoesNotExist as fdne:
            raise Exception(
                "Central server version is lower than client version.  This is ... impossible!"
            )
        client_device = models.next().object
    except Exception as e:
        return JsonResponseMessageError(
            "Could not decode the client device model: %s" % e,
            code=EC.CLIENT_DEVICE_CORRUPTED,
            status=400)

    # Validate the loaded data
    if not isinstance(client_device, Device):
        return JsonResponseMessageError(
            "Client device must be an instance of the 'Device' model.",
            code=EC.CLIENT_DEVICE_NOT_DEVICE)

    try:
        if not client_device.verify():
            # We've been getting this verification error a lot, even when we shouldn't. Send more details to us by email so we can diagnose.
            msg = "\n\n".join([
                request.body,
                client_device._hashable_representation(),
                str(client_device.validate()), client_device.signed_by_id,
                client_device.id,
                str(request)
            ])
            send_mail("Client device did not verify", msg,
                      "*****@*****.**",
                      ["*****@*****.**"])
            return JsonResponseMessageError(
                "Client device must be self-signed with a signature matching its own public key!",
                code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)
    except Exception as e:
        # Can't properly namespace to a particular Exception here, since the only reason we would be getting here is
        # that what should be proper exception namespacing in code being called isn't correctly catching this exception
        msg = "\n\n".join([
            request.body,
            client_device._hashable_representation(),
            "Exception: %s" % e,
            str(type(e)), client_device.signed_by_id, client_device.id,
            str(request)
        ])
        send_mail("Exception while verifying client device", msg,
                  "*****@*****.**",
                  ["*****@*****.**"])
        return JsonResponseMessageError(
            "Client device must be self-signed with a signature matching its own public key!",
            code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)

    try:
        zone = register_self_registered_device(client_device, models, data)
    except Exception as e:
        if e.args[0] == "Client not yet on zone.":
            zone = None
        else:
            # Client not on zone: allow fall-through via "old route"

            # This is the codepath for unregistered devices trying to start a session.
            #   This would only get hit, however, if they visit the registration page.
            # But still, good to keep track of!
            UnregisteredDevicePing.record_ping(id=client_device.id,
                                               ip=get_request_ip(request))

            return JsonResponseMessageError(
                "Failed to validate the chain of trust (%s)." % e,
                code=EC.CHAIN_OF_TRUST_INVALID,
                status=500)

    if not zone:  # old code-path
        try:
            registration = RegisteredDevicePublicKey.objects.get(
                public_key=client_device.public_key)
            if not registration.is_used():
                registration.use()

            # Use the RegisteredDevicePublicKey, now that we've initialized the device and put it in its zone
            zone = registration.zone

        except RegisteredDevicePublicKey.DoesNotExist:
            try:
                # A redirect loop here is also possible, if a Device exists in the central server database
                # corresponding to the client_device, but no corresponding RegisteredDevicePublicKey exists
                device = Device.objects.get(
                    public_key=client_device.public_key)
                return JsonResponseMessageError(
                    "This device has already been registered",
                    code=EC.DEVICE_ALREADY_REGISTERED,
                    status=409)
            except Device.DoesNotExist:
                return JsonResponseMessageError(
                    "Device registration with public key not found; login and register first?",
                    code=EC.PUBLIC_KEY_UNREGISTERED,
                    status=404)

    client_device.save(imported=True)

    try:
        device_zone = DeviceZone.objects.get(device=client_device, zone=zone)
        device_zone.save(
        )  # re-save, to give it a central server signature that will be honored by old clients
    except DeviceZone.DoesNotExist:
        device_zone = DeviceZone(device=client_device, zone=zone)
        device_zone.save(
        )  # create the DeviceZone for the new device, with an 'upgraded' signature

    # return our local (server) Device, its Zone, and the newly created DeviceZone, to the client
    #   Note the order :)
    #
    # Addition: always back central server object--in case they didn't get it during install,
    #   they need it for software updating.
    return JsonResponse(
        serialize([
            Device.get_central_server(),
            Device.get_own_device(), zone, device_zone
        ],
                  dest_version=client_device.version,
                  ensure_ascii=False))
Beispiel #4
0
def register_device(request):
    """Receives the client device info from the distributed server.
    Tries to register either because the device has been pre-registered,
    or because it has a valid INSTALL_CERTIFICATE."""
    # attempt to load the client device data from the request data
    data = simplejson.loads(request.body or "{}")
    if "client_device" not in data:
        return JsonResponseMessageError("Serialized client device must be provided.", status=400)
    try:
        # When hand-shaking on the device models, since we don't yet know the version,
        #   we have to just TRY with our own version.
        #
        # This is currently "central server" code, so
        #   this will only fail (currently) if the central server version
        #   is less than the version of a client--something that should never happen
        try:
            local_version = Device.get_own_device().get_version()
            models = deserialize(data["client_device"], src_version=local_version, dest_version=local_version)
        except db_models.FieldDoesNotExist as fdne:
            raise Exception("Central server version is lower than client version.  This is ... impossible!")
        client_device = models.next().object
    except Exception as e:
        return JsonResponseMessageError("Could not decode the client device model: %s" % e, code=EC.CLIENT_DEVICE_CORRUPTED, status=400)

    # Validate the loaded data
    if not isinstance(client_device, Device):
        return JsonResponseMessageError("Client device must be an instance of the 'Device' model.", code=EC.CLIENT_DEVICE_NOT_DEVICE)

    try:
        if not client_device.verify():
            # We've been getting this verification error a lot, even when we shouldn't. Send more details to us by email so we can diagnose.
            msg = "\n\n".join([request.body, client_device._hashable_representation(), str(client_device.validate()), client_device.signed_by_id, client_device.id, str(request)])
            send_mail("Client device did not verify", msg, "*****@*****.**", ["*****@*****.**"])
            return JsonResponseMessageError("Client device must be self-signed with a signature matching its own public key!", code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)
    except Exception as e:
        # Can't properly namespace to a particular Exception here, since the only reason we would be getting here is
        # that what should be proper exception namespacing in code being called isn't correctly catching this exception
        msg = "\n\n".join([request.body, client_device._hashable_representation(), "Exception: %s" % e, str(type(e)), client_device.signed_by_id, client_device.id, str(request)])
        send_mail("Exception while verifying client device", msg, "*****@*****.**", ["*****@*****.**"])
        return JsonResponseMessageError("Client device must be self-signed with a signature matching its own public key!", code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)

    try:
        zone = register_self_registered_device(client_device, models, data)
    except Exception as e:
        if e.args[0] == "Client not yet on zone.":
            zone = None
        else:
            # Client not on zone: allow fall-through via "old route"

            # This is the codepath for unregistered devices trying to start a session.
            #   This would only get hit, however, if they visit the registration page.
            # But still, good to keep track of!
            UnregisteredDevicePing.record_ping(id=client_device.id, ip=get_request_ip(request))

            return JsonResponseMessageError("Failed to validate the chain of trust (%s)." % e, code=EC.CHAIN_OF_TRUST_INVALID, status=500)

    if not zone: # old code-path
        try:
            registration = RegisteredDevicePublicKey.objects.get(public_key=client_device.public_key)
            if not registration.is_used():
                registration.use()

            # Use the RegisteredDevicePublicKey, now that we've initialized the device and put it in its zone
            zone = registration.zone

        except RegisteredDevicePublicKey.DoesNotExist:
            try:
                # A redirect loop here is also possible, if a Device exists in the central server database 
                # corresponding to the client_device, but no corresponding RegisteredDevicePublicKey exists
                device = Device.objects.get(public_key=client_device.public_key)
                return JsonResponseMessageError("This device has already been registered", code=EC.DEVICE_ALREADY_REGISTERED, status=409)
            except Device.DoesNotExist:
                return JsonResponseMessageError("Device registration with public key not found; login and register first?", code=EC.PUBLIC_KEY_UNREGISTERED, status=404)

    client_device.save(imported=True)

    try:
        device_zone = DeviceZone.objects.get(device=client_device, zone=zone)
        device_zone.save()  # re-save, to give it a central server signature that will be honored by old clients
    except DeviceZone.DoesNotExist:
        device_zone = DeviceZone(device=client_device, zone=zone)
        device_zone.save()     # create the DeviceZone for the new device, with an 'upgraded' signature

    # return our local (server) Device, its Zone, and the newly created DeviceZone, to the client
    #   Note the order :)
    #
    # Addition: always back central server object--in case they didn't get it during install,
    #   they need it for software updating.
    return JsonResponse(
        serialize([Device.get_central_server(), Device.get_own_device(), zone, device_zone], dest_version=client_device.version, ensure_ascii=False)
    )
Beispiel #5
0
def create_session(request):
    data = simplejson.loads(request.body or "{}")
    if "client_nonce" not in data:
        return JsonResponseMessageError("Client nonce must be specified.",
                                        status=400)
    if len(data["client_nonce"]) != 32 or re.match("[^0-9a-fA-F]",
                                                   data["client_nonce"]):
        return JsonResponseMessageError(
            "Client nonce is malformed (must be 32-digit hex).", status=400)
    if "client_device" not in data:
        return JsonResponseMessageError("Client device must be specified.",
                                        status=400)
    if "server_nonce" not in data:
        if SyncSession.objects.filter(
                client_nonce=data["client_nonce"]).count():
            return JsonResponseMessageError(
                "Session already exists; include server nonce and signature.",
                status=401)
        session = SyncSession()
        session.client_nonce = data["client_nonce"]
        session.client_os = data.get("client_os", "")
        session.client_version = data.get("client_version", "")
        session.ip = get_request_ip(request)
        try:
            client_device = Device.objects.get(pk=data["client_device"])
            session.client_device = client_device
        except Device.DoesNotExist:
            # This is the codepath for unregistered devices trying to start a session.
            #   This would only get hit, however, if they manually run syncmodels.
            # But still, good to keep track of!
            UnregisteredDevicePing.record_ping(id=data["client_device"],
                                               ip=session.ip)
            return JsonResponseMessageError(
                "Client device matching id could not be found. (id=%s)" %
                data["client_device"],
                status=400)

        session.server_nonce = uuid.uuid4().hex
        session.server_device = Device.get_own_device()
        if session.client_device.pk == session.server_device.pk:
            return JsonResponseMessageError(
                "I know myself when I see myself, and you're not me.",
                status=401)
        session.save()
    else:
        try:
            session = SyncSession.objects.get(
                client_nonce=data["client_nonce"])
        except SyncSession.DoesNotExist:
            return JsonResponseMessageError(
                "Session with specified client nonce could not be found.",
                status=401)
        if session.server_nonce != data["server_nonce"]:
            return JsonResponseMessageError(
                "Server nonce did not match saved value.", status=401)
        if not data.get("signature", ""):
            return JsonResponseMessageError("Must include signature.",
                                            status=400)
        if not session.verify_client_signature(data["signature"]):
            return JsonResponseMessageError("Signature did not match.",
                                            status=401)
        session.verified = True
        session.save()

    # Return the serialized session, in the version intended for the other device
    return JsonResponse({
        "session":
        serialize([session],
                  dest_version=session.client_version,
                  ensure_ascii=False,
                  sign=False,
                  increment_counters=False),
        "signature":
        session.sign(),
    })