Некогда копать и искать - готов оплатить пример

Аватар пользователя bmikl

Всем добрый день!
Хочу научиться, но искать и глубоко копать нет возможностей и времени.
Мне иногда проще заплатить, чтобы научили.

Нужен пример одностраничного приложения на Vue.js и бэкэнда на Drupal - система тикетов.
(только функционал)

Кратко ТЗ
-доступ только для авторизованных пользователей, неавторизованные - на страницу логина.
-разные роли могут создавать ноды(тикеты), различные по типам (в зависимости от роли).
-юзеры могут просматривать/редактировать тикеты , авторами которых являются.

Честно говоря нужен каркас - идея? Кто-нидь за разумные деньги добавит разума? :-)

Модули и темы:
Тип материала:
Версия Drupal:
0 Спасибо

Комментарии

Аватар пользователя gun_dose
gun_dose 1 неделя назад

Я умею)) но времени тоже нет. Если что, могу вкратце рассказать.

0 Спасибо
Аватар пользователя bmikl
bmikl 1 неделя назад

Будь другом, покажи-расскажи :-)
Достаточно примера апп типа "Hello, Username" - для авторизованного
и форма логина для неавторизованного и поясни как друпал проверяет токен?

0 Спасибо
Аватар пользователя fairrandir
fairrandir 1 неделя назад

Нужен пост!

0 Спасибо
Аватар пользователя Semantics
Semantics 1 неделя назад

Готов дать 500р в копилку

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад 2

Буду писать понемногу, т.к. времени особо нет. Итак, начнём:

Есть два способа авторизации: сверка токенов или куки. Для реализации первого способа подойдёт модуль  simple_oauth (на странице модуля есть очень подробный мануал, а также ссылка на видеоуроки). Второй способ (куки) реализован в ядре.

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

Способ с куками идеален для любых веб-приложений. Единственный нюанс - чтобы фронт знал роль текущего пользователя, мне пришлось написать кастомный эндпоинт, возвращающий роль текущего юзера, ибо по умолчанию юзер может видеть роли (даже собственные) только в том случае, если у него есть доступ к управлению разрешениями. Для отправки авторизованных запросов достаточно выставить в запросе опцию withCredentials = true. В таком случае юзер логинится одновременно в бэкенд и фронтенд. Однако не забывайте, что данные куки кросдоменные, поэтому с фронта нельзя никак узнать срок их действия, ввиду чего юзер может оказаться залогиненым на фронте, но разлогиненым на бэке. Такие моменты можно отлавливать в запросах и автоматом делать повторный логин пользователя. Для этого даже предусмотрен специальный роут /user/login_status - возвращающий 1 или 0 в зависимости от того, залогинен юзер или нет.

https://www.drupal.org/docs/8/core/modules/rest/javascript-and-drupal-8-restful-web-services - вот тут смотрим, как логиниться.

Также на бэкенде необходимо поправить файл sites/default/services.yml, а именно последнюю его секцию Cors-config. Например:

# Configure Cross-Site HTTP requests (CORS).
   # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
   # for more information about the topic in general.
   # Note: By default the configuration is disabled.
  cors.config:
    enabled: true
    # Specify allowed headers, like 'x-allowed-header'.
    allowedHeaders: ['Authorization', 'Content-Type', 'X-CSRF-Token']
    # Specify allowed request methods, specify ['*'] to allow all possible ones.
    allowedMethods: ['*']
    # Configure requests allowed from specific origins.
    allowedOrigins: ['*']
    # Sets the Access-Control-Expose-Headers header.
    exposedHeaders: false
    # Sets the Access-Control-Max-Age header.
    maxAge: false
    # Sets the Access-Control-Allow-Credentials header.
    supportsCredentials: true

Последняя опция нужна для авторизации через куки. allowedOrigins лучше заменить на реальное доменное имя фронт-приложения.

Ну и самое главное - необходимо инфу о пользователе хранить в глобальном состоянии - Vuex для vuejs или Redux для reactjs. Также необходимо эту инфу сохранять в localStorage, например через redux-persist, в таком случае состояние приложения будет оставаться в браузере даже после перезагрузки машины. Но само собой, при начальной загрузке приложения даже если в состоянии записано, что юзер залогинен, нужно запустить сверку этих данных с бэкендом.

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

