21 ноября 2018 г.

Django не давать логиниться одновременно с двух разных мест

Другими словами, не разрешаются две параллельные сессии. При новом входе старый разлогинивается.
Может понадобиться для какого-нибудь портала в интранете или чего-то такого секюрного.
Решается всё довольно банально, есть специальный сигнал user_logged_in.
Ищем сессию для этого юзера, если нашли - сбрасываем в базе, сбрасываем в текущем engine (если это применимо).
Если что, у меня настроен один из стандартных:
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
Код обработчика сигнала. Всё заполонил комментариями, думаю, там всё понятно.
from django.contrib.auth.signals import user_logged_in
from django.contrib import messages
from django.contrib.sessions.models import Session
from django.utils import timezone

def user_logged_in_handler(sender, request, user, **kwargs):
    # текущая сессия: request.session.session_key
    for session in Session.objects.filter(expire_date__gte=timezone.now()).exclude(session_key=request.session.session_key):
        if session.get_decoded().get('_auth_user_id', None) == str(user.id):
            # нашли какую-то сессию, делаем её expired
            session.expire_date = timezone.now()
            session.save()
            # далее нам надо очистить все данные сессии
            # сначала я пробовал так
            # SessionStore = session.get_session_store_class()
            # sesstor = SessionStore(session.session_key)
            # sesstor.flush()
            # где flush - это метод именно для моего бекенда cached_db
            # я ожидал, что для модели вернётся текущий настроенный в settings.SESSION_ENGINE бекенд, но в модели Session захардкожено (как ни странно?) db.SessionStore
            # в принципе можно напрямую тупо импортировать cached_db.SessionStore, но сделал проще и универсальнее:
            # request.session у нас уже содержит установленный мидлварей нужный SessionStore
            # с помощью "чужой" нашей новой SessionStore удаляем левый наш старый session_key, там удачно есть такой даже метод
            request.session.delete(session.session_key)
            # раз у нас доступен request, то при желании показываем юзеру сообщение
            messages.add_message(request, messages.INFO, 'Была закрыта дублирующаяся сессия на каком-то другом компьютере')

user_logged_in.connect(user_logged_in_handler)

14 апреля 2016 г.

turn-сервер coturn webrtc error 401: Unauthorised

Сервер coturn по дефолту не вполне годный для использования в качестве turn-сервера для webrtc-клиентов. На stun-запросы нормально отвечает, но при попытке использования в качестве turn всё время проблемы в клиенте (не вполне очевидно отлаживаемые), а в логах либо ошибки подключения, либо что-то типа "401: Unauthorised".

WebRtc не станет работать нормально без авторизации с turn-сервером, потому там надо настроить и авторизацию. Используем long-term механизм с предопределённым логином-паролем. Там есть более хитрые механизмы, а также использование ключей в кач-ве паролей итд, но суть задачи не в этом.

в /etc/turnuserdb.conf прописывается логин-пароль:
qwerty:asdfgh

в конфиге /etc/turnserver.conf прописываются/раскоменчиваются минимально необходимые настройки:
# использование fingerprint, обычно webrtc его хочет
fingerprint
# включение long-term авторизации (хотя вроде автоматически должен включаться, если прописан хоть один аккаунт походящий)
lt-cred-mech
# файл с логинами-паролями (можно прописать напрямую в этом же конфиге, но не очень красиво)
userdb=/etc/turnuserdb.conf
# дефолтрый реалм тоже нужно
realm=qwerty
После этого сервер откликается на настройки из webrtc типа
{urls:'turn:IP:3478',username:'qwerty',credential:'asdfgh',}

8 февраля 2016 г.

dlango: autocomplete_light дополнительный рендер в json

Если цель - просто изменить рендер не в готовый html (для родных виджетов приложения), а в другой вид, в том числе json, то всё просто. Здесь же речь о том, чтобы добавить просто новый способ параллельно с полноценно работающим web-способом. В итоге будет и точно так же работающие автодополнения, рендерящие не в html, а в json, и индексная справка по корню, с корректными новыми url, указывающими на наши новые методы из api.

urls.py
from autocomplete_light.views import RegistryView
...
url(r'^api/autocomplete/(?P[-\w]+)/$', views.ApiAutocompleteView.as_view(), name='api_autocomplete_light_autocomplete'),
url(r'^api/autocomplete/$', RegistryView.as_view(template_name='autocomplete_light/api_registry.html'), name='api_autocomplete_light_registry'),
...

Вторая задача (вывод списка зарегистрированных автодополнений) решается вообще без переопределения, используем стандартную RegistryView, которая вполне подходит, нужно только переопределить template_name и в новом шаблоне вызвать get_absolute_url_api вместо get_absolute_url.

api_registry.html
{% if registry|length %}
    

List of your {{ registry_items|length }} registered api-autocompletes

{% for name, autocomplete in registry_items %}

{{ name }}

{{ autocomplete.get_absolute_url_api }}
{% endfor %}
{% else %}

You have not registered any api-autocomplete

{% endif %}

28 сентября 2015 г.

${0%${0##*/}}

Есть небольшой трюк в bash, который мне давно нравился — получение текущей директории запущенного скрипта, используя только $0 и операции над строками bash-а. Это то, что в заголовке. Как вариант, его можно использовать в виде:
cd ${0%${0##*/}}
Исходные данные: $0 - полный путь запущенного скрипта. Понятно, что скрипт надо выполнять по полному пути, иначе использование метода лишено смысла.

Используются последовательно две операции над строками:
${string##substring} - удаление самой длинной, из найденных, подстроки $substring в строке $string. Поиск ведется с начала строки.
${string%substring} - удаление самой короткой, из найденных, подстроки $substring в строке $string. Поиск ведется с конца строки.


${0%${0##*/}} - самая длинная из найденных строк */ — это весь путь до последнего слеша включительно. Если удалить это из полного пути, то получится просто имя файла самого скрипта (без пути).

${0%${0##*/}} - далее, если из $0 («полный путь») удалить «имя файла» (получено выше), то получится как раз «путь без имени файла».

5 марта 2014 г.

django: самодельный if-else тег

Здесь пример самодельного тега шаблонизатора django, выполняющего роль if-else-endif. Мой код подразумевает переменную 'current_statuskey' в контексте (кладётся напрямую или с помощью middleware). Писал для очень сложной страницы с кучей частей, имеющих разный вид в зависимости от статуса. Вообще там ещё у меня дополнительная логика, но это минимальный пример здесь. Использование в шаблоне:
{% ifstatus one,five,blabla %}
...
{% else %}
...
{% endifstatus %}
Можно было описать в десяток строк, но здесь больше и код очень сильно похож на стандартные теги ifequals и сопутствующие, откуда я его и адаптировал, чтобы получить более правильную и канонiчную реализацию.

4 февраля 2014 г.

ant: компиляция и сборка jar прямиком с git

Пояснять особо нечего тут. Используются только стандартные таски. Для git clone используется exec, далее компилируется и собирается как обычно. Ниже пример моего ant-скрипта для сборки с git либы json.jar.

15 декабря 2013 г.

проект django project на shared hosting хостинге mod_wsgi

Историческая заметка по большей части. Черновик схемы настройки проекта на django 1.4 на хостинге с mod_wsgi.