fix some issues in authentication

This commit is contained in:
odysseusmax 2021-06-14 21:23:33 +05:30
parent bf9348699a
commit e566d5048c
8 changed files with 106 additions and 28 deletions

View File

@ -33,18 +33,20 @@ class Indexer:
TEMPLATES_ROOT = pathlib.Path(__file__).parent / "templates"
def __init__(self):
self.server = web.Application(
middlewares=[
middlewares = []
if authenticated:
middlewares.append(
session_middleware(
EncryptedCookieStorage(
secret_key=SECRET_KEY.encode(),
max_age=60 * SESSION_COOKIE_LIFETIME,
cookie_name="TG_INDEX_SESSION"
cookie_name="TG_INDEX_SESSION",
)
),
middleware_factory(),
]
)
)
)
middlewares.append(middleware_factory())
self.server = web.Application(middlewares=middlewares)
self.loop = asyncio.get_event_loop()
self.tg_client = Client(session_string, api_id, api_hash)

View File

@ -22,16 +22,30 @@ async def setup_routes(app, handler):
web.get("/login", h.login_get, name="login_page"),
web.post("/login", h.login_post, name="login_handle"),
web.get("/logout", h.logout_get, name="logout"),
web.get("/favicon.ico", h.faviconicon, name="favicon"),
]
def get_common_routes(p):
def get_common_routes(alias_id):
p = "/{chat:" + alias_id + "}"
return [
web.get(p, h.index),
web.get(p + r"/logo", h.logo),
web.get(p + r"/{id:\d+}/view", h.info),
web.get(p + r"/{id:\d+}/thumbnail", h.thumbnail_get),
web.get(p + r"/{id:\d+}/{filename}", h.download_get),
web.head(p + r"/{id:\d+}/{filename}", h.download_head),
web.get(p, h.index, name=f"index_{alias_id}"),
web.get(p + r"/logo", h.logo, name=f"logo_{alias_id}"),
web.get(p + r"/{id:\d+}/view", h.info, name=f"info_{alias_id}"),
web.get(
p + r"/{id:\d+}/thumbnail",
h.thumbnail_get,
name=f"thumbnail_get_{alias_id}",
),
web.get(
p + r"/{id:\d+}/{filename}",
h.download_get,
name=f"download_get_{alias_id}",
),
web.head(
p + r"/{id:\d+}/{filename}",
h.download_head,
name=f"download_head_{alias_id}",
),
]
if index_all:
@ -56,16 +70,14 @@ async def setup_routes(app, handler):
continue
alias_id = h.generate_alias_id(chat)
p = "/{chat:" + alias_id + "}"
routes.extend(get_common_routes(p))
routes.extend(get_common_routes(alias_id))
log.debug(f"Index added for {chat.id} at /{alias_id}")
else:
for chat_id in include_chats:
chat = await client.get_entity(chat_id)
alias_id = h.generate_alias_id(chat)
p = "/{chat:" + alias_id + "}"
routes.extend(get_common_routes(p)) # returns list() of common routes
routes.extend(get_common_routes(alias_id)) # returns list() of common routes
log.debug(f"Index added for {chat.id} at /{alias_id}")
routes.append(web.view(r"/{wildcard:.*}", h.wildcard))
app.add_routes(routes)

View File

@ -12,27 +12,37 @@ class Client(TelegramClient):
self.log = logging.getLogger(__name__)
async def download(self, file, file_size, offset, limit):
part_size_kb = utils.get_appropriated_part_size(file_size)
part_size = int(part_size_kb * 1024)
part_size = utils.get_appropriated_part_size(file_size) * 1024
first_part_cut = offset % part_size
first_part = math.floor(offset / part_size)
last_part_cut = part_size - (limit % part_size)
last_part = math.ceil(limit / part_size)
part_count = math.ceil(file_size / part_size)
part = first_part
self.log.debug(
f"""Request Details
part_size(bytes) = {part_size},
first_part = {first_part}, cut = {first_part_cut}(length={part_size-first_part_cut}),
last_part = {last_part}, cut = {last_part_cut}(length={last_part_cut}),
parts_count = {part_count}
"""
)
try:
async for chunk in self.iter_download(
file, offset=first_part * part_size, request_size=part_size
):
self.log.debug(f"Part {part}/{last_part} (total {part_count}) served!")
if part == first_part:
yield chunk[first_part_cut:]
elif part == last_part - 1:
elif part == last_part:
yield chunk[:last_part_cut]
break
else:
yield chunk
self.log.debug(f"Part {part}/{last_part} (total {part_count}) served!")
part += 1
self.log.debug("serving finished")
self.log.debug(f"serving finished")
except (GeneratorExit, StopAsyncIteration, asyncio.CancelledError):
self.log.debug("file serve interrupted")
raise

