Как работает Passport.js / Хабр

Что такое jwt?

JSON Web Token (JWT) — это закодированный в строковой форме JSON-объект. Токены можно воспринимать как замену куки-файлов, имеющую несколько преимуществ перед ними.

Токен состоит из трёх частей. Это — заголовок (header), полезная нагрузка (payload) и подпись (signature). На следующем рисунке показан его внешний вид.

JWT

Данные токена могут быть декодированы на стороне клиента без использования секретного ключа или подписи.

Это может быть полезным для передачи, например метаданных, закодированных внутри токена. Подобные метаданные, могут описывать роль пользователя, его профиль, время действия токена, и так далее. Они могут быть предназначены для использования во фронтенд-приложениях.

Вот как может выглядеть декодированный токен.

Декодированный токен

Почему токены защищены от злоумышленников?

Почитав об использовании JWT, вы можете задаться следующим вопросом: «Если данные JWT могут быть декодированы на стороне клиента — можно ли так обработать токен, чтобы изменить идентификатор пользователя или другие данные?».

Декодирование токена — операция очень простая. Однако нельзя «переделать» этот токен, не имея той подписи, тех секретных данных, которые были использованы при подписывании JWT на сервере.

Именно поэтому так важна защита этих секретных данных.

Наш сервер проверяет подпись в промежуточном ПО isAuth. За проверку отвечает библиотека express-jwt.

Теперь, после того, как мы разобрались с тем, как работает технология JWT, поговорим о некоторых интересных дополнительных возможностях, которые она нам даёт.

Что такое redis?

Redis это опенсорс (лицензии BSD) хранилище структур данных в оперативной памяти, используемое как база данных, кэш и брокер сообщений — redis.io

Мы собираемся хранить информацию о сессии пользователя в Redis, а не в памяти процесса. Таким образом, наше приложение будет намного проще масштабировать.

▍потребитель sso

  1. Подсистема-потребитель SSO не выполняет аутентификацию пользователя, перенаправляя пользователя на сервер SSO.
  2. Эта подсистема получает токен, передаваемый ей сервером SSO.
  3. Она взаимодействует с сервером, проверяя действительность токена.
  4. Она получает JWT и проверяет этот токен с использованием публичного ключа.
  5. Эта подсистема устанавливает локальную сессию.

▍сервер sso

  1. Сервер SSO проверяет данные, вводимые пользователем для входа в систему.
  2. Сервер создаёт глобальную сессию.
  3. Он создаёт токен авторизации.
  4. Токен авторизации отправляется потребителю SSO.
  5. Сервер проверяет действительность токенов, передаваемых ему потребителями SSO.
  6. Сервер отправляет потребителю SSO JWT с информацией о пользователе.

Why create a login system with Node.js as opposed to PHP?

Node.js is a powerful open-source server environment that leverages JavaScript as its core scripting language.

As more people become aware of Node.js, it is becoming increasingly popular in the development of web applications. Therefore, if you plan to develop applications for the future web, I highly suggest you enhance your knowledge with Node.js.

Node.js’s package manager (NPM) already has over 450,000 packages available for you to download. Those numbers alone indicate how fast it’s growing.

If you are familiar with JavaScript, you will enjoy developing applications with Node.js, and will be able to adapt very easily.

3. Setup & File Structure

Follow the below instructions.

A new directory will appear called node_modules, which is populated with all the modules we’ve installed. Don’t delete this directory, otherwise it will break our app.

File structure

— nodelogin
|– login.html
|– login.js
— static
|– style.css

Authentication integration completed

That’s it! In this tutorial, you learned how Passport.js works, how to configure it, and how to integrate it with Node.js and Auth0 to add authentication to web applications. You also learned about security and identity best practices and how an identity platform such as Auth0 lets you delegate to a team of experts the giant responsibility of keeping logins secure.

All that is left is for you to continue building this application as you may like. Feel free to dive deeper into the Auth0 Documentation to learn more about how Auth0 helps you save time on implementing and managing identity. Use the comments below this blog post to ask questions or join the Auth0 Community to connect with other developers like yourself.

Thank you for your time!

I ran into an issue

Configure express-session

🛠️️ Next, open index.js and add the following under the Required External Modules section:

