add files
parent
f0382cc7ee
commit
4de1d3d9ff
212
README.md
212
README.md
|
@ -1,2 +1,214 @@
|
||||||
# Harmony
|
# 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 файл улучшит документацию проекта и поможет другим разработчикам понять, как использовать эти возможности в своих проектах.
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'allauth',
|
'allauth',
|
||||||
'allauth.account',
|
'allauth.account',
|
||||||
'allauth.socialaccount',
|
|
||||||
'allauth.socialaccount.providers.openid_connect',
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
|
|
|
@ -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 |
Binary file not shown.
Binary file not shown.
10
main/urls.py
10
main/urls.py
|
@ -11,10 +11,6 @@ urlpatterns = [
|
||||||
path('about/', views.about, name='about'),
|
path('about/', views.about, name='about'),
|
||||||
path('', views.callback, name='product_list'),
|
path('', views.callback, name='product_list'),
|
||||||
path('products/', views.callback, name='product_list'),
|
path('products/', views.callback, name='product_list'),
|
||||||
path('<slug:category_slug>/', views.callback,
|
path('<slug:category_slug>/', views.callback,name='product_list_by_category'),
|
||||||
name='product_list_by_category'),
|
path('<int:id>/<slug:slug>/', views.product_detail,name='product_detail'),
|
||||||
path('<int:id>/<slug:slug>/', views.product_detail,
|
path('about/', views.about, name='about'),]
|
||||||
name='product_detail'),
|
|
||||||
path('about/', views.about, name='about'),
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
122
main/views.py
122
main/views.py
|
@ -1,14 +1,13 @@
|
||||||
import requests
|
import requests
|
||||||
from django.shortcuts import render,get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
|
||||||
import logging
|
import logging
|
||||||
from djangoProject1 import settings
|
from djangoProject1 import settings
|
||||||
from .models import Category, Product
|
from .models import Category, Product
|
||||||
from cart.forms import CartAddProductForm
|
from cart.forms import CartAddProductForm
|
||||||
from jose import jwt
|
from jose import jwt
|
||||||
from django.contrib import messages
|
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):
|
def about(request):
|
||||||
return render(request, 'main/about.html')
|
return render(request, 'main/about.html')
|
||||||
|
|
||||||
|
|
||||||
|
#Получение access token пользователя
|
||||||
def exchange_code_for_token(authorization_code, redirect_uri):
|
def exchange_code_for_token(authorization_code, redirect_uri):
|
||||||
token_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/token'
|
token_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/token'
|
||||||
client_id = 'admin-cli'
|
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("Failed to exchange code: HTTP status", response.status_code)
|
||||||
print("Response body:", response.text)
|
print("Response body:", response.text)
|
||||||
raise Exception('Failed to exchange authorization code for tokens')
|
raise Exception('Failed to exchange authorization code for tokens')
|
||||||
|
|
||||||
|
#Через access_token получать данные о пользователе декодировав его в json
|
||||||
def get_user_info(access_token):
|
def get_user_info(access_token):
|
||||||
userinfo_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/userinfo'
|
userinfo_endpoint = 'https://auth.myterior.kz/realms/Harmony/protocol/openid-connect/userinfo'
|
||||||
headers = {'Authorization': f'Bearer {access_token}'}
|
headers = {'Authorization': f'Bearer {access_token}'}
|
||||||
|
@ -62,7 +65,7 @@ def get_user_info(access_token):
|
||||||
else:
|
else:
|
||||||
print("Failed to fetch user info: HTTP status", response.status_code)
|
print("Failed to fetch user info: HTTP status", response.status_code)
|
||||||
return None
|
return None
|
||||||
import jwt
|
|
||||||
|
|
||||||
def decode_access_token(access_token):
|
def decode_access_token(access_token):
|
||||||
|
|
||||||
|
@ -70,98 +73,130 @@ def decode_access_token(access_token):
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def callback(request,category_slug=None):
|
def callback(request, category_slug=None):
|
||||||
#Получение кода авторизации из запроса
|
|
||||||
context = {}
|
context = {}
|
||||||
|
# Получение кода авторизации из запроса.
|
||||||
|
# где клиент получает код после аутентификации пользователя на сервере.
|
||||||
authorization_code = request.GET.get('code')
|
authorization_code = request.GET.get('code')
|
||||||
if authorization_code:
|
if authorization_code:
|
||||||
try:
|
try:
|
||||||
|
# Обмен кода на access_token с помощью функции exchange_code_for_token.
|
||||||
|
# Этот токен необходим для доступа к защищенным ресурсам (например, к информации о пользователе)
|
||||||
|
# на сервере аутентификации.
|
||||||
access_token = exchange_code_for_token(authorization_code, settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
|
access_token = exchange_code_for_token(authorization_code, settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
|
||||||
|
# Декодирование access_token для получения информации о пользователе.
|
||||||
|
# Это может включать имя пользователя, email и другие данные, в зависимости от настроек сервера.
|
||||||
user_info = decode_access_token(access_token)
|
user_info = decode_access_token(access_token)
|
||||||
|
# Сохранение полученной информации о пользователе в сессии.
|
||||||
|
# Это позволяет использовать данные пользователя в дальнейшем, не обращаясь к серверу аутентификации.
|
||||||
request.session['user_info'] = user_info
|
request.session['user_info'] = user_info
|
||||||
|
|
||||||
|
# Добавление информации о пользователе в контекст для отображения на странице.
|
||||||
context['userinfo'] = request.session.get('user_info', {})
|
context['userinfo'] = request.session.get('user_info', {})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
# В случае ошибки (например, проблемы с обменом кода или декодированием токена)
|
||||||
|
# добавляем информацию об ошибке в контекст.
|
||||||
context['error'] = str(e)
|
context['error'] = str(e)
|
||||||
else:
|
else:
|
||||||
|
# Если код авторизации не найден в запросе, сообщаем об этом через контекст.
|
||||||
context['error'] = "Authorization code not found."
|
context['error'] = "Authorization code not found."
|
||||||
|
|
||||||
|
# Загрузка всех категорий и продуктов для отображения на странице.
|
||||||
|
# Если указан category_slug, фильтруем продукты по данной категории.
|
||||||
category = None
|
category = None
|
||||||
categories = Category.objects.all()
|
categories = Category.objects.all()
|
||||||
products = Product.objects.filter(available=True)
|
products = Product.objects.filter(available=True)
|
||||||
if category_slug:
|
if category_slug:
|
||||||
category = get_object_or_404(Category,
|
category = get_object_or_404(Category, slug=category_slug)
|
||||||
slug=category_slug)
|
|
||||||
products = products.filter(category=category)
|
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
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
def profile(request):
|
def profile(request):
|
||||||
|
# Проверка метода запроса: если POST, значит пользователь отправил форму для обновления своих данных.
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# Получение текущей информации о пользователе из сессии.
|
||||||
user_info = request.session.get('user_info', {})
|
user_info = request.session.get('user_info', {})
|
||||||
|
|
||||||
|
# Обновление информации о пользователе данными из формы.
|
||||||
|
# Если какое-то поле не было предоставлено, используется текущее значение из сессии.
|
||||||
user_info['given_name'] = request.POST.get('first_name', user_info.get('given_name', ''))
|
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['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['preferred_username'] = request.POST.get('username', user_info.get('preferred_username', ''))
|
||||||
user_info['email'] = request.POST.get('email', user_info.get('email', ''))
|
user_info['email'] = request.POST.get('email', user_info.get('email', ''))
|
||||||
|
|
||||||
|
# Сохранение обновленной информации о пользователе обратно в сессию.
|
||||||
request.session['user_info'] = user_info
|
request.session['user_info'] = user_info
|
||||||
|
|
||||||
|
# После обновления информации редиректим пользователя на страницу callback,
|
||||||
|
# которая, скорее всего, отображает информацию о профиле или подтверждение об обновлении данных.
|
||||||
return render(request, 'main/callback.html')
|
return render(request, 'main/callback.html')
|
||||||
else:
|
else:
|
||||||
|
# Если метод запроса не POST, пользователь просто посещает страницу профиля,
|
||||||
|
# мы загружаем текущую информацию о пользователе из сессии и передаем ее в контекст шаблона.
|
||||||
context = {'userinfo': request.session.get('user_info', {})}
|
context = {'userinfo': request.session.get('user_info', {})}
|
||||||
|
|
||||||
|
# Отображаем страницу профиля пользователя, передавая информацию о пользователе в контекст.
|
||||||
return render(request, 'main/callback.html', context)
|
return render(request, 'main/callback.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Настройте логирование
|
# логирование
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def get_keycloak_admin_token():
|
def get_keycloak_admin_token():
|
||||||
|
# Формируем данные для запроса токена
|
||||||
data = {
|
data = {
|
||||||
'client_id': settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
'client_id': settings.KEYCLOAK_CONFIG['CLIENT_ID'], # ID клиента
|
||||||
'client_secret': settings.KEYCLOAK_CONFIG['CLIENT_SECRET'],
|
'client_secret': settings.KEYCLOAK_CONFIG['CLIENT_SECRET'], # Секрет клиента
|
||||||
'grant_type': 'client_credentials',
|
'grant_type': 'client_credentials', # Тип авторизации
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Отправка запроса для получения токена администратора Keycloak
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/realms/{settings.KEYCLOAK_CONFIG['REALM']}/protocol/openid-connect/token",
|
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/realms/{settings.KEYCLOAK_CONFIG['REALM']}/protocol/openid-connect/token",
|
||||||
data=data
|
data=data
|
||||||
)
|
)
|
||||||
return response.json()['access_token']
|
# Возвращаем токен доступа из ответа
|
||||||
|
return response.json()['access_token']
|
||||||
|
|
||||||
|
|
||||||
def get_user_sessions(admin_token, user_id):
|
def get_user_sessions(admin_token, user_id):
|
||||||
|
# Установка заголовков с токеном администратора для авторизации запроса
|
||||||
headers = {'Authorization': f'Bearer {admin_token}'}
|
headers = {'Authorization': f'Bearer {admin_token}'}
|
||||||
|
# Запрос на получение списка сессий пользователя в Keycloak
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/users/{user_id}/sessions",
|
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/users/{user_id}/sessions",
|
||||||
headers=headers
|
headers=headers
|
||||||
)
|
)
|
||||||
|
# Проверка успешности запроса и возвращение результата
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return response.json()
|
return response.json()
|
||||||
else:
|
else:
|
||||||
logger.error(f"Error fetching user sessions: {response.status_code}")
|
logger.error(f"Error fetching user sessions: {response.status_code}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def logout_user(admin_token, session_id):
|
||||||
def logout_user(admin_token,session_id):
|
# Установка заголовков с токеном администратора для авторизации запроса
|
||||||
headers = {
|
headers = {'Authorization': f'Bearer {admin_token}'}
|
||||||
'Authorization': f'Bearer {admin_token}',
|
# Отправка запроса на удаление сессии пользователя по ID сессии
|
||||||
}
|
|
||||||
response = requests.delete(
|
response = requests.delete(
|
||||||
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/sessions/{session_id}",
|
f"{settings.KEYCLOAK_CONFIG['SERVER_URL']}/admin/realms/{settings.KEYCLOAK_CONFIG['REALM']}/sessions/{session_id}",
|
||||||
headers=headers
|
headers=headers
|
||||||
)
|
)
|
||||||
|
# Проверка успешности запроса на удаление
|
||||||
return response.status_code == 204
|
return response.status_code == 204
|
||||||
|
|
||||||
def get_user_id_from_session(request):
|
def get_user_id_from_session(request):
|
||||||
|
# Получение информации о пользователе из сессии
|
||||||
user_info = request.session.get('user_info', {})
|
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):
|
if isinstance(user_id, str):
|
||||||
return user_id
|
return user_id
|
||||||
else:
|
else:
|
||||||
|
@ -169,40 +204,55 @@ def get_user_id_from_session(request):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def admin_logout_user(request,category_slug = None):
|
|
||||||
|
def admin_logout_user(request, category_slug=None):
|
||||||
|
# Получение ID пользователя из сессии текущего запроса.
|
||||||
user_id = get_user_id_from_session(request)
|
user_id = get_user_id_from_session(request)
|
||||||
|
|
||||||
|
# Получение административного токена для обращения к Keycloak API.
|
||||||
admin_token = get_keycloak_admin_token()
|
admin_token = get_keycloak_admin_token()
|
||||||
if not admin_token:
|
if not admin_token:
|
||||||
|
# Логгирование ошибки, если не удалось получить токен администратора.
|
||||||
logger.error("Can't obtain admin token.")
|
logger.error("Can't obtain admin token.")
|
||||||
|
# Отображение сообщения об ошибке пользователю.
|
||||||
messages.error(request, "Can't obtain admin token for logout.")
|
messages.error(request, "Can't obtain admin token for logout.")
|
||||||
|
# Перенаправление на страницу выхода/авторизации в случае ошибки.
|
||||||
return redirect(settings.KEYCLOAK_CONFIG['LOGOUT_REDIRECT_URL'])
|
return redirect(settings.KEYCLOAK_CONFIG['LOGOUT_REDIRECT_URL'])
|
||||||
|
|
||||||
|
# Получение списка активных сессий пользователя через Keycloak API.
|
||||||
sessions = get_user_sessions(admin_token, user_id)
|
sessions = get_user_sessions(admin_token, user_id)
|
||||||
if sessions is None or not sessions:
|
if sessions is None or not sessions:
|
||||||
|
# Логгирование, если сессии не найдены.
|
||||||
logger.error("Can't find user sessions.")
|
logger.error("Can't find user sessions.")
|
||||||
|
# Уведомление пользователя о невозможности найти активные сессии.
|
||||||
messages.error(request, "No active sessions found for user.")
|
messages.error(request, "No active sessions found for user.")
|
||||||
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
|
# Перенаправление на callback URL.
|
||||||
return redirect(redirect_uri)
|
return redirect(settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
|
||||||
|
|
||||||
|
# Флаг, указывающий на успешный выход из всех сессий.
|
||||||
all_sessions_logged_out = True
|
all_sessions_logged_out = True
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
|
# Попытка выхода из каждой сессии через Keycloak API.
|
||||||
if not logout_user(admin_token, session['id']):
|
if not logout_user(admin_token, session['id']):
|
||||||
|
# Если выход из какой-либо сессии не удался, флаг устанавливается в False.
|
||||||
all_sessions_logged_out = False
|
all_sessions_logged_out = False
|
||||||
logger.error(f"Failed to log out session {session['id']}")
|
logger.error(f"Failed to log out session {session['id']}")
|
||||||
|
|
||||||
if all_sessions_logged_out:
|
if all_sessions_logged_out:
|
||||||
|
# Логгирование успешного выхода из всех сессий.
|
||||||
logger.info("User successfully logged out from all Keycloak sessions.")
|
logger.info("User successfully logged out from all Keycloak sessions.")
|
||||||
|
# Уведомление пользователя о успешном выходе.
|
||||||
messages.success(request, "Successfully logged out.")
|
messages.success(request, "Successfully logged out.")
|
||||||
else:
|
else:
|
||||||
|
# Уведомление пользователя, если выход был выполнен не из всех сессий.
|
||||||
messages.warning(request, "Partial logout. Some sessions may still be active.")
|
messages.warning(request, "Partial logout. Some sessions may still be active.")
|
||||||
|
|
||||||
|
# Очистка сессии Django после выхода пользователя.
|
||||||
request.session.flush()
|
request.session.flush()
|
||||||
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
|
# Перенаправление пользователя на callback URL после выхода.
|
||||||
return redirect(redirect_uri)
|
return redirect(settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
|
||||||
|
|
||||||
|
|
||||||
from django.http import JsonResponse
|
|
||||||
|
|
||||||
|
|
||||||
def check_user_authenticated(request):
|
def check_user_authenticated(request):
|
||||||
|
|
Binary file not shown.
|
@ -1,16 +1,14 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from keycloak import KeycloakOpenID
|
from keycloak import KeycloakOpenID
|
||||||
from urllib.parse import urlencode
|
|
||||||
from django.contrib.auth import logout
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
def keycloak_login(request):
|
|
||||||
|
|
||||||
|
|
||||||
|
def keycloak_login(request):
|
||||||
|
# Инициализация клиента Keycloak с конфигурационными данными из настроек Django.
|
||||||
keycloak_openid = KeycloakOpenID(
|
keycloak_openid = KeycloakOpenID(
|
||||||
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
|
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
|
||||||
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
|
||||||
|
@ -18,29 +16,31 @@ def keycloak_login(request):
|
||||||
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
|
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Установка URL для перенаправления пользователя после аутентификации в Keycloak.
|
||||||
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
|
redirect_uri = settings.KEYCLOAK_CONFIG['CALLBACK_URL']
|
||||||
|
# Получение URL для аутентификации пользователя через Keycloak.
|
||||||
auth_url = keycloak_openid.auth_url(redirect_uri=redirect_uri)
|
auth_url = keycloak_openid.auth_url(redirect_uri=redirect_uri)
|
||||||
|
# Логгирование успешной обработки запроса.
|
||||||
logger.info('Запрос успешно обработан')
|
logger.info('Запрос успешно обработан')
|
||||||
|
# Перенаправление пользователя на страницу аутентификации Keycloak.
|
||||||
return redirect(auth_url)
|
return redirect(auth_url)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.views.decorators.http import require_http_methods
|
|
||||||
|
|
||||||
import logging
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def keycloak_redirect(request):
|
def keycloak_redirect(request):
|
||||||
|
# Извлечение кода авторизации из параметров запроса.
|
||||||
# Extract the authorization code from the query parameters
|
|
||||||
authorization_code = request.GET.get('code')
|
authorization_code = request.GET.get('code')
|
||||||
|
|
||||||
if authorization_code:
|
if authorization_code:
|
||||||
# Proceed with the token exchange process here
|
# Если код авторизации получен, продолжается процесс обмена токенами.
|
||||||
print(authorization_code)
|
print(authorization_code)
|
||||||
logger.info(' 2 Запрос успешно обработан')
|
logger.info(' 2 Запрос успешно обработан')
|
||||||
return HttpResponse("Authorization code received.")
|
return HttpResponse("Authorization code received.")
|
||||||
else:
|
else:
|
||||||
return HttpResponse("Authorization code not found.", status=400)
|
# Если код авторизации отсутствует, возвращается ошибка.
|
||||||
|
return HttpResponse("Authorization code not found.", status=400)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue