Debian, AngularJS, Django, Python

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

Ошибка при запуске Eclipse Luna в Debian

Проблема

Я на работе окончательно переехал на Debian. После двух дней ненависти к Ubuntu понял, что лучше иметь старые проверенные пакеты, чем новые и постоянно глючащие. Как обычно, после установки системы поставил JRE:

apt-get update && apt-get install openjdk-7-jre -y

Скачал и распаковал последнюю стабильную версию Eclipse с официального сайта. Попробовал запустить:

./eclipse

Получил кучу ошибок, среди которых была такая: # C [libgdk-x11-2.0.so.0+0x5173f] gdk_display_open+0x3f.

Решение

Решение оказалось на редкость простым. Нужно импортировать в окружение переменную SWT_GTK3=0, тогда всё будет работать. Прописал в конце файла /etc/profile:

export SWT_GTK3=0

Чтобы не перезагружать систему, сделал то же самое для текущего пользователя, т.е. выполнил указанную выше команду. Всё работает!

Django и $resource

Суть проблемы

Решил использовать для реализации RESTful API своего приложения такой модуль Angular, как angular-resource. Проблем с установной и подключением, как обычно, не было. Создал свой сервис:

(function (A, U) { //U === undefined
    "use strict";
        
    A.module('DesktopApplication').factory('Category', function ($resource) {
        var instance = {
            selectedRow: U, //Используется в моём проекте для обмена данными между контроллерами,
                            //не является обязательным элементом
            resource: $resource('/category/rows/:id', {id: '@id'})
        };
        return instance;        
    });
}(this.angular));

Казалось бы, всё должно работать. И ведь правда, вот такое работает (GET-запрос на получение данных):

//Где-то в коде, использующем сервис Category
Category.resource.query(function (items){
    $scope.rows = items;
});

А вот такое уже нет:

//Чуть ниже
Category.save({'id': 4, 'name': 'Username'}, function (result) {
    $scope.success = result.success;
});

Проблема оказалась в том, что даже явно указав слэш в конце строки ресурса, я получил POST-запросы, идущие к адресам без слэша, и Django такие запросы тут же банит. Начался поиск и чтение StackOverflow.

Решение

Решение оказалось довольно простым. Нужно указать в настройках приложения, что удалять концевой слэш для URL в AJAX-запросах не надо (а по-умолчанию включено). Кроме того, для корректной работы запросов методом PUT и DELETE нужно дополнительно конфигурировать $http. Иначе декоратор @csrf_protect из комплекта Django будет резать такие запросы. С учётом вышесказанного, скрипт настройки приложения стал выглядеть так (для сокращения размера выброшены прочие модули, которые я на самом деле использую в своём проекте):

