async def _get_user(conn, organization_id: OrganizationID, user_id: UserID) -> User: row = await conn.fetchrow( *_q_get_user(organization_id=organization_id.str, user_id=user_id.str)) if not row: raise UserNotFoundError(user_id) human_handle = None if row["human_email"]: human_handle = HumanHandle(email=row["human_email"], label=row["human_label"]) return User( user_id=user_id, human_handle=human_handle, profile=UserProfile(row["profile"]), user_certificate=row["user_certificate"], redacted_user_certificate=row["redacted_user_certificate"], user_certifier=DeviceID(row["user_certifier"]) if row["user_certifier"] else None, created_on=row["created_on"], revoked_on=row["revoked_on"], revoked_user_certificate=row["revoked_user_certificate"], revoked_user_certifier=DeviceID(row["revoked_user_certifier"]) if row["revoked_user_certifier"] else None, )
async def _get_device_invitation(self, conn, organization_id: OrganizationID, device_id: DeviceID): if await self._device_exists(conn, organization_id, device_id): raise UserAlreadyExistsError( f"Device `{device_id}` already exists") result = await conn.fetchrow( """ SELECT device_invitations.device_id, devices.device_id, device_invitations.created_on FROM device_invitations LEFT JOIN devices ON device_invitations.creator = devices._id WHERE device_invitations.organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND device_invitations.device_id = $2 """, organization_id, device_id, ) if not result: raise UserNotFoundError(device_id) return DeviceInvitation(device_id=DeviceID(result[0]), creator=DeviceID(result[1]), created_on=result[2])
async def _create_device(conn, organization_id: OrganizationID, device: Device, first_device: bool = False) -> None: if not first_device: existing_devices = await conn.fetch(_q_get_user_devices, organization_id, device.user_id) if not existing_devices: raise UserNotFoundError(f"User `{device.user_id}` doesn't exists") if device.device_id in itertools.chain(*existing_devices): raise UserAlreadyExistsError( f"Device `{device.device_id}` already exists") try: result = await conn.execute( _q_insert_device, organization_id, device.user_id, device.device_id, device.device_certificate, device.device_certifier, device.created_on, ) except UniqueViolationError: raise UserAlreadyExistsError( f"Device `{device.device_id}` already exists") if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}")
async def query_revoke_user( conn, organization_id: OrganizationID, user_id: UserID, revoked_user_certificate: bytes, revoked_user_certifier: DeviceID, revoked_on: Optional[pendulum.DateTime] = None, ) -> None: result = await conn.execute(*_q_revoke_user( organization_id=organization_id, user_id=user_id, revoked_user_certificate=revoked_user_certificate, revoked_user_certifier=revoked_user_certifier, revoked_on=revoked_on or pendulum.now(), )) if result != "UPDATE 1": # TODO: avoid having to do another query to find the error err_result = await conn.fetchrow(*_q_revoke_user_error( organization_id=organization_id, user_id=user_id)) if not err_result: raise UserNotFoundError(user_id) elif err_result[0]: raise UserAlreadyRevokedError() else: raise UserError(f"Update error: {result}") else: await send_signal(conn, BackendEvent.USER_REVOKED, organization_id=organization_id, user_id=user_id)
async def _get_user_invitation(self, conn, organization_id: OrganizationID, user_id: UserID): if await self._user_exists(conn, organization_id, user_id): raise UserAlreadyExistsError(f"User `{user_id}` already exists") result = await conn.fetchrow( """ SELECT user_invitations.user_id, devices.device_id, user_invitations.created_on FROM user_invitations LEFT JOIN devices ON user_invitations.creator = devices._id WHERE user_invitations.organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND user_invitations.user_id = $2 """, organization_id, user_id, ) if not result: raise UserNotFoundError(user_id) return UserInvitation(user_id=UserID(result[0]), creator=DeviceID(result[1]), created_on=result[2])
async def revoke_user( self, organization_id: OrganizationID, user_id: UserID, revoked_user_certificate: bytes, revoked_user_certifier: DeviceID, revoked_on: pendulum.Pendulum = None, ) -> None: org = self._organizations[organization_id] try: user = org.users[user_id] except KeyError: raise UserNotFoundError(user_id) if user.revoked_on: raise UserAlreadyRevokedError() org.users[user_id] = user.evolve( revoked_on=revoked_on or pendulum.now(), revoked_user_certificate=revoked_user_certificate, revoked_user_certifier=revoked_user_certifier, ) if user.human_handle: del org.human_handle_to_user_id[user.human_handle] await self._send_event("user.revoked", organization_id=organization_id, user_id=user_id)
async def query_revoke_user( conn, organization_id: OrganizationID, user_id: UserID, revoked_user_certificate: bytes, revoked_user_certifier: DeviceID, revoked_on: pendulum.Pendulum = None, ) -> None: result = await conn.execute( _q_revoke_user, organization_id, user_id, revoked_user_certificate, revoked_user_certifier, revoked_on or pendulum.now(), ) if result != "UPDATE 1": # TODO: avoid having to do another query to find the error err_result = await conn.fetchrow(_q_revoke_user_error, organization_id, user_id) if not err_result: raise UserNotFoundError(user_id) elif err_result[0]: raise UserAlreadyRevokedError() else: raise UserError(f"Update error: {result}") else: await send_signal(conn, "user.revoked", organization_id=organization_id, user_id=user_id)
async def revoke_device( self, organization_id: OrganizationID, device_id: DeviceID, certified_revocation: bytes, revocation_certifier: DeviceID, ) -> None: org = self._organizations[organization_id] user = await self.get_user(organization_id, device_id.user_id) try: if user.devices[device_id.device_name].revocated_on: raise UserAlreadyRevokedError() except KeyError: raise UserNotFoundError(device_id) patched_devices = [] for device in user.devices.values(): if device.device_id == device_id: device = device.evolve( revocated_on=pendulum.now(), certified_revocation=certified_revocation, revocation_certifier=revocation_certifier, ) patched_devices.append(device) org._users[device_id.user_id] = user.evolve(devices=DevicesMapping( *patched_devices))
async def _get_user(conn, organization_id: OrganizationID, user_id: UserID) -> User: row = await conn.fetchrow( *_q_get_user(organization_id=organization_id, user_id=user_id)) if not row: raise UserNotFoundError(user_id) if row["human_email"]: human_handle = HumanHandle(email=row["human_email"], label=row["human_label"]) else: human_handle = None return User( user_id=user_id, human_handle=human_handle, profile=STR_TO_USER_PROFILE[row["profile"]], user_certificate=row["user_certificate"], redacted_user_certificate=row["redacted_user_certificate"], user_certifier=row["user_certifier"], created_on=row["created_on"], revoked_on=row["revoked_on"], revoked_user_certificate=row["revoked_user_certificate"], revoked_user_certifier=row["revoked_user_certifier"], )
async def query_get_user_with_device( conn, organization_id: OrganizationID, device_id: DeviceID) -> Tuple[User, Device]: d_row = await conn.fetchrow(_q_get_device, organization_id, device_id) u_row = await conn.fetchrow(_q_get_user, organization_id, device_id.user_id) if not u_row or not d_row: raise UserNotFoundError(device_id) device = Device( device_id=device_id, device_label=d_row["device_label"], device_certificate=d_row["device_certificate"], redacted_device_certificate=d_row["redacted_device_certificate"], device_certifier=d_row["device_certifier"], created_on=d_row["created_on"], ) user = User( user_id=device_id.user_id, profile=STR_TO_USER_PROFILE[u_row["profile"]], user_certificate=u_row["user_certificate"], redacted_user_certificate=u_row["redacted_user_certificate"], user_certifier=u_row["user_certifier"], created_on=u_row["created_on"], revoked_on=u_row["revoked_on"], revoked_user_certificate=u_row["revoked_user_certificate"], revoked_user_certifier=u_row["revoked_user_certifier"], ) return user, device
async def get_device(self, organization_id: OrganizationID, device_id: DeviceID) -> Device: user = await self.get_user(organization_id, device_id.user_id) try: return user.devices[device_id.device_name] except KeyError: raise UserNotFoundError(device_id)
def _get_user(self, organization_id: OrganizationID, user_id: UserID) -> User: org = self._organizations[organization_id] try: return org.users[user_id] except KeyError: raise UserNotFoundError(user_id)
def _get_device(self, organization_id: OrganizationID, device_id: DeviceID) -> Device: org = self._organizations[organization_id] try: return org.devices[device_id.user_id][device_id.device_name] except KeyError: raise UserNotFoundError(device_id)
async def get_user_invitation(self, organization_id: OrganizationID, user_id: UserID) -> UserInvitation: org = self._organizations[organization_id] if user_id in org.users: raise UserAlreadyExistsError(user_id) try: return org.invitations[user_id] except KeyError: raise UserNotFoundError(user_id)
async def revoke_device( self, organization_id: OrganizationID, device_id: DeviceID, certified_revocation: bytes, revocation_certifier: DeviceID, ) -> None: async with self.dbh.pool.acquire() as conn: async with conn.transaction(): result = await conn.execute( """ UPDATE devices SET certified_revocation = $3, revocation_certifier = ( SELECT _id FROM devices WHERE device_id = $4 ), revocated_on = $5 WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND device_id = $2 AND revocated_on IS NULL """, organization_id, device_id, certified_revocation, revocation_certifier, pendulum.now(), ) if result != "UPDATE 1": # TODO: avoid having to do another query to find the error err_result = await conn.fetchrow( """ SELECT revocated_on FROM devices WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND device_id = $2 """, organization_id, device_id, ) if not err_result: raise UserNotFoundError(device_id) if err_result[0]: raise UserAlreadyRevokedError() else: raise UserError(f"Update error: {result}")
async def _get_device_invitation(conn, organization_id: OrganizationID, device_id: DeviceID) -> DeviceInvitation: if await _device_exists(conn, organization_id, device_id): raise UserAlreadyExistsError(f"Device `{device_id}` already exists") result = await conn.fetchrow(_q_get_invitation, organization_id, device_id) if not result: raise UserNotFoundError(device_id) return DeviceInvitation(device_id=DeviceID(result[0]), creator=DeviceID(result[1]), created_on=result[2])
async def _get_device(conn, organization_id: OrganizationID, device_id: DeviceID) -> Device: row = await conn.fetchrow(_q_get_device, organization_id, device_id) if not row: raise UserNotFoundError(device_id) return Device( device_id=device_id, device_certificate=row["device_certificate"], device_certifier=row["device_certifier"], created_on=row["created_on"], )
async def _get_user_invitation(conn, organization_id: OrganizationID, user_id: UserID): if await _user_exists(conn, organization_id, user_id): raise UserAlreadyExistsError(f"User `{user_id}` already exists") result = await conn.fetchrow(_q_get_invitation, organization_id, user_id) if not result: raise UserNotFoundError(user_id) return UserInvitation(user_id=UserID(result[0]), creator=DeviceID(result[1]), created_on=result[2])
async def get_device_invitation(self, organization_id: OrganizationID, device_id: DeviceID) -> DeviceInvitation: org = self._organizations[organization_id] try: org._users[device_id.user_id].devices[device_id.device_name] raise UserAlreadyExistsError(device_id) except KeyError: pass try: return org._invitations[device_id] except KeyError: raise UserNotFoundError(device_id)
async def get_device_invitation(self, organization_id: OrganizationID, device_id: DeviceID) -> DeviceInvitation: org = self._organizations[organization_id] user_devices = self._get_user_devices(organization_id, device_id.user_id) if device_id.device_name in user_devices: raise UserAlreadyExistsError(device_id) try: return org.invitations[device_id] except KeyError: raise UserNotFoundError(device_id)
async def _get_device(conn, organization_id: OrganizationID, device_id: DeviceID) -> Device: row = await conn.fetchrow(*_q_get_device( organization_id=organization_id.str, device_id=device_id.str)) if not row: raise UserNotFoundError(device_id) return Device( device_id=device_id, device_label=DeviceLabel(row["device_label"]), device_certificate=row["device_certificate"], redacted_device_certificate=row["redacted_device_certificate"], device_certifier=DeviceID(row["device_certifier"]), created_on=row["created_on"], )
async def _get_user(conn, organization_id: OrganizationID, user_id: UserID) -> User: row = await conn.fetchrow(_q_get_user, organization_id, user_id) if not row: raise UserNotFoundError(user_id) return User( user_id=user_id, is_admin=row["is_admin"], user_certificate=row["user_certificate"], user_certifier=row["user_certifier"], created_on=row["created_on"], revoked_on=row["revoked_on"], revoked_user_certificate=row["revoked_user_certificate"], revoked_user_certifier=row["revoked_user_certifier"], )
async def _get_user(conn, organization_id: OrganizationID, user_id: UserID) -> User: row = await conn.fetchrow(_q_get_user, organization_id, user_id) if not row: raise UserNotFoundError(user_id) return User( user_id=user_id, profile=STR_TO_USER_PROFILE[row["profile"]], user_certificate=row["user_certificate"], redacted_user_certificate=row["redacted_user_certificate"], user_certifier=row["user_certifier"], created_on=row["created_on"], revoked_on=row["revoked_on"], revoked_user_certificate=row["revoked_user_certificate"], revoked_user_certifier=row["revoked_user_certifier"], )
async def query_get_user_with_device( conn, organization_id: OrganizationID, device_id: DeviceID) -> Tuple[User, Device]: d_row = await conn.fetchrow(*_q_get_device( organization_id=organization_id.str, device_id=device_id.str)) u_row = await conn.fetchrow(*_q_get_user( organization_id=organization_id.str, user_id=device_id.user_id.str)) if not u_row or not d_row: raise UserNotFoundError(device_id) human_handle = None if u_row["human_email"]: human_handle = HumanHandle(email=u_row["human_email"], label=u_row["human_label"]) device = Device( device_id=device_id, device_label=DeviceLabel(d_row["device_label"]) if d_row["device_label"] else None, device_certificate=d_row["device_certificate"], redacted_device_certificate=d_row["redacted_device_certificate"], device_certifier=DeviceID(d_row["device_certifier"]) if d_row["device_certifier"] else None, created_on=d_row["created_on"], ) user = User( user_id=device_id.user_id, human_handle=human_handle, profile=UserProfile(u_row["profile"]), user_certificate=u_row["user_certificate"], redacted_user_certificate=u_row["redacted_user_certificate"], user_certifier=DeviceID(u_row["user_certifier"]) if u_row["user_certifier"] else None, created_on=u_row["created_on"], revoked_on=u_row["revoked_on"], revoked_user_certificate=u_row["revoked_user_certificate"], revoked_user_certifier=DeviceID(u_row["revoked_user_certifier"]) if u_row["revoked_user_certifier"] else None, ) return user, device
async def create_device(self, organization_id: OrganizationID, device: Device, encrypted_answer: bytes = b"") -> None: org = self._organizations[organization_id] if device.user_id not in org.users: raise UserNotFoundError(f"User `{device.user_id}` doesn't exists") user_devices = org.devices[device.user_id] if device.device_name in user_devices: raise UserAlreadyExistsError( f"Device `{device.device_id}` already exists") user_devices[device.device_name] = device await self._send_event( "device.created", organization_id=organization_id, device_id=device.device_id, device_certificate=device.device_certificate, encrypted_answer=encrypted_answer, )
async def create_device(self, organization_id: OrganizationID, device: Device, encrypted_answer: bytes = b"") -> None: org = self._organizations[organization_id] if device.user_id not in org._users: raise UserNotFoundError(f"User `{device.user_id}` doesn't exists") user = org._users[device.user_id] if device.device_name in user.devices: raise UserAlreadyExistsError( f"Device `{device.device_id}` already exists") org._users[device.user_id] = user.evolve( devices=DevicesMapping(*user.devices.values(), device)) self.event_bus.send( "device.created", organization_id=organization_id, device_id=device.device_id, encrypted_answer=encrypted_answer, )
async def _create_device(self, conn, organization_id: OrganizationID, device: Device) -> None: existing_devices = await conn.fetch( """ SELECT device_id FROM devices WHERE user_ = ( SELECT _id FROM users WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND user_id = $2 ) """, organization_id, device.user_id, ) if not existing_devices: raise UserNotFoundError(f"User `{device.user_id}` doesn't exists") if device.device_id in itertools.chain(*existing_devices): raise UserAlreadyExistsError( f"Device `{device.device_id}` already exists") result = await conn.execute( """ INSERT INTO devices ( organization, user_, device_id, certified_device, device_certifier, created_on, revocated_on, certified_revocation, revocation_certifier ) SELECT _id, ( SELECT _id FROM users WHERE user_id = $2 AND organization = organizations._id ), $3, $4, ( SELECT _id FROM devices WHERE device_id = $5 AND organization = organizations._id ), $6, $7, $8, $9 FROM organizations WHERE organization_id = $1 """, organization_id, device.user_id, device.device_id, device.certified_device, device.device_certifier, device.created_on, device.revocated_on, device.certified_revocation, device.revocation_certifier, ) if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}")
async def _get_user(self, conn, organization_id: OrganizationID, user_id: UserID) -> User: user_result = await conn.fetchrow( """ SELECT is_admin, certified_user, (SELECT device_id FROM devices WHERE _id = user_certifier), created_on FROM users WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND user_id = $2 """, organization_id, user_id, ) if not user_result: raise UserNotFoundError(user_id) devices_results = await conn.fetch( """ SELECT d1.device_id, d1.certified_device, ( SELECT devices.device_id FROM devices WHERE devices._id = d1.device_certifier ) AS device_certifier, d1.created_on, d1.revocated_on, d1.certified_revocation, ( SELECT devices.device_id FROM devices WHERE devices._id = d1.revocation_certifier ) as revocation_certifier FROM devices as d1 WHERE user_ = ( SELECT _id FROM users WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND user_id = $2 ); """, organization_id, user_id, ) devices = DevicesMapping(*[ Device(DeviceID(device_result[0]), *device_result[1:]) for device_result in devices_results ]) return User( user_id=UserID(user_id), is_admin=user_result[0], certified_user=user_result[1], user_certifier=user_result[2], created_on=user_result[3], devices=devices, )