const express =require("express");const path =require("path");const expressSession =require("express-session");const passport =require("passport");const Auth0Strategy =require("passport-auth0");require("dotenv").config();

You are adding imports for express-session, passport, and passport-auth0, which you’ll configure in the next sections.

🛠️️ Between the App Variables and App Configuration sections, create two new sections, Session Configuration and Passport Configuration:

const app =express();const port = process.env.PORT||"8000";

Order matters in Express. Please ensure that you add the sections in the right order.

🛠️️ Under Session Configuration, configure expressSession as follows:

const session ={
  secret: process.env.SESSION_SECRET,
  cookie:{},
  resave:false,
  saveUninitialized:false};if(app.get("env")==="production"){
  session.cookie.secure =true;}

expressSession takes a configuration object, session, that defines what options to enable in a session. Here, you are configuring the following options:

Configure passport with the application settings

In this section, you’ll focus on wiring up Passport.js with your Express app.

🛠️️ With Auth0Strategy already imported, proceed to define this strategy under the Passport Configuration section in index.js:

const strategy =newAuth0Strategy({
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL: process.env.AUTH0_CALLBACK_URL},function(accessToken, refreshToken, extraParams, profile, done){returndone(null, profile);});

Here, the Auth0Strategy method takes your Auth0 credentials and initializes the strategy. It’s essential to understand what the Auth0Strategy is doing for you:

Create custom middleware with express

🛠️️ In index.js, update the App Configuration section to enhance the response object, res, with data from the authentication server. Do this right above the mounting of the authentication router:

File structure

— nodelogin|– login.html|– login.js– static|– style.css

Log into a node.js express app

🛠️️ Head to the browser tab where your application is running and click the login button to test that it is communicating correctly with Auth0 and that you can get authenticated.

Похожее:  «АльфаСтрахование-Жизнь» - Вход в Личный Кабинет по Номеру Полиса Капитал в Плюс | » - Официальный сайт

🛠️️ If you’ve set up everything correctly, the application redirects you to the Universal Login page.

Node.js и express авторизация:

Для начала скачаем все нужные нам компоненты, все их три, это mongoose, для работы с базой данных MongoDB, passport, для создания авторизации, passport-jwt, для проверки токена, и jsonwebtokenну для создания JWT токена, и конечно не забывайте про сам Express, но если вы читаете эту статью, то думаю в вашем проекте он уже работает, и как его установить я не буду показывать, но если вам интересно, то посмотрите статью «Express.js быстрый старт».

Установка нужных компонентов:

После того как вы всё установили, то можете начать разработку.

Теперь перейдите в файл «auth.js», который должен находиться в папке «controller», первым делом импортируем все нужные компоненты в него:

Как видите мы импортировали модель пользователя, и библиотеку для создания JWT токена.

Дальше создадим специальную функцию, которая будет отвечать за создание JWT токена, вот как она будет выглядеть:

Тут мы создали стрелочную функцию, в неё передаём id пользователя и его Email, следующие делаем объект, в котором будем хранить так же id пользователя и его Email, это нужно для того, чтобы когда будем получать этот токен, мы могли идентифицировать пользователя с токеном.

Последние тут мы создаём токен, делаем это с помощью метода jwt.sign(), первым параметром принимает объект который мы сделали выше, вторым слово которая нужно будет использовать потом, чтобы расшифровать токен и получить данные из него, третий это объект с различными параметрами.

После того как мы сделали функцию для создания токена, переходим внутри этого файла в класс authController, который мы ещё создали в статье по регистрации ссылкой выше, внутри класса создаём метод login(), который будет отвечать за вход на сайт.

Вот как он работает:

Первое что вы тут замечаете, это то, что во всём коде мы будем отлавливать его ошибки, и в той части, где мы отлавливаем ошибки, мы берём email и пароль, находим пользователя по его email, и если нет, то отправляем ошибку.

Set up real-world authentication for node.js

This tutorial’s core objective is to teach you how to set up real-world authentication in a Node.js Express app. For that reason, you’ll start by setting up Auth0 to interact with a real authentication server throughout the tutorial. Otherwise, Passport.js gives you a ton of error messages in the terminal, and the app won’t run.

Auth0 is a global leader in Identity-as-a-Service (IDaaS). Its extensible platform seamlessly authenticates and secures more than 2.5 billion logins per month, making it loved by developers and trusted by global enterprises.

