14 Commits

Author SHA1 Message Date
e49f2e00eb Made pip upgrade before copying requirements.txt in Dockerfile 2022-11-18 16:38:25 +00:00
fd002e3718 Added command to update pip setuptools and install wheel in Dockerfile 2022-11-18 11:34:04 +00:00
d68a7bb6e8 Moved check of generated password into separate function 2022-11-15 15:39:38 +03:00
d5f3708c50 Refactored utils.py
Created constants FORBIDDEN_CHARS, PUNCTUATION, PASSWORD_CHARS
Removed pipe from forbidden chars
Moved Message type alias to the top of the file
Optimized _base_check and made its parameter positional only
Changed gen_password to use said constants
2022-11-15 15:17:36 +03:00
cc13b35282 Made class Account private in utils.py 2022-11-14 16:58:34 +03:00
3802943225 Changed telebot.types.Message to Message in handlers for old functions 2022-11-14 16:58:34 +03:00
309fb2108b Removed unnessesary import from src.__init__ 2022-11-14 16:58:34 +03:00
9d5c52bebe optimized password generation function 2022-11-14 16:58:26 +03:00
a61deca6fa Removed unnecessary comments and type hints in src.__init__ 2022-11-13 19:49:21 +03:00
18abdecb74 Increased time untill deletion for /gen_password to 15 secs 2022-11-13 18:53:11 +03:00
46a1fe59b2 Documented /gen_password command 2022-11-13 18:51:23 +03:00
9ee51dc19f Added /gen_password command 2022-11-13 18:49:57 +03:00
2c07b3bed2 Added function for password generation is utils.py 2022-11-13 18:49:14 +03:00
b8113dc47b Sorted imports in utils.py 2022-11-13 18:16:03 +03:00
6 changed files with 65 additions and 22 deletions

View File

@ -15,8 +15,9 @@ RUN adduser -u 1000 --disabled-password --gecos "" appuser && chown -R appuser /
RUN apt update && apt full-upgrade -y RUN apt update && apt full-upgrade -y
# Install pip requirements # Install pip requirements
RUN pip install -U pip setuptools wheel
COPY requirements.txt . COPY requirements.txt .
RUN python -m pip install -r requirements.txt RUN pip install -r requirements.txt
COPY . /app COPY . /app

View File

@ -23,6 +23,7 @@
- /help - помощь - /help - помощь
- /export - получить пароли в json формате - /export - получить пароли в json формате
- /import - импортировать пароли из json в файле в таком же формате, как из /export - /import - импортировать пароли из json в файле в таком же формате, как из /export
- /gen_password - создать 10 надёжных паролей
### Настройка ### Настройка

View File

@ -1,12 +1,10 @@
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
from sqlalchemy.future import Engine
from . import bot, cryptography, database from . import bot, cryptography, database
__all__ = ["bot", "cryptography", "database"] __all__ = ["bot", "cryptography", "database"]
engine: Engine
def main() -> None: def main() -> None:
@ -16,7 +14,7 @@ def main() -> None:
user=os.getenv("DB_USER"), user=os.getenv("DB_USER"),
passwd=os.getenv("DB_PASS"), passwd=os.getenv("DB_PASS"),
db=os.getenv("DB_NAME"), db=os.getenv("DB_NAME"),
) # type: ignore )
database.prepare.prepare(engine) database.prepare.prepare(engine)
bot_ = bot.create_bot(os.getenv("TG_TOKEN"), engine) # type: ignore bot_ = bot.create_bot(os.getenv("TG_TOKEN"), engine)
bot_.infinity_polling() bot_.infinity_polling()

View File

@ -46,4 +46,7 @@ def create_bot(token: str, engine: Engine) -> telebot.TeleBot:
bot.register_message_handler( bot.register_message_handler(
functools.partial(handlers.import_accounts, bot, engine), commands=["import"] functools.partial(handlers.import_accounts, bot, engine), commands=["import"]
) )
bot.register_message_handler(
functools.partial(handlers.gen_password, bot), commands=["gen_password"]
)
return bot return bot

View File

