def trigger_data_update(self, update: DataUpdate): # make sure the id has a unique id for tracking if update.id is None: update.id = uuid.uuid4().hex logger.info("Triggering data update with id: {id}", update=update, id=update.id) asyncio.create_task(self.update_policy_data( update, policy_store=self._policy_store, data_fetcher=self._data_fetcher))
async def _update_policy_data_callback(self, data: dict = None, topic=""): """ Pub/Sub callback - triggering data updates will run when we get notifications on the policy_data topic. i.e: when new roles are added, changes to permissions, etc. """ if data is not None: reason = data.get("reason", "") else: reason = "Periodic update" logger.info("Updating policy data, reason: {reason}", reason=reason) update = DataUpdate.parse_obj(data) self.trigger_data_update(update)
async def get_base_policy_data(self, config_url: str = None, data_fetch_reason="Initial load"): """ Load data into the policy store according to the data source's config provided in the config URL Args: config_url (str, optional): URL to retrive data sources config from. Defaults to None ( self._data_sources_config_url). data_fetch_reason (str, optional): Reason to log for the update operation. Defaults to "Initial load". """ logger.info("Performing data configuration, reason: {reason}", reason={data_fetch_reason}) sources_config = await self.get_policy_data_config(url=config_url) # translate config to a data update entries = sources_config.entries update = DataUpdate(reason=data_fetch_reason, entries=entries) self.trigger_data_update(update)
def publish_data_update( token: Optional[str] = typer. Argument( None, help= "the JWT obtained from the server for authentication (see obtain-token command)", envvar="OPAL_CLIENT_TOKEN"), server_url: str = typer.Option( "http://localhost:7002", help="url of the OPAL-server to send the update through"), server_route: str = typer.Option( "/data/config", help="route in the server for update"), reason: str = typer.Option("", help="The reason for the update"), entries: str = typer.Option( "[]", "--entries", "-e", help="Pass in the the DataUpdate entries as JSON", callback=lambda x: json.loads(x)), src_url: str = typer. Option( None, help= "[SINGLE-ENTRY-UPDATE] url of the data-source this update relates to, which the clients should approach" ), topics: List[str] = typer. Option( None, "--topic", "-t", help= "[SINGLE-ENTRY-UPDATE] [List] topic (can several) for the published update (to be matched to client subscriptions)" ), src_config: str = typer.Option( "{}", help="[SINGLE-ENTRY-UPDATE] Fetching Config as JSON", callback=lambda x: json.loads(x)), dst_path: str = typer. Option( "", help= "[SINGLE-ENTRY-UPDATE] Path the client should set this value in its data-store" ), save_method: str = typer. Option( "PUT", help= "[SINGLE-ENTRY-UPDATE] How the data should be saved into the give dst-path" )): """ Publish a DataUpdate through an OPAL-server (indicated by --server_url). [SINGLE-ENTRY-UPDATE] Send a single update DataSourceEntry via the --src-url, --src-config, --topics, --dst-path, --save-method must include --src-url to use this flow. [Multiple entries] Set DataSourceEntires as JSON (via --entries) if you include a single entry as well- it will be merged into the given JSON """ from aiohttp import ClientSession, ClientResponse if not isinstance(entries, list): typer.secho("Bad input for --entires was ignored", fg="red") entries = [] entries: List[DataSourceEntry] # single entry update if src_url is not None: entry = DataSourceEntry(url=src_url, topics=topics, dst_path=dst_path, save_method=save_method, config=src_config) entries.append(entry) server_url = f"{server_url}{server_route}" update = DataUpdate(entries=entries, reason=reason) async def publish_update(): headers = {} if token is not None: headers = {"Authorization": f"bearer {token}"} async with ClientSession(headers=headers) as session: body = update.json() res = await session.post(server_url, data=body) return res async def get_response_text(res: ClientResponse): return await res.text() typer.echo(f"Publishing event:") typer.secho(f"{str(update)}", fg="cyan") res = asyncio.run(publish_update()) if res.status == 200: typer.secho("Event Published Successfully", fg="green") else: typer.secho("Event publishing failed with status-code - {res.status}", fg="red") text = asyncio.run(get_response_text(res)) typer.echo(text)