Драйвер принтера TCS-210 Linux

Ну не хотят еще до сих пор разработчики драйверов под Linux писать их нормально. Ну как можно сделать драйвера, без возможности добавить свои размеры бумаги? Только хардкор, только ручная правка файла ppd для CUPS.

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

OTP авторизация

Суть в чём: для каждого пользователя хранится некий OTP ключ (соль), на основании которого генерируется некий код, который в один момент времени (плюс-минус 30 секунд) будет одинаковый с ключём сгенерированным сторонним приложением. Естественно этому стороннему приложению сначала необходимо передать ключ.

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

OTP авторизация обычно используется как «дополнительная», т.е. в качестве реализации двухфакторной авторизации.

Соберём класс для генерации кода на основании токена:

class TOTP {

 const keyRegeneration  = 30; // Время действия ключа
 const otpLength  = 6; // длина кода OTP

 private static $lut = array( 
  "A" => 0, "B" => 1,"C" => 2, "D" => 3,"E" => 4, "F" => 5,"G" => 6, "H" => 7,"I" => 8, "J" => 9,"K" => 10, "L" => 11,
  "M" => 12, "N" => 13,"O" => 14, "P" => 15,"Q" => 16, "R" => 17,"S" => 18, "T" => 19,"U" => 20, "V" => 21,"W" => 22, "X" => 23,
  "Y" => 24, "Z" => 25,"2" => 26, "3" => 27,"4" => 28, "5" => 29,"6" => 30, "7" => 31
 );


 /**
 * Возвратим текущее время Unix Timestamp округлённое до времени действия ключа
 * @return integer
 **/
 public static function get_timestamp() {
  return floor( (microtime(true) + 10) /self::keyRegeneration);
 }

 /**
 * Перекодируем строку base32 в бинарный вид
 **/
 public static function base32_decode($b32) {
  $b32  = strtoupper($b32);
  if (!preg_match('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/', $b32, $match))
   throw new Exception('Используются не верные символы в ключе.');
  $l  = strlen($b32);
  $n = 0;
  $j = 0;
  $binary = "";
  for ($i = 0; $i < $l; $i++) {
   $n = $n << 5;     // Move buffer left by 5 to make room
   $n = $n + self::$lut[$b32[$i]];  // Add value into buffer
   $j = $j + 5;    // Keep track of number of bits in buffer
   if ($j >= 8) {
    $j = $j - 8;
    $binary .= chr(($n & (0xFF << $j)) >> $j);
   }
  }
  return $binary;
 }

 /**
 * Вернём текущий код
 * @param binary $key - бинарный ключ
 * @param integer $counter - Timestamp
 * @return string
 **/
 public static function oath_hotp($key, $counter){
    if (strlen($key) < 8)
      throw new Exception('Секретный ключ слишком мал.Он должен быть больше 16 символов');
    $bin_counter = pack('N*', 0) . pack('N*', $counter);  
    $hash  = hash_hmac ('sha1', $bin_counter, $key, true);
    return str_pad(self::oath_truncate($hash), self::otpLength, '0', STR_PAD_LEFT);
 }

 /**
 * Вычленяет OTP из SHA1 hash.
 * @param binary $hash
 * @return integer
 **/
 public static function oath_truncate($hash){
    $offset = ord($hash[19]) & 0xf;
    return (
        ((ord($hash[$offset+0]) & 0x7f) << 24 ) |
        ((ord($hash[$offset+1]) & 0xff) << 16 ) |
        ((ord($hash[$offset+2]) & 0xff) << 8 ) |
        (ord($hash[$offset+3]) & 0xff)
    ) % pow(10, self::otpLength);
 }
}

Использование:

$InitalizationKey = "QWERTYUIOPASDFGH";     // Начальный ключ

$TimeStamp  = TOTP::get_timestamp();
$secretkey   = TOTP::base32_decode($InitalizationKey); // Перекодируем в бинарную строку
$otp         = TOTP::oath_hotp($secretkey, $TimeStamp); // получим текщий токен

echo("Ключ: $InitalizationKey\n");
echo("Timestamp: $TimeStamp\n");
echo("Код OTP: $otp\n");

Для того чтобы руками не вводить в OTP приложение начальный токен, можно генерировать QR код, с текстом вида:

otpauth://totp/мой_сайт:логин?secret=токен&issuer=логин

1c 7.7 Получения последней даты занесения периодического реквизита

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

Решение: используем следующую функцию:

Функция ПолучитьПоследнююДатуВводаПериодическогоРеквизита(ИмяРеквизита,спрЭлементСправочника)
	ДатаПерЗначения = ПолучитьПустоеЗначение("Дата");
	Пер = СоздатьОбъект("Периодический");
	Пер.ИспользоватьОбъект(ИмяРеквизита,спрЭлементСправочника);
	Пер.ОбратныйПорядок(1);
	Если Пер.ВыбратьЗначения() = 1 Тогда
		Если Пер.ПолучитьЗначение() = 1 Тогда
	   		ДатаПерЗначения = Пер.ДатаЗнач;
		КонецЕсли;
	КонецЕсли;
	Пер = 0;         
	//сообщить(ДатаПерЗначения);
	Возврат ДатаПерЗначения;