@ -13,6 +13,7 @@ from .utils import (
check_account_name, check_account_name,
check_login, check_login,
check_passwd, check_passwd,
gen_passwd,
get_all_accounts, get_all_accounts,
json_to_accounts, json_to_accounts,
send_tmp_message, send_tmp_message,
@ -21,9 +22,7 @@ from .utils import (
Message = telebot.types.Message Message = telebot.types.Message
def get_accounts( def get_accounts(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None:
bot: telebot.TeleBot, engine: Engine, mes: telebot.types.Message
) -> None:
base_handler(bot, mes) base_handler(bot, mes)
accounts = database.get.get_accounts(engine, mes.from_user.id) accounts = database.get.get_accounts(engine, mes.from_user.id)
if not accounts: if not accounts:
@ -41,9 +40,7 @@ def get_accounts(
) )
def delete_all( def delete_all(bot: telebot.TeleBot, engine: Engine, mes: Message) -> None:
bot: telebot.TeleBot, engine: Engine, mes: telebot.types.Message
) -> None:
base_handler(bot, mes) base_handler(bot, mes)
bot_mes = bot.send_message( bot_mes = bot.send_message(
mes.chat.id, mes.chat.id,
@ -332,7 +329,7 @@ def _delete_account2(
send_tmp_message(bot, mes.chat.id, "Аккаунт удалён") send_tmp_message(bot, mes.chat.id, "Аккаунт удалён")
def help(bot: telebot.TeleBot, mes: telebot.types.Message) -> None: def help(bot: telebot.TeleBot, mes: Message) -> None:
message = """Команды: message = """Команды:
/set_master_pass - установить мастер пароль /set_master_pass - установить мастер пароль
/add_account - создать аккаунт /add_account - создать аккаунт
@ -344,7 +341,8 @@ def help(bot: telebot.TeleBot, mes: telebot.types.Message) -> None:
/cancel - отмена текущего действия /cancel - отмена текущего действия
/help - помощь /help - помощь
/export - получить пароли в json формате /export - получить пароли в json формате
/import - импортировать пароли из json в файле в таком же формате, как из /export""" /import - импортировать пароли из json в файле в таком же формате, как из /export
/gen_password - создать 10 надёжных паролей"""
bot.send_message(mes.chat.id, message) bot.send_message(mes.chat.id, message)
@ -471,3 +469,15 @@ def _import3(
send_tmp_message(bot, mes.chat.id, mes_text, 10) send_tmp_message(bot, mes.chat.id, mes_text, 10)
del text, mes, accounts del text, mes, accounts
gc.collect() gc.collect()
def gen_password(bot: telebot.TeleBot, mes: Message) -> None:
# Generate 10 passwords and put 'em in the backticks
base_handler(bot, mes)
passwords = (f"`{gen_passwd()}`" for _ in range(10))
text = (
"Пароли:\n"
+ "\n".join(passwords)
+ "\nНажмите на пароль, чтобы его скопировать"
)
send_tmp_message(bot, mes.chat.id, text, 15)

View File

@ -1,15 +1,26 @@
import io import io
import string
import time import time
from random import SystemRandom
from typing import Self, Type from typing import Self, Type
import pydantic import pydantic
import telebot import telebot
from sqlalchemy.future import Engine from sqlalchemy.future import Engine
from .. import database, cryptography from .. import cryptography, database
class Account(pydantic.BaseModel): FORBIDDEN_CHARS = frozenset("`\n")
PUNCTUATION = frozenset(string.punctuation).difference(FORBIDDEN_CHARS)
PASSWORD_CHARS = tuple(
frozenset(string.ascii_letters + string.digits).difference(FORBIDDEN_CHARS)
| PUNCTUATION
)
Message = telebot.types.Message
class _Account(pydantic.BaseModel):
name: str name: str
login: str login: str
passwd: str passwd: str
@ -23,11 +34,11 @@ class Account(pydantic.BaseModel):
class _Accounts(pydantic.BaseModel): class _Accounts(pydantic.BaseModel):
accounts: list[Account] = pydantic.Field(default_factory=list) accounts: list[_Account] = pydantic.Field(default_factory=list)
def _accounts_list_to_json(accounts: list[tuple[str, str, str]]) -> str: def _accounts_list_to_json(accounts: list[tuple[str, str, str]]) -> str:
accounts = _Accounts(accounts=[Account.from_tuple(i) for i in accounts]) accounts = _Accounts(accounts=[_Account.from_tuple(i) for i in accounts])
return accounts.json() return accounts.json()
@ -36,9 +47,6 @@ def json_to_accounts(json_: str) -> list[tuple[str, str, str]]:
return [i.as_tuple() for i in accounts.accounts] return [i.as_tuple() for i in accounts.accounts]
Message = telebot.types.Message
def send_tmp_message( def send_tmp_message(
bot: telebot.TeleBot, chat_id: telebot.types.Message, text: str, timeout: int = 5 bot: telebot.TeleBot, chat_id: telebot.types.Message, text: str, timeout: int = 5
) -> None: ) -> None:
@ -76,9 +84,9 @@ def accounts_to_json(accounts: list[tuple[str, str, str]]) -> io.StringIO:
return file return file
def _base_check(val: str) -> bool: def _base_check(val: str, /) -> bool:
"Returns false if finds new lines or backtick (`)" "Returns false if finds new lines or backtick (`)"
return not ("\n" in val or "`" in val) return not any(i in FORBIDDEN_CHARS for i in val)
def check_account_name(name: str) -> bool: def check_account_name(name: str) -> bool:
@ -99,3 +107,25 @@ def check_passwd(passwd: str) -> bool:
def check_account(name: str, login: str, passwd: str) -> bool: def check_account(name: str, login: str, passwd: str) -> bool:
"""Runs checks for account name, login and password""" """Runs checks for account name, login and password"""
return check_account_name(name) and check_login(login) and check_passwd(passwd) return check_account_name(name) and check_login(login) and check_passwd(passwd)
def _check_gened_password(passwd: str, /) -> bool:
"""Retuns true if generated password is valid,
false otherwise.
Password is valid if there is at least one lowercase character,
uppercase character and one punctuation character"""
return (
any(c.islower() for c in passwd)
and any(c.isupper() for c in passwd)
and any(c.isdigit() for c in passwd)
and any(c in PUNCTUATION for c in passwd)
)
def gen_passwd() -> str:
"""Generates password of length 32"""
choices = SystemRandom().choices
while True:
passwd = "".join(choices(PASSWORD_CHARS, k=32))
if _check_gened_password(passwd):
return passwd