async def api_realm_create(self, client_ctx, msg): if client_ctx.profile == UserProfile.OUTSIDER: return { "status": "not_allowed", "reason": "Outsider user cannot create realm" } msg = realm_create_serializer.req_load(msg) try: data = RealmRoleCertificateContent.verify_and_load( msg["role_certificate"], author_verify_key=client_ctx.verify_key, expected_author=client_ctx.device_id, ) except DataError as exc: return { "status": "invalid_certification", "reason": f"Invalid certification data ({exc}).", } now = pendulum.now() if not timestamps_in_the_ballpark(data.timestamp, now): return { "status": "invalid_certification", "reason": "Invalid timestamp in certification.", } granted_role = RealmGrantedRole( certificate=msg["role_certificate"], realm_id=data.realm_id, user_id=data.user_id, role=data.role, granted_by=data.author, granted_on=data.timestamp, ) if granted_role.granted_by.user_id != granted_role.user_id: return { "status": "invalid_data", "reason": "Initial realm role certificate must be self-signed.", } if granted_role.role != RealmRole.OWNER: return { "status": "invalid_data", "reason": "Initial realm role certificate must set OWNER role.", } try: await self.create(client_ctx.organization_id, granted_role) except RealmNotFoundError as exc: return realm_create_serializer.rep_dump({ "status": "not_found", "reason": str(exc) }) except RealmAlreadyExistsError: return realm_create_serializer.rep_dump( {"status": "already_exists"}) return realm_create_serializer.rep_dump({"status": "ok"})
async def api_realm_update_roles(self, client_ctx, msg): msg = realm_update_roles_serializer.req_load(msg) try: data = RealmRoleCertificateContent.verify_and_load( msg["role_certificate"], author_verify_key=client_ctx.verify_key, expected_author=client_ctx.device_id, ) except DataError as exc: return { "status": "invalid_certification", "reason": f"Invalid certification data ({exc}).", } now = pendulum.now() if not timestamps_in_the_ballpark(data.timestamp, now): return { "status": "invalid_certification", "reason": "Invalid timestamp in certification.", } granted_role = RealmGrantedRole( certificate=msg["role_certificate"], realm_id=data.realm_id, user_id=data.user_id, role=data.role, granted_by=data.author, granted_on=data.timestamp, ) if granted_role.granted_by.user_id == granted_role.user_id: return { "status": "invalid_data", "reason": "Realm role certificate cannot be self-signed.", } try: await self.update_roles(client_ctx.organization_id, granted_role, msg["recipient_message"]) except RealmRoleAlreadyGranted: return realm_update_roles_serializer.rep_dump( {"status": "already_granted"}) except RealmAccessError: return realm_update_roles_serializer.rep_dump( {"status": "not_allowed"}) except RealmIncompatibleProfileError as exc: return realm_update_roles_serializer.rep_dump({ "status": "incompatible_profile", "reason": str(exc) }) except RealmNotFoundError as exc: return realm_update_roles_serializer.rep_dump({ "status": "not_found", "reason": str(exc) }) except RealmInMaintenanceError: return realm_update_roles_serializer.rep_dump( {"status": "in_maintenance"}) return realm_update_roles_serializer.rep_dump({"status": "ok"})
async def _load_realm_role_certificates(self, realm_id: Optional[EntryID] = None ): rep = await self._backend_cmds("realm_get_role_certificates", realm_id or self.workspace_id) if rep["status"] == "not_allowed": # Seems we lost the access to the realm raise FSWorkspaceNoReadAccess( "Cannot get workspace roles: no read access") elif rep["status"] != "ok": raise FSError( f"Cannot retrieve workspace roles: `{rep['status']}`") try: # Must read unverified certificates to access metadata unsecure_certifs = sorted( [(RealmRoleCertificateContent.unsecure_load(uv_role), uv_role) for uv_role in rep["certificates"]], key=lambda x: x[0].timestamp, ) current_roles: Dict[UserID, RealmRole] = {} owner_only = (RealmRole.OWNER, ) owner_or_manager = (RealmRole.OWNER, RealmRole.MANAGER) # Now verify each certif for unsecure_certif, raw_certif in unsecure_certifs: with translate_remote_devices_manager_errors(): author = await self.remote_devices_manager.get_device( unsecure_certif.author) RealmRoleCertificateContent.verify_and_load( raw_certif, author_verify_key=author.verify_key, expected_author=author.device_id, ) # Make sure author had the right to do this existing_user_role = current_roles.get(unsecure_certif.user_id) if not current_roles and unsecure_certif.user_id == author.device_id.user_id: # First user is autosigned needed_roles: Tuple[Optional[RealmRole], ...] = (None, ) elif (existing_user_role in owner_or_manager or unsecure_certif.role in owner_or_manager): needed_roles = owner_only else: needed_roles = owner_or_manager # TODO: typing, author is optional in base.py but it seems that manifests always have an author (no RVK) if (current_roles.get( cast(DeviceID, unsecure_certif.author).user_id) not in needed_roles): raise FSError( f"Invalid realm role certificates: " f"{unsecure_certif.author} has not right to give " f"{unsecure_certif.role} role to {unsecure_certif.user_id} " f"on {unsecure_certif.timestamp}") if unsecure_certif.role is None: current_roles.pop(unsecure_certif.user_id, None) else: current_roles[ unsecure_certif.user_id] = unsecure_certif.role # Decryption error except DataError as exc: raise FSError(f"Invalid realm role certificates: {exc}") from exc # Now unsecure_certifs is no longer unsecure given we have valided it items return [c for c, _ in unsecure_certifs], current_roles