def _create_new_user_certificates( author: LocalDevice, device_label: Optional[DeviceLabel], human_handle: Optional[HumanHandle], profile: UserProfile, public_key: PublicKey, verify_key: VerifyKey, ) -> Tuple[bytes, bytes, bytes, bytes, InviteUserConfirmation]: """Helper to prepare the creation of a new user.""" device_id = DeviceID.new() try: timestamp = author.timestamp() user_certificate = UserCertificateContent( author=author.device_id, timestamp=timestamp, user_id=device_id.user_id, human_handle=human_handle, public_key=public_key, profile=profile, ) redacted_user_certificate = user_certificate.evolve(human_handle=None) device_certificate = DeviceCertificateContent( author=author.device_id, timestamp=timestamp, device_id=device_id, device_label=device_label, verify_key=verify_key, ) redacted_device_certificate = device_certificate.evolve( device_label=None) user_certificate = user_certificate.dump_and_sign(author.signing_key) redacted_user_certificate = redacted_user_certificate.dump_and_sign( author.signing_key) device_certificate = device_certificate.dump_and_sign( author.signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign( author.signing_key) except DataError as exc: raise InviteError( f"Cannot generate device certificate: {exc}") from exc invite_user_confirmation = InviteUserConfirmation( device_id=device_id, device_label=device_label, human_handle=human_handle, profile=profile, root_verify_key=author.root_verify_key, ) return ( user_certificate, redacted_user_certificate, device_certificate, redacted_device_certificate, invite_user_confirmation, )
async def bind_revocation(self, user_id: UserID, certifier: LocalDevice): timestamp = certifier.timestamp() revoked_user_certificate = RevokedUserCertificateContent( author=certifier.device_id, timestamp=timestamp, user_id=user_id ).dump_and_sign(certifier.signing_key) await self.backend.user.revoke_user( certifier.organization_id, user_id, revoked_user_certificate, certifier.device_id ) self.certificates_store.store_revoked_user( certifier.organization_id, user_id, revoked_user_certificate )
def local_device_to_backend_user( device: LocalDevice, certifier: Union[LocalDevice, OrganizationFullData] ) -> Tuple[BackendUser, BackendDevice]: if isinstance(certifier, OrganizationFullData): certifier_id = None certifier_signing_key = certifier.root_signing_key else: certifier_id = certifier.device_id certifier_signing_key = certifier.signing_key timestamp = device.timestamp() user_certificate = UserCertificateContent( author=certifier_id, timestamp=timestamp, user_id=device.user_id, public_key=device.public_key, profile=device.profile, human_handle=device.human_handle, ) device_certificate = DeviceCertificateContent( author=certifier_id, timestamp=timestamp, device_id=device.device_id, device_label=device.device_label, verify_key=device.verify_key, ) redacted_user_certificate = user_certificate.evolve(human_handle=None) redacted_device_certificate = device_certificate.evolve(device_label=None) user = BackendUser( user_id=device.user_id, human_handle=device.human_handle, profile=device.profile, user_certificate=user_certificate.dump_and_sign(certifier_signing_key), redacted_user_certificate=redacted_user_certificate.dump_and_sign( certifier_signing_key), user_certifier=certifier_id, created_on=timestamp, ) first_device = BackendDevice( device_id=device.device_id, device_label=device.device_label, device_certificate=device_certificate.dump_and_sign( certifier_signing_key), redacted_device_certificate=redacted_device_certificate.dump_and_sign( certifier_signing_key), device_certifier=certifier_id, created_on=timestamp, ) return user, first_device
async def workspace_storage_non_speculative_init( data_base_dir: Path, device: LocalDevice, workspace_id: EntryID) -> None: db_path = get_workspace_data_storage_db_path(data_base_dir, device, workspace_id) # Local data storage service async with LocalDatabase.run(db_path) as data_localdb: # Manifest storage service async with ManifestStorage.run(device, data_localdb, workspace_id) as manifest_storage: timestamp = device.timestamp() manifest = LocalWorkspaceManifest.new_placeholder( author=device.device_id, id=workspace_id, timestamp=timestamp, speculative=False) await manifest_storage.set_manifest(workspace_id, manifest)
async def user_storage_non_speculative_init(data_base_dir: Path, device: LocalDevice) -> None: data_path = get_user_data_storage_db_path(data_base_dir, device) # Local data storage service async with LocalDatabase.run(data_path) as localdb: # Manifest storage service async with ManifestStorage.run( device, localdb, device.user_manifest_id) as manifest_storage: timestamp = device.timestamp() manifest = LocalUserManifest.new_placeholder( author=device.device_id, id=device.user_manifest_id, timestamp=timestamp, speculative=False, ) await manifest_storage.set_manifest(device.user_manifest_id, manifest)
async def do_create_new_device( self, author: LocalDevice, device_label: Optional[DeviceLabel]) -> None: device_id = author.user_id.to_device_id(DeviceName.new()) try: timestamp = author.timestamp() device_certificate = DeviceCertificateContent( author=author.device_id, timestamp=timestamp, device_id=device_id, device_label=device_label, verify_key=self._verify_key, ) redacted_device_certificate = device_certificate.evolve( device_label=None) device_certificate = device_certificate.dump_and_sign( author.signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign( author.signing_key) except DataError as exc: raise InviteError( f"Cannot generate device certificate: {exc}") from exc rep = await self._cmds.device_create( device_certificate=device_certificate, redacted_device_certificate=redacted_device_certificate, ) _check_rep(rep, step_name="step 4 (device certificates upload)") # From now on the device has been created on the server, but greeter # is not aware of it yet. If something goes wrong, we can end up with # the greeter losing it private keys. # This is considered acceptable given 1) the error window is small and # 2) if this occurs the inviter can revoke the device and retry the # enrollment process to fix this try: payload = InviteDeviceConfirmation( device_id=device_id, device_label=device_label, human_handle=author.human_handle, profile=author.profile, private_key=author.private_key, user_manifest_id=author.user_manifest_id, user_manifest_key=author.user_manifest_key, root_verify_key=author.root_verify_key, ).dump_and_encrypt(key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteUserConfirmation payload") from exc rep = await self._cmds.invite_4_greeter_communicate(token=self.token, payload=payload) _check_rep(rep, step_name="step 4 (confirmation exchange)") # Invitation deletion is not strictly necessary (enrollment has succeeded # anyway) so it's no big deal if something goes wrong before it can be # done (and it can be manually deleted from invitation list). await self._cmds.invite_delete(token=self.token, reason=InvitationDeletedReason.FINISHED)