The best part of the Auth0 platform is how streamlined it is to get started by following these three easy steps.

Step 1: sign up and create an auth0 application

🛠️️ If you are new to Auth0, sign up for a free Auth0 account here. A free account offers you:

🛠️️ During the sign-up process, you’ll create something called a Tenant, representing the product or service to which you are adding authentication — more on this in a moment.

🛠️️ Once you are signed in, you are welcomed into the Auth0 Dashboard. In the left sidebar menu, click on “Applications”.

Step 3: add auth0 configuration variables to node.js

🛠️️ Under the project directory, create a hidden file called .env to store configuration variables and secrets that your app needs.

touch .env

Make sure to add this file to .gitignore so that it isn’t committed to version control.

🛠️️ Add the following to .env:

AUTH0_CLIENT_ID=AUTH0_DOMAIN=AUTH0_CLIENT_SECRET=

🛠️️ Head back to your Auth0 application Settings tab and populate each property of the .env hidden file with its corresponding Auth0 application value:

Use middleware for authentication

As explained in the “Using middleware” section of the Express docs, an Express application is essentially a series of middleware function calls that execute during the request-response cycle. Each function can modify the request and response objects as needed and then either pass control to the next middleware function or end the request-response cycle.

What you will build

You’ll secure the login portal for a restaurant named WHATABYTE using Passport.js with Auth0:

We tested this tutorial using Node.js v12.16.0 and npm v6.13.

Аутентификация в node.js с использованием passport.js

Перевод книги Node Hero от RisingStack. Переведено с разрешения правообладателей.

Верификация


Подключим и настроим стратегию авторизации.

Вход пользователей в систему

Вот как выглядит схема действий, выполняемых в том случае, когда пользователь пытается войти в систему.

Вход пользователя в систему

Вот что происходит при входе пользователя в систему:

  • Клиент отправляет серверу комбинацию, состоящую из публичного идентификатора и приватного ключа пользователя. Обычно это — адрес электронной почты и пароль.
  • Сервер ищет пользователя в базе данных по адресу электронной почты.
  • Если пользователь существует в базе данных — сервер хэширует отправленный ему пароль и сравнивает то, что получилось, с хэшем пароля, сохранённым в базе данных.
  • Если проверка оказывается успешной — сервер генерирует так называемый токен или маркер аутентификации — JSON Web Token (JWT). 

JWT — это временный ключ. Клиент должен отправлять этот ключ серверу с каждым запросом к аутентифицированной конечной точке.

Генерирование jwt в node.js


Давайте создадим функцию

generateToken

, которая нужна нам для завершения работы над сервисом аутентификации пользователей.

Создавать JWT можно с помощью библиотеки jsonwebtoken. Найти эту библиотеку можно в npm.

Демонстрационное приложение

Для демонстрационных целей создадим приложение, которое умеет только следующее:

Зависимости:

Так же я буду использовать для удобства несколько дополнительных утилит. Без них вполне можно обойтись:

Защита конечных точек и проверка jwt

Теперь клиентскому коду нужно отправлять JWT в каждом запросе к защищённой конечной точке.

Рекомендуется включать JWT в заголовки запросов. Обычно их включают в заголовок Authorization.

Заголовок Authorization

Теперь, на сервере, нужно создать код, представляющий собой промежуточное ПО для маршрутов express. Поместим этот код в файл isAuth.ts:

Как имперсонировать пользователя?

Имперсонация пользователей — это техника, используемая для входа в систему под видом некоего конкретного пользователя без знания его пароля.

Похожее:  КрымЭнерго личный кабинет абонента

Эта возможность весьма полезна для супер-администраторов, разработчиков или сотрудников служб поддержки. Имперсонация позволяет им решать проблемы, которые проявляются только в ходе работы пользователей с системой.

Работать с приложением от имени пользователя можно и не зная его пароля. Для этого достаточно сгенерировать JWT с правильной подписью и с необходимыми метаданными, описывающими пользователя.

Создадим конечную точку, которая может генерировать токены для входа в систему под видом конкретных пользователей. Этой конечной точкой сможет пользоваться только супер-администратор системы.

