From 6d687cabc690fb0723fee15411562905461e5444 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 10:13:18 +1000 Subject: [PATCH 01/14] add error display for loggedin page --- .../src/components/UI/FloatingBox/index.jsx | 27 +++++++++++ .../src/pages/LoggedIn/index.jsx | 37 +++++++++++--- .../src/pages/LoginPage/index.jsx | 48 +++++-------------- 3 files changed, 69 insertions(+), 43 deletions(-) create mode 100644 pairent_frontend_react/src/components/UI/FloatingBox/index.jsx diff --git a/pairent_frontend_react/src/components/UI/FloatingBox/index.jsx b/pairent_frontend_react/src/components/UI/FloatingBox/index.jsx new file mode 100644 index 0000000..c70df60 --- /dev/null +++ b/pairent_frontend_react/src/components/UI/FloatingBox/index.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import { styled } from "styled-components"; + +const FloatingBox = styled.div` + position: fixed; + top: 45%; + left: 50%; + transform: translate(-50%, -50%); + border: 1px solid #c2c4c2; + border-radius: 12px; + padding: 24px 36px; + background: white; + box-shadow: 0 2px 1px #00000010; + + text-align: center; + + & hr { + margin-bottom: 24px; + height: 0px; + border: 0; + border-bottom: 1px solid #c2c4c2; + box-shadow: 0 2px 1px #c2c4c280; + } + +`; + +export default FloatingBox; \ No newline at end of file diff --git a/pairent_frontend_react/src/pages/LoggedIn/index.jsx b/pairent_frontend_react/src/pages/LoggedIn/index.jsx index 39780b8..ceffe06 100644 --- a/pairent_frontend_react/src/pages/LoggedIn/index.jsx +++ b/pairent_frontend_react/src/pages/LoggedIn/index.jsx @@ -2,6 +2,7 @@ import React from "react"; import { styled } from "styled-components"; import { HashLoader } from "react-spinners"; import { SigninResponse, SigninState } from 'oidc-client-ts'; +import FloatingBox from "../../components/UI/FloatingBox"; const CenterContainer = styled.div` position: relative; @@ -18,27 +19,49 @@ const CenterContainer = styled.div` } `; +const ErrorText = styled.p` + font-family: monospace; + margin: 10px 0; + width: 780px; +`; + export default class LoggedIn extends React.Component { constructor(props) { super(props); this.response = new SigninResponse(new URL(window.location.href).searchParams); this.signin_state = SigninState.fromStorageString(window.localStorage.getItem('oidc_signin_state')); + } async componentDidMount() { console.log(this.response) + console.log(this.signin_state); } render() { return ( -
- -

Подождите пожалуйста

-
- + <> +
+ +

Подождите пожалуйста

+
+ +
+
+
+ + { + this.response.error ? +
+ +

Ошибка авторизации

+

{this.response.error}

+ {this.response.error_description} +
- -
+ : null + } + ) } } \ No newline at end of file diff --git a/pairent_frontend_react/src/pages/LoginPage/index.jsx b/pairent_frontend_react/src/pages/LoginPage/index.jsx index 09f6ce7..bc89e0e 100644 --- a/pairent_frontend_react/src/pages/LoginPage/index.jsx +++ b/pairent_frontend_react/src/pages/LoginPage/index.jsx @@ -1,45 +1,15 @@ import React from 'react'; import { styled } from 'styled-components'; -import BlueButton from '../../components/UI/BlueButton'; import { HashLoader } from 'react-spinners'; +import BlueButton from '../../components/UI/BlueButton'; +import FloatingBox from '../../components/UI/FloatingBox'; import * as OpenID from 'oidc-client-ts'; import constants from '../../constants'; const { OIDCConfig } = constants; -const LoginBox = styled.div` - position: fixed; - top: 45%; - left: 50%; - transform: translate(-50%, -50%); - border: 1px solid #c2c4c2; - border-radius: 12px; - padding: 24px 36px; - background: white; - box-shadow: 0 2px 1px #00000010; - - text-align: center; - - & h2 { - margin: 0px 0; - } - & hr { - margin: 16px 0; - margin-bottom: 24px; - height: 0px; - border: 0; - border-bottom: 1px solid #c2c4c2; - box-shadow: 0 2px 1px #c2c4c280; - } - & p { - font-size: 8pt; - color: gray; - margin-top: 16px; - } -`; - const LoginButton = styled(BlueButton)` font-size: 11pt; width: 250px; @@ -60,6 +30,12 @@ const LoginButton = styled(BlueButton)` } `; +const SmallText = styled.p` + font-size: 8pt; + color: gray; + margin-top: 16px; +`; + const VVSULogoSVG = () => { return ; } @@ -92,7 +68,7 @@ export default class LoginPage extends React.Component { render() { return (
- +

Вход


@@ -105,11 +81,11 @@ export default class LoginPage extends React.Component { } -

+ Вход осуществляется только через
Систему Единого Входа ВВГУ -

