Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable OAuth2 authentication. #646

Open
wants to merge 97 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
2294daa
Enable OAuth2 authentication.
Aug 16, 2024
03f4668
Reformat code by running pre-commit run --all-files
Sep 2, 2024
6390e10
Enable OAuth2 authentication.
Aug 16, 2024
55eea17
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
173f4d7
Enable OAuth2 authentication.
Aug 16, 2024
b414204
Reformat code by running pre-commit run --all-files
Sep 2, 2024
4fbf341
Enable OAuth2 authentication.
Aug 16, 2024
4e3b3c0
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
01bb6f4
Enable OAuth2 authentication.
Aug 16, 2024
591702e
Reformat code by running pre-commit run --all-files
Sep 2, 2024
6bffb83
Enable OAuth2 authentication.
Aug 16, 2024
34e0b29
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
8483251
Enable OAuth2 authentication.
Aug 16, 2024
9775c12
Reformat code by running pre-commit run --all-files
Sep 2, 2024
6e9baab
Enable OAuth2 authentication.
Aug 16, 2024
8f1ebef
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
42c2371
Enable OAuth2 authentication.
Aug 16, 2024
115284a
Reformat code by running pre-commit run --all-files
Sep 2, 2024
96296e5
Enable OAuth2 authentication.
Aug 16, 2024
6ac5ef9
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
13fcdcc
Enable OAuth2 authentication.
Aug 16, 2024
96d6494
Reformat code by running pre-commit run --all-files
Sep 2, 2024
f9b5610
Enable OAuth2 authentication.
Aug 16, 2024
dc39f47
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
4e2d145
Enable OAuth2 authentication.
Aug 16, 2024
364233f
Reformat code by running pre-commit run --all-files
Sep 2, 2024
aa50a95
Enable OAuth2 authentication.
Aug 16, 2024
c31b205
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
fe5ed63
Enable OAuth2 authentication.
Aug 16, 2024
23fb0e3
Reformat code by running pre-commit run --all-files
Sep 2, 2024
7e2d1aa
Enable OAuth2 authentication.
Aug 16, 2024
cff48d4
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
f3df918
Enable OAuth2 authentication.
Aug 16, 2024
6eecb86
Reformat code by running pre-commit run --all-files
Sep 2, 2024
660c4a9
Enable OAuth2 authentication.
Aug 16, 2024
76f62ad
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
31add1d
Enable OAuth2 authentication.
Aug 16, 2024
3578b7b
Reformat code by running pre-commit run --all-files
Sep 2, 2024
e494616
Enable OAuth2 authentication.
Aug 16, 2024
fa40acb
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
0fea626
Enable OAuth2 authentication.
Aug 16, 2024
430dafe
Reformat code by running pre-commit run --all-files
Sep 2, 2024
ca07081
Enable OAuth2 authentication.
Aug 16, 2024
1630863
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
3eea867
Enable OAuth2 authentication.
Aug 16, 2024
f56a08d
Reformat code by running pre-commit run --all-files
Sep 2, 2024
242317d
Enable OAuth2 authentication.
Aug 16, 2024
c5b3a67
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
9afd029
Enable OAuth2 authentication.
Aug 16, 2024
8227b59
Reformat code by running pre-commit run --all-files
Sep 2, 2024
5c20906
Enable OAuth2 authentication.
Aug 16, 2024
42d5569
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
5d0badd
Enable OAuth2 authentication.
Aug 16, 2024
f7b6225
Reformat code by running pre-commit run --all-files
Sep 2, 2024
ae9ec14
Enable OAuth2 authentication.
Aug 16, 2024
6438cbe
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
cd2797f
Enable OAuth2 authentication.
Aug 16, 2024
59a93ff
Reformat code by running pre-commit run --all-files
Sep 2, 2024
f23cdd5
Enable OAuth2 authentication.
Aug 16, 2024
ca43bb1
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
ce9b007
Enable OAuth2 authentication.
Aug 16, 2024
fe54a09
Reformat code by running pre-commit run --all-files
Sep 2, 2024
2d5799a
Enable OAuth2 authentication.
Aug 16, 2024
0a2fa06
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
3255b40
Enable OAuth2 authentication.
Aug 16, 2024
3cf07f7
Reformat code by running pre-commit run --all-files
Sep 2, 2024
cc41e31
Enable OAuth2 authentication.
Aug 16, 2024
f4d3872
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
3ed8923
Enable OAuth2 authentication.
Aug 16, 2024
1d246ca
Reformat code by running pre-commit run --all-files
Sep 2, 2024
ae2a242
Enable OAuth2 authentication.
Aug 16, 2024
c1fb2bd
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
ca4321c
Enable OAuth2 authentication.
Aug 16, 2024
4f8d94c
Reformat code by running pre-commit run --all-files
Sep 2, 2024
a8bb0cc
Enable OAuth2 authentication.
Aug 16, 2024
76d0875
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
f78eada
Enable OAuth2 authentication.
Aug 16, 2024
856a06d
Reformat code by running pre-commit run --all-files
Sep 2, 2024
1558e6a
Enable OAuth2 authentication.
Aug 16, 2024
b2db3de
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
22fe0d7
Enable OAuth2 authentication.
Aug 16, 2024
f922993
Reformat code by running pre-commit run --all-files
Sep 2, 2024
bfd596f
Enable OAuth2 authentication.
Aug 16, 2024
10ccc63
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
29684a2
Enable OAuth2 authentication.
Aug 16, 2024
f74db74
Reformat code by running pre-commit run --all-files
Sep 2, 2024
856262d
Enable OAuth2 authentication.
Aug 16, 2024
48c8333
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
f2e32a0
Enable OAuth2 authentication.
Aug 16, 2024
3174d51
Reformat code by running pre-commit run --all-files
Sep 2, 2024
758ea64
Enable OAuth2 authentication.
Aug 16, 2024
02f2cca
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
ece1895
Enable OAuth2 authentication.
Aug 16, 2024
ce6a8be
Reformat code by running pre-commit run --all-files
Sep 2, 2024
bc8229c
Enable OAuth2 authentication.
Aug 16, 2024
de929af
Refactor Authenticator to interface and initialize one with provided …
Sep 4, 2024
a7a9404
Enable OAuth2 authentication.
Aug 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions docker/docker-compose-with-oauth-jwt-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
services:
# When scaling the opal-server to multiple nodes and/or multiple workers, we use
# a *broadcast* channel to sync between all the instances of opal-server.
# Under the hood, this channel is implemented by encode/broadcaster (see link below).
# At the moment, the broadcast channel can be either: postgresdb, redis or kafka.
# The format of the broadcaster URI string (the one we pass to opal server as `OPAL_BROADCAST_URI`) is specified here:
# https://github.com/encode/broadcaster#available-backends
broadcast_channel:
image: postgres:alpine
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
opal_server:
# by default we run opal-server from latest official image
image: permitio/opal-server:latest
environment:
# the broadcast backbone uri used by opal server workers (see comments above for: broadcast_channel)
- OPAL_BROADCAST_URI=postgres://postgres:postgres@broadcast_channel:5432/postgres
# number of uvicorn workers to run inside the opal-server container
- UVICORN_NUM_WORKERS=4
# the git repo hosting our policy
# - if this repo is not public, you can pass an ssh key via `OPAL_POLICY_REPO_SSH_KEY`)
# - the repo we pass in this example is *public* and acts as an example repo with dummy rego policy
# - for more info, see: https://docs.opal.ac/tutorials/track_a_git_repo
- OPAL_POLICY_REPO_URL=https://github.com/permitio/opal-example-policy-repo
# in this example we will use a polling interval of 30 seconds to check for new policy updates (git commits affecting the rego policy).
# however, it is better to utilize a git *webhook* to trigger the server to check for changes only when the repo has new commits.
# for more info see: https://docs.opal.ac/tutorials/track_a_git_repo
- OPAL_POLICY_REPO_POLLING_INTERVAL=30
# configures from where the opal client should initially fetch data (when it first goes up, after disconnection, etc).
# the data sources represents from where the opal clients should get a "complete picture" of the data they need.
# after the initial sources are fetched, the client will subscribe only to update notifications sent by the server.
- OPAL_DATA_CONFIG_SOURCES={"config":{"entries":[{"url":"http://opal_server:7002/policy-data","topics":["policy_data"],"dst_path":"/static"}]}}
- OPAL_LOG_FORMAT_INCLUDE_PID=true
# to protect resources with OAuth2 Opaque token provided by dedicated server
- OPAL_AUTH_TYPE=oauth2
# URL to generate new OAuth 2.0 Client Credentials Grant token
- OPAL_OAUTH2_TOKEN_URL=https://example/oauth2/token
# JWT validation
- OPAL_OAUTH2_OPENID_CONFIGURATION_URL=https://example/.well-known/openid-configuration
- OPAL_OAUTH2_EXACT_MATCH_CLAIMS=aud=some_audience,iss=some_issuer
- OPAL_OAUTH2_REQUIRED_CLAIMS=sub,iat,exp
- OPAL_OAUTH2_JWT_ALGORITHM=RS256
- OPAL_OAUTH2_JWT_AUDIENCE=some_audience
- OPAL_OAUTH2_JWT_ISSUER=https://example/issuer
ports:
# exposes opal server on the host machine, you can access the server at: http://localhost:7002
- "7002:7002"
depends_on:
- broadcast_channel
opal_client:
# by default we run opal-client from latest official image
image: permitio/opal-client:latest
environment:
- OPAL_SERVER_URL=http://opal_server:7002
- OPAL_LOG_FORMAT_INCLUDE_PID=true
- OPAL_INLINE_OPA_LOG_FORMAT=http
# to protect resources with OAuth2 Opaque token provided by dedicated server
- OPAL_AUTH_TYPE=oauth2
# client credentials
- OPAL_OAUTH2_CLIENT_ID=some_client_id
- OPAL_OAUTH2_CLIENT_SECRET=some_client_secret
# URL to generate new OAuth 2.0 Client Credentials Grant token
- OPAL_OAUTH2_TOKEN_URL=https://example/oauth2/token
# JWT validation
- OPAL_OAUTH2_OPENID_CONFIGURATION_URL=https://example/.well-known/openid-configuration
- OPAL_OAUTH2_EXACT_MATCH_CLAIMS=aud=some_audience,iss=some_issuer
- OPAL_OAUTH2_REQUIRED_CLAIMS=sub,iat,exp
- OPAL_OAUTH2_JWT_ALGORITHM=RS256
- OPAL_OAUTH2_JWT_AUDIENCE=some_audience
- OPAL_OAUTH2_JWT_ISSUER=https://example/issuer
# Enable Authorization / Authentication in OPA
- 'OPAL_INLINE_OPA_CONFIG={"authentication":"token", "authorization":"basic", "files": ["authz.rego"]}'
volumes:
# The goal is to create an initial authorization rego that allows OPAL to write the first policy from the POLICY_REPO_URL.
# This is achieved through policy overwrite based on the "id" attribute.
# When the authz.rego file is placed in the root directory of OPA, it is given the id 'authz.rego'.
# Similarly, if there is another authz.rego file in the root of POLICY_REPO_URL, it will also be given the id 'authz.rego'.
# Therefore, if the authz.rego file from the POLICY_REPO_URL exists, it will overwrite the initial authz.rego file.
- ./docker_files/policy_test/authz.rego:/opal/authz.rego
ports:
# exposes opal client on the host machine, you can access the client at: http://localhost:7766
- "7766:7000"
# exposes the OPA agent (being run by OPAL) on the host machine
# you can access the OPA api that you know and love at: http://localhost:8181
# OPA api docs are at: https://www.openpolicyagent.org/docs/latest/rest-api/
- "8181:8181"
depends_on:
- opal_server
# this command is not necessary when deploying OPAL for real, it is simply a trick for dev environments
# to make sure that opal-server is already up before starting the client.
command: sh -c "exec ./wait-for.sh opal_server:7002 --timeout=20 -- ./start.sh"
83 changes: 83 additions & 0 deletions docker/docker-compose-with-oauth-opaque-token.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
services:
# When scaling the opal-server to multiple nodes and/or multiple workers, we use
# a *broadcast* channel to sync between all the instances of opal-server.
# Under the hood, this channel is implemented by encode/broadcaster (see link below).
# At the moment, the broadcast channel can be either: postgresdb, redis or kafka.
# The format of the broadcaster URI string (the one we pass to opal server as `OPAL_BROADCAST_URI`) is specified here:
# https://github.com/encode/broadcaster#available-backends
broadcast_channel:
image: postgres:alpine
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
opal_server:
# by default we run opal-server from latest official image
image: permitio/opal-server:latest
environment:
# the broadcast backbone uri used by opal server workers (see comments above for: broadcast_channel)
- OPAL_BROADCAST_URI=postgres://postgres:postgres@broadcast_channel:5432/postgres
# number of uvicorn workers to run inside the opal-server container
- UVICORN_NUM_WORKERS=4
# the git repo hosting our policy
# - if this repo is not public, you can pass an ssh key via `OPAL_POLICY_REPO_SSH_KEY`)
# - the repo we pass in this example is *public* and acts as an example repo with dummy rego policy
# - for more info, see: https://docs.opal.ac/tutorials/track_a_git_repo
- OPAL_POLICY_REPO_URL=https://github.com/permitio/opal-example-policy-repo
# in this example we will use a polling interval of 30 seconds to check for new policy updates (git commits affecting the rego policy).
# however, it is better to utilize a git *webhook* to trigger the server to check for changes only when the repo has new commits.
# for more info see: https://docs.opal.ac/tutorials/track_a_git_repo
- OPAL_POLICY_REPO_POLLING_INTERVAL=30
# configures from where the opal client should initially fetch data (when it first goes up, after disconnection, etc).
# the data sources represents from where the opal clients should get a "complete picture" of the data they need.
# after the initial sources are fetched, the client will subscribe only to update notifications sent by the server.
- OPAL_DATA_CONFIG_SOURCES={"config":{"entries":[{"url":"http://opal_server:7002/policy-data","topics":["policy_data"],"dst_path":"/static"}]}}
- OPAL_LOG_FORMAT_INCLUDE_PID=true
# to protect resources with OAuth2 Opaque token provided by dedicated server
- OPAL_AUTH_TYPE=oauth2
# URL to generate new OAuth 2.0 Client Credentials Grant token
- OPAL_OAUTH2_TOKEN_URL=https://example/oauth2/token
# introspect URL for Opaque token validation
- OPAL_OAUTH2_INTROSPECT_URL=https://example/oauth2/introspect
ports:
# exposes opal server on the host machine, you can access the server at: http://localhost:7002
- "7002:7002"
depends_on:
- broadcast_channel
opal_client:
# by default we run opal-client from latest official image
image: permitio/opal-client:latest
environment:
- OPAL_SERVER_URL=http://opal_server:7002
- OPAL_LOG_FORMAT_INCLUDE_PID=true
- OPAL_INLINE_OPA_LOG_FORMAT=http
# to protect resources with OAuth2 Opaque token provided by dedicated server
- OPAL_AUTH_TYPE=oauth2
# client credentials
- OPAL_OAUTH2_CLIENT_ID=some_client_id
- OPAL_OAUTH2_CLIENT_SECRET=some_client_secret
# URL to generate new OAuth 2.0 Client Credentials Grant token
- OPAL_OAUTH2_TOKEN_URL=https://example/oauth2/token
# introspect URL for Opaque token validation
- OPAL_OAUTH2_INTROSPECT_URL=https://example/oauth2/introspect
# Enable Authorization / Authentication in OPA
- 'OPAL_INLINE_OPA_CONFIG={"authentication":"token", "authorization":"basic", "files": ["authz.rego"]}'
volumes:
# The goal is to create an initial authorization rego that allows OPAL to write the first policy from the POLICY_REPO_URL.
# This is achieved through policy overwrite based on the "id" attribute.
# When the authz.rego file is placed in the root directory of OPA, it is given the id 'authz.rego'.
# Similarly, if there is another authz.rego file in the root of POLICY_REPO_URL, it will also be given the id 'authz.rego'.
# Therefore, if the authz.rego file from the POLICY_REPO_URL exists, it will overwrite the initial authz.rego file.
- ./docker_files/policy_test/authz.rego:/opal/authz.rego
ports:
# exposes opal client on the host machine, you can access the client at: http://localhost:7766
- "7766:7000"
# exposes the OPA agent (being run by OPAL) on the host machine
# you can access the OPA api that you know and love at: http://localhost:8181
# OPA api docs are at: https://www.openpolicyagent.org/docs/latest/rest-api/
- "8181:8181"
depends_on:
- opal_server
# this command is not necessary when deploying OPAL for real, it is simply a trick for dev environments
# to make sure that opal-server is already up before starting the client.
command: sh -c "exec ./wait-for.sh opal_server:7002 --timeout=20 -- ./start.sh"
4 changes: 2 additions & 2 deletions packages/opal-client/opal_client/callbacks/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from fastapi import APIRouter, Depends, HTTPException, Response, status
from opal_client.callbacks.register import CallbacksRegister
from opal_client.config import opal_client_config
from opal_common.authentication.authenticator import Authenticator
from opal_common.authentication.authz import require_peer_type
from opal_common.authentication.deps import JWTAuthenticator
from opal_common.authentication.types import JWTClaims
from opal_common.authentication.verifier import Unauthorized
from opal_common.logger import logger
Expand All @@ -13,7 +13,7 @@
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR


def init_callbacks_api(authenticator: JWTAuthenticator, register: CallbacksRegister):
def init_callbacks_api(authenticator: Authenticator, register: CallbacksRegister):
async def require_listener_token(claims: JWTClaims = Depends(authenticator)):
try:
require_peer_type(
Expand Down
39 changes: 15 additions & 24 deletions packages/opal-client/opal_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
import functools
import os
import signal
import tempfile
import uuid
from logging import disable
from typing import Awaitable, Callable, List, Literal, Optional, Union

import aiofiles
Expand All @@ -19,8 +17,8 @@
from opal_client.callbacks.register import CallbacksRegister
from opal_client.config import PolicyStoreTypes, opal_client_config
from opal_client.data.api import init_data_router
from opal_client.data.fetcher import DataFetcher
from opal_client.data.updater import DataUpdater
from opal_client.data.updater_factory import DataUpdaterFactory
from opal_client.engine.options import CedarServerOptions, OpaServerOptions
from opal_client.engine.runner import CedarRunner, OpaRunner
from opal_client.limiter import StartupLoadLimiter
Expand All @@ -31,8 +29,8 @@
from opal_client.policy_store.policy_store_client_factory import (
PolicyStoreClientFactory,
)
from opal_common.authentication.deps import JWTAuthenticator
from opal_common.authentication.verifier import JWTVerifier
from opal_common.authentication.authenticator import Authenticator
from opal_common.authentication.authenticator_factory import AuthenticatorFactory
from opal_common.config import opal_common_config
from opal_common.logger import configure_logs, logger
from opal_common.middleware import configure_middleware
Expand All @@ -51,7 +49,7 @@ def __init__(
inline_opa_options: OpaServerOptions = None,
inline_cedar_enabled: bool = None,
inline_cedar_options: CedarServerOptions = None,
verifier: Optional[JWTVerifier] = None,
authenticator: Optional[Authenticator] = None,
store_backup_path: Optional[str] = None,
store_backup_interval: Optional[int] = None,
offline_mode_enabled: bool = False,
Expand All @@ -70,6 +68,10 @@ def __init__(
data_updater (DataUpdater, optional): Defaults to None.
policy_updater (PolicyUpdater, optional): Defaults to None.
"""
if authenticator is not None:
self.authenticator = authenticator
else:
self.authenticator = AuthenticatorFactory.create()
self._shard_id = shard_id
# defaults
policy_store_type: PolicyStoreTypes = (
Expand Down Expand Up @@ -127,6 +129,7 @@ def __init__(
opal_client_id=opal_client_identifier,
on_connect=on_policy_updater_connect,
on_disconnect=on_policy_updater_disconnect,
authenticator=self.authenticator,
)
else:
self.policy_updater = None
Expand All @@ -142,14 +145,15 @@ def __init__(
else opal_client_config.DATA_TOPICS
)

self.data_updater = DataUpdater(
self.data_updater = DataUpdaterFactory.create(
policy_store=self.policy_store,
data_topics=data_topics,
callbacks_register=self._callbacks_register,
opal_client_id=opal_client_identifier,
shard_id=self._shard_id,
on_connect=on_data_updater_connect,
on_disconnect=on_data_updater_disconnect,
authenticator=self.authenticator,
)
else:
self.data_updater = None
Expand All @@ -172,19 +176,6 @@ def __init__(
"OPAL client is configured to trust self-signed certificates"
)

if verifier is not None:
self.verifier = verifier
else:
self.verifier = JWTVerifier(
public_key=opal_common_config.AUTH_PUBLIC_KEY,
algorithm=opal_common_config.AUTH_JWT_ALGORITHM,
audience=opal_common_config.AUTH_JWT_AUDIENCE,
issuer=opal_common_config.AUTH_JWT_ISSUER,
)
if not self.verifier.enabled:
logger.info(
"API authentication disabled (public encryption key was not provided)"
)
self.store_backup_path = (
store_backup_path or opal_client_config.STORE_BACKUP_PATH
)
Expand Down Expand Up @@ -264,13 +255,13 @@ async def _is_ready(self):
def _configure_api_routes(self, app: FastAPI):
"""Mounts the api routes on the app object."""

authenticator = JWTAuthenticator(self.verifier)

# Init api routers with required dependencies
policy_router = init_policy_router(policy_updater=self.policy_updater)
data_router = init_data_router(data_updater=self.data_updater)
policy_store_router = init_policy_store_router(authenticator)
callbacks_router = init_callbacks_api(authenticator, self._callbacks_register)
policy_store_router = init_policy_store_router(self.authenticator)
callbacks_router = init_callbacks_api(
self.authenticator, self._callbacks_register
)

# mount the api routes on the app object
app.include_router(policy_router, tags=["Policy Updater"])
Expand Down
46 changes: 46 additions & 0 deletions packages/opal-client/opal_client/data/oauth2_updater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from urllib.parse import parse_qs, urlencode, urlparse

import aiohttp
from aiohttp.client import ClientSession
from opal_client.logger import logger

from .updater import DefaultDataUpdater


class OAuth2DataUpdater(DefaultDataUpdater):
async def _load_policy_data_config(
self, url: str, headers
) -> aiohttp.ClientResponse:
await self._authenticator.authenticate(headers)

async with ClientSession(headers=headers) as session:
response = await session.get(
url, **self._ssl_context_kwargs, allow_redirects=False
)

if response.status == 307:
return await self._load_redirected_policy_data_config(
response.headers['location'], headers
)
else:
return response

async def _load_redirected_policy_data_config(self, url: str, headers):
redirect_url = self.__redirect_url(url)

logger.info(
"Redirecting to data-sources configuration '{source}'", source=redirect_url
)

async with ClientSession(headers=headers) as session:
return await session.get(
redirect_url, **self._ssl_context_kwargs, allow_redirects=False
)

def __redirect_url(self, url: str) -> str:
u = urlparse(url)
query = parse_qs(u.query, keep_blank_values=True)
query.pop("token", None)
u = u._replace(query=urlencode(query, True))

return u.geturl()
Loading