Для чала нам нужно назначить этому пользователю роль, с которой связан более высокий, чем у других пользователей, уровень привилегий. Это можно сделать множеством различных способов. Например, достаточно просто добавить поле role в сведения о пользователе, хранящиеся в базе данных.

Выглядеть это может так, как показано ниже.

Новое поле в сведениях о пользователе

Как организован вход в систему с использованием sso?

В сердце реализации SSO находится единственный независимый сервер аутентификации, который способен принимать информацию, позволяющую аутентифицировать пользователей. Например — адрес электронной почты, имя пользователя, пароль. Другие системы не дают пользователю прямых механизмов входа в них.

Вот репозиторий с кодом проекта simple-sso, реализацию которого я здесь опишу. Я использую платформу Node.js, но вы можете реализовать то же самое и используя что-то другое. Давайте пошагово разберём действия пользователя, работающего с системой, и механизмы, из которых состоит эта система

Клиентская часть:

Тут я просто объясню что делать на стороне клиента, но я не буду показывать сам код, это как нибудь в другой статье, тут только объяснения.

Для начала полученный токен записываем в localStorage, делаем это примерно так:

После чего при запросе к серверу передаём наш токен, примерно так это должно выглядеть:

Как видите здесь мы в header или в заголовки запроса добавили параметр Authorization, который хранит токен, но перед ним ещё ставим слово Bearer, и после пробела, уже сам токен.

Краткий обзор потребителя sso и сервера sso

Давайте сделаем краткий обзор функционала потребителя SSO и сервера SSO.

Маршруты и опции аутентификации

Создайте новую папку с названием “routes” и файлом “auth.js” внутри.

В этом файле мы использем функцию getTokenFromHeaders для получения JWT токена, который будет отправлен с клиентской стороны в заголовки запроса. Мы также создаём auth-объект c optional и required свойствами. Мы используем это позже в наших маршрутах.

В той же папке “routes” создайте файл “index.js”:

Теперь нам нужна папка “api” внутри папки “routes” с ещё одним файлом “index.js” внутри.

Ваша структура должна выглядеть так

Модель пользователя


Для начала, я думаю, можно создать модель пользователя:

Настройка паспорта

Создайте новую папку с названием “config” и файлом “passport.js” внутри:

Организация централизованного выхода из системы

Аналогично тому, как была реализована технология единого входа, можно реализовать и «технологию единого выхода». Здесь нужно лишь учитывать следующие соображения:

  1. Если существует локальная сессия — обязательно существует и глобальная сессия.
  2. Если существует глобальная сессия, это необязательно означает существование локальной сессии.
  3. Если локальная сессия уничтожается — должна быть уничтожена и глобальная сессия.

Подключение passport к express

Окей, с этим разобрались, теперь нужно подключить Passport к Express:

// Middlewares, которые должны быть определены до passport:
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'SECRET' }));
 
// Passport:
app.use(passport.initialize());
app.use(passport.session());

Проверка авторизации


Проверку авторизации можно делать с помощью req.isAuthenticated(). Я вынесу проверку в middleware.

exports.mustAuthenticatedMw = function (req, res, next){
  req.isAuthenticated()
    ? next()
    : res.redirect('/');
};

И добавлю в routes.

  App.all('private', mustAuthenticatedMw);
  App.all('private/*', mustAuthenticatedMw);

Проверка токена:

Последние что осталось рассмотреть, так это как сделать проверку токена на стороне сервера, для этого первым делом создадим папку «middleware», где будем хранить middleware функции, в неё создаём файл «authJwtMiddleware.js», и вот что в нём пишем:

В начале мы импортируем JwtStrategy и ExtractJwt, первый нам нужен будет для получения пользователя по токену, а второй для проверки что правильно передался токен.

Последние импортируем mongoose, он нужен будет для получения пользователя, следующие создаём настройки получения токена, первый параметр это jwtFromRequest, он обозначает откуда берём токен, и как раз тут и используем ExtractJwt, второй параметр это secretOrKey, это строка для дешифрование наше токена, как вы помните выше, мы использовали для шифрования слово «Hello», это значит что здесь используем его же.

