add files

main
Lowlights 2024-03-20 18:25:07 +06:00
parent f0382cc7ee
commit 4de1d3d9ff
15 changed files with 5366 additions and 140 deletions

212
README.md
View File

@ -1,2 +1,214 @@
# Harmony
Давайте добавим раздел о запуске вашего проекта Django в README файл, чтобы новые пользователи и разработчики могли легко начать работу.
---
## Запуск проекта Django
Чтобы запустить проект Django локально, следуйте этим шагам:
### Шаг 1: Клонирование репозитория
```bash
git clone https://git.myterior.kz/Lowlight_Akbota/Harmony
cd djangoproject1
```
### Шаг 2: Установка зависимостей
Убедитесь, что у вас установлен Python и pip. Затем установите зависимости проекта:
```bash
pip install -r requirements.txt
```
Это установит все необходимые библиотеки Python, указанные в файле `requirements.txt`.
### Шаг 3: Настройка базы данных
Проекты Django часто используют базу данных для хранения данных. Выполните миграции, чтобы настроить вашу базу данных:
```bash
python manage.py migrate
```
### Шаг 4: Создание суперпользователя (необязательно)
Чтобы получить доступ к административной панели Django, создайте суперпользователя:
```bash
python manage.py createsuperuser
```
Следуйте инструкциям на экране, чтобы завершить создание.
### Шаг 5: Запуск сервера разработки
Запустите локальный сервер разработки Django:
```bash
python manage.py runserver
```
После запуска сервера, ваше приложение будет доступно по адресу [http://localhost:8000](http://localhost:8000) в вашем веб-браузере.
### Шаг 6: Переход к аутентификации через Keycloak
Чтобы начать процесс аутентификации через Keycloak, перейдите по URL, который вы настроили для входа через Keycloak (например, `/login`), который инициирует перенаправление на страницу входа Keycloak.
---
Добавив этот раздел в ваш README файл, вы предоставите четкие инструкции по запуску и базовой настройке вашего Django проекта. Это облегчит новым пользователям и разработчикам начало работы с вашим проектом и поможет им быстрее ориентироваться в его структуре и функциональности.
Давайте интегрируем Keycloak с Django и добавим соответствующий раздел в README, чтобы описать процесс интеграции и использования. Это будет дополнительный раздел в вашем README файле, посвященный настройке аутентификации через Keycloak.
---
## Интеграция Keycloak с Django
Keycloak предоставляет систему управления идентификацией и доступом, которую можно интегрировать с вашим приложением Django для аутентификации пользователей. Ниже приведены шаги для настройки и интеграции Keycloak с Django.
### Шаг 1: Настройка Keycloak
1. **Создание Realm**: Войдите в административную панель Keycloak и создайте новый realm.
2. **Создание Client**: Внутри созданного realm создайте новый client. Задайте `Client ID` и укажите `Valid Redirect URIs` (например, `http://localhost:8000/*` для разработки). Также, выберите `confidential` в качестве типа доступа и сохраните секретный ключ клиента.
3. **Пользователи**: Создайте пользователей в Keycloak, которые будут использоваться для входа в ваше Django приложение.
### Шаг 2: Настройка Django
Для интеграции Keycloak с Django, вам понадобится установить библиотеку, которая упростит взаимодействие с Keycloak, например, `python-keycloak` или аналогичную.
#### Установка зависимостей
```bash
pip install python-keycloak
```
#### Конфигурация
Добавьте настройки Keycloak в `settings.py` вашего проекта Django:
```python
KEYCLOAK_CONFIG = {
'SERVER_URL': 'http://keycloak-server/auth/',
'REALM': 'your-realm',
'CLIENT_ID': 'your-client-id',
'CLIENT_SECRET': 'your-client-secret',
'CALLBACK_URL': 'http://localhost:8000/callback/',
}
```
### Шаг 3: Реализация Аутентификации
Используйте предоставленные ранее функции для реализации процесса аутентификации:
- `keycloak_login` для инициации процесса входа через Keycloak.
- `keycloak_redirect` для обработки перенаправления от Keycloak после аутентификации пользователя.
### Шаг 4: Маршрутизация
Добавьте URL-маршруты в `urls.py` для обработки входа через Keycloak и обратного перенаправления:
```python
from django.urls import path
from .views import keycloak_login, keycloak_redirect
urlpatterns = [
path('login/', keycloak_login, name='keycloak_login'),
path('callback/', keycloak_redirect, name='keycloak_redirect'),
]
```
### Шаг 5: Запуск
Запустите ваше приложение Django и используйте `/login` для начала процесса аутентификации через Keycloak.
---
Понял, давайте включим в README детали о функциях, которые вы мне предоставили, описывая их использование в контексте интеграции Keycloak с Django. Это поможет создать полное руководство по интеграции.
---
## Использование Keycloak для аутентификации в Django
После настройки Keycloak и Django, вам нужно будет использовать следующие функции для обработки аутентификации:
### Функция `keycloak_login`
Эта функция инициирует процесс аутентификации пользователя, перенаправляя его на страницу входа в Keycloak.
```python
def keycloak_login(request):
# Создаем экземпляр Keycloak OpenID клиента с вашими настройками
keycloak_openid = KeycloakOpenID(
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
realm_name=settings.KEYCLOAK_CONFIG['REALM'],
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
)
# Получаем URL для аутентификации от Keycloak и перенаправляем пользователя
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
auth_url = keycloak_openid.auth_url(redirect_uri=redirect_uri)
return redirect(auth_url)
```
### Функция `keycloak_redirect`
Эта функция обрабатывает перенаправление от Keycloak после аутентификации пользователя, извлекая код авторизации и обменивая его на токен доступа.
```python
@require_http_methods(["GET"])
def keycloak_redirect(request):
# Извлекаем код авторизации из запроса
authorization_code = request.GET.get('code')
if authorization_code:
# Здесь вы можете обменять код на токен и произвести дальнейшие действия
return HttpResponse("Authorization code received.")
else:
return HttpResponse("Authorization code not found.", status=400)
```
### Взаимодействие с Keycloak
Для управления сессиями и пользователями, вы можете использовать функции `get_keycloak_admin_token`, `get_user_sessions`, и `logout_user`. Эти функции позволяют вам программно управлять сессиями пользователя в Keycloak, включая их выход из системы.
#### Получение административного токена
```python
def get_keycloak_admin_token():
# Код для получения административного токена Keycloak
```
#### Получение сессий пользователя
```python
def get_user_sessions(admin_token, user_id):
# Код для получения списка сессий пользователя
```
#### Выход пользователя
```python
def logout_user(admin_token, session_id):
# Код для завершения сессии пользователя
```
### Интеграция с вашим Django приложением
Используйте эти функции в вашем Django приложении для управления процессом аутентификации и сессиями пользователя. Вы можете настроить перенаправления, обработку ошибок и уведомления для пользователя в соответствии с вашими требованиями безопасности и пользовательским опытом.
---
Это руководство по интеграции Keycloak с Django поможет обеспечить безопасную и удобную систему аутентификации в вашем веб-приложении. Включение этих функций и их описаний в ваш README файл улучшит документацию проекта и поможет другим разработчикам понять, как использовать эти возможности в своих проектах.

Binary file not shown.

5049
debug.log

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
{
"realm": "Harmony",
"auth-server-url": "https://auth.myterior.kz",
"ssl-required": "external",
"resource": "admin-cli",
"credentials": {
"secret": "wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ"
},
"confidential-port": 0
}

View File

@ -35,8 +35,7 @@ INSTALLED_APPS = [
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.openid_connect',
'django.contrib.admin',
'django.contrib.auth',
'django_extensions',

View File

@ -1,68 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (anonymous) /CreationDate (D:20240218230455-06'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240218230455-06'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 102
>>
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MntRifICK%KE/ee05&l(FoZ!9neZ[Kb,ht@Ke@a%(R!A~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000778 00000 n
0000000837 00000 n
trailer
<<
/ID
[<58e49d6780c80c154396c8a9030fddee><58e49d6780c80c154396c8a9030fddee>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
1029
%%EOF

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -11,10 +11,6 @@ urlpatterns = [
path('about/', views.about, name='about'),
path('', views.callback, name='product_list'),
path('products/', views.callback, name='product_list'),
path('<slug:category_slug>/', views.callback,
name='product_list_by_category'),
path('<int:id>/<slug:slug>/', views.product_detail,
name='product_detail'),
path('about/', views.about, name='about'),
]
path('<slug:category_slug>/', views.callback,name='product_list_by_category'),
path('<int:id>/<slug:slug>/', views.product_detail,name='product_detail'),
path('about/', views.about, name='about'),]

View File

@ -1,14 +1,13 @@
import requests
from django.shortcuts import render,get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
import logging
from djangoProject1 import settings
from .models import Category, Product
from cart.forms import CartAddProductForm
from jose import jwt
from django.contrib import messages
import jwt
from django.http import JsonResponse
@ -30,6 +29,8 @@ def product_detail(request, id, slug):
def about(request):
return render(request, 'main/about.html')
#Получение access token пользователя
def exchange_code_for_token(authorization_code, redirect_uri):
token_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/token'
client_id = 'admin-cli'
@ -53,6 +54,8 @@ def exchange_code_for_token(authorization_code, redirect_uri):
print("Failed to exchange code: HTTP status", response.status_code)
print("Response body:", response.text)
raise Exception('Failed to exchange authorization code for tokens')
#Через access_token получать данные о пользователе декодировав его в json
def get_user_info(access_token):
userinfo_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/userinfo'
headers = {'Authorization': f'Bearer {access_token}'}
@ -62,7 +65,7 @@ def get_user_info(access_token):
else:
print("Failed to fetch user info: HTTP status", response.status_code)
return None
import jwt
def decode_access_token(access_token):
@ -71,97 +74,129 @@ def decode_access_token(access_token):
def callback(request, category_slug=None):
#Получение кода авторизации из запроса
context = {}
# Получение кода авторизации из запроса.
# где клиент получает код после аутентификации пользователя на сервере.
authorization_code = request.GET.get('code')
if authorization_code:
try:
# Обмен кода на access_token с помощью функции exchange_code_for_token.
# Этот токен необходим для доступа к защищенным ресурсам (например, к информации о пользователе)
# на сервере аутентификации.
access_token = exchange_code_for_token(authorization_code, settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
# Декодирование access_token для получения информации о пользователе.
# Это может включать имя пользователя, email и другие данные, в зависимости от настроек сервера.
user_info = decode_access_token(access_token)
# Сохранение полученной информации о пользователе в сессии.
# Это позволяет использовать данные пользователя в дальнейшем, не обращаясь к серверу аутентификации.
request.session['user_info'] = user_info
# Добавление информации о пользователе в контекст для отображения на странице.
context['userinfo'] = request.session.get('user_info', {})
except Exception as e:
# В случае ошибки (например, проблемы с обменом кода или декодированием токена)
# добавляем информацию об ошибке в контекст.
context['error'] = str(e)
else:
# Если код авторизации не найден в запросе, сообщаем об этом через контекст.
context['error'] = "Authorization code not found."
# Загрузка всех категорий и продуктов для отображения на странице.
# Если указан category_slug, фильтруем продукты по данной категории.
category = None
categories = Category.objects.all()
products = Product.objects.filter(available=True)
if category_slug:
category = get_object_or_404(Category,
slug=category_slug)
category = get_object_or_404(Category, slug=category_slug)
products = products.filter(category=category)
return render(request, 'main/product/list.html',{'category': category,
'categories': categories,
'products': products})
# Рендеринг страницы с продуктами, передавая информацию о категориях, продуктах и контекст с данными пользователя или ошибками.
return render(request, 'main/product/list.html',
{'category': category, 'categories': categories, 'products': products})
from django.shortcuts import render, redirect
def profile(request):
# Проверка метода запроса: если POST, значит пользователь отправил форму для обновления своих данных.
if request.method == 'POST':
# Получение текущей информации о пользователе из сессии.
user_info = request.session.get('user_info', {})
# Обновление информации о пользователе данными из формы.
# Если какое-то поле не было предоставлено, используется текущее значение из сессии.
user_info['given_name'] = request.POST.get('first_name', user_info.get('given_name', ''))
user_info['family_name'] = request.POST.get('last_name', user_info.get('family_name', ''))
user_info['preferred_username'] = request.POST.get('username', user_info.get('preferred_username', ''))
user_info['email'] = request.POST.get('email', user_info.get('email', ''))
# Сохранение обновленной информации о пользователе обратно в сессию.
request.session['user_info'] = user_info
# После обновления информации редиректим пользователя на страницу callback,
# которая, скорее всего, отображает информацию о профиле или подтверждение об обновлении данных.
return render(request, 'main/callback.html')
else:
# Если метод запроса не POST, пользователь просто посещает страницу профиля,
# мы загружаем текущую информацию о пользователе из сессии и передаем ее в контекст шаблона.
context = {'userinfo': request.session.get('user_info', {})}
# Отображаем страницу профиля пользователя, передавая информацию о пользователе в контекст.
return render(request, 'main/callback.html', context)
# Настройте логирование
# логирование
logger = logging.getLogger(__name__)
def get_keycloak_admin_token():
# Формируем данные для запроса токена
data = {
'client_id': settings.KEYCLOAK_CONFIG['CLIENT_ID'],
'client_secret': settings.KEYCLOAK_CONFIG['CLIENT_SECRET'],
'grant_type': 'client_credentials',
'client_id': settings.KEYCLOAK_CONFIG['CLIENT_ID'], # ID клиента
'client_secret': settings.KEYCLOAK_CONFIG['CLIENT_SECRET'], # Секрет клиента
'grant_type': 'client_credentials', # Тип авторизации
}
# Отправка запроса для получения токена администратора Keycloak
response = requests.post(
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/realms/{settings.KEYCLOAK_CONFIG['REALM']}/protocol/openid-connect/token",
data=data
)
# Возвращаем токен доступа из ответа
return response.json()['access_token']
def get_user_sessions(admin_token, user_id):
# Установка заголовков с токеном администратора для авторизации запроса
headers = {'Authorization': f'Bearer {admin_token}'}
# Запрос на получение списка сессий пользователя в Keycloak
response = requests.get(
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/users/{user_id}/sessions",
headers=headers
)
# Проверка успешности запроса и возвращение результата
if response.status_code == 200:
return response.json()
else:
logger.error(f"Error fetching user sessions: {response.status_code}")
return []
def logout_user(admin_token, session_id):
headers = {
'Authorization': f'Bearer {admin_token}',
}
# Установка заголовков с токеном администратора для авторизации запроса
headers = {'Authorization': f'Bearer {admin_token}'}
# Отправка запроса на удаление сессии пользователя по ID сессии
response = requests.delete(
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/sessions/{session_id}",
headers=headers
)
# Проверка успешности запроса на удаление
return response.status_code == 204
def get_user_id_from_session(request):
# Получение информации о пользователе из сессии
user_info = request.session.get('user_info', {})
user_id = user_info.get('sub')
user_id = user_info.get('sub') # 'sub' обычно используется как идентификатор пользователя
# Проверка валидности формата ID пользователя
if isinstance(user_id, str):
return user_id
else:
@ -169,40 +204,55 @@ def get_user_id_from_session(request):
return None
def admin_logout_user(request, category_slug=None):
# Получение ID пользователя из сессии текущего запроса.
user_id = get_user_id_from_session(request)
# Получение административного токена для обращения к Keycloak API.
admin_token = get_keycloak_admin_token()
if not admin_token:
# Логгирование ошибки, если не удалось получить токен администратора.
logger.error("Can't obtain admin token.")
# Отображение сообщения об ошибке пользователю.
messages.error(request, "Can't obtain admin token for logout.")
# Перенаправление на страницу выхода/авторизации в случае ошибки.
return redirect(settings.KEYCLOAK_CONFIG['LOGOUT_REDIRECT_URL'])
# Получение списка активных сессий пользователя через Keycloak API.
sessions = get_user_sessions(admin_token, user_id)
if sessions is None or not sessions:
# Логгирование, если сессии не найдены.
logger.error("Can't find user sessions.")
# Уведомление пользователя о невозможности найти активные сессии.
messages.error(request, "No active sessions found for user.")
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
return redirect(redirect_uri)
# Перенаправление на callback URL.
return redirect(settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
# Флаг, указывающий на успешный выход из всех сессий.
all_sessions_logged_out = True
for session in sessions:
# Попытка выхода из каждой сессии через Keycloak API.
if not logout_user(admin_token, session['id']):
# Если выход из какой-либо сессии не удался, флаг устанавливается в False.
all_sessions_logged_out = False
logger.error(f"Failed to log out session {session['id']}")
if all_sessions_logged_out:
# Логгирование успешного выхода из всех сессий.
logger.info("User successfully logged out from all Keycloak sessions.")
# Уведомление пользователя о успешном выходе.
messages.success(request, "Successfully logged out.")
else:
# Уведомление пользователя, если выход был выполнен не из всех сессий.
messages.warning(request, "Partial logout. Some sessions may still be active.")
# Очистка сессии Django после выхода пользователя.
request.session.flush()
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
return redirect(redirect_uri)
# Перенаправление пользователя на callback URL после выхода.
return redirect(settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
from django.http import JsonResponse
def check_user_authenticated(request):

View File

View File

@ -1,16 +1,14 @@
import logging
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods
from django.shortcuts import redirect
from django.conf import settings
from keycloak import KeycloakOpenID
from urllib.parse import urlencode
from django.contrib.auth import logout
logger = logging.getLogger(__name__)
def keycloak_login(request):
def keycloak_login(request):
# Инициализация клиента Keycloak с конфигурационными данными из настроек Django.
keycloak_openid = KeycloakOpenID(
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
@ -18,29 +16,31 @@ def keycloak_login(request):
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
)
# Установка URL для перенаправления пользователя после аутентификации в Keycloak.
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
# Получение URL для аутентификации пользователя через Keycloak.
auth_url = keycloak_openid.auth_url(redirect_uri=redirect_uri)
# Логгирование успешной обработки запроса.
logger.info('Запрос успешно обработан')
# Перенаправление пользователя на страницу аутентификации Keycloak.
return redirect(auth_url)
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods
import logging
@require_http_methods(["GET"])
def keycloak_redirect(request):
# Extract the authorization code from the query parameters
# Извлечение кода авторизации из параметров запроса.
authorization_code = request.GET.get('code')
if authorization_code:
# Proceed with the token exchange process here
# Если код авторизации получен, продолжается процесс обмена токенами.
print(authorization_code)
logger.info(' 2 Запрос успешно обработан')
return HttpResponse("Authorization code received.")
else:
# Если код авторизации отсутствует, возвращается ошибка.
return HttpResponse("Authorization code not found.", status=400)