КонецФункции

Flutter: событие с параметрами при использовании BLOC

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

event.dart

abstract class MyStaticEvent {}

class InitEvent extends MyStaticEvent {}
class StartUpdateList extends MyStaticEvent {} // Запущен процесс обновления списка статистики
class UpdateList extends MyStaticEvent {} // Обновить список статистики пользователя
class UpdateListFull extends MyStaticEvent {
  final DateTime startDate;
  final DateTime endDate;
  final int car;
  UpdateListFull(this.startDate,this.endDate,this.car);
} // Обновить список статистики пользователя

В нужном месте дернем вызов UpdateListFull:

            actions: [
              IconButton(
                icon: SvgPicture.asset('lib/images/galka.svg'),
                onPressed: () {
                  print("Нажманули сохранение");
                  BlocProvider.of<MyStaticBloc>(context).add(StartUpdateList());
                  BlocProvider.of<MyStaticBloc>(context).add(UpdateListFull(startDate??DateTime.now(),endDate??DateTime.now(),globals.UserInfo["default_car"]));
                  Navigator.pop(context);
                },
              ),
            ],

Ну и сам bloc.dart:

    on<UpdateListFull>((event, emit) async {
      print("FullList: $event");
      MyStaticState res = MyStaticState();
      print("--обновляем список статистики ");
      TRequests req = new TRequests();
      http.Response response;
      await Future.delayed(Duration(seconds: 2), () async {
        try {
          response=await req.asyncRequest("rfqwrferf", jsonEncode({           
            "offset" : globals.time_offset.toString(),
            "dtfrom" : globals.HumanDateFormat2(event.startDate),
            "dtto"   : globals.HumanDateFormat2(event.endDate),
            "autoes" : "[${event.car.toString()}]"
          }));
          if (response.statusCode==200) {
            Map<String, dynamic> answer = jsonDecode(response.body);
            if (answer["error"]==true){
              EasyLoading.showToast(answer["errortxt"]);
            } else {
              res.StaticList = answer["result"];
              res.loading=true;
              print("--пришел список статистики заправки автомобилей: ${res.StaticList}");
            }
          } else {
            EasyLoading.showToast("Ошибка обращения к API");
          }
          emit(res);
        } catch (e) {
          EasyLoading.showToast("Ошибка обращения к API $e");
        };
        emit(res);
      });
    });

FastApi: авторизированный вход

Из предыдущей статьи у нас есть некий вызов API после выполнения которого у нас на руках будет некий сессионый ключ.

Попробуем теперь используя этот сессионый ключ, запросить некую информацию о пользователе. Для этого добавим в схему auth.py классы:

class info_class(BaseModel):
    username : Optional[str] = Field(None, title="Имя пользователя", example="Вася Пупкин")
    phone    : Optional[str] = Field(None, title="Номер телефона", example="+79222347594")
    email    : Optional[str] = Field(None, title="Email пользователя", example="dowiurefh@mail.ru")

class info_out(BaseModel):
    error     : Optional[bool]  = Field(..., title="Результат выполнения запроса", example="false")
    comment   : Optional[str]   = Field(None, title="Текст ошибки", example="Пользователь удалён")
    result    : Optional[info_class]

Следующим шагом будет защитить доступ к остальным компонентам API, от доступа без сессионного ключа. Для этого воспользуемся классом FastApi security и добавим в требования заголовка запроса тег авторизации.

Получим итоговый роутинг auth.py:

# -*- coding: utf-8 -*-
"""Different helper-functions to work with users."""
import json
import fastapi;
from fastapi import APIRouter,Depends
from app.schemas import auth as auth_models
from pydantic import BaseModel

token_key = fastapi.security.APIKeyHeader(name="Authorization")

class Token(BaseModel):
    token: str

# получить текущий токен
def get_current_token(oauth_header: str = fastapi.Security(token_key)):
    #print(f"Token: {oauth_header}")
    oauth_header=oauth_header.replace("Token ","")
    return oauth_header

router = APIRouter()

@router.post('/auth',response_model=auth_models.auth_out,tags=["auth"]) # заданы исходящие параметры
async def auth(into = Depends(auth_models.auth_in)): # заданы входящие параметры
    # если параметры не заданы
    if into.password==None: into.password="NONE"
    if into.login == None: into.password = "NONE"
    hash="12345678"
    # сформируем ответ
    out=auth_models.auth_out(hash=hash)
    return out

@router.post('/GetInfo',response_model=auth_models.info_out,tags=["auth"]) # заданы исходящие параметры
async def GetInfo(token: Token=Depends(get_current_token)): # заданы входящие параметры
    print(f"Токен: {token}")
    if token!="12345678":
        print("Токен не верен!")
        return auth_models.info_out(error=True,comment="Токен не найден")
    # сформируем ответ

    return auth_models.info_out(error=False,result={"username":"Pavel","email":"vasya@mail.ru"})

В результате мы можем увидеть в документации появившийся «замочек»:

При попытке выполнения запроса без авторизации, соответственно получим ошибку аторизации:

Not authenticated
1 48 49 50 51 52 300