Следующие возвращаем функцию которая будет обрабатывать JWT токен, внутри неё используем passport.use() или middleware функцию, которая принимает в себя создание нового объекта класса JwtStrategy, при создание он принимает наши настройки options и асинхронную callback функцию, которая в свою очередь принимает payload, в которой храниться всё что мы получили из токена, и функцию done, которая нужна для подтверждения, что всё прошло успешно или нет.

Внутри callback функции так же отлавливаем ошибки. В начале мы получаем модуль пользователя, и находим пользователя по id, который мы получили из payload, последние проверяем, получили мы пользователя или нет, если до, то возвращаем его, если нет, то возвращаем false.

дальше переходим в папку «routers», и там заходим в любой файл, где есть путь в котором нам надо проверять токен, для примера я возьму случайный путь, вот что я напишу:

В начале мы импортируем passport, потом при GET запросе мы используем метод passport.authenticate(), в качестве первого параметра он принимает тип аунтификации, у нас это JWT, второй объект с настройкам, но мы используем только один, это session: false, то есть не использование сессий, они нам нам нужны.

Последние что осталось сделать, так это добавить не много кода в «index.js», заходим в него и вот что добавляем:

В начале мы импортируем passport, потом инициализируем его, последние импортируем нашу middleware функцию для проверки  токена и сразу запускаем passport, вот и всё, если вы сделали всё правильно, у вас должно всё работать.

Процесс аутентификации в node.js

Наша цель — реализовать в нашем приложении следующий процесс аутентификации:

  1. Пользователь вводит имя и пароль
  2. Приложение проверяет, являются ли они корректными
  3. Если имя и пароль корректны, приложение отправляет заголовок Set-Cookie, который будет использоваться для аутентификации дальнейших страниц
  4. Когда пользователь посещает страницы в том же домене, ранее установленный cookie будет добавлен ко всем запросам
  5. Аутентификация на закрытых страницах происходит с помощью этого файла cookie
Похожее:  Биллинг новотелеком личный кабинет

Чтобы настроить такую стратегию аутентификации, выполните следующие три действия:

  1. Настройте Express
  2. Настройте Passport
  3. Добавьте защищённые разделы на сайте

Регистрация пользователей в системе

Когда в системе создаётся новый пользователь, его пароль необходимо хэшировать и сохранить в базе данных. Пароль в базе сохраняют вместе с адресом электронной почты и другими сведениями о пользователе (например, среди них может быть профиль пользователя, время регистрации и так далее).

Создаём get-запрос для возвращения текущего зашедшего пользователя

Запрос URL:

Создаём с нуля свой node-сервер

Создаём новую директорию с файлом “app.js” внутри:

Теперь установим nodemon для более лёгкой разработки.

А теперь с помощью этого запустим своё приложение “app.js”.

$ nodemon app.js
Ожидаемый результат после введения команды выше

Создание post-запроса для создания пользователя

Испытываемая форма:

Ответ:

Создание роутера и контроллеров


Настало время настройки роутера. Привяжем запросы к соответствующим контроллерам:

Структура проекта

Вы уже научились структурировать Node.js-проекты в предыдущей главе Node Hero, поэтому давайте использовать эти знания!

Мы собираемся использовать следующую структуру:

Тестирование маршрутов

Я буду использовать Postman для отсылки запросов на наш сервер.

Наш сервер принимает следующую форму:

Технологии, которые мы будем использовать

Прежде чем перейти к написанию кода, давайте рассмотрим новые технологии, которые мы будем использовать в этой главе.

Технология единого входа

Базовый принцип, на котором основана технология единого входа, заключается в том, что пользователь может войти в одну систему проекта, состоящего из множества систем, и оказаться авторизованным и во всех остальных системах без необходимости повторного входа в них. При этом речь идёт и о централизованном выходе из всех систем.

Мы, в учебных целях, собираемся реализовать технологию SSO на платформе Node.js.

Надо отметить, что реализация этой технологии в корпоративных масштабах потребует гораздо больших усилий, чем мы собираемся приложить к разработке нашей учебной системы. Именно поэтому и существуют специализированные SSO-решения, предназначенные для крупномасштабных проектов.

Требования к проекту

Вот требования к проекту, которым мы будем здесь заниматься:

  • Наличие базы данных, в которой будет храниться адрес электронной почты пользователя и его пароль, либо — clientId и clientSecret, либо — нечто вроде комбинации из приватного и публичного ключей.
  • Использование сильного и эффективного криптографического алгоритма для шифрования пароля.


