add redirects on login

This commit is contained in:
b1ek 2023-05-17 01:08:04 +10:00
parent bac279ae9e
commit d35b50ad70
Signed by: blek
GPG Key ID: 14546221E3595D0C
6 changed files with 142 additions and 29 deletions

View File

@ -131,7 +131,7 @@ class PsychTestAnswers(models.Model):
verbose_name = "Ответ на психологический тест" verbose_name = "Ответ на психологический тест"
verbose_name_plural = "Ответы на психологический тест" verbose_name_plural = "Ответы на психологический тест"
class AuthTokens(models.Model): class AuthToken(models.Model):
user = models.BigIntegerField(null=False, verbose_name='ID Пользователя, которому принадлежит токен'); user = models.BigIntegerField(null=False, verbose_name='ID Пользователя, которому принадлежит токен');
key = models.TextField(verbose_name='Ключ API'); key = models.TextField(verbose_name='Ключ API');
expires = models.BigIntegerField(verbose_name='Когда ключ истечет (Unix timestamp)'); expires = models.BigIntegerField(verbose_name='Когда ключ истечет (Unix timestamp)');

View File

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Apartament, User from .models import Apartament, User, AuthToken
class ApartamentListSerializer(serializers.ModelSerializer): class ApartamentListSerializer(serializers.ModelSerializer):
@ -27,3 +27,8 @@ class PublicUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
exclude = ('favorites_apartments', 'comparison_apartments') exclude = ('favorites_apartments', 'comparison_apartments')
class TokenSerializer(serializers.ModelSerializer):
class Meta:
model = AuthToken
fields = '__all__'

View File