(function(A) {
    "use strict";
    A.module('DesktopApplication', [ 'ngCookies', 'ngResource')
     .config(function ($interpolateProvider, $resourceProvider) {
         $interpolateProvider.startSymbol('{$');
         $interpolateProvider.endSymbol('$}');
         $resourceProvider.defaults.stripTrailingSlashes = false;
      })
     .run(function ($http, $cookies) {
          $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
          $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
          $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
     });
}(this.angular, this.jQuery));

Вывод дат в Django

Проблема с датами

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

item.pub_date = child.date_of_birth.strftime('%d.%m.%Y')

В таком виде данные без проблем выводятся в шаблонах и при отправке через HttpResponse.

Как, например, вывожу данные о новости:

for item in news:
    rows.append({
        id: item.id,
        "pub_date": '%s в %s' % (item.pub_date.strftime('%d.%m.%Y'), item.pub_date.strftime('%H:%M'))       
    })
return render_to_response('detail.html', RequestContext(request, {'rows': rows}))

AngularJS, Django и POST

Введение

Начал писать приложение на Angular. Стало сразу две проблемы.

  • Как передавать CSRF-Token Django при каждом запросе?
  • Почему данные, отправляемые через POST, не видны в объекте request Django?
  • CSRF-Token

    Для CSRF-Token нашлось очень простое решение. Достаточно подключить модуль angular-cookies и при старте приложения задать параметры сервиса $http. С учётом того, что скобки для Angular у меня заменены с {{ и }} на {$ и $}, заготовка главного модуля приложения у меня выглядит так:

    (function(A){
        "use strict";
        A.module('DesktopApplication', ['ngCookies']).config(function($interpolateProvider){
            $interpolateProvider.startSymbol('{$');
            $interpolateProvider.endSymbol('$}');
    
        }).run(['$http', '$cookies', function ($http, $cookies){
            $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
            $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
            }]);
    }(this.angular));

    Естественно, на сервере при отдаче шаблонов нужно, чтобы соответствующие Cookies были установлены. Делается очень просто:

    return render_to_response('template.html', RequestContext(request))

    Angular и POST

    Отдельной болью стала отправка данных на сервер через POST. Было потрачено несколько часов на чтение Тостера, официальной документации по Angular и Django, пока наконец я не наткнулся на решение одного из пользователей StackOverflow. Суть решения такова: Angular при отправке данных не превращает их в ожидаемый на бекэнде формат, а передаёт их как строку.

    Допустим, у нас имеется такой объект:

    userdata = {
        login: 'userlogin',
        password: '123456'
    };

    Попробуем отправить эти данные:

    $http.post('/login/', userdata).success(function(data){
        //Что-то делаем с полученными в ответ данными
    });

    В консоли Firebug будет выведена строка запроса:

    "{"login": "userlogin", "password": "123456"}"

    Попробуем получить эти данные:

    def login_view(request):
        if request.method == 'POST' and request.is_ajax():
            username = request.POST.get('login', '')
            password = request.POST.get('password', '')

    Мы ожидаем, что в третьей и четвёртой строках будет примерно следующее:

    username = 'username'
    password = '123456'

    На самом деле этого не происходит, т.к. данные должны быть переданы в таком виде:

    login=userlogin&password=123456

    Проблема в том, что в текущей версии Angular (1.3.4 на момент написания статьи) нет функции для приведения объекта в такой вид. Поэтому автор посоветовал использовать старый добрый jQuery, точнее его функцию param:

    $http.post('/login/', $.param(userdata)).success(function(data){
        //Обработка ответа сервера
    });

    Вот такой код будет работать. Так же пользователи предлагали самописные функции, выполняющие ту же работу, но я советую использовать проверенные решения. Кроме этого обязательно присутствие в заголовке пост параметра Content-Type, равного application/x-www-form-urlencoded, соответствующий код приведён выше.

    Debian, OpenSSL и ГОСТ

    Введение

    В статье рассказывается, как в Debian 7 включить поддержку шифрования по ГОСТ для OpenSSL. Все операции, как обычно, от имени root. Статью написал в связи с тем, что в интернете всюду перепечатаны две инструкции - первая предлагает поставить Gentoo и собирать OpenSSL из исходников, а вторая - сломать OpenSSL так, чтобы он поддерживал только ГОСТ.

    Активные действия

    Единственный пакет, который нужно поставить - openssl, но он уже и так должен быть в системе. Не мешает также обновить его, если он устарел, т.к. OpenSSL - один из самых забагованных и дырявых пакетов в *nix-системах вообще.

    apt-get update && apt-get dist-upgrade -y
    apt-get install openssl

    Переходим в каталог /etc/ssl/ и открываем на запись файл openssl.conf. Мне нравится использовать vim, новичкам же советую открывать файлы с помощью nano.

    cd /etc/ssl/
    vim openssl.conf

    В самое начало файла, перед первой секцией, пишем следующее:

    openssl_conf = openssl_def

    Получится так:

    #
    # OpenSSL example configuration file.
    # This is mostly being used for generation of certificate requests.
    #
    
    # This definition stops the following lines choking if HOME isn't
    # defined.
    
    HOME            = .
    RANDFILE        = $ENV::HOME/.rnd
    
    # Extra OBJECT IDENTIFIER info:
    #oid_file       = $ENV::HOME/.oid
    oid_section     = new_oids
    
    # To use this configuration file with the "-extfile" option of the
    # "openssl x509" utility, name here the section containing the
    # X.509v3 extensions to use:
    # extensions        =
    # (Alternatively, use a configuration file that has only
    # X.509v3 extensions in its main [= default] section.)
    
    openssl_conf = openssl_def
    
    [ new_oids ]
    
    # We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
    # Add a simple OID like this:
    # testoid1=1.2.3.4

    Теперь в конец файла дописываем:

    [openssl_def]
    engines = engine_section
    
    [engine_section]
    gost = gost_section
    
    [gost_section]
    soft_load=1
    default_algorithms = ALL
    CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet

    После сохранения проверяем доступность алгоритмов ГОСТ в системе:

    Раз:

    openssl list-message-digest-algorithms | grep gost
    gost-mac
    md_gost94
    gost-mac
    md_gost94

    Два:

    openssl list-cipher-algorithms | grep gost
    gost89
    gost89
    gost89-cnt

    Написано на основе багрепорта, но, в отличие от инструкции на сайте одного известного отечественного производителя средств шифрования, всё работает.

    Grunt

    Введение

    Надоело мне как-то перепроверять все CSS-файлы при изменении дизайна. Поискав немного, я остановился на LESS. Вместо того, чтобы управлять одним огромным файлом styles.css, LESS позволяет создать множество мелких файлов, каждый из которых отвечает за свою часть общей таблицы стилей. Однако, собирать всё вручную мне было лень, поэтому я решил для своего проекта использовать Grunt. Для него есть множество модулей, которые способны сильно облегчить жизнь Web-разработчика, в частности, компилировать множество LESS файлов автоматически, собирать и сжимать JavaScript'ы и т.д.

    В данной статье пойдёт рассказ о том, зачем нужен Grunt, показаны примеры его использования и базовой настройки, а также мимоходом захвачено использование LESS для разработки каскадных таблиц стилей.

    Для проекта, над которым я работаю, было принято решение создать в корневом каталоге папку /less, куда поместить следующие файлы:

    • bootstrap.less - объединяет в себе все остальные файлы. Изменяя содержимое этого файла легко управлять результирующей таблицей стилей.
    • colors.less - содержит переменные, описывающие цвета, используемые на страницах, например, @red: #FF0000;.
    • fonts.less - описывает используемые на страницах шрифты. В своём проекте я решил использовать символьный шрифт FontAwesome вместо того, чтобы рисовать иконки.

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

    Установка

    Целевой системой сегодня будет не Debian, а Windows 8. Установка всех средств в Debian, кроме NodeJS, производится аналогично - командами в консоли. Если хотите NodeJS в Debian Wheezy - прочитайте вот эту статью.

    Скачиваем установщик с сайта проекта NodeJS и запускаем, при установке не забываем указать, что нам также понадобится менеджер пакетов (Node Package Manager - NPM).

    Настройка проекта для использования Grunt

    Существует два способа установки Grunt'а - глобально и локально, в проект. Я рекомендую сам Grunt установить глобально, а необходимые модули - в папку проекта. Её всегда можно добавить в исключения вашей СКВ.

    Запускаем командную строку от имени администратора и вводим следующие команды:

    npm install grunt -g
    npm install grunt-cli -g

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

    Перейдём в каталог нашего проекта (вместо %project_root_folder% подставьте нужное) и инициализируем его для использования Grunt'а:

    cd %project_root_folder%
    grunt init

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

    {
        "name": "tester",
        "version": "0.0.0",
        "description": "Система тестирования",
        "main": "index.html",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": "Dunaevsky Maxim",
        "license": "MIT",
        "devDependencies": {
        "grunt": "^0.4.5",
        "grunt-contrib-concat": "^0.5.0",
        "grunt-contrib-less": "^0.11.4",
        "grunt-contrib-uglify": "^0.5.1"
      }
    }

    Теперь нам нужно будет установить необходимые модули, я ограничусь всего тремя:

    • concat - собирает несколько указанных файлов в один.
    • less - компилирует LESS-файлы в каскадные таблицы стилей CSS.
    • uglify - оптимизирует и сжимает JavaScript-код.

    Установка производится очень просто:

    npm install grunt-contrib-concat --save-dev
    npm install grunt-contrib-less --save-dev
    npm install grunt-contrib-uglify --save-dev

    Ключ --save-dev означает, что пакеты должны быть также прописаны в файле package.json.

    Пакеты установлены, самое время дать Grunt'у задание - скомпилировать LESS-файлы, склеить вместе все JavaScript'ы из каталога /src/ и сжать итоговый файл. Для этого нам понадобится файл Gruntfile.js. Обратите внимание, первая буква в названии файла должна быть большой. Создать его следует в корневом каталоге проекта.

    Рассмотрим Gruntfile.js, которым пользуюсь я сам:

    "use strict";
    module.exports = function(grunt) {
        grunt.initConfig({
            pkg: grunt.file.readJSON('package.json'),
            less: {
                dev: {
                    files: {
                        'css/styles.css': 'less/bootstrap.less'
                    }
                },
                min: {
                    options: {
                        ieCompat: true,
                        compress: 2,
                        optimization: true
                    },
                    files: {
                        'css/styles.min.css': 'less/bootstrap.less'
                    }
                }
            },
            concat: {
                js: {
                    src: [
                        'src/*.js'
                    ],
                    dest: 'js/ui.js'
                }
            },
            uglify: {
                build: {
                    src: 'js/ui.js',
                    dest: 'js/ui.min.js'
                }
            }
        });
    
        grunt.loadNpmTasks('grunt-contrib-less');
        grunt.loadNpmTasks('grunt-contrib-concat');
        grunt.loadNpmTasks('grunt-contrib-uglify');
    
        grunt.registerTask('default', ['less:dev', 'concat:js', 'uglify']);
    };

    Первым делом определяются настройки для задач, а также указывается, откуда брать информацию о проекте (файл package.json).

    Задание для сборки CSS из LESS-файлов содержит две секции - настройки для разработки (dev) и для релиза (min), когда важен маленький размер файла и оптимизация кода. В обоих заданиях указано, что нужно на основе файла bootstrap.less из каталога less собрать файл styles.css в каталоге css.

    Задание для concat'а небольшое - ему нужно всего навсего взять все js-файлы в каталоге src и склеить в один - ui.js, положив его в каталог js.

    Самое последнее задание - оптимизация и сжатие получившегося при склеивании файла с помощью утилиты uglify.

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

    Закончив редактирование файла Gruntfile.js, запустите командую строку в текущем каталоге и выполните команду grunt. В случае ошибки в конфигурации или одном из файлов проекта будет выдано соответствующее сообщение. В противном случае вывод на экран будет выглядеть примерно так:

    Running "less:dev" (less) task
    File css/styles.css created: 0 B → 251 B
    
    Running "concat:js" (concat) task
    File js/ui.js created.
    
    Running "uglify:build" (uglify) task
    
    Done, without errors.

    Минимальная настройка SSH в Debian

    Цели

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

    Все действия выполняются от имени root, если это не оговорено особо.

    Установка пакетов

    Установить сервер OpenSSH в Debian очень просто:

    apt-get install openssh-server -y
    

    Вместе с самим сервером ставится несколько зависимостей. В моём случае (Debian 7.5) это openssh-client и ncurses-term.

    Служба сервера sshd будет автоматически запущена после установки. Обращаться к ней в терминале следует по имени ssh:

    service ssh start
    service ssh stop
    service ssh restart
    

    Минимальная настройка

    Все найстроки сервера OpenSSH хранятся в файле /etc/ssh/sshd_config. Откроем его для редактирования и рассмотрим минимально необходимые опции, которые не рекомендуется трогать либо наоборот настоятельно рекомендуется изменить.

    • Port 22 - лучше всего сразу сменить порт на другой, однако, номер порта желательно выбрать больше 1024, чтобы случайно не создать конфликт с какой-нибудь другой программой, работающей на этом порту.
    • ListenAddress 0.0.0.0 - по умолчанию закомментировано. Можно указать, какой интерфейс должен слушать наш сервер. Если на сервере несколько интерфейсов, один из которых торчин наружу, например, предоставляет доступ к WEB-серверу, а работа с сервером будет производиться только из внутренней сети, я советую указать здесь адрес сервера во внутренней сети, чтобы заблокировать доступ из Интернета.
    • UsePrivilegeSeparation yes - ни в коем случае не отключать разделение привилегий!
    • LoginGraceTime 120 - время в секундах, в течение которого следует авторизоваться на сервере. По истечение указанного интервала сервер автоматически разорвёт соединение.
    • PermitRootLogin yes - настоятельно рекомендую сменить на no! Пользователь root не должен подключаться к серверу! Для выполнения команд, требующих повышения привилегий, следует использовать sudo!
    • PubkeyAuthentication yes - включаем, т.к. дальше будет рассматриваться использование публичных ключей для подключения к серверу.
    • AuthorizedKeysFile %h/.ssh/authorized_keys - следует раскомментировать для доступа с помощью открытых ключей шифрования. По умолчанию эта настройка указывает на то, что открытые ключи каждого пользователя следует искать в его домашнем каталоге, в файле authorized_keys, лежащем в подкаталоге .ssh.

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

    Проверка работоспособности

    Попробуйте подключиться к своему серверу с него самого:

    ssh user@localhost
    

    где user - ваш настоящий логин в системе.

    При самом первом подключении ваш SSH-клиент будет спрашивать, следует ли доверять указанному хосту и принять от него ключ шифрования. Отвечаем yes (нужно написать слово полностью). Сервер предложит ввести пароль для авторизации в системе. Вводим его. Если всё правильно, вы окажетесь в консоли, где сможете вводить команды.

    Генерация и размещение ключей

    Генерация ключей

    При шифровании с открытым ключом потребуется создать пару из двух ключей - открытого и закрытого. В *nix-системах стандартным средством для их генерации является программа ssh-keygen. Запуск её с различными параметрами приводит к созданию различных ключей. В нашем случае будет использован минимум настроек.

    Запустите в терминале команду указанную ниже команду для создания ключа длиной 2048 бит и с комментарием "New generated key". Я советую добавить комментарий для ключа - в этом случае при подключении с его помощью в логах сервера будет отображаться этот комментарий. Можете ввести сюда свои имя и фамилию.

    ssh-keygen -b 2024 -C "New generated key"

    Система предложит указать имя для новых файлов. Для примера будем использовать new_key

    Затем будет задан вопрос, какая парольная фраза будет использоваться для ключа. Если не заполнять это поле, то ключ не будет защищён паролем. Рекомендую ввести сюда что-нибудь. После первого ввода парольной фразы будет предложено ввести её ещё раз.

    После этого в текущем каталоге (если вы ввели имя файла выше) будут созданы два файла - new_key и new_key.pub

    Файл new_key - это закрытый ключ. Его нужно сохранить в надёжное место на вашем компьютере и не давать никому.

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

    Размещение ключей на целевой системе

    Если в домашнем каталоге пользователя не существует каталога .ssh, создайте его, а внутри него разместите пустой файл authorized_keys.

        cd ~
        mkdir .ssh
        cd .ssh/
        touch authorized_keys
    

    Теперь в этот файл мы добавим наш открытый ключ с помощью стандартной команды >>

        cat ~/new_key.pub >> authorized_keys
    

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

    Использование закрытых ключей и Putty

    Формат закрытых ключей, используемых программой Putty, несколько отличается от того, который используется в *nix-системах. Тем не менее, в комплекте идёт программа puttygen. Запустите её.

    В меню выберите пункт Conversion, а в нём - Import key.

    Найдите ваш закрытый ключ и откройте его.

    Если ранее вы указали парольную фразу, сейчас её нужно будет ввести для открытия ключа.

    Нажмите кнопку Save private key

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

    Создайте в Putty новое подключение. В настройках найдите пункт Auth. На этой странице в поле Private key file for authentication выберите сохранённый ключ.

    Сохраните подключение.

    Нажмите Open

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

    Использование ключей для авторизации не отменяет ввода пароля пользователя при вызове команды sudo!