From 75980190d79c094be9a4671e6cf90c8a57edf654 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Thu, 5 Dec 2024 18:25:52 +0300 Subject: [PATCH 1/2] Update services.py --- .../services.py | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/back/taiga_contrib_access_token_auth/services.py b/back/taiga_contrib_access_token_auth/services.py index cb331b4..e0f899c 100644 --- a/back/taiga_contrib_access_token_auth/services.py +++ b/back/taiga_contrib_access_token_auth/services.py @@ -1,5 +1,6 @@ import os import logging +import requests from django.db import transaction as tx from django.apps import apps from django.conf import settings @@ -15,7 +16,68 @@ FILTER_GROUPS = settings.FILTER_GROUPS PROJECTS = settings.PROJECTS DEFAULT_ROLE = settings.DEFAULT_ROLE -ADMIN_GROUP = settings.ADMIN_GROUP # Добавляем переменную окружения для группы администраторов +ADMIN_GROUP = settings.ADMIN_GROUP +OIDC_ISSUER_URL = settings.OIDC_ISSUER_URL +OIDC_CLIENT_ID = settings.OIDC_CLIENT_ID +OIDC_CLIENT_SECRET = settings.OIDC_CLIENT_SECRET +OIDC_TOKEN_ENDPOINT = f"{OIDC_ISSUER_URL}/token" +OIDC_USERINFO_ENDPOINT = f"{OIDC_ISSUER_URL}/userinfo" + +def get_oidc_configuration(): + """ + Получает конфигурацию OIDC сервера. + """ + discovery_doc_url = f"{OIDC_ISSUER_URL}/.well-known/openid-configuration" + try: + r = requests.get(discovery_doc_url, timeout=2) + config = r.json() + except Exception as e: + raise ConnectorBaseException(f"Could not get OpenID configuration from well known URL: {str(e)}") + + if 'issuer' not in config: + error = config.get('error') or config.get('message') or config + raise ConnectorBaseException(f"OpenID Connect issuer response invalid: {error}") + + if config['issuer'] != OIDC_ISSUER_URL: + raise ConnectorBaseException("Issuer Claim does not match Issuer URL used to retrieve OpenID configuration") + + return config + +def exchange_code_for_tokens(code, redirect_uri): + """ + Обменивает код авторизации на токены доступа и идентификации. + """ + data = { + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': redirect_uri, + 'client_id': OIDC_CLIENT_ID, + 'client_secret': OIDC_CLIENT_SECRET, + } + try: + r = requests.post(OIDC_TOKEN_ENDPOINT, data=data) + token = r.json() + except Exception as e: + raise ConnectorBaseException(f"Error exchanging code for tokens: {str(e)}") + + if 'error' in token: + error_text = token.get('error_description') or token['error'] + raise ConnectorBaseException(error_text) + + return token + +def get_user_info_from_token(access_token): + """ + Получает информацию о пользователе с использованием токена доступа. + """ + headers = {'Authorization': f"Bearer {access_token}"} + try: + r = requests.get(OIDC_USERINFO_ENDPOINT, headers=headers) + user_info = r.json() + except Exception as e: + raise ConnectorBaseException(f"Error fetching user info: {str(e)}") + + return user_info def determine_role_and_project(groups): """ @@ -59,7 +121,7 @@ def access_token_register( auth_data_model = apps.get_model("users", "AuthData") user_model = apps.get_model("users", "User") membership_model = apps.get_model("projects", "Membership") - role_model = apps.get_model("users", "Role") # Исправлено: указываем правильное приложение + role_model = apps.get_model("users", "Role") project_model = apps.get_model("projects", "Project") try: @@ -114,8 +176,15 @@ def access_token_register( def access_token_login_func(request): try: - access_token = request.POST['access_token'] - user_info = get_user_info(access_token) + code = request.GET.get('code') + redirect_uri = request.build_absolute_uri(request.path) + + # Обмен кода авторизации на токены доступа и идентификации + token = exchange_code_for_tokens(code, redirect_uri) + access_token = token['access_token'] + + # Получение информации о пользователе с использованием токена доступа + user_info = get_user_info_from_token(access_token) groups = user_info.get('groups', []) user = access_token_register( From 09964552cbb114d2987060c951a83db98e460cf0 Mon Sep 17 00:00:00 2001 From: Aleksey Dymchenko Date: Thu, 5 Dec 2024 18:51:02 +0300 Subject: [PATCH 2/2] Merge pull request #2 from untitledds/untitledds-patch-1 Update services.py --- .../services.py | 77 ++++++++++++++++++- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/back/taiga_contrib_access_token_auth/services.py b/back/taiga_contrib_access_token_auth/services.py index cb331b4..e0f899c 100644 --- a/back/taiga_contrib_access_token_auth/services.py +++ b/back/taiga_contrib_access_token_auth/services.py @@ -1,5 +1,6 @@ import os import logging +import requests from django.db import transaction as tx from django.apps import apps from django.conf import settings @@ -15,7 +16,68 @@ FILTER_GROUPS = settings.FILTER_GROUPS PROJECTS = settings.PROJECTS DEFAULT_ROLE = settings.DEFAULT_ROLE -ADMIN_GROUP = settings.ADMIN_GROUP # Добавляем переменную окружения для группы администраторов +ADMIN_GROUP = settings.ADMIN_GROUP +OIDC_ISSUER_URL = settings.OIDC_ISSUER_URL +OIDC_CLIENT_ID = settings.OIDC_CLIENT_ID +OIDC_CLIENT_SECRET = settings.OIDC_CLIENT_SECRET +OIDC_TOKEN_ENDPOINT = f"{OIDC_ISSUER_URL}/token" +OIDC_USERINFO_ENDPOINT = f"{OIDC_ISSUER_URL}/userinfo" + +def get_oidc_configuration(): + """ + Получает конфигурацию OIDC сервера. + """ + discovery_doc_url = f"{OIDC_ISSUER_URL}/.well-known/openid-configuration" + try: + r = requests.get(discovery_doc_url, timeout=2) + config = r.json() + except Exception as e: + raise ConnectorBaseException(f"Could not get OpenID configuration from well known URL: {str(e)}") + + if 'issuer' not in config: + error = config.get('error') or config.get('message') or config + raise ConnectorBaseException(f"OpenID Connect issuer response invalid: {error}") + + if config['issuer'] != OIDC_ISSUER_URL: + raise ConnectorBaseException("Issuer Claim does not match Issuer URL used to retrieve OpenID configuration") + + return config + +def exchange_code_for_tokens(code, redirect_uri): + """ + Обменивает код авторизации на токены доступа и идентификации. + """ + data = { + 'grant_type': 'authorization_code', + 'code': code, + 'redirect_uri': redirect_uri, + 'client_id': OIDC_CLIENT_ID, + 'client_secret': OIDC_CLIENT_SECRET, + } + try: + r = requests.post(OIDC_TOKEN_ENDPOINT, data=data) + token = r.json() + except Exception as e: + raise ConnectorBaseException(f"Error exchanging code for tokens: {str(e)}") + + if 'error' in token: + error_text = token.get('error_description') or token['error'] + raise ConnectorBaseException(error_text) + + return token + +def get_user_info_from_token(access_token): + """ + Получает информацию о пользователе с использованием токена доступа. + """ + headers = {'Authorization': f"Bearer {access_token}"} + try: + r = requests.get(OIDC_USERINFO_ENDPOINT, headers=headers) + user_info = r.json() + except Exception as e: + raise ConnectorBaseException(f"Error fetching user info: {str(e)}") + + return user_info def determine_role_and_project(groups): """ @@ -59,7 +121,7 @@ def access_token_register( auth_data_model = apps.get_model("users", "AuthData") user_model = apps.get_model("users", "User") membership_model = apps.get_model("projects", "Membership") - role_model = apps.get_model("users", "Role") # Исправлено: указываем правильное приложение + role_model = apps.get_model("users", "Role") project_model = apps.get_model("projects", "Project") try: @@ -114,8 +176,15 @@ def access_token_register( def access_token_login_func(request): try: - access_token = request.POST['access_token'] - user_info = get_user_info(access_token) + code = request.GET.get('code') + redirect_uri = request.build_absolute_uri(request.path) + + # Обмен кода авторизации на токены доступа и идентификации + token = exchange_code_for_tokens(code, redirect_uri) + access_token = token['access_token'] + + # Получение информации о пользователе с использованием токена доступа + user_info = get_user_info_from_token(access_token) groups = user_info.get('groups', []) user = access_token_register(