def register_via_preregistered_key(self): """Register this device with a zone, through the central server directly""" own_device = Device.get_own_device() # Since we can't know the version of the remote device (yet), # we give it everything we possibly can (don't specify a dest_version) # # Note that (currently) this should never fail--the central server (which we're sending # these objects to) should always have a higher version. r = self.post("register", { "client_device": engine.serialize([own_device], ensure_ascii=False), }) # If they don't understand, our assumption is broken. if r.status_code == 500 and "Device has no field named 'version'" in r.content: raise Exception("Central server is of an older version than us?") # Failed to register with any certificate elif r.status_code != 200: raise Exception(r.content) else: # Save to our local store. By NOT passing a src_version, # we're saying it's OK to just store what we can. return engine.deserialize(r.content, src_version=None, dest_version=own_device.get_version())
def register_prove_self_registration(self): """ Prove that we belong on our zone by providing the chain of trust, from us to the creator of the zone. """ # Get all the own_device = Device.get_own_device() try: own_devicezone = DeviceZone.objects.get( device=own_device) # We exit if not found except DeviceZone.DoesNotExist: # This should never actually happen--when upgrading to this new code, # all devices should be on a zone. # However, if somehow that fails, let's try to create one now! raise Exception( "Shared network not installed. Try running the `generate_zone` command." ) own_zone = own_devicezone.zone chain_of_trust = ChainOfTrust(device=own_device, zone=own_zone) # For now, just try with one certificate # # Serialize for any version; in the current implementation, we assume the central server has # a version at least as new as ours, so can handle whatever data we send. # # the other side will have to reconstruct the chain from the object list object_list = [own_device] + chain_of_trust.objects() r = self.post("register", { "client_device": engine.serialize(object_list, ensure_ascii=False), }) # Failed to register with any certificate if r.status_code != 200: raise Exception(r.content) # When we register, we should receive the model information we require. # Make sure to deserialize for our version. return (engine.deserialize(r.content, dest_version=own_device.get_version()), r)
def create_json_file(include_data): central_server = Device.get_central_server() if not zone_id: models = [central_server] if central_server else [] else: # Get a chain of trust to the zone owner. # Because we're on the central server, this will # simply be the central server, but in the future # this would return an actual chain. logging.debug("Generating a zone invitation...") zone = Zone.objects.get(id=zone_id) chain = ChainOfTrust(zone=zone) assert chain.validate() new_invitation = ZoneInvitation.generate( zone=zone, invited_by=Device.get_own_device()) new_invitation.save( ) # keep a record of the invitation, for future revocation. Also, signs the thing # This ordering of objects is a bit be hokey, but OK--invitation usually must be # inserted before devicezones--but because it's not pointing to any devices, # it's OK to be at the end. # Note that the central server will always be at the front of the chain of trust, # so no need to explicitly include. models = chain.objects() + [new_invitation] # if include_data: logging.debug("Serializing entire dataset...") devices = Device.objects.by_zone(zone) devicezones = DeviceZone.objects.filter(zone=zone) models += list(devices) + list(devicezones) models += engine.get_models( zone=zone, limit=None) # get all models on this zone models_file = tempfile.mkstemp()[1] with open(models_file, "w") as fp: fp.write(engine.serialize(models)) return models_file
def register_prove_self_registration(self): """ Prove that we belong on our zone by providing the chain of trust, from us to the creator of the zone. """ # Get all the own_device = Device.get_own_device() try: own_devicezone = DeviceZone.objects.get(device=own_device) # We exit if not found except DeviceZone.DoesNotExist: # This should never actually happen--when upgrading to this new code, # all devices should be on a zone. # However, if somehow that fails, let's try to create one now! raise Exception("Shared network not installed. Try running the `generate_zone` command.") own_zone = own_devicezone.zone chain_of_trust = ChainOfTrust(device=own_device, zone=own_zone) # For now, just try with one certificate # # Serialize for any version; in the current implementation, we assume the central server has # a version at least as new as ours, so can handle whatever data we send. # # the other side will have to reconstruct the chain from the object list object_list = [own_device] + chain_of_trust.objects() r = self.post("register", { "client_device": engine.serialize(object_list, ensure_ascii=False), }) # Failed to register with any certificate if r.status_code != 200: raise Exception(r.content) # When we register, we should receive the model information we require. # Make sure to deserialize for our version. return (engine.deserialize(r.content, dest_version=own_device.get_version()), r)
def create_json_file(include_data): central_server = Device.get_central_server() if not zone_id: models = [central_server] if central_server else [] else: # Get a chain of trust to the zone owner. # Because we're on the central server, this will # simply be the central server, but in the future # this would return an actual chain. logging.debug("Generating a zone invitation...") zone = Zone.objects.get(id=zone_id) chain = ChainOfTrust(zone=zone) assert chain.validate() new_invitation = ZoneInvitation.generate(zone=zone, invited_by=Device.get_own_device()) new_invitation.save() # keep a record of the invitation, for future revocation. Also, signs the thing # This ordering of objects is a bit be hokey, but OK--invitation usually must be # inserted before devicezones--but because it's not pointing to any devices, # it's OK to be at the end. # Note that the central server will always be at the front of the chain of trust, # so no need to explicitly include. models = chain.objects() + [new_invitation] # if include_data: logging.debug("Serializing entire dataset...") devices = Device.objects.by_zone(zone) devicezones = DeviceZone.objects.filter(zone=zone) models += list(devices) + list(devicezones) models += engine.get_models(zone=zone, limit=None) # get all models on this zone models_file = tempfile.mkstemp()[1] with open(models_file, "w") as fp: fp.write(engine.serialize(models)) return models_file
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.raw_post_data or "{}") if "client_device" not in data: return JsonResponse( {"error": "Serialized client device must be provided."}, status=500) 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 = engine.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 JsonResponse( { "error": "Could not decode the client device model: %r" % e, "code": "client_device_corrupted", }, status=500) # Validate the loaded data if not isinstance(client_device, Device): return JsonResponse( { "error": "Client device must be an instance of the 'Device' model.", "code": "client_device_not_device", }, status=500) if not client_device.verify(): return JsonResponse( { "error": "Client device must be self-signed with a signature matching its own public key.", "code": "client_device_invalid_signature", }, status=500) try: zone = register_self_registered_device(client_device, models, data) except Exception as e: if e.message == "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 JsonResponse( { "error": "Failed to validate the chain of trust (%s)." % e, "code": "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() elif get_object_or_None(Device, public_key=client_device.public_key): return JsonResponse( { "error": "This device has already been registered", "code": "device_already_registered", }, status=500) else: # If not... we're in a very weird state--we have a record of their # registration, but no device record. # Let's just let the registration happens, so we can refresh things here. # No harm, and some failsafe benefit. # So, pass through... no code :) pass # Use the RegisteredDevicePublicKey, now that we've initialized the device and put it in its zone zone = registration.zone except RegisteredDevicePublicKey.DoesNotExist: try: device = Device.objects.get( public_key=client_device.public_key) return JsonResponse( { "error": "This device has already been registered", "code": "device_already_registered", }, status=500) except Device.DoesNotExist: return JsonResponse( { "error": "Device registration with public key not found; login and register first?", "code": "public_key_unregistered", }, status=500) 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( engine.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.raw_post_data or "{}") if "client_device" not in data: return JsonResponseMessageError("Serialized client device must be provided.") 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 = engine.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="client_device_corrupted") # Validate the loaded data if not isinstance(client_device, Device): return JsonResponseMessageError("Client device must be an instance of the 'Device' model.", code="client_device_not_device") if not client_device.verify(): return JsonResponseMessageError("Client device must be self-signed with a signature matching its own public key.", code="client_device_invalid_signature") try: zone = register_self_registered_device(client_device, models, data) except Exception as e: if e.message == "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="chain_of_trust_invalid") if not zone: # old code-path try: registration = RegisteredDevicePublicKey.objects.get(public_key=client_device.public_key) if not registration.is_used(): registration.use() elif get_object_or_None(Device, public_key=client_device.public_key): return JsonResponseMessageError("This device has already been registered", code="device_already_registered") else: # If not... we're in a very weird state--we have a record of their # registration, but no device record. # Let's just let the registration happens, so we can refresh things here. # No harm, and some failsafe benefit. # So, pass through... no code :) pass # Use the RegisteredDevicePublicKey, now that we've initialized the device and put it in its zone zone = registration.zone except RegisteredDevicePublicKey.DoesNotExist: try: device = Device.objects.get(public_key=client_device.public_key) return JsonResponseMessageError("This device has already been registered", code="device_already_registered") except Device.DoesNotExist: return JsonResponseMessageError("Device registration with public key not found; login and register first?", code="public_key_unregistered") 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( engine.serialize([Device.get_central_server(), Device.get_own_device(), zone, device_zone], dest_version=client_device.version, ensure_ascii=False) )