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"))
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"))
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))
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) )
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(), })