-
+ +
); } From 14704c36d442f5675cca1b4c51ebf29aa7e28dbd Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 13:24:23 +1000 Subject: [PATCH 02/14] fix router --- pairent_frontend_react/src/router/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pairent_frontend_react/src/router/index.jsx b/pairent_frontend_react/src/router/index.jsx index f974609..86ad7a6 100644 --- a/pairent_frontend_react/src/router/index.jsx +++ b/pairent_frontend_react/src/router/index.jsx @@ -23,7 +23,7 @@ export default Object.freeze({ { path: "/comparisons", component: , exact: true }, { path: "/tinder", component: , exact: true }, { path: "/login", component: , exact: true }, - { path: "/sign-in", component: , exact: true }, + { path: "/sign-in", component: , exact: true }, { path: "/psych_test", component: , exact: true }, ], privateRoutes: [], From 77ab081aafb0ae566b8287e5bec196b54d852411 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 15:15:37 +1000 Subject: [PATCH 03/14] use UserManager instead of client --- pairent_frontend_react/src/API/User.js | 25 ++++++++++++++++- pairent_frontend_react/src/constants.js | 27 +++++-------------- .../src/pages/LoggedIn/index.jsx | 8 +++--- .../src/pages/LoginPage/index.jsx | 7 +++-- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/pairent_frontend_react/src/API/User.js b/pairent_frontend_react/src/API/User.js index 0f029f8..4a24dd8 100644 --- a/pairent_frontend_react/src/API/User.js +++ b/pairent_frontend_react/src/API/User.js @@ -1,17 +1,40 @@ import axios from 'axios'; import constants from '../constants'; -const { API_ROOT } = constants; +const { API_ROOT, api_path } = constants; + +class UserLoginResponse { + /** @type {string} */ + session_key; + + /** @type {string} */ + openid_login; + + /** @type {number} */ + id; +} class User { constructor(data) { this = { ...data, ...this }; } + /** @param {string} id */ static async getById(id) { const data = await axios.post(API_ROOT + '/users/get', { id }); if (data.data['error']) throw new Error(data.data['error']); return new User(data.data); } + + /** @param {import('oidc-client-ts').SigninResponse} response */ + static async login(response) { + if (response.error !== null) { + throw new Error(response.error + ': ' + response.error_description); + return; + } + + const data = await axios.post(api_path('/auth/user/login'), response); + + } } \ No newline at end of file diff --git a/pairent_frontend_react/src/constants.js b/pairent_frontend_react/src/constants.js index 7c5e213..7e07e9a 100644 --- a/pairent_frontend_react/src/constants.js +++ b/pairent_frontend_react/src/constants.js @@ -1,5 +1,8 @@ import { nanoid } from 'nanoid' +/** @returns {string} */ +const api_path = path => API_ROOT + path; + /** * Api root path * @type {string} @@ -7,32 +10,14 @@ import { nanoid } from 'nanoid' const API_ROOT = window.location.protocol + '//127.0.0.1:8000'; // ДЛЯ ПРОДА ПОСТАВИТЬ ЭТО: '//pairent.vvsu.ru' -if (window.localStorage.getItem('oidc_client_key') == undefined) { - window.localStorage.setItem('oidc_client_key', nanoid(32)); -} - -const OIDC_CLIENT_KEY = window.localStorage.getItem('oidc_client_key'); - - /** OpenID Connect Client Config * @type {import('oidc-client-ts').OidcClientSettings} */ const OIDCConfig = { - onSignIn: () => {}, - authority: API_ROOT + '/api/auth/vvsu/', + authority: api_path('/api/auth/vvsu/'), client_id: 'it-hub-client', redirect_uri: 'https://pairent.vvsu.ru/sign-in/', - scope: [ - 'openid', - 'vvsu_IdUser', - 'vvsu_IdEmpl', - 'vvsu_IdStud', - 'vvsu_login', - 'given_name', - 'family_name' - ], - client_secret: OIDC_CLIENT_KEY + scope: 'openid vvsu_IdUser vvsu_IdEmpl vvsu_IdStud vvsu_login given_name family_name' }; - -export default Object.freeze({ API_ROOT, OIDCConfig, OIDC_CLIENT_KEY }); \ No newline at end of file +export default Object.freeze({ API_ROOT, OIDCConfig, api_path }); \ No newline at end of file diff --git a/pairent_frontend_react/src/pages/LoggedIn/index.jsx b/pairent_frontend_react/src/pages/LoggedIn/index.jsx index ceffe06..6f3165d 100644 --- a/pairent_frontend_react/src/pages/LoggedIn/index.jsx +++ b/pairent_frontend_react/src/pages/LoggedIn/index.jsx @@ -1,9 +1,11 @@ import React from "react"; import { styled } from "styled-components"; import { HashLoader } from "react-spinners"; -import { SigninResponse, SigninState } from 'oidc-client-ts'; +import { SigninResponse, SigninState, UserManager } from 'oidc-client-ts'; import FloatingBox from "../../components/UI/FloatingBox"; +import constants from "../../constants"; + const CenterContainer = styled.div` position: relative; top: 50%; @@ -34,8 +36,8 @@ export default class LoggedIn extends React.Component { } async componentDidMount() { - console.log(this.response) - console.log(this.signin_state); + if (this.response.error) return; + } render() { diff --git a/pairent_frontend_react/src/pages/LoginPage/index.jsx b/pairent_frontend_react/src/pages/LoginPage/index.jsx index bc89e0e..40b5ba4 100644 --- a/pairent_frontend_react/src/pages/LoginPage/index.jsx +++ b/pairent_frontend_react/src/pages/LoginPage/index.jsx @@ -58,11 +58,10 @@ export default class LoginPage extends React.Component { OpenID.Log.setLogger(console); OpenID.Log.setLevel(OpenID.Log.DEBUG); - let client = new OpenID.OidcClient(OIDCConfig); + let client = new OpenID.UserManager(OIDCConfig); - const req = await client.createSigninRequest({}); - window.localStorage.setItem('oidc_signin_state', req.state.toStorageString()); - window.location.href = req.url; + client.signinRedirect(); + } render() { From 61f8fd7d68352becc8267ed055a15a2bafe02801 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 16:24:22 +1000 Subject: [PATCH 04/14] add boilerplate login route --- pairent_backend/pairent_app/urls.py | 3 +++ pairent_backend/pairent_app/views.py | 7 ++++++- pairent_backend/pairent_backend/settings.py | 16 +++++++++++++++- pairent_frontend_react/src/API/User.js | 16 ++++++++++++---- .../src/components/Header/index.jsx | 1 + .../src/pages/LoggedIn/index.jsx | 8 ++++---- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/pairent_backend/pairent_app/urls.py b/pairent_backend/pairent_app/urls.py index 61e21df..55a63df 100644 --- a/pairent_backend/pairent_app/urls.py +++ b/pairent_backend/pairent_app/urls.py @@ -12,5 +12,8 @@ urlpatterns = format_suffix_patterns([ path("apartaments/filters/", views.ApartmentFilter.as_view({'post': 'list'})), path("psych_test/add_result/", views.PsychTestAddResultViewSet.as_view({'post': 'create'})), # пример: psych_test/add_result/1/?result=50 result - результат псих теста пользователя path("users/get_compatible", views.CompatibleUsersView.as_view({'post': 'list'})), + + # auth re_path(r'^auth/vvsu/', views.VVSUAuthProxy), + path('auth/user/login', views.UserLogin) ]) diff --git a/pairent_backend/pairent_app/views.py b/pairent_backend/pairent_app/views.py index e773b32..12737d5 100644 --- a/pairent_backend/pairent_app/views.py +++ b/pairent_backend/pairent_app/views.py @@ -2,6 +2,7 @@ from rest_framework import viewsets from rest_framework.response import Response from rest_framework.views import APIView, View from rest_framework.request import Request +from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponseBadRequest, HttpResponse from django.db.models.query import QuerySet @@ -169,4 +170,8 @@ def VVSUAuthProxy(req: Request): resp = HttpResponse(preq.content); resp.headers['Content-Type'] = preq.headers['Content-Type']; - return resp; \ No newline at end of file + return resp; + +def UserLogin(req: Request): + data = req.data + return HttpResponse(data); diff --git a/pairent_backend/pairent_backend/settings.py b/pairent_backend/pairent_backend/settings.py index 9a97b53..c9eef6b 100644 --- a/pairent_backend/pairent_backend/settings.py +++ b/pairent_backend/pairent_backend/settings.py @@ -65,7 +65,7 @@ REST_FRAMEWORK = { } # Настройка отвечающая, что все могут отправлять запрос на бекенд. УБРАТЬ ПРИ ПРОДАКШЕНЕ! -CORS_ORIGIN_ALLOW_ALL = True + ROOT_URLCONF = 'pairent_backend.urls' @@ -146,3 +146,17 @@ STATIC_URL = 'src/' # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# VVSU Auth +OIDC_CONF = { + 'client-id': 'it-hub-client', + 'client-secret': 'U8y@uPVee6Q^*729esHTo4Vd', + 'authority': "https://vvsu.ru/connect" +} + +# CSRF +CORS_ORIGIN_ALLOW_ALL = DEBUG +CSRF_COOKIE_SECURE = not DEBUG +CSRF_COOKIE_HTTPONLY = not DEBUG +CSRF_TRUSTED_ORIGINS = ['http://pairent.vvsu.ru', 'http://localhost'] +CORS_ORIGIN_WHITELIST = ('http://pairent.vvsu.ru', 'http://localhost') \ No newline at end of file diff --git a/pairent_frontend_react/src/API/User.js b/pairent_frontend_react/src/API/User.js index 4a24dd8..a7ad59f 100644 --- a/pairent_frontend_react/src/API/User.js +++ b/pairent_frontend_react/src/API/User.js @@ -16,7 +16,13 @@ class UserLoginResponse { class User { constructor(data) { - this = { ...data, ...this }; + for (const key in data) { + this[key] = data[key]; + } + } + + static restoreFromLocalStorage() { + } /** @param {string} id */ @@ -34,7 +40,9 @@ class User { return; } - const data = await axios.post(api_path('/auth/user/login'), response); - + const data = await axios.post(api_path('/api/auth/user/login'), response); + return data.data; } -} \ No newline at end of file +} + +export { User, UserLoginResponse } \ No newline at end of file diff --git a/pairent_frontend_react/src/components/Header/index.jsx b/pairent_frontend_react/src/components/Header/index.jsx index b4cceb3..ae882f4 100644 --- a/pairent_frontend_react/src/components/Header/index.jsx +++ b/pairent_frontend_react/src/components/Header/index.jsx @@ -6,6 +6,7 @@ import SVGIcon from '../UI/Icon/SVGIcon'; // import './styles/Header.css'; const HeaderElement = styled.header` + min-width: 950px; display: flex; justify-content: space-between; align-items: center; diff --git a/pairent_frontend_react/src/pages/LoggedIn/index.jsx b/pairent_frontend_react/src/pages/LoggedIn/index.jsx index 6f3165d..c17409e 100644 --- a/pairent_frontend_react/src/pages/LoggedIn/index.jsx +++ b/pairent_frontend_react/src/pages/LoggedIn/index.jsx @@ -1,7 +1,8 @@ import React from "react"; import { styled } from "styled-components"; import { HashLoader } from "react-spinners"; -import { SigninResponse, SigninState, UserManager } from 'oidc-client-ts'; +import { SigninResponse } from 'oidc-client-ts'; +import { User } from "../../API/User"; import FloatingBox from "../../components/UI/FloatingBox"; import constants from "../../constants"; @@ -31,13 +32,12 @@ export default class LoggedIn extends React.Component { constructor(props) { super(props); this.response = new SigninResponse(new URL(window.location.href).searchParams); - this.signin_state = SigninState.fromStorageString(window.localStorage.getItem('oidc_signin_state')); } async componentDidMount() { if (this.response.error) return; - + User.login(this.response); } render() { @@ -47,7 +47,7 @@ export default class LoggedIn extends React.Component {

Подождите пожалуйста

- +
From a4987aff819a17f0bca20723d217611da7ac55a1 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 17:42:44 +1000 Subject: [PATCH 05/14] add request to vvsu --- pairent_backend/pairent_app/urls.py | 2 +- pairent_backend/pairent_app/views.py | 38 +++++++++++++++++-- pairent_frontend_react/src/API/User.js | 2 +- .../src/pages/LoggedIn/index.jsx | 2 +- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/pairent_backend/pairent_app/urls.py b/pairent_backend/pairent_app/urls.py index 55a63df..d19ec7f 100644 --- a/pairent_backend/pairent_app/urls.py +++ b/pairent_backend/pairent_app/urls.py @@ -15,5 +15,5 @@ urlpatterns = format_suffix_patterns([ # auth re_path(r'^auth/vvsu/', views.VVSUAuthProxy), - path('auth/user/login', views.UserLogin) + path('auth/user/login', views.UserLogin.as_view()) ]) diff --git a/pairent_backend/pairent_app/views.py b/pairent_backend/pairent_app/views.py index 12737d5..e008efc 100644 --- a/pairent_backend/pairent_app/views.py +++ b/pairent_backend/pairent_app/views.py @@ -4,7 +4,7 @@ from rest_framework.views import APIView, View from rest_framework.request import Request from django.views.decorators.csrf import csrf_exempt -from django.http import HttpResponseBadRequest, HttpResponse +from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse, HttpRequest from django.db.models.query import QuerySet from django.core.validators import validate_email @@ -172,6 +172,36 @@ def VVSUAuthProxy(req: Request): return resp; -def UserLogin(req: Request): - data = req.data - return HttpResponse(data); +class UserLogin(APIView): + # TODO: Remove csrf exempt when index.html is loaded through django + @csrf_exempt + def post(self, req: HttpRequest): + if (req.content_type != 'application/json'): + res = HttpResponse({'error': 'bad content type'}); + res.status_code = 400; + return res; + + data = json.loads(req.body.decode('utf8')); + + # if (not hasattr(data, 'code')): + # res = JsonResponse({'error': 'no code'}); + # res.status_code = 400; + # return res + + cb = requests.post('https://vvsu.ru/connect/oauth2/token', { + 'grant_type': 'authorization_code', + 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/', + 'code': data['code'], + # 'code_verifier': data['code_verifier'], + 'client_id': 'it-hub-client', + 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd' + }, headers={ + 'Origin': 'https://pairent.vvsu.ru', + 'Referer': 'https://pairent.vvsu.ru' + }); + + + resp = HttpResponse(cb.content); + resp.headers['Content-Type'] = cb.headers['Content-Type']; + + return resp; \ No newline at end of file diff --git a/pairent_frontend_react/src/API/User.js b/pairent_frontend_react/src/API/User.js index a7ad59f..472585f 100644 --- a/pairent_frontend_react/src/API/User.js +++ b/pairent_frontend_react/src/API/User.js @@ -41,7 +41,7 @@ class User { } const data = await axios.post(api_path('/api/auth/user/login'), response); - return data.data; + return new User(data.data); } } diff --git a/pairent_frontend_react/src/pages/LoggedIn/index.jsx b/pairent_frontend_react/src/pages/LoggedIn/index.jsx index c17409e..0920d60 100644 --- a/pairent_frontend_react/src/pages/LoggedIn/index.jsx +++ b/pairent_frontend_react/src/pages/LoggedIn/index.jsx @@ -37,7 +37,7 @@ export default class LoggedIn extends React.Component { async componentDidMount() { if (this.response.error) return; - User.login(this.response); + console.log(await User.login(this.response)); } render() { From 7525b2d56b7db71c61b012c1de87f78a8ec9dac2 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 19:35:20 +1000 Subject: [PATCH 06/14] complete server side auth --- pairent_backend/pairent_app/views.py | 20 ++++++++++++------- pairent_backend/requirements.txt | 3 ++- .../src/pages/LoggedIn/index.jsx | 15 ++++++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pairent_backend/pairent_app/views.py b/pairent_backend/pairent_app/views.py index e008efc..d03c8db 100644 --- a/pairent_backend/pairent_app/views.py +++ b/pairent_backend/pairent_app/views.py @@ -16,7 +16,7 @@ from .serializer import (ApartamentListSerializer, PsychTestAddResultSerializer, PublicUserSerializer) -import json, math, random, re, requests +import json, math, random, re, requests, oidc_client, base64, hashlib class ApartamentViewSet(viewsets.ReadOnlyModelViewSet): """Вывод списка квартир или отдельной квартиры""" @@ -176,23 +176,27 @@ class UserLogin(APIView): # TODO: Remove csrf exempt when index.html is loaded through django @csrf_exempt def post(self, req: HttpRequest): + if (req.content_type != 'application/json'): res = HttpResponse({'error': 'bad content type'}); res.status_code = 400; return res; - + data = json.loads(req.body.decode('utf8')); - # if (not hasattr(data, 'code')): - # res = JsonResponse({'error': 'no code'}); - # res.status_code = 400; - # return res + if not ('code' in data and 'code_verifier' in data): + res = JsonResponse({'error': 'no code'}); + res.status_code = 400; + return res + + + print(data); cb = requests.post('https://vvsu.ru/connect/oauth2/token', { 'grant_type': 'authorization_code', 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/', 'code': data['code'], - # 'code_verifier': data['code_verifier'], + 'code_verifier': data['code_verifier'], 'client_id': 'it-hub-client', 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd' }, headers={ @@ -201,6 +205,8 @@ class UserLogin(APIView): }); + + resp = HttpResponse(cb.content); resp.headers['Content-Type'] = cb.headers['Content-Type']; diff --git a/pairent_backend/requirements.txt b/pairent_backend/requirements.txt index fb3ac95..a367ef2 100644 --- a/pairent_backend/requirements.txt +++ b/pairent_backend/requirements.txt @@ -3,4 +3,5 @@ django djangorestframework django-cors-headers Pillow -requests \ No newline at end of file +requests +oic \ No newline at end of file diff --git a/pairent_frontend_react/src/pages/LoggedIn/index.jsx b/pairent_frontend_react/src/pages/LoggedIn/index.jsx index 0920d60..476983b 100644 --- a/pairent_frontend_react/src/pages/LoggedIn/index.jsx +++ b/pairent_frontend_react/src/pages/LoggedIn/index.jsx @@ -1,7 +1,7 @@ import React from "react"; import { styled } from "styled-components"; import { HashLoader } from "react-spinners"; -import { SigninResponse } from 'oidc-client-ts'; +import { SigninResponse, UserManager } from 'oidc-client-ts'; import { User } from "../../API/User"; import FloatingBox from "../../components/UI/FloatingBox"; @@ -37,7 +37,18 @@ export default class LoggedIn extends React.Component { async componentDidMount() { if (this.response.error) return; - console.log(await User.login(this.response)); + + let code_verifier = '?'; + // get code verifier + for (const key in localStorage) { + if (key.startsWith('oidc.')) { + code_verifier = JSON.parse(localStorage[key]).code_verifier; + localStorage.removeItem(key); + break; + } + } + + console.log(await User.login({...this.response, code_verifier})); } render() { From 54c276d972fc94fe29767522babfd629cfb4ebb5 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 20:07:50 +1000 Subject: [PATCH 07/14] add get user route --- pairent_backend/pairent_app/urls.py | 8 ++++++-- pairent_backend/pairent_app/views.py | 27 +++++++++++++++++++++++++- pairent_backend/requirements.txt | 3 +-- pairent_frontend_react/src/API/User.js | 7 +++++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pairent_backend/pairent_app/urls.py b/pairent_backend/pairent_app/urls.py index d19ec7f..a834842 100644 --- a/pairent_backend/pairent_app/urls.py +++ b/pairent_backend/pairent_app/urls.py @@ -10,8 +10,12 @@ urlpatterns = format_suffix_patterns([ path("apartaments/comparison/", views.ApartamentGetManyViewSet.as_view({'get': 'retrieve'})), # пример: apartaments/comparison/?user_id=1 user_id - id пользователя path("apartaments/favorite/", views.ApartamentGetManyViewSet.as_view({'get': 'list'})), # пример: apartaments/favorite/?user_id=1 user_id - id пользователя path("apartaments/filters/", views.ApartmentFilter.as_view({'post': 'list'})), - path("psych_test/add_result/", views.PsychTestAddResultViewSet.as_view({'post': 'create'})), # пример: psych_test/add_result/1/?result=50 result - результат псих теста пользователя - path("users/get_compatible", views.CompatibleUsersView.as_view({'post': 'list'})), + + # user + path("user/get_compatible", views.CompatibleUsersView.as_view({'post': 'list'})), + # пример: psych_test/add_result/1/?result=50 result - результат псих теста пользователя + path("psych_test/add_result/", views.PsychTestAddResultViewSet.as_view({'post': 'create'})), + path('user/get', views.UserGet.as_view()), # auth re_path(r'^auth/vvsu/', views.VVSUAuthProxy), diff --git a/pairent_backend/pairent_app/views.py b/pairent_backend/pairent_app/views.py index d03c8db..227f118 100644 --- a/pairent_backend/pairent_app/views.py +++ b/pairent_backend/pairent_app/views.py @@ -210,4 +210,29 @@ class UserLogin(APIView): resp = HttpResponse(cb.content); resp.headers['Content-Type'] = cb.headers['Content-Type']; - return resp; \ No newline at end of file + return resp; + +class UserGet(APIView): + def get(self, req: HttpRequest): + if not ('id' in req.GET.keys() or 'login' in req.GET.keys()): + res = JsonResponse({'error': 'no id or login'}); + res.status_code = 400; + return res; + + id_type = 'id' if 'id' in req.GET.keys() else 'login'; + id = req.GET.get(id_type); + + if (id_type == 'login'): + if not id.endswith('@vvsu.ru'): + id += '@vvsu.ru'; + id_type = 'openid_addr'; + + user = None; + try: + user = User.objects.get(**{id_type: id}); + except User.DoesNotExist: + res = JsonResponse({'error': 'not found'}); + res.status_code = 404; + return res; + + return JsonResponse(PublicUserSerializer(user).data); \ No newline at end of file diff --git a/pairent_backend/requirements.txt b/pairent_backend/requirements.txt index a367ef2..fb3ac95 100644 --- a/pairent_backend/requirements.txt +++ b/pairent_backend/requirements.txt @@ -3,5 +3,4 @@ django djangorestframework django-cors-headers Pillow -requests -oic \ No newline at end of file +requests \ No newline at end of file diff --git a/pairent_frontend_react/src/API/User.js b/pairent_frontend_react/src/API/User.js index 472585f..4be7f14 100644 --- a/pairent_frontend_react/src/API/User.js +++ b/pairent_frontend_react/src/API/User.js @@ -25,9 +25,12 @@ class User { } - /** @param {string} id */ + /** + * @param {string} id + * @returns {User} + */ static async getById(id) { - const data = await axios.post(API_ROOT + '/users/get', { id }); + const data = await axios.get(API_ROOT + '/api/user/get', { params: { id } }); if (data.data['error']) throw new Error(data.data['error']); return new User(data.data); From d96395754719c053fd242f17adbd3cfd1c045205 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 20:16:02 +1000 Subject: [PATCH 08/14] add openid id field to user model --- pairent_backend/pairent_app/migrations/0005_user.py | 6 ++++-- pairent_backend/pairent_app/models.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pairent_backend/pairent_app/migrations/0005_user.py b/pairent_backend/pairent_app/migrations/0005_user.py index fc86736..1e1e16a 100644 --- a/pairent_backend/pairent_app/migrations/0005_user.py +++ b/pairent_backend/pairent_app/migrations/0005_user.py @@ -19,7 +19,6 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('favorites_apartments', models.CharField(max_length=100, help_text="Избранные квартиры (CSV)")), ('comparison_apartments', models.CharField(max_length=100, help_text="Квартиры для сравнения (CSV)")), - ('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')), ('name', models.CharField(max_length=256, help_text='ФИО Пользователя')), ('date_of_birth', models.DateField(help_text='Дата рождения пользователя')), ('about_me', models.CharField(max_length=1000, help_text='Поле "О Себе"')), @@ -29,7 +28,10 @@ class Migration(migrations.Migration): ('telegram', models.CharField(max_length=1000, help_text='Телеграм пользователя', null=True)), ('discord', models.CharField(max_length=1000, help_text='Дискорд ник пользователя', null=True)), ('city', models.CharField(max_length=1000, help_text='Город пользователя', null=True)), - ('role', models.CharField(max_length=1, help_text='Роль пользователя (s - student, a - admin, m - moderator)', null=False)) + ('role', models.CharField(max_length=1, help_text='Роль пользователя (s - student, a - admin, m - moderator)', null=False)), + + ('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')), + ('openid_id', models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)')) ] ) ] diff --git a/pairent_backend/pairent_app/models.py b/pairent_backend/pairent_app/models.py index 6e7f33b..25eb257 100644 --- a/pairent_backend/pairent_app/models.py +++ b/pairent_backend/pairent_app/models.py @@ -86,7 +86,6 @@ class User(models.Model): favorites_apartments = models.CharField(max_length=100, verbose_name="Избранные квартиры (CSV)") comparison_apartments = models.CharField(max_length=100, verbose_name="Квартиры для сравнения (CSV)") - openid_addr = models.CharField(max_length=1000, null=False, verbose_name='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)') name = models.CharField(max_length=256, verbose_name='ФИО Пользователя') date_of_birth = models.DateField(verbose_name='Дата рождения пользователя') about_me = models.CharField(max_length=1000, verbose_name='Поле "О Себе"') @@ -102,6 +101,9 @@ class User(models.Model): role = models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)', null=False) + openid_addr = models.CharField(max_length=1000, null=False, verbose_name='Адрес Open ID (login@provider.com, для ВВГУ - login@vvsu.ru)') + openid_id = models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)') + class Meta: verbose_name = "Пользователь" verbose_name_plural = "Пользователи" From c93f42dcca4b322a65e516c166c29def55b336b5 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 20:17:15 +1000 Subject: [PATCH 09/14] add photo provider fields --- pairent_backend/pairent_app/migrations/0005_user.py | 3 ++- pairent_backend/pairent_app/models.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pairent_backend/pairent_app/migrations/0005_user.py b/pairent_backend/pairent_app/migrations/0005_user.py index 1e1e16a..a77947c 100644 --- a/pairent_backend/pairent_app/migrations/0005_user.py +++ b/pairent_backend/pairent_app/migrations/0005_user.py @@ -29,7 +29,8 @@ class Migration(migrations.Migration): ('discord', models.CharField(max_length=1000, help_text='Дискорд ник пользователя', null=True)), ('city', models.CharField(max_length=1000, help_text='Город пользователя', null=True)), ('role', models.CharField(max_length=1, help_text='Роль пользователя (s - student, a - admin, m - moderator)', null=False)), - + ('photo_provider', models.CharField(max_length=100, verbose_name='Сервис, из которого загружается фотография пользователя (VVSU, GRAVATAR)')), + ('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')), ('openid_id', models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)')) ] diff --git a/pairent_backend/pairent_app/models.py b/pairent_backend/pairent_app/models.py index 25eb257..924df0b 100644 --- a/pairent_backend/pairent_app/models.py +++ b/pairent_backend/pairent_app/models.py @@ -101,6 +101,8 @@ class User(models.Model): role = models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)', null=False) + photo_provider = models.CharField(max_length=100, verbose_name='Сервис, из которого загружается фотография пользователя (VVSU, GRAVATAR)') + openid_addr = models.CharField(max_length=1000, null=False, verbose_name='Адрес Open ID (login@provider.com, для ВВГУ - login@vvsu.ru)') openid_id = models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)') From b29204ff34d83f3ad4a6adf3cf36cb3126c4b3c6 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 22:53:30 +1000 Subject: [PATCH 10/14] add new fields to faker --- pairent_backend/pairent_app/factories.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pairent_backend/pairent_app/factories.py b/pairent_backend/pairent_app/factories.py index 08479df..db0cd00 100644 --- a/pairent_backend/pairent_app/factories.py +++ b/pairent_backend/pairent_app/factories.py @@ -115,7 +115,6 @@ class UserFactory(factory.django.DjangoModelFactory): favorites_apartments = CSV(1, 100, 1, 16); comparison_apartments = CSV(1, 100, 1, 5); - openid_addr = OpenID_Address(); name = factory.faker.Faker('name'); date_of_birth = Date(1980, 2006); about_me = factory.faker.Faker('sentence'); @@ -127,4 +126,8 @@ class UserFactory(factory.django.DjangoModelFactory): discord = '@uwu' # они поменяли формат city = Random(0,0,0, ['Владивосток', 'Хабаровск', 'Урюпинск', 'Мухосранск', 'Нью-Йорк']) - role = 's' \ No newline at end of file + role = 's' + + openid_addr = OpenID_Address(); + openid_id = factory.faker.Faker('uuid'); + photo_provider = Random(0,0,0, ['VVSU', 'GRAVATAR']); \ No newline at end of file From e73c1622c978c24e130f093146fb6d947db87ef7 Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 22:55:05 +1000 Subject: [PATCH 11/14] add login route, remove ambiguous migrations --- ...er_options_alter_user_about_me_and_more.py | 104 ------------------ ...alter_psychtestanswers_options_and_more.py | 22 ---- pairent_backend/pairent_app/views.py | 92 ++++++++++++---- 3 files changed, 72 insertions(+), 146 deletions(-) delete mode 100644 pairent_backend/pairent_app/migrations/0006_alter_user_options_alter_user_about_me_and_more.py delete mode 100644 pairent_backend/pairent_app/migrations/0007_alter_psychtestanswers_options_and_more.py diff --git a/pairent_backend/pairent_app/migrations/0006_alter_user_options_alter_user_about_me_and_more.py b/pairent_backend/pairent_app/migrations/0006_alter_user_options_alter_user_about_me_and_more.py deleted file mode 100644 index 67bf6f4..0000000 --- a/pairent_backend/pairent_app/migrations/0006_alter_user_options_alter_user_about_me_and_more.py +++ /dev/null @@ -1,104 +0,0 @@ -# Generated by Django 4.2.1 on 2023-05-15 14:41 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('pairent_app', '0005_user'), - ] - - operations = [ - migrations.AlterModelOptions( - name='user', - options={'verbose_name': 'Пользователь', 'verbose_name_plural': 'Пользователи'}, - ), - migrations.AlterField( - model_name='user', - name='about_me', - field=models.CharField(max_length=1000, verbose_name='Поле "О Себе"'), - ), - migrations.AlterField( - model_name='user', - name='city', - field=models.CharField(max_length=1000, null=True, verbose_name='Город пользователя'), - ), - migrations.AlterField( - model_name='user', - name='comparison_apartments', - field=models.CharField(max_length=100, verbose_name='Квартиры для сравнения (CSV)'), - ), - migrations.AlterField( - model_name='user', - name='date_of_birth', - field=models.DateField(verbose_name='Дата рождения пользователя'), - ), - migrations.AlterField( - model_name='user', - name='discord', - field=models.CharField(max_length=1000, null=True, verbose_name='Дискорд ник пользователя'), - ), - migrations.AlterField( - model_name='user', - name='email', - field=models.CharField(max_length=1000, null=True, verbose_name='Почтовый ящик пользователя в формате user@example.com'), - ), - migrations.AlterField( - model_name='user', - name='favorites_apartments', - field=models.CharField(max_length=100, verbose_name='Избранные квартиры (CSV)'), - ), - migrations.AlterField( - model_name='user', - name='gender', - field=models.CharField(max_length=1, verbose_name='Пол пользователя (f,m,n,?)'), - ), - migrations.AlterField( - model_name='user', - name='name', - field=models.CharField(max_length=256, verbose_name='ФИО Пользователя'), - ), - migrations.AlterField( - model_name='user', - name='openid_addr', - field=models.CharField(max_length=1000, verbose_name='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)'), - ), - migrations.AlterField( - model_name='user', - name='phone', - field=models.CharField(max_length=30, null=True, verbose_name='Телефон пользователя в международном формате (+00000000)'), - ), - migrations.AlterField( - model_name='user', - name='role', - field=models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)'), - ), - migrations.AlterField( - model_name='user', - name='telegram', - field=models.CharField(max_length=1000, null=True, verbose_name='Телеграм пользователя'), - ), - migrations.CreateModel( - name='PsychTestAnswers', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('first_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на первый вопрос')), - ('second_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на второй вопрос')), - ('third_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на третий вопрос')), - ('fourth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на четвертый вопрос')), - ('fifth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на пятый вопрос')), - ('sixth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на шестой вопрос')), - ('seventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на седьмой вопрос')), - ('eighth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на восьмой вопрос')), - ('nineth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на девятый вопрос')), - ('tenth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на десятый вопрос')), - ('eleventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на одиннадцатый вопрос')), - ('twelfth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на двенадцатый вопрос')), - ('thirteenth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на тринадцатый вопрос')), - ('users', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pairent_app.user', verbose_name='Пользователь')), - ], - ), - ] diff --git a/pairent_backend/pairent_app/migrations/0007_alter_psychtestanswers_options_and_more.py b/pairent_backend/pairent_app/migrations/0007_alter_psychtestanswers_options_and_more.py deleted file mode 100644 index 167cf7a..0000000 --- a/pairent_backend/pairent_app/migrations/0007_alter_psychtestanswers_options_and_more.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.1 on 2023-05-15 15:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('pairent_app', '0006_alter_user_options_alter_user_about_me_and_more'), - ] - - operations = [ - migrations.AlterModelOptions( - name='psychtestanswers', - options={'verbose_name': 'Ответ на психологический тест', 'verbose_name_plural': 'Ответы на психологический тест'}, - ), - migrations.RenameField( - model_name='psychtestanswers', - old_name='users', - new_name='user', - ), - ] diff --git a/pairent_backend/pairent_app/views.py b/pairent_backend/pairent_app/views.py index 227f118..608b5be 100644 --- a/pairent_backend/pairent_app/views.py +++ b/pairent_backend/pairent_app/views.py @@ -172,11 +172,48 @@ def VVSUAuthProxy(req: Request): return resp; +def regiserUser(oid, provider_id, name, date_of_birth): + user = User( + favorites_apartments='', + comparison_apartments='', + name=name, + date_of_birth=date_of_birth, + about_me='', + gender='?', + phone='+00000', + # email=, + # telegram=, + # discord=, + # city=, + role='s', + # photo_provider=, + openid_addr=oid, + openid_id=provider_id, + ) + +def get_oauth_token(remote, data): + return requests.post(remote + '/oauth2/token', data, + headers={ + 'Origin': 'https://pairent.vvsu.ru', + 'Referer': 'https://pairent.vvsu.ru' + }).json(); + +def get_oauth_data(remote, key): + return requests.get(remote + '/userinfo', headers={ + 'Origin': 'https://pairent.vvsu.ru', + 'Authorization': 'Bearer ' + key, + 'User-Agent': 'curl/8.1' + }).json(); + class UserLogin(APIView): # TODO: Remove csrf exempt when index.html is loaded through django @csrf_exempt def post(self, req: HttpRequest): + if (req.session.has_key('auth_data')): + # TODO: Return user object instead of error + return JsonResponse({'error': 'already authenticated'}) + if (req.content_type != 'application/json'): res = HttpResponse({'error': 'bad content type'}); res.status_code = 400; @@ -187,31 +224,46 @@ class UserLogin(APIView): if not ('code' in data and 'code_verifier' in data): res = JsonResponse({'error': 'no code'}); res.status_code = 400; + return res; + + # auth_data = get_oauth_token('https://vvsu.ru/connect', { + # 'grant_type': 'authorization_code', + # 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/', + # 'code': data['code'], + # 'code_verifier': data['code_verifier'], + # 'client_id': 'it-hub-client', + # 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd' + # }); + + auth_data = {'access_token': '5kHvrjy91LJgJLKitejBBG24c7JiX45tEstKVHRpfHc._WQDwQ2F13aytbGFjlGnjXJeUWcDD1V3om3cRW0IujM', 'expires_in': 3600, 'id_token': 'eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpoeWRyYS5vcGVuaWQuaWQtdG9rZW4iLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiIwIiwiYXRfaGFzaCI6IjRMR1dRekxVaXFodUVTYjU0QWFIM0EiLCJhdWQiOlsiaXQtaHViLWNsaWVudCJdLCJhdXRoX3RpbWUiOjE2ODQyMzc4MDksImNhbGxiYWNrX3VybCI6IiIsImV4cCI6MTY4NDI0MTQ1NSwiZmFtaWx5X25hbWUiOiLQn9GD0YHRgtC-0LLQsNC70L7QsiIsImdpdmVuX25hbWUiOiLQndC40LrQuNGC0LAiLCJpYXQiOjE2ODQyMzc4NTUsImlkIjoiMDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzIiwiaXNzIjoiaHR0cHM6Ly93d3cudnZzdS5ydS9jb25uZWN0LyIsImp0aSI6IjEzMTBhNzcwLWFhZWUtNGExYS1hMTc1LWM3MzY3ZWM0ZjVhNyIsImxvZ2luIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJvcGVuaWQiOiJodHRwczovL29wZW5pZC52dnN1LnJ1L2JsZWtfXyIsInBpY3R1cmUiOiJodHRwczovL3d3dy52dnN1LnJ1L29pc2twL3Bob3RvL3B0aC5hc3A_SUQ9MDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzXHUwMDI2IiwicHJvZmlsZV91cmwiOm51bGwsInJhdCI6MTY4NDIzNzc5Nywic2lkIjoiOTYyYzg0OGYtZThkNS00ZDJjLWEwZmEtYjI5YmU3YjBlODAxIiwic3ViIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJzdXJuYW1lIjoi0J_Rg9GB0YLQvtCy0LDQu9C-0LIiLCJ0aXRsZSI6ItCh0YLRg9C00LXQvdGCIiwidnZzdV9JZEVtcGwiOm51bGwsInZ2c3VfSWRTdHVkIjoiMTk3MDgwIiwidnZzdV9JZFVzZXIiOjE5MDQ4OSwidnZzdV9sb2dpbiI6ImJsZWtfXyJ9.A4BiOxpOqnesSiTGRdcTsC-lGhSABswivpUovD9EOdYmqKW753VlLcXQxfBPcfmq8Fdf7RmVvXTXPXYqkX7AKxQT-yUUm7XtJHCb85g2YfL64cjTP2sFYD6wPIU9nzXbCrsgKqKubY3p16Dn9VyrBCXE9N6jdbuNOFbWMLPLPlp7U5fx2SzVGaBMUONlTf8KiLkcisQoN4c_rPGqdi38gzhLf7WGEiKLOldXH1q-s_kPeObFvcdbsFrrnDPnJtdqBx8SF02wqJsrZlBiB9Hl-d6sSJYLZZWumFhS-qscfwRlTEZKqC-hWF5c9R8CUYewk89JxRvCcKrHZvPMip9j9vJF1_OjkSrC5EkGaprl765FgVPEBJqXj9LjGRkTOYfYUFAAMia_HhjtinQFp6XJ-Rh3JrmIfLAQ7DEUSOldMQ1xUw9GeHo_0sIsnjaM6lVx6M_SiDTWihxNu58DiI8tmvkdw7in95OJRoJZ30EhR3SGYsK3b51qdYK1aieufJHX40bN_S1gc84pisTg58z-zC5kGsjsZNv6gRSTO4oOpZMK1FMjv7HyasSMWEu-J052X4Qxquj4pWglpiGQNt3-E0jZUUjqmZ0-7AYiyEC_3IItBqWrve-LTXRF5faIZB5v3F3urY6Qjgn93m_AoK1oujfNAPk8WOLTv419CuC2fAc', 'scope': 'openid vvsu_IdUser vvsu_IdEmpl vvsu_IdStud vvsu_login given_name family_name', 'token_type': 'bearer'} + + user = None; + new_user = False; + + print(auth_data); + + return JsonResponse(get_oauth_data('https://vvsu.ru/connect', auth_data['access_token'])); + + req.session['auth_data'] = vvsu_data; + + if ('error' in vvsu_data): + res = JsonResponse(vvsu_data); + res.status_code = cb.status_code; return res + vvsu_data['vvsu_login'] += '@vvsu.ru'; + try: + user = User.objects.get(openid_addr=vvsu_data['vvsu_login']); + except User.DoesNotExist: + registerUser(vvsu_data['vvsu_login'], cb.id, f'{cb.given_name} {cb.family_name}'); + user = User.objects.get(openid_addr=vvsu_data['vvsu_login']); + new_user = True; - print(data); - - cb = requests.post('https://vvsu.ru/connect/oauth2/token', { - 'grant_type': 'authorization_code', - 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/', - 'code': data['code'], - 'code_verifier': data['code_verifier'], - 'client_id': 'it-hub-client', - 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd' - }, headers={ - 'Origin': 'https://pairent.vvsu.ru', - 'Referer': 'https://pairent.vvsu.ru' + return JsonResponse({ + 'user_data': user, + 'new_user': new_user }); - - - - resp = HttpResponse(cb.content); - resp.headers['Content-Type'] = cb.headers['Content-Type']; - - return resp; - class UserGet(APIView): def get(self, req: HttpRequest): if not ('id' in req.GET.keys() or 'login' in req.GET.keys()): From 71241e7c63831779c6f0950e84268cf5993b1dde Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 22:57:12 +1000 Subject: [PATCH 12/14] fix row size too large --- pairent_backend/pairent_app/migrations/0005_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pairent_backend/pairent_app/migrations/0005_user.py b/pairent_backend/pairent_app/migrations/0005_user.py index a77947c..f3951ef 100644 --- a/pairent_backend/pairent_app/migrations/0005_user.py +++ b/pairent_backend/pairent_app/migrations/0005_user.py @@ -32,7 +32,7 @@ class Migration(migrations.Migration): ('photo_provider', models.CharField(max_length=100, verbose_name='Сервис, из которого загружается фотография пользователя (VVSU, GRAVATAR)')), ('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')), - ('openid_id', models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)')) + ('openid_id', models.CharField(max_length=5000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)')) ] ) ] From c018b745ff60893d24797a1180ad27d0237a882b Mon Sep 17 00:00:00 2001 From: b1ek Date: Tue, 16 May 2023 23:00:44 +1000 Subject: [PATCH 13/14] fix factory uuid method --- pairent_backend/pairent_app/factories.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pairent_backend/pairent_app/factories.py b/pairent_backend/pairent_app/factories.py index db0cd00..e6d046a 100644 --- a/pairent_backend/pairent_app/factories.py +++ b/pairent_backend/pairent_app/factories.py @@ -1,6 +1,6 @@ import factory import random -import time, datetime +import time, datetime, uuid from django.db import models @@ -8,6 +8,10 @@ from pairent_app.models import Apartament, User factory.Faker.override_default_locale('ru_RU'); +class UUID(factory.declarations.BaseDeclaration): + def evaluate(self, instance, step, extra): + return str(uuid.uuid4()).upper(); + class OpenID_Address(factory.declarations.BaseDeclaration): def evaluate(self, instance, step, extra): return ''.join(random.choices(list('abcdef12345678990'), k=6)) + "@vvsu.ru"; @@ -129,5 +133,5 @@ class UserFactory(factory.django.DjangoModelFactory): role = 's' openid_addr = OpenID_Address(); - openid_id = factory.faker.Faker('uuid'); + openid_id = UUID(); photo_provider = Random(0,0,0, ['VVSU', 'GRAVATAR']); \ No newline at end of file From d7cc1522542f8e4311f3fd2958b2822813a09f3c Mon Sep 17 00:00:00 2001 From: xepaerz Date: Tue, 16 May 2023 23:11:11 +1000 Subject: [PATCH 14/14] add personal cabinet react --- pairent_backend/requirements.txt | 3 +- .../public/images/avatar-test.jpg | Bin 0 -> 102029 bytes .../public/images/icons/camera.svg | 3 + .../src/pages/PersonalCabinet/index.jsx | 360 ++++++++++++++++++ pairent_frontend_react/src/router/index.jsx | 2 + 5 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 pairent_frontend_react/public/images/avatar-test.jpg create mode 100644 pairent_frontend_react/public/images/icons/camera.svg create mode 100644 pairent_frontend_react/src/pages/PersonalCabinet/index.jsx diff --git a/pairent_backend/requirements.txt b/pairent_backend/requirements.txt index fb3ac95..d495564 100644 --- a/pairent_backend/requirements.txt +++ b/pairent_backend/requirements.txt @@ -3,4 +3,5 @@ django djangorestframework django-cors-headers Pillow -requests \ No newline at end of file +requests +oidc-client \ No newline at end of file diff --git a/pairent_frontend_react/public/images/avatar-test.jpg b/pairent_frontend_react/public/images/avatar-test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..88a0230939021b9b8d99cc2f57d35d5b7b2e2bb6 GIT binary patch literal 102029 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 literal 0 HcmV?d00001 diff --git a/pairent_frontend_react/public/images/icons/camera.svg b/pairent_frontend_react/public/images/icons/camera.svg new file mode 100644 index 0000000..e76a820 --- /dev/null +++ b/pairent_frontend_react/public/images/icons/camera.svg @@ -0,0 +1,3 @@ + + + diff --git a/pairent_frontend_react/src/pages/PersonalCabinet/index.jsx b/pairent_frontend_react/src/pages/PersonalCabinet/index.jsx new file mode 100644 index 0000000..02a6c37 --- /dev/null +++ b/pairent_frontend_react/src/pages/PersonalCabinet/index.jsx @@ -0,0 +1,360 @@ +import React, { useEffect, useState }from 'react'; +import styled, { keyframes } from 'styled-components'; +import SVGIcon from "../../components/UI/Icon/SVGIcon"; +import { User } from '../../API/User'; + +import { useFetching } from '../../hooks/useFetching'; + +import { Row, Col, Stack } from 'react-bootstrap'; + +const BackButton = styled.button` + border: 1px solid #c2c4c2; + display: inline-block; + padding: 12px 16px; + border-radius: 14px; + + background: #ffffff; + color: gray; + box-shadow: 0 2px 1px #00000010; + + font-size: 12pt; + float: left; + + & ${SVGIcon} { + transform: translate(-4px, 2px) + } +`; + +const Title = styled.div` + height: 100px; + padding-top: 32px; + z-index: 1; + position: relative; + display: flex; + align-items: center; + top: 0; + + & h2 { + margin-left: 28px; + font-weight: 600; + padding: 0; + display: inline-block; + transform: translateY(4px); + } + + & span { + margin-left: 14px; + font-weight: 500; + font-size: 18px; + line-height: 19px; + } +`; + +const CabinetSection = styled.div` + width: 1270px; + height: 650px; + + background: #FFFFFF; + box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.25); + border-radius: 20px; + margin: 27px auto; +`; + +const CabinetContainer = styled(Row)` + padding: 30px 26px 30px 36px; +`; + +const Avatar = styled.img` + width: 256px; + height: 256px; + + margin: 25px 0px 0px 65px; + + border-radius: 50%; + box-shadow: 0px 0px 12px 2px rgba(0, 0, 0, 0.34); +`; + +const UploadPhoto = styled.button` + display: flex; + align-items: center; + justify-content: center; + + width: 192px; + height: 32px; + + margin: 16px auto 0px; + + font-weight: 700; + font-size: 16px; + line-height: 19px; + color: #FFFFFF; + + background: #007EFF; + border-radius: 12px; +`; + +const WelcomeText = styled.p` + font-weight: 700; + font-size: 20px; + line-height: 24px; +`; + +const MainText = styled.p` + font-weight: 500; + font-size: 14px; + line-height: 24px; +`; + +const InputBlock = styled.input` + border: 1px solid #CCCCCC; + border-radius: 12px; + + width: ${props => props.width}px; + height: ${props => props.height}px; + + padding-left: 10px; + + font-weight: 500; + font-size: 14px; + line-height: 18px; + color: #000000; + + &::placeholder { + font-weight: 500; + font-size: 12px; + line-height: 24px; + color: #CCCCCC; + } +`; + +const TextAreaBlock = styled.textarea` + border: 1px solid #CCCCCC; + border-radius: 12px; + + margin: 16px 0px 0px 55px; + resize: none; + + padding: 8px; + + font-weight: 500; + font-size: 14px; + line-height: 18px; + color: #000000; + + &::placeholder { + font-weight: 500; + font-size: 14px; + line-height: 24px; + color: #CCCCCC; + } +`; + +const CharacterTraitBlock = styled.div` + width: calc(100% + 15px); + height: 28px; + + padding: 0; + + font-size: 14px; + line-height: 28px; + display: inline-block; + align-items: center; + text-align: center; + color: #FFFFFF; + + background: ${props => props.background}; + border-radius: 20px; +`; + +const ButtonCircleChangeTrait = styled.button` + width: 28px; + height: 28px; + + padding: 0px; + + font-weight: 700; + font-size: 20px; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + border-radius: 70%; + + color: #FFFFFF; + background: #D9D9D9; +`; + +const ButtonChangeTrait = styled.button` + margin-top: 14px; + + width: auto; + height: 27px; + + padding: 0; + + font-weight: 500; + font-size: 14px; + line-height: 17px; + + text-align: left; + + color: #007EFF; + background: none; +`; + +const InformationBlock = (props) => { + return ( + <> + + + {props.title} + + + {props.text} + + + + ); +} + +const InformationBlockInput = (props) => { + return ( + <> + + + {props.title} + + + + + + + ); +} + +const TraitsBlock = (props) => { + const list = props.list; + + if (list.length == 0) return ( + <> +

Вы ничего не указали

+ + ); + + return ( + <> + + { + list.map((el, i) => { + return ( + + + {el.text} + + + ); + }) + } + {props.button + ? + <> + + + + + + + + : + <> + Изменить.. + + } + + + ); +} + + +const PersonalCabinet = function () { + + const [user, setUser] = useState() + const userID = 1; + + const [fetchUser, isUserLoading, userError] = useFetching(async (userID) => { + const response = await User.getById(userID); + setUser(response) + console.log(user.name) + }) + + useEffect(() => { + fetchUser(userID) + }, []) + + const TraitsListOne = [{text: 'Честность', color: '#3F51B5'}, {text: 'Аккуратность', color: '#03BCD6'}, {text: 'Музыкальность', color: '#E91D65'}, + {text: 'Общительность', color: '#03A9F4'}, {text: 'Дружелюбность', color: '#8CC34D'}] + + const TraitsListTwo = [{text: 'Честность', color: '#3F51B5'}, {text: 'Аккуратность', color: '#03BCD6'}, {text: 'Музыкальность', color: '#E91D65'}, + {text: 'Общительность', color: '#03A9F4'}, {text: 'Дружелюбность', color: '#8CC34D'}, {text: 'Мудрость', color: '#FF5923'}, + {text: 'Адекватность', color: '#2196F4'}, {text: 'Щедрость', color: '#FFC308'}, {text: 'Вежливость', color: '#9D28B2'}] + + const TraitsListThree = [{text: 'Курит', color: '#94740B'}, {text: 'Равнодушие', color: '#D9B8B0'}, {text: 'Эгоист', color: '#6E3F58'}, {text: 'Лень', color: '#9F6844'}, + {text: 'Лживость', color: '#AD9029'}, {text: 'Диструктивность', color: '#9A150C'}, {text: 'Токсичность', color: '#608426'}] + + return ( + <> + + <BackButton> + <SVGIcon src='/images/icons/left-arrow-light.svg' width={10} height={16}/> + Вернуться назад + </BackButton> + <h2>Личный кабинет<span>/ Мои данные</span></h2> + + + + + + Добрый день, Александр! + Ваш статус - Студент + + + + Загрузить фото + + + + + + + + + + + + + + + + + + + + + + + Ваши личностные характеристики + + Желаемые черты соседа + + Нежелаемые черты соседа + + + + + + + ); +}; + +export default PersonalCabinet; \ No newline at end of file diff --git a/pairent_frontend_react/src/router/index.jsx b/pairent_frontend_react/src/router/index.jsx index 86ad7a6..3101195 100644 --- a/pairent_frontend_react/src/router/index.jsx +++ b/pairent_frontend_react/src/router/index.jsx @@ -6,6 +6,7 @@ import PsychTest from "../pages/PsychTest"; import Tinder from "../pages/Tinder"; import LoginPage from "../pages/LoginPage"; import LoggedIn from "../pages/LoggedIn"; +import PersonalCabinet from "../pages/PersonalCabinet"; // НА ПРОДАШКЕНЕ СДЕЛАТЬ ПРИВАТНЫЕ МАРШРУТЫ // export const privateRoutes = [ @@ -25,6 +26,7 @@ export default Object.freeze({ { path: "/login", component: , exact: true }, { path: "/sign-in", component: , exact: true }, { path: "/psych_test", component: , exact: true }, + { path: "/personal_cabinet", component: , exact: true }, ], privateRoutes: [], });