-
Notifications
You must be signed in to change notification settings - Fork 3
feat(keycloak authentication) #4162
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
Open
ChrOertlin
wants to merge
72
commits into
master
Choose a base branch
from
feat-keycloak
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
49f1254
chore: remove google auth verification
ChrOertlin b8d6103
Merge branch 'master' into feat-keycloak
ChrOertlin 5d348f7
feat: add python-keycloak dependency
ChrOertlin d6c2eeb
Add keycloak client
ahdamin 2ae86a3
chore: test
ChrOertlin 4f617a4
feat: implement auth service
ChrOertlin ac275da
feat: fix before request
ChrOertlin b2f3397
chore: add more user util
ChrOertlin b613bed
refactor: create user service for user operations
ChrOertlin 43f4a12
feat: register user service in flask app
ChrOertlin fddb478
revert: introduction of authenticated user model
ChrOertlin 9253efd
feat: redirect login button to new flow
ChrOertlin 96cea98
chore: add redirect to appconfig
ChrOertlin 3cffc98
fix: unused import
ChrOertlin 6f8404b
feat: working prototype
ChrOertlin 86b6466
chore: merge master
ChrOertlin 90eaf4b
chore: test pass
ChrOertlin 8f4d75a
chore: remove google from invoices
ChrOertlin 202ca6d
chore: various improvements
ChrOertlin d702276
chore: linting
ChrOertlin 5527ea0
feat: init from config
ChrOertlin d352cc6
Merge branch 'master' into feat-keycloak
ChrOertlin 8de7bb5
chore: merge
ChrOertlin bbb6cbd
Merge branch 'master' into feat-keycloak
ChrOertlin cd0a2ba
Merge branch 'feat-keycloak' of https://github.com/Clinical-Genomics/…
ChrOertlin 4d6995f
chore:changes
ChrOertlin 589ea5d
feat: add user role check
ChrOertlin 729cde3
feat: fix admin view
ChrOertlin 0316ff4
chore: linitng
ChrOertlin 5dbb483
logging
ChrOertlin 924fd4c
chore: extract client from auth service
ChrOertlin 1eee249
fix: auth init
ChrOertlin 39f3f5d
Merge branch 'master' into feat-keycloak
ChrOertlin 6b4233d
chore: simplify flow
ChrOertlin c1e6ef4
rm: token refresh
ChrOertlin 9673d22
chore: cleanup flows
ChrOertlin afea95a
fix: access
ChrOertlin 4f4520f
chore: improve error handling auth
ChrOertlin 031e4ba
chore: error handling
ChrOertlin 54a94fb
Merge branch 'master' into feat-keycloak
ChrOertlin ad0b484
chore: linting
ChrOertlin 22ee0ae
Merge branch 'feat-keycloak' of https://github.com/Clinical-Genomics/…
ChrOertlin a7af4f8
fix: proper token access
ChrOertlin aeddad6
fix: cache user roles for access
ChrOertlin 848f245
chore: linting
ChrOertlin 49c3b82
fix: various fixes
ChrOertlin 689d54a
fix
ChrOertlin d04a279
chore: register client and client config in cg config
ChrOertlin 16763b5
chore: add proper instantiation
ChrOertlin e3c3258
add: general client tests
ChrOertlin f188269
chore: add tests
ChrOertlin 3b539d8
chore: linting
ChrOertlin c671af8
Merge branch 'master' into feat-keycloak
ChrOertlin 029b14f
Merge branch 'master' into feat-keycloak
islean 2be972c
fix: potential XSS with markupsafe.Markup
ahdamin 81f771f
fix: syntax and black formatting
ahdamin f94876c
fix: view_case_sample_link issue
ahdamin 9d852e3
fix: potential XSS issue
ahdamin bab7189
fix: potential XSS issue
ahdamin 3d5b93b
fix: potential XSS issue with Markup
ahdamin 1945ad1
fix: potential XSS issue with Markup
ahdamin d157ead
fix: potential XSS issue with Markup
ahdamin 8a40328
fix: potential XSS issue with Markup
ahdamin a0f633d
fix: potential XSS issue with Markup
ahdamin b7ae920
fix: potential XSS issue with Markup
ahdamin eec1acf
fix: potential XSS issue with Markup
ahdamin 61594b6
fix: potential XSS issue with Markup
ahdamin 5a0ed30
fix: potential XSS issue with Markup
ahdamin f2f9754
fix: potential XSS issue with Markup
ahdamin 12604ca
fix: potential XSS issue with Markup
ahdamin a44cc1c
fix: potential XSS issue with Markup
ahdamin 7277719
fix: potential XSS issue with Markup
ahdamin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,10 @@ | |
import datetime | ||
import logging | ||
from typing import Any | ||
|
||
from google.auth.transport.requests import Request | ||
from google.oauth2 import service_account | ||
|
||
from cg.apps.tb.dto.create_job_request import CreateJobRequest | ||
from cg.apps.tb.dto.summary_response import AnalysisSummary, SummariesResponse | ||
from cg.apps.tb.models import AnalysesResponse, TrailblazerAnalysis | ||
from cg.clients.authentication import keycloak_client | ||
from cg.constants import Workflow | ||
from cg.constants.constants import APIMethods, FileFormat, JobType, WorkflowManager | ||
from cg.constants.priority import TrailblazerPriority | ||
|
@@ -20,7 +17,8 @@ | |
TrailblazerAPIHTTPError, | ||
) | ||
from cg.io.controller import APIRequest, ReadStream | ||
|
||
from cg.services.authentication.models import TokenResponseModel | ||
from cg.clients.authentication.keycloak_client import KeycloakClient | ||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
@@ -43,19 +41,22 @@ class TrailblazerAPI: | |
AnalysisStatus.QC, | ||
] | ||
|
||
def __init__(self, config: dict): | ||
self.service_account = config["trailblazer"]["service_account"] | ||
self.service_account_auth_file = config["trailblazer"]["service_account_auth_file"] | ||
def __init__(self, config: dict, keycloak_client: KeycloakClient): | ||
self.keycloak_client: KeycloakClient = keycloak_client | ||
self.keycloak_backend_user = config["trailblazer"]["keycloak_backend_user"] | ||
self.keycloak_backend_user_password = config["trailblazer"][ | ||
Comment on lines
+45
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
"keycloak_backend_user_password" | ||
] | ||
self.host = config["trailblazer"]["host"] | ||
|
||
@property | ||
def auth_header(self) -> dict: | ||
credentials = service_account.IDTokenCredentials.from_service_account_file( | ||
filename=self.service_account_auth_file, | ||
target_audience="trailblazer", | ||
token = TokenResponseModel( | ||
**self.keycloak_client.get_token_by_user_password( | ||
user_name=self.keycloak_backend_user, password=self.keycloak_backend_user_password | ||
) | ||
) | ||
credentials.refresh(Request()) | ||
return {"Authorization": f"Bearer {credentials.token}"} | ||
return {"Authorization": f"Bearer {token.access_token}"} | ||
|
||
def query_trailblazer( | ||
self, command: str, request_body: dict, method: str = APIMethods.POST | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from keycloak import KeycloakOpenID | ||
from keycloak import KeycloakConnectionError | ||
|
||
|
||
class KeycloakClient: | ||
def __init__(self, server_url, client_id, realm_name, client_secret_key, redirect_uri): | ||
self.server_url = server_url | ||
self.client_id = client_id | ||
self.realm_name = realm_name | ||
self.client_secret_key = client_secret_key | ||
self.redirect_uri = redirect_uri | ||
self._client_instance: KeycloakOpenID | None = None | ||
|
||
def get_client(self) -> KeycloakOpenID: | ||
if self._client_instance is None: | ||
try: | ||
self._client_instance = KeycloakOpenID( | ||
server_url=self.server_url, | ||
client_id=self.client_id, | ||
realm_name=self.realm_name, | ||
client_secret_key=self.client_secret_key, | ||
) | ||
except KeycloakConnectionError as error: | ||
raise KeycloakConnectionError(f"Failed to connect to Keycloak: {error}") | ||
except Exception as error: | ||
raise Exception(f"An error occurred while creating Keycloak client: {error}") | ||
return self._client_instance | ||
|
||
def get_auth_url(self, scope: str = "openid profile email") -> str: | ||
"""Get the authorization URL for user login.""" | ||
client = self.get_client() | ||
return client.auth_url(redirect_uri=self.redirect_uri, scope=scope) | ||
|
||
def logout_user(self, refresh_token: str) -> None: | ||
""" | ||
Logout a user. | ||
Args: | ||
refresh_token: the refresh token stored in the session | ||
""" | ||
client: KeycloakOpenID = self.get_client() | ||
client.logout(refresh_token) | ||
|
||
def get_token_by_authorisation_code(self, code: str) -> dict: | ||
""" | ||
Get a token using the authorisation code. | ||
Args: | ||
code: code retrieved request | ||
""" | ||
client: KeycloakOpenID = self.get_client() | ||
return client.token( | ||
grant_type="authorization_code", code=code, redirect_uri=self.redirect_uri | ||
) | ||
|
||
def get_token_by_user_password(self, user_name: str, password: str) -> dict: | ||
""" | ||
Get a token using a username and password. | ||
Args: | ||
code: code retrieved request | ||
""" | ||
client: KeycloakOpenID = self.get_client() | ||
return client.token(grant_type="password", username=user_name, password=pa) | ||
|
||
def get_user_info(self, access_token: str) -> dict: | ||
"""Get the user info for a provided access token. | ||
|
||
Args: | ||
access_token: access token given by keycloak | ||
|
||
Returns: | ||
dict: with the user information | ||
""" | ||
client: KeycloakOpenID = self.get_client() | ||
return client.userinfo(access_token) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
from cg.apps.scout.scoutapi import ScoutAPI | ||
from cg.apps.tb import TrailblazerAPI | ||
from cg.clients.arnold.api import ArnoldAPIClient | ||
from cg.clients.authentication.keycloak_client import KeycloakClient | ||
from cg.clients.chanjo2.client import Chanjo2APIClient | ||
from cg.clients.janus.api import JanusAPIClient | ||
from cg.constants.observations import LoqusdbInstance | ||
|
@@ -125,8 +126,8 @@ class ClientConfig(BaseModel): | |
|
||
|
||
class TrailblazerConfig(BaseModel): | ||
service_account: str | ||
service_account_auth_file: str | ||
keycloak_backend_user: str | ||
keycloak_backend_user_password: str | ||
Comment on lines
+129
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove and use client_secret_key instead |
||
host: str | ||
|
||
|
||
|
@@ -387,6 +388,14 @@ class PostProcessingServices(BaseModel): | |
model_config = ConfigDict(arbitrary_types_allowed=True) | ||
|
||
|
||
class KeyCloakConfig(BaseModel): | ||
server_url: str | ||
client_id: str | ||
realm_name: str | ||
client_secret_key: str | ||
redirect_uri: str | ||
|
||
|
||
class CGConfig(BaseModel): | ||
data_input: DataInput | None = None | ||
database: str | ||
|
@@ -456,6 +465,8 @@ class CGConfig(BaseModel): | |
tar: CommonAppConfig | None = None | ||
trailblazer: TrailblazerConfig = None | ||
trailblazer_api_: TrailblazerAPI = None | ||
keycloak: KeyCloakConfig = None | ||
keycloak_client_: KeycloakClient | None = None | ||
|
||
# Meta APIs that will use the apps from CGConfig | ||
balsamic: BalsamicConfig | None = None | ||
|
@@ -737,7 +748,7 @@ def trailblazer_api(self) -> TrailblazerAPI: | |
api = self.__dict__.get("trailblazer_api_") | ||
if api is None: | ||
LOG.debug("Instantiating trailblazer api") | ||
api = TrailblazerAPI(config=self.dict()) | ||
api = TrailblazerAPI(config=self.dict(), keycloak_client=self.keycloak_client) | ||
self.trailblazer_api_ = api | ||
return api | ||
|
||
|
@@ -792,3 +803,18 @@ def delivery_service_factory(self) -> DeliveryServiceFactory: | |
) | ||
self.delivery_service_factory_ = factory | ||
return factory | ||
|
||
@property | ||
def keycloak_client(self) -> KeycloakClient: | ||
client = self.keycloak_client_ | ||
if client is None: | ||
LOG.debug("Instantiating keycloak client") | ||
client = KeycloakClient( | ||
server_url=self.keycloak.server_url, | ||
client_id=self.keycloak.client_id, | ||
client_secret_key=self.keycloak.client_secret_key, | ||
realm_name=self.keycloak.realm_name, | ||
redirect_uri=self.keycloak.redirect_uri, | ||
) | ||
self.keycloak_client_ = client | ||
return client |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this part needs to be tested on hasta-stage with the cli commands.