View File

@ -12,6 +12,7 @@ from .logo_view import LogoView
from .thumbnail_view import ThumbnailView
from .login_view import LoginView
from .logout_view import LogoutView
from .faviconicon_view import FaviconIconView
from .middlewhere import middleware_factory
@ -25,6 +26,7 @@ class Views(
WildcardView,
LoginView,
LogoutView,
FaviconIconView,
):
def __init__(self, client):
self.client = client
@ -38,7 +40,7 @@ class Views(
while True:
orig_id = f"{chat_id}" # the original id
unique_hash = hashlib.md5(orig_id.encode()).digest()
alias_id = base64.urlsafe_b64encode(unique_hash).decode()[: self.url_len]
alias_id = base64.b64encode(unique_hash, b"__").decode()[: self.url_len]
if alias_id in self.chat_ids:
self.url_len += (

View File

@ -0,0 +1,33 @@
import random
from PIL import Image, ImageDraw, ImageFont
from aiohttp import web
from app.config import logo_folder
class FaviconIconView:
async def faviconicon(self, req):
favicon_path = logo_folder.joinpath("favicon.ico")
text = "T"
if not favicon_path.exists():
W, H = (360, 360)
color = tuple((random.randint(0, 255) for _ in range(3)))
im = Image.new("RGB", (W, H), color)
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("arial.ttf", 50)
w, h = draw.textsize(text, font=font)
draw.text(((W - w) / 2, (H - h) / 2), text, fill="white", font=font)
im.save(favicon_path)
with open(favicon_path, "rb") as fp:
body = fp.read()
return web.Response(
status=200,
body=body,
headers={
"Content-Type": "image/jpeg",
"Content-Disposition": 'inline; filename="favicon.ico"',
},
)

View File

@ -105,5 +105,5 @@ class IndexView:
"block_downloads": block_downloads,
"m3u_option": ""
if not req.app["is_authenticated"]
else f"{req.app['is_authenticated']}:{req.app['is_authenticated']}@",
else f"{req.app['username']}:{req.app['password']}@",
}

View File

@ -1,5 +1,4 @@
import logging
import math
from PIL import Image, ImageDraw, ImageFont
import random

View File

@ -1,7 +1,7 @@
import time
import logging
from aiohttp.web import middleware, HTTPFound
from aiohttp.web import middleware, HTTPFound, Response
from aiohttp import BasicAuth, hdrs
from aiohttp_session import get_session
@ -12,6 +12,17 @@ log = logging.getLogger(__name__)
def _do_basic_auth_check(request):
auth_header = request.headers.get(hdrs.AUTHORIZATION)
if not auth_header:
if "download_" in request.match_info.route.name:
return Response(
body=b"",
status=401,
reason="UNAUTHORIZED",
headers={
hdrs.WWW_AUTHENTICATE: 'Basic realm=""',
hdrs.CONTENT_TYPE: "text/html; charset=utf-8",
hdrs.CONNECTION: "keep-alive",
},
)
return
try:
@ -25,6 +36,12 @@ def _do_basic_auth_check(request):
if auth.login is None or auth.password is None:
return
if (
auth.login != request.app["username"]
or auth.password != request.app["password"]
):
return
return True
@ -50,7 +67,7 @@ def middleware_factory():
basic_auth_check_resp = _do_basic_auth_check(request)
if basic_auth_check_resp is not None:
if basic_auth_check_resp is True:
return await handler(request)
cookies_auth_check_resp = await _do_cookies_auth_check(request)
@ -58,6 +75,9 @@ def middleware_factory():
if cookies_auth_check_resp is not None:
return await handler(request)
if isinstance(basic_auth_check_resp, Response):
return basic_auth_check_resp
return HTTPFound(url)
return await handler(request)