В тот момент, когда я пишу этот материал, я считаю, что лучшим из существующих криптографических алгоритмов является Argon2. Я прошу вас не использовать простые криптографические алгоритмы вроде SHA256, SHA512 или MD5.

Кроме того, предлагаю вам взглянуть на этот замечательный материал, в котором вы можете найти подробности о выборе алгоритма для хэширования паролей.

Учимся работать с аутентификацией в node используя passport.js

Перевод статьи Antonio Erdeljac: Learn how to handle authentication with Node using Passport.js

Фотография Oskar Yildiz на Unsplash

В этой статье вы узнаете, как разработать аутентификацию для вашего Node-сервера используя Passport.js. Эта статья не включает в себя фронт-энд аутентификацию. Используйте её для настройки бэк-энда вашей аутентификации (сгенерировать токен для каждого пользователя и защитить маршруты).

Также помните, что если вы застряли на каком-либо из этапов, вы можете использовать этот GitHub-репозиторий.

Шаг 2

SSO-сервер выясняет то, что пользователь в систему не вошёл, и перенаправляет его на страницу входа в систему:

Шаг 2: настройка passport

Passport — отличный пример библиотеки, использующей плагины. В этом уроке мы добавляем модуль passport-local, который добавляет простую локальную стратегию аутентификации с использованием имён пользователей и паролей.

Шаг 3

Пользователь вводит имя пользователя и пароль, которые отправляются SSO-серверу в запросе на вход в систему.

Страница входа в систему

Шаг 3: добавляем защищённые разделы на сайте

Чтобы добавить защищенные разделы на сайт, мы используем шаблон middleware Express. Для этого сначала создадим middleware для аутентификации:

Шаг 4

SSO-сервер аутентификации проверяет информацию пользователя и создаёт сессию между собой и пользователем. Это — так называемая «глобальная сессия». Тут же создаётся и токен авторизации. Токен представляет собой строку, состоящую из случайных символов.

Шаг 5

SSO-сервер берёт токен авторизации и передаёт его туда, откуда к нему пришёл только что авторизовавшийся пользователь (то есть — передаёт токен потребителю SSO).

Шаг 6

Потребитель SSO получает токен и обращается к серверу SSO для проверки токена. Сервер проверяет токен и возвращает ещё один токен с информацией о пользователе. Этот токен используется потребителем SSO для создания сессии с пользователем. Эта сессия называется локальной.

Вот код ПО промежуточного слоя, используемого в потребителе SSO, построенном на основе Express:

Вывод:

В этой статье вы прочитали как делается на Node.js и express авторизация с использованием JWT токенов, надеюсь вам было полезно и вы сделали авторизацию как надо.

Create express authentication endpoints

🛠️️ In this section, you will create three endpoints that handle the application’s authentication flow:

GET /login

GET /logout

GET /callback

To manage these endpoints better, you will create them within an authentication module and export them through an Express router so that your Express application can use them.

🛠️️ To start, create an auth.js file under the project directory.

touch auth.js

Populate it with this template to define its structure:

🛠️️ Next, add the following under the Required External Modules section to import packages that are needed and load your environmental variables:

const express =require("express");const router = express.Router();const passport =require("passport");const querystring =require("querystring");require("dotenv").config();

Here’s an overview of the new modules you are using:

You’ll soon see how these modules streamline your route controller logic.

🛠️️ The first endpoint you’ll create is the GET/login one. Update the Routes Definitions section of your auth.js file as follows:



router.get("/login",
  passport.authenticate("auth0",{
    scope:"openid email profile"}),(req, res)=>{
    res.redirect("/");});

Итоги

В итоге можно отметить, что существует множество готовых реализаций технологии единого входа, которые можно интегрировать в свою систему. У всех них есть собственные преимущества и недостатки. Разработка подобной системы самостоятельно, с нуля, это — итеративный процесс, в ходе которого нужно анализировать характеристики каждой из систем. Сюда входят способы входа в систему, хранилища пользовательской информации, синхронизация данных и многое другое.

Используются ли в ваших проектах механизмы SSO?

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Добавить комментарий

Ваш адрес email не будет опубликован.

Adblock
detector