@ -10,13 +10,15 @@ from django.db.models.query import QuerySet
from django.core.validators import validate_email from django.core.validators import validate_email
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from .models import Apartament, User, PsychTestAnswers from .models import Apartament, User, PsychTestAnswers, AuthToken
from .serializer import (ApartamentListSerializer, from .serializer import (ApartamentListSerializer,
ApartamentDetailSerializer, ApartamentDetailSerializer,
PsychTestAddResultSerializer, PsychTestAddResultSerializer,
PublicUserSerializer) PublicUserSerializer,
TokenSerializer)
import json, math, random, re, requests, oidc_client, base64, hashlib import json, math, random, re, requests, oidc_client, base64, uuid, time, ipware as iplib
ipware = iplib.IpWare();
class ApartamentViewSet(viewsets.ReadOnlyModelViewSet): class ApartamentViewSet(viewsets.ReadOnlyModelViewSet):
"""Вывод списка квартир или отдельной квартиры""" """Вывод списка квартир или отдельной квартиры"""
@ -185,7 +187,7 @@ def register(oid, provider_id, name):
# discord=, # discord=,
# city=, # city=,
role='s', role='s',
# photo_provider=, photo_provider='VVSU',
openid_addr=oid, openid_addr=oid,
openid_id=provider_id, openid_id=provider_id,
); );
@ -206,7 +208,45 @@ def get_oauth_data(remote, key):
'User-Agent': 'curl/8.1' 'User-Agent': 'curl/8.1'
}).json(); }).json();
def create_auth_token(userid, ip):
try:
token = AuthToken.objects.get(user=userid, ip=ip);
if (verify_auth_token(token.key, token.ip)):
return token;
except AuthToken.DoesNotExist:
0 # ignore
token = AuthToken(
user=userid,
key=str(uuid.uuid4()),
# 2 days
# vvv
expires=time.time() + 60 * 60 * 24 * 2,
ip=ip
);
token.save();
return token;
def verify_auth_token(key, ip):
try:
token = AuthToken.objects.get(key=key);
except AuthToken.DoesNotExist:
return False;
if (token.ip != ip):
token.delete();
return False;
if (token.expires > time.time()):
token.delete();
return False;
return True;
class UserLogin(APIView): class UserLogin(APIView):
# TODO: Remove csrf exempt when index.html is loaded through django # TODO: Remove csrf exempt when index.html is loaded through django
@csrf_exempt @csrf_exempt
def post(self, req: HttpRequest): def post(self, req: HttpRequest):
@ -227,18 +267,14 @@ class UserLogin(APIView):
res.status_code = 400; res.status_code = 400;
return res; return res;
# auth_data = get_oauth_token('https://vvsu.ru/connect', { auth_data = get_oauth_token('https://vvsu.ru/connect', {
# 'grant_type': 'authorization_code', 'grant_type': 'authorization_code',
# 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/', 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/',
# 'code': data['code'], 'code': data['code'],
# 'code_verifier': data['code_verifier'], 'code_verifier': data['code_verifier'],
# 'client_id': 'it-hub-client', 'client_id': 'it-hub-client',
# 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd' 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd'
# }); });
auth_data = {'access_token': 'gcH96CSYQBeiq9te1lpJV4T9mBH4UabT4_m6fJQFQK4.K4GA7sXFtBEM26kDladZjZ8phsI3aRPmqu5oRts4Csg', 'expires_in': 3600, 'id_token': 'eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpoeWRyYS5vcGVuaWQuaWQtdG9rZW4iLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiIwIiwiYXRfaGFzaCI6ImJIZS1pWmlvX2Npa3diOFc3bnBkbEEiLCJhdWQiOlsiaXQtaHViLWNsaWVudCJdLCJhdXRoX3RpbWUiOjE2ODQyNDM0NjUsImNhbGxiYWNrX3VybCI6IiIsImV4cCI6MTY4NDI0NzA3MywiZmFtaWx5X25hbWUiOiLQn9GD0YHRgtC-0LLQsNC70L7QsiIsImdpdmVuX25hbWUiOiLQndC40LrQuNGC0LAiLCJpYXQiOjE2ODQyNDM0NzMsImlkIjoiMDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzIiwiaXNzIjoiaHR0cHM6Ly93d3cudnZzdS5ydS9jb25uZWN0LyIsImp0aSI6IjU5M2FiYTQzLTU4OTQtNGZmNy1iMmU1LTdmOWZkYTZjZjFhZSIsImxvZ2luIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJvcGVuaWQiOiJodHRwczovL29wZW5pZC52dnN1LnJ1L2JsZWtfXyIsInBpY3R1cmUiOiJodHRwczovL3d3dy52dnN1LnJ1L29pc2twL3Bob3RvL3B0aC5hc3A_SUQ9MDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzXHUwMDI2IiwicHJvZmlsZV91cmwiOm51bGwsInJhdCI6MTY4NDI0MzQ1NCwic2lkIjoiMzEwZjU5MWEtZmNjYy00NzY3LTkzMmItYjM3OTQyZmFmMTA1Iiwic3ViIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJzdXJuYW1lIjoi0J_Rg9GB0YLQvtCy0LDQu9C-0LIiLCJ0aXRsZSI6ItCh0YLRg9C00LXQvdGCIiwidnZzdV9JZEVtcGwiOm51bGwsInZ2c3VfSWRTdHVkIjoiMTk3MDgwIiwidnZzdV9JZFVzZXIiOjE5MDQ4OSwidnZzdV9sb2dpbiI6ImJsZWtfXyJ9.mClShf1lzGoKarsshafM6H2_57wrINbLSUjDQrEOAICN0V6TMNmC2zevgjxBbMl3BTIWhGJ37SNViyGvdNjPeG_S32TBr0m_vJEddZbHLzO7U7J2vqYVkiFQl8hziZkvhZUboSCu71aWexvN6rtX5grxIPAZswgGP4Mszg7ueQlhybgDELVg-UG-2OVH01-ynsfoZbaPYN6_8x44FJDUiltFbdx57kD8OEh4CdqEPTl3rL2T1U04cfNY0Ij2ivo9esEyAmuuXQCmwn_YwHO3TQc0S2Bq6DeIWa4gauynxGjPl2tf4fcyz-XOVWGeMNIwXCHvIDB_aHsZromG3UV2gY3ji-RlkEq81mYzFjOwB-LArkJQ68zQZlu5cFKqtWvZOzKqCzDDRUvfiRTu3OexQse_g10EeMi7vSeocGnfETlq5utar05gFGY-DxSaFYNCKzxqqS8V78d5aRFrWcQNbE6CVpKZPbZBBEQ-ItX-wh1FEyL3Uw-MsDztwJu6p_ftwRZLF0lk3ECFlbFt4NzzutFYqwS1s5ZoSZa-ylLY8PsZdr9gj58jBYD8c1foXZ9I_KzC_bYDOyUQfjec5njxGWN3828TvySclHkXMUgQxCM16OmPq8MICk_tfhqOSezcs0JpXIEtHHn0h9HNavZuhMTIaTWErYRIIxEPgtBn8r8', 'scope': 'openid vvsu_IdUser vvsu_IdEmpl vvsu_IdStud vvsu_login given_name family_name', 'token_type': 'bearer'}
# print(auth_data);
if ('error' in auth_data): if ('error' in auth_data):
return JsonResponse(auth_data); return JsonResponse(auth_data);
@ -246,16 +282,13 @@ class UserLogin(APIView):
user = None; user = None;
new_user = False; new_user = False;
# vvsu_data = get_oauth_data('https://vvsu.ru/connect', auth_data['access_token']); vvsu_data = get_oauth_data('https://vvsu.ru/connect', auth_data['access_token']);
vvsu_data = {'acr': '0', 'aud': ['it-hub-client'], 'auth_time': 1684243465, 'callback_url': '', 'family_name': 'Пустовалов', 'given_name': 'Никита', 'iat': 1684243466, 'id': '096C78CD-4943-4D57-BC6D-5CDE12F686E3', 'iss': 'https://www.vvsu.ru/connect/', 'login': 'https://openid.vvsu.ru/blek__', 'openid': 'https://openid.vvsu.ru/blek__', 'picture': 'https://www.vvsu.ru/oiskp/photo/pth.asp?ID=096C78CD-4943-4D57-BC6D-5CDE12F686E3&', 'profile_url': None, 'rat': 1684243454, 'sub': 'https://openid.vvsu.ru/blek__', 'surname': 'Пустовалов', 'title': 'Студент', 'vvsu_IdEmpl': None, 'vvsu_IdStud': '197080', 'vvsu_IdUser': 190489, 'vvsu_login': 'blek__'}
if ('error' in vvsu_data): if ('error' in vvsu_data):
res = JsonResponse(vvsu_data); res = JsonResponse(vvsu_data);
res.status_code = 500; res.status_code = 500;
return res; return res;
req.session['auth_data'] = vvsu_data;
if ('error' in vvsu_data): if ('error' in vvsu_data):
res = JsonResponse(vvsu_data); res = JsonResponse(vvsu_data);
res.status_code = 500; res.status_code = 500;
@ -270,7 +303,8 @@ class UserLogin(APIView):
return JsonResponse({ return JsonResponse({
'user_data': PublicUserSerializer(user).data, 'user_data': PublicUserSerializer(user).data,
'new_user': new_user 'new_user': new_user,
'token': TokenSerializer(create_auth_token(user.id, ipware.get_client_ip(req.META)[0].exploded)).data
}); });
class UserGet(APIView): class UserGet(APIView):

