async def cancel_device_invitation(self, organization_id: OrganizationID, device_id: DeviceID) -> None: async with self.dbh.pool.acquire() as conn: async with conn.transaction(): if await self._device_exists(conn, organization_id, device_id): raise UserAlreadyExistsError( f"Device `{device_id}` already exists") result = await conn.execute( """ DELETE FROM device_invitations WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND device_id = $2 """, organization_id, device_id, ) if result not in ("DELETE 1", "DELETE 0"): raise UserError(f"Deletion error: {result}") await send_signal( conn, "device.invitation.cancelled", organization_id=organization_id, device_id=device_id, )
async def _create_user(conn, organization_id: OrganizationID, user: User, first_device: Device) -> None: try: result = await conn.execute( _q_insert_user, organization_id, user.user_id, user.is_admin, user.user_certificate, user.user_certifier, user.created_on, ) except UniqueViolationError: raise UserAlreadyExistsError(f"User `{user.user_id}` already exists") if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}") await _create_device(conn, organization_id, first_device, first_device=True) await send_signal( conn, "user.created", organization_id=organization_id, user_id=user.user_id, user_certificate=user.user_certificate, first_device_id=first_device.device_id, first_device_certificate=first_device.device_certificate, )
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 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 _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_cancel_user_invitation(conn, organization_id: OrganizationID, user_id: UserID) -> None: if await _user_exists(conn, organization_id, user_id): raise UserAlreadyExistsError(f"User `{user_id}` already exists") result = await conn.execute(_q_delete_invitation, organization_id, user_id) if result not in ("DELETE 1", "DELETE 0"): raise UserError(f"Deletion error: {result}")
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 query_cancel_device_invitation(conn, organization_id: OrganizationID, device_id: DeviceID) -> None: if await _device_exists(conn, organization_id, device_id): raise UserAlreadyExistsError(f"Device `{device_id}` already exists") result = await conn.execute(_q_delete_invitation, organization_id, device_id) if result not in ("DELETE 1", "DELETE 0"): raise UserError(f"Deletion error: {result}") await send_signal(conn, "device.invitation.cancelled", organization_id=organization_id, device_id=device_id)
async def query_create_user_invitation(conn, organization_id: OrganizationID, invitation: UserInvitation) -> None: if await _user_exists(conn, organization_id, invitation.user_id): raise UserAlreadyExistsError( f"User `{invitation.user_id}` already exists") result = await conn.execute(*_q_insert_invitation( organization_id=organization_id, creator=invitation.creator, user_id=invitation.user_id, created_on=invitation.created_on, )) if result not in ("INSERT 0 1", "UPDATE 1"): raise UserError(f"Insertion error: {result}")
async def query_cancel_device_invitation(conn, organization_id: OrganizationID, device_id: DeviceID) -> None: if await _device_exists(conn, organization_id, device_id): raise UserAlreadyExistsError(f"Device `{device_id}` already exists") result = await conn.execute(*_q_delete_invitation( organization_id=organization_id, device_id=device_id)) if result not in ("DELETE 1", "DELETE 0"): raise UserError(f"Deletion error: {result}") await send_signal( conn, BackendEvent.DEVICE_INVITATION_CANCELLED, organization_id=organization_id, device_id=device_id, )
async def _create_device_invitation(conn, organization_id: OrganizationID, invitation: DeviceInvitation) -> None: if await _device_exists(conn, organization_id, invitation.device_id): raise UserAlreadyExistsError( f"Device `{invitation.device_id}` already exists") result = await conn.execute( _q_insert_invitation, organization_id, invitation.creator, invitation.device_id, invitation.created_on, ) if result not in ("INSERT 0 1", "UPDATE 1"): raise UserError(f"Insertion error: {result}")
async def _do_create_user_with_human_handle(conn, organization_id: OrganizationID, user: User, first_device: Device) -> None: # Create human handle if needed await conn.execute( _q_insert_human_if_not_exists, organization_id, user.human_handle.email, user.human_handle.label, ) # Now insert the new user try: result = await conn.execute( _q_insert_user_with_human_handle, organization_id, user.user_id, user.profile.value, user.user_certificate, user.redacted_user_certificate, user.user_certifier, user.created_on, user.human_handle.email, ) except UniqueViolationError: raise UserAlreadyExistsError(f"User `{user.user_id}` already exists") if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}") # Finally make sure there is only one non-revoked user with this human handle now = pendulum_now() not_revoked_users = await conn.fetch(_q_get_not_revoked_users_for_human, organization_id, user.human_handle.email, now) if len(not_revoked_users ) != 1 or not_revoked_users[0]["user_id"] != user.user_id: # Exception cancels the transaction so the user insertion is automatically cancelled raise UserAlreadyExistsError( f"Human handle `{user.human_handle}` already corresponds to a non-revoked user" )
async def _do_create_user_without_human_handle( conn, organization_id: OrganizationID, user: User, first_device: Device ) -> None: try: result = await conn.execute( _q_insert_user, organization_id, user.user_id, user.is_admin, user.user_certificate, user.user_certifier, user.created_on, ) except UniqueViolationError: raise UserAlreadyExistsError(f"User `{user.user_id}` already exists") if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}")
async def set_user_admin(self, organization_id: OrganizationID, user_id: UserID, is_admin: bool) -> None: await self.get_user(organization_id, user_id) async with self.dbh.pool.acquire() as conn: result = await conn.execute( """ UPDATE users SET is_admin = $3 WHERE organization = ( SELECT _id from organizations WHERE organization_id = $1 ) AND user_id = $2 """, organization_id, user_id, is_admin, ) if result != "UPDATE 1": raise UserError(f"Update error: {result}")
async def create_device_invitation(self, organization_id: OrganizationID, invitation: DeviceInvitation) -> None: async with self.dbh.pool.acquire() as conn: async with conn.transaction(): if await self._device_exists(conn, organization_id, invitation.device_id): raise UserAlreadyExistsError( f"Device `{invitation.device_id}` already exists") result = await conn.execute( """ INSERT INTO device_invitations ( organization, creator, device_id, created_on ) VALUES ( (SELECT _id FROM organizations WHERE organization_id = $1), (SELECT _id FROM devices WHERE device_id = $2), $3, $4 ) ON CONFLICT (device_id) DO UPDATE SET organization = excluded.organization, creator = excluded.creator, created_on = excluded.created_on """, organization_id, invitation.creator, invitation.device_id, invitation.created_on, ) if result not in ("INSERT 0 1", "UPDATE 1"): raise UserError(f"Insertion error: {result}")
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 _create_user(conn, organization_id: OrganizationID, user: User) -> None: try: result = await conn.execute( """ INSERT INTO users ( organization, user_id, is_admin, certified_user, user_certifier, created_on ) SELECT _id, $2, $3, $4, ( SELECT _id FROM devices WHERE device_id = $5 AND organization = organizations._id ), $6 FROM organizations WHERE organization_id = $1 """, organization_id, user.user_id, user.is_admin, user.certified_user, user.user_certifier, user.created_on, ) except UniqueViolationError: raise UserAlreadyExistsError( f"User `{user.user_id}` already exists") if result != "INSERT 0 1": raise UserError(f"Insertion error: {result}") await conn.executemany( """ 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, ) for device in user.devices.values()], )