Аватар пользователя bmikl
bmikl 1 неделя назад

Не останавливайся :-)

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад 1

Маленькая ремарочка: когда-то я очень заинтересовался vue.js и начал его изучать, что-то даже немного написал, но потом на работе сказали писать на реакте и с тех пор я vue подзабросил, но глобально, в рамках обсуждаемой темы особой разницы нет.

Тем не менее, вот пример кода компонента формы логина, который отправляет данные в бэк и при успешном логине пишет их в Vuex
https://github.com/alexey-kuznetsov/whoaddles-drupal/blob/master/src/components/CraftLoginForm.vue

А вот тут сам state Vuex https://github.com/alexey-kuznetsov/whoaddles-drupal/blob/master/src/main.js

В общем, всё довольно просто.
Далее проверку логина можно делать в компоненте либо через хук onEnter либо обернув общий компонент Route в свой компонент, который после проверки будет возвращать собственно роут или редирект (правда вариант с обёрткой точно хорош в реакте, о во Vue я не уверен).

Помимо прочего, зная роль юзера (она у нас в Vuex или Redux), мы можем рендерить или не рендерить некоторые элементы страницы, например кнопки редактирования на нодах.

Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад 1

Всё бы было хорошо, если бы не было всё наоборот))))

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

А вот это уже дыра. Любой вредоносный js способен считать localstorage. Так делать нельзя.

И вообще, сессии имеют место быть только в случае одного домена. В остальном их использование не имеет смысла.
Архитектура бека любого подобного приложения должна содержать сервер данных и сервер аутентификации.

Сервер аутентификации отвечает за раздачу токенов, их ротацию и права на действия с контентом. Сервер данных только осуществляет обмен этих данных с конечным пользователем. Сервер данных не должен знать ничего о физических юзерах, их сессиях и тому подобное. Это просто структурированное хранилище данных. Вы же не ходите без спросу по библиотеке и не тащите с полки книги, которые сами решают даваться вам в руки или нет. Есть тётечка в очках(сервер аутентификации), которая вас записывает и пускает в тот или иной зал и указывает где искать интересующую вас литературу. Если нужен другой раздел, то вы опять же идёте к этой тётечке и говорите, что вам нужна, например экстремистская литература, тётечка видит, что вы не вправе просматривать подобную литературу и отказывает вам, в замен может предложить свежие мемасики с котиками))) Поэтому никаких сессий быть не может. Только токены.
И дабы повысить безопасность таких токенов должно быть как минимум 2. Это токен аутентификации, с помощью которого вы получаете информацию и рефреш токен, с помощью которого можно перегенерировать первый. В случае получения первого токена злоумышленником, вторым токеном можно рефрешить первый. Так же с помощью второго автоматически рефрешится, если время жизни первого истекло(как правило оно не большое: час - сутки), Если злоумышленник получил доступ ко второму токену, то всегда можно войти по логину и паролю, что запустит перегенерацию обоих токенов. Это что касается аутентификации.

Механизм обмена данных:
1. Пользователь регистрируется/авторизуется по логину и паролю и получает 2 токена(вот их можно хранить где угодно, в куках, в локалсторадж...)
2. Пользователь совершает обмен данных с использованием токена аутентификации. Если время жизни токена вышло, то в ответ получает код ошибки просроченного токена, с помощью рефреш токена получает новый токен и повторяет запрос к данным. Если и рефреш просрочен, то опять же получает код ошибки, после чего его просят авторизоваться с помощью логина и пароля. далее повторяем с начала.
3. Сервер данных получает запрос и запрашивает у сервера права для этого токена, если всё ок, то возвращает результат запроса, если нет то 403.

Всё!!!

Не нужно забивать гвозди микроскопом. Не нужно из трактора делать спорткар. Дружно учим мат. часть и делаем правильно)))

З.Ы. Для того, чтобы не спамить токенами в телах запросов, Их можно передавать в заголовках с префиксом "X-", например X-MAGIC_TOKEN. Подобные конструкции не вызывают когнитивного диссонанса в кроссдоменных запросах

Аватар пользователя sas@drupal.org
sas@drupal.org 1 неделя назад

Подпишусь, други.

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад 1
ХўлиGUN написал:
3. Сервер данных получает запрос и запрашивает у сервера права для этого токена, если всё ок, то возвращает результат запроса, если нет то 403.

А теперь смотрим вот это:

gun_dose написал:
Таким образом простой смертный, авторизовавшись через админский клиент даже под своим паролем и логином получит админские права.

Клиент в данном случае - это нечто, раздающее токены. И я разбирался с этим вопросом в восьмёрке очень скурпулёзно, прежде чем всё это написать.

Опять же я говорил о разноранговых пользователях. Если есть роли типа "Клиент", "Диспетчер", "Исполнитель" то появляется необходимость из одной формы раздавать разные токены. Если кто-то напишет имплементацию этой фичи под друпал, я буду очень рад.

Аватар пользователя BatKor
BatKor 1 неделя назад

sdf
:-)

0 Спасибо
Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад
gun_dose написал:
Таким образом простой смертный, авторизовавшись через админский клиент даже под своим паролем и логином получит админские права

gun_dose написал:
Клиент в данном случае - это нечто, раздающее токены.

gun_dose написал:
Опять же я говорил о разноранговых пользователях. Если есть роли типа "Клиент", "Диспетчер", "Исполнитель"

И я говорил, что нету никаких физических пользователей у сервера с данными и не должно их быть вообще. Есть сервер авторизации и сущность с полями логина, хеша пароля, токенами и их временем жизни. Всё. На этом область ответственности сервера авторизации заканчивается. Можно, конечно, добавить индикатор "роли", но в таком случае эта структура жёстко привязывает аутентификацию к единственному серверу данных. Поэтому все роли и пермишены для них должны быть на сервере данных. То есть отдельная таблица (external_user_id, role_id) и таблица с пермишенами. В случае одного сервера данных, это можно так же отнести к серверу авторизации. В случае мультисервисной архитектуры права на контент всё же следует хранить в соответствующем сервере данных. У сервера авторизации в свою очередь всего несколько методов:
1. Регистрация
2. Авторизация
3. Рефреш по второму токену
4. Проверка по первому токену
Всё.
При проверке по первому токену из сервера данных мы можем получить либо ИД сущности, ИД роли(если всё же запихнули роли в авторизацию), либо оба, ну или ошибки доступа(такого пользователя нет, токен просрочен). И после получения этих данных сервер данных решает апрувить запрос для токена или нет. Человек напрямую может работать с сервером авторизации только на получение токенов. Проверка же их осуществляется с сервера данных.
Так же стоит осознать, что для сервера данных может максимум существовать 1 физический пользователь(админ) с прямым входом, а все остальные клиенты, директора, диспетчеры на этом сервере это всего лишь наборы айдишников, не более.
Не думаю, что друпал на такое способен не то, что без напильника - без отбойного молотка. Поэтому все эти выкрутасы с headless ни что иное, как попытка выдать желаемое за действительное.
Если подобное делать в рамках друпала то следует:
1. Запретить регистрацию.
2. Создать обыкновенную сущность "User" с набором полей как, я описывал выше. Реализовать методы set_password, check_password, check и generate для токенов.
3. Создать кастомный механизм разграничения прав. Причём стандартный нужно оставить, ведь должен же быть прямой вход для админа.
4. Создать эндпоинты для нашей новой авторизации.
5. Впилить какую нить пермишн мидлварь, которая будет проверять токены и сравнивать с запросами к серверу данных

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

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад

Денис, поменьше теории, плиз. Давай к практике. Как сделать так, чтобы некий Владимир видел на странице заголовок и текст, некий Николай помимо прочего видел ещё и кнопку "Редактировать"? При условии, что весь html рендерится каким-нибудь js-фреймворком.

0 Спасибо
Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад
gun_dose написал:
Как сделать так, чтобы некий Владимир видел на странице заголовок и текст, некий Николай помимо прочего видел ещё и кнопку "Редактировать"? При условии, что весь html рендерится каким-нибудь js-фреймворком.

Этим должен заниматься сериализатор твоего сервера данных. То есть Владимир запрашивает по токену комменты и у него в ответе:

{
"data": [
    {
       "text": "Ты козёл!",
       "actions": {}
    },
    {
       "text": "Сам козёл!",
       "actions": {}
    },
]
}

Тот же запрос для Николая:

{
"data": [
    {
       "text": "Ты козёл!",
       "actions": {
           "edit_link": "domain.name/api/v1/comments/15",  // с методом PUT
           "delete_link": "domain.name/api/v1/comments/15", // с методом DELETE
       }
    },
    {
       "text": "Сам козёл!",
       "actions": {
           "edit_link": "domain.name/api/v1/comments/23",
           "delete_link": "domain.name/api/v1/comments/23",
       }
    },
]
}

В чём именно проблема то? Если нужна глобальная индикация "роли", то у ты ж при первом заходе на страницу приложения всё равно ж проверяешь свой токен. В ответе проверке так же как и с сервером данных ты получаешь свой ид, по которому вторым запросом получаешь с сервера данных свой профиль с аватаркой, юзернеймом, количеством непрочитанных ЛС и тут же можешь вернуть id своей "роли" исходя из которой можешь рендерить те или иные элементы интерфейса. Если же конечный пользователь засетит себе через консоль другую "роль", то всё равно ж при запросе к серверу это проверится и выдаст, что нет прав.

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад 1

Проблема тут пока только у тебя, потому что именно ты пытаешься на всё возразить))) Не поверишь, но при авторизации через куки у меня происходит ровно то же самое, что ты описал.

Единственное, что кнопки "удалить" и "редактировать" у меня решает фронт в зависимости от роли юзера. Ну и само собой, в бэке стоит проверка прав.

Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад 1
gun_dose написал:
Не поверишь, но при авторизации через куки у меня происходит ровно то же самое, что ты описал.

Я написал:
Не нужно выдавать желаемое за действительное

Зачем тебе ласты на горнолыжном курорте? Так и тут - это всего лишь подгон ТЗ под возможности (системы|исполнителя)
Лёх, я ни в коем случае не сомневаюсь в твоей квалификации, но сессий нет в рест архитектуре и все эти танцы с куками и creditails больше напоминают
1

Аватар пользователя gun_dose
gun_dose 1 неделя назад

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

0 Спасибо
Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад
gun_dose написал:
что токен, что куки - это всего лишь заголовок запроса.

Только вот заголовки set-cookie в кроссдоменных запросах не воркают)))

gun_dose написал:
И если для обычного друпала использование кукис является достаточно секьюрным, то почему оно не может быть таковым для хэдлес системы?

И если зимой на лыжах по снегу норм, то почему нельзя так летом по траве?

gun_dose написал:
А сессии? Ну пишутся в таблицу и пусть пишутся.

Действительно раз в месяц платишь по 300р за консьержа, толку от работы которого ты не видишь, но прикольно, что есть консьерж)))

Я написал:
но сессий нет в рест архитектуре

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад

Работают кросдоменные куки, ещё как работают.

0 Спасибо
Аватар пользователя ХулиGUN
ХулиGUN 1 неделя назад
gun_dose написал:
Работают кросдоменные куки, ещё как работают.

set-cookie максимум внутри одного домена с условием, что доменное имя для кук .domain.name (Но скажу честно особо в эту наркоманию с сессиями не погружался, после того как пришло понимание, что сессии не для рест)

0 Спасибо
Аватар пользователя gun_dose
gun_dose 1 неделя назад

Если есть в ответе заголовок Allow Origin, то куки ставятся. Единственный момент - ты не можешь читать их содержимое из скрипта страницы, но оно как бы и не особо надо. Что касается рест-архитектуры, то считается, что и jsonapi, и graphql её нарушают)))

0 Спасибо