View File

@ -4,4 +4,4 @@ djangorestframework
django-cors-headers django-cors-headers
Pillow Pillow
requests requests
oidc-client python-ipware

View File

@ -14,7 +14,70 @@ class UserLoginResponse {
id; id;
} }
class User { class IAPIObject {
/**
* Local storage key used to save data.
* @type {string}
*/
static storage_key = undefined;
/** @returns {ThisType} */
static restoreFromLocalStorage() {
if (storage_key !== undefined) {
throw Error('This doesn\'t support local storage');
}
if (!window.localStorage.getItem(storage_key))
return false;
return new APIToken(window.localStorage.getItem(storage_key));
}
/**
* Save this object to local storage
* @throws {QuotaExceededError}
* @returns {void}
*/
saveToLocalStorage() {
// static this
sthis = Object.getPrototypeOf(this);
if (sthis.storage_key !== undefined) {
throw Error('This doesn\'t support local storage');
}
window.localStorage.setItem(sthis.storage_key, this);
}
}
class APIToken extends IAPIObject {
static storage_key = 'pairent_api_key';
constructor(data) {
this.user = data.user;
this.key = data.key;
this.expires = data.expires;
this.ip = data.ip;
}
/** @type {number} */
user;
/** @type {string} */
key;
/** A Unix timestamp (when the token will expire)
* @type {number}
*/
expires;
/** @type {string} */
ip;
}
class User extends IAPIObject {
static storage_key = 'pairent_user_data';
constructor(data) { constructor(data) {
for (const key in data) { for (const key in data) {
this[key] = data[key]; this[key] = data[key];
@ -44,7 +107,11 @@ class User {
} }
const data = await axios.post(api_path('/api/auth/user/login'), response); const data = await axios.post(api_path('/api/auth/user/login'), response);
return new User(data.data);
window.localStorage.setItem(APIToken.storage_key, data.data.token);
window.localStorage.setItem(User.storage_key, data.data.user_data);
return data.data;
} }
} }

View File

@ -4,6 +4,7 @@ import { HashLoader } from "react-spinners";
import { SigninResponse, UserManager } from 'oidc-client-ts'; import { SigninResponse, UserManager } from 'oidc-client-ts';
import { User } from "../../API/User"; import { User } from "../../API/User";
import FloatingBox from "../../components/UI/FloatingBox"; import FloatingBox from "../../components/UI/FloatingBox";
import { useNavigate } from "react-router-dom";
import constants from "../../constants"; import constants from "../../constants";
@ -48,7 +49,13 @@ export default class LoggedIn extends React.Component {
} }
} }
console.log(await User.login({...this.response, code_verifier})); const response = await User.login({...this.response, code_verifier});
if (response.new_user) {
// TODO: Make the page
useNavigate('/register');
} else {
useNavigate('/');
}
} }
render() { render() {