Compare commits
10 Commits
2d7c413ed9
...
6b766496f1
Author | SHA1 | Date | |
---|---|---|---|
|
6b766496f1 | ||
|
578cc3be08 | ||
|
5d6610bf0e | ||
|
21b2b907fd | ||
|
5c0f786a8e | ||
|
dab165396d | ||
|
335d4daf8d | ||
|
bf71b10ab0 | ||
|
dcfc9d7ae6 | ||
|
12f599ea21 |
@ -1,13 +1,16 @@
|
|||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
from aiohttp_auth import auth
|
from aiohttp_auth import auth
|
||||||
from aiohttp_auth.auth.ticket_auth import TktAuthentication
|
from aiohttp_auth.auth.ticket_auth import TktAuthentication
|
||||||
|
from aiohttp_session.redis_storage import RedisStorage
|
||||||
from os import urandom
|
from os import urandom
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import aiohttp_session
|
import aiohttp_session
|
||||||
import aioredis
|
import aioredis
|
||||||
import base64
|
import base64
|
||||||
|
import binascii
|
||||||
|
|
||||||
from aiohttp_session import get_session, session_middleware
|
from aiohttp_session import get_session, session_middleware, Session
|
||||||
from aiohttp_session.cookie_storage import EncryptedCookieStorage
|
from aiohttp_session.cookie_storage import EncryptedCookieStorage
|
||||||
from aiohttp_session.redis_storage import RedisStorage
|
from aiohttp_session.redis_storage import RedisStorage
|
||||||
|
|
||||||
@ -15,6 +18,46 @@ from appPublic.jsonConfig import getConfig
|
|||||||
from appPublic.rsawrap import RSA
|
from appPublic.rsawrap import RSA
|
||||||
from appPublic.app_logger import AppLogger
|
from appPublic.app_logger import AppLogger
|
||||||
|
|
||||||
|
def get_client_ip(obj, request):
|
||||||
|
ip = request.headers.get('X-Forwarded-For')
|
||||||
|
if not ip:
|
||||||
|
ip = request.remote
|
||||||
|
request['client_ip'] = ip
|
||||||
|
return ip
|
||||||
|
|
||||||
|
class MyRedisStorage(RedisStorage):
|
||||||
|
def key_gen(self, request):
|
||||||
|
key = request.headers.get('client_uuid')
|
||||||
|
if not key:
|
||||||
|
key = uuid.uuid4().hex
|
||||||
|
return key
|
||||||
|
if isinstance(key, str):
|
||||||
|
key = key.encode('utf-8')
|
||||||
|
key = binascii.hexlify(key)
|
||||||
|
key = key.decode('utf-8')
|
||||||
|
return key
|
||||||
|
|
||||||
|
async def save_session(self, request: web.Request,
|
||||||
|
response: web.StreamResponse,
|
||||||
|
session: Session) -> None:
|
||||||
|
key = session.identity
|
||||||
|
if key is None:
|
||||||
|
key = self.key_gen(request)
|
||||||
|
self.save_cookie(response, key, max_age=session.max_age)
|
||||||
|
else:
|
||||||
|
if session.empty:
|
||||||
|
self.save_cookie(response, "", max_age=session.max_age)
|
||||||
|
else:
|
||||||
|
key = str(key)
|
||||||
|
self.save_cookie(response, key, max_age=session.max_age)
|
||||||
|
|
||||||
|
data_str = self._encoder(self._get_session_data(session))
|
||||||
|
await self._redis.set(
|
||||||
|
self.cookie_name + "_" + key,
|
||||||
|
data_str,
|
||||||
|
ex=session.max_age,
|
||||||
|
)
|
||||||
|
|
||||||
class AuthAPI(AppLogger):
|
class AuthAPI(AppLogger):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -37,13 +80,15 @@ class AuthAPI(AppLogger):
|
|||||||
|
|
||||||
async def setupAuth(self,app):
|
async def setupAuth(self,app):
|
||||||
# setup session middleware in aiohttp fashion
|
# setup session middleware in aiohttp fashion
|
||||||
|
secret = b'iqwertyuiopasdfghjklzxcvbnm12345'
|
||||||
storage = EncryptedCookieStorage(urandom(32))
|
if self.conf.website.cookie_secret:
|
||||||
|
secret = self.conf.website.cookie_secret.encode('utf-8')
|
||||||
|
storage = EncryptedCookieStorage(secret)
|
||||||
if self.conf.website.session_redis:
|
if self.conf.website.session_redis:
|
||||||
url = self.conf.website.session_redis.url
|
url = self.conf.website.session_redis.url
|
||||||
# redis = await aioredis.from_url("redis://127.0.0.1:6379")
|
# redis = await aioredis.from_url("redis://127.0.0.1:6379")
|
||||||
redis = await aioredis.from_url(url)
|
redis = await aioredis.from_url(url)
|
||||||
storage = aiohttp_session.redis_storage.RedisStorage(redis)
|
storage = MyRedisStorage(redis)
|
||||||
aiohttp_session.setup(app, storage)
|
aiohttp_session.setup(app, storage)
|
||||||
|
|
||||||
# Create an auth ticket mechanism that expires after 1 minute (60
|
# Create an auth ticket mechanism that expires after 1 minute (60
|
||||||
@ -56,45 +101,52 @@ class AuthAPI(AppLogger):
|
|||||||
if self.conf.website.session_reissue_time:
|
if self.conf.website.session_reissue_time:
|
||||||
session_reissue_time = self.conf.website.session_reissue_time
|
session_reissue_time = self.conf.website.session_reissue_time
|
||||||
|
|
||||||
def _get_ip(self,request):
|
|
||||||
ip = request.headers.get('X-Forwarded-For')
|
|
||||||
if not ip:
|
|
||||||
ip = request.remote
|
|
||||||
return ip
|
|
||||||
|
|
||||||
def _new_ticket(self, request, user_id):
|
def _new_ticket(self, request, user_id):
|
||||||
client_uuid = request.headers.get('client_uuid')
|
client_uuid = request.headers.get('client_uuid')
|
||||||
ip = self._get_ip(request)
|
ip = self._get_ip(request)
|
||||||
if not ip:
|
|
||||||
ip = request.remote
|
|
||||||
valid_until = int(time.time()) + self._max_age
|
valid_until = int(time.time()) + self._max_age
|
||||||
print(f'hack: my _new_ticket() called ...remote {ip=}, {client_uuid=}')
|
print(f'hack: my _new_ticket() called ...remote {ip=}, {client_uuid=}')
|
||||||
return self._ticket.new(user_id, valid_until=valid_until, client_ip=ip, user_data=client_uuid)
|
return self._ticket.new(user_id,
|
||||||
|
valid_until=valid_until,
|
||||||
|
client_ip=ip,
|
||||||
|
user_data=client_uuid)
|
||||||
|
|
||||||
TktAuthentication._get_ip = _get_ip
|
TktAuthentication._get_ip = get_client_ip
|
||||||
TktAuthentication._new_ticket = _new_ticket
|
TktAuthentication._new_ticket = _new_ticket
|
||||||
policy = auth.SessionTktAuthentication(urandom(32), session_max_time,
|
policy = auth.SessionTktAuthentication(secret,
|
||||||
|
session_max_time,
|
||||||
reissue_time=session_reissue_time,
|
reissue_time=session_reissue_time,
|
||||||
include_ip=True)
|
include_ip=True)
|
||||||
|
|
||||||
# setup aiohttp_auth.auth middleware in aiohttp fashion
|
# setup aiohttp_auth.auth middleware in aiohttp fashion
|
||||||
# print('policy = ', policy)
|
# print('policy = ', policy)
|
||||||
auth.setup(app, policy)
|
auth.setup(app, policy)
|
||||||
print('add auth middleware ....................')
|
|
||||||
app.middlewares.append(self.checkAuth)
|
app.middlewares.append(self.checkAuth)
|
||||||
|
|
||||||
@web.middleware
|
@web.middleware
|
||||||
async def checkAuth(self,request,handler):
|
async def checkAuth(self,request,handler):
|
||||||
print('checkAuth() called ..................')
|
self.info(f'checkAuth() called ...{request.path}')
|
||||||
|
t1 = time.time()
|
||||||
path = request.path
|
path = request.path
|
||||||
user = await auth.get_auth(request)
|
user = await auth.get_auth(request)
|
||||||
is_ok = await self.checkUserPermission(user, path)
|
is_ok = await self.checkUserPermission(user, path)
|
||||||
|
t2 = time.time()
|
||||||
|
ip = get_client_ip(None, request)
|
||||||
if is_ok:
|
if is_ok:
|
||||||
return await handler(request)
|
try:
|
||||||
|
ret = await handler(request)
|
||||||
|
t3 = time.time()
|
||||||
|
self.info(f'timecost=client({ip}) {user} access {path} cost {t3-t1}, ({t2-t1})')
|
||||||
|
return ret
|
||||||
|
except Exception as e:
|
||||||
|
t3 = time.time()
|
||||||
|
self.info(f'timecost=client({ip}) {user} access {path} cost {t3-t1}, ({t2-t1}), except={e}')
|
||||||
|
raise e
|
||||||
|
|
||||||
if user is None:
|
if user is None:
|
||||||
print(f'**{user=}, {path} need login**')
|
self.info(f'timecost=client({ip}) {user} access need login to access {path} ({t2-t1})')
|
||||||
raise web.HTTPUnauthorized
|
raise web.HTTPUnauthorized
|
||||||
print(f'**{user=}, {path} forbidden**')
|
self.info(f'timecost=client({ip}) {user} access {path} forbidden ({t2-t1})')
|
||||||
raise web.HTTPForbidden()
|
raise web.HTTPForbidden()
|
||||||
|
|
||||||
async def needAuth(self,path):
|
async def needAuth(self,path):
|
||||||
|
@ -113,3 +113,8 @@ class FileStorage:
|
|||||||
# print(f'{name=} file({fpath}) write {siz} bytes')
|
# print(f'{name=} file({fpath}) write {siz} bytes')
|
||||||
self.tfr.newtmpfile(fpath)
|
self.tfr.newtmpfile(fpath)
|
||||||
return fpath
|
return fpath
|
||||||
|
|
||||||
|
def file_realpath(path):
|
||||||
|
fs = FileStorage()
|
||||||
|
return fs.realPath(path)
|
||||||
|
|
||||||
|
@ -4,6 +4,9 @@ import sys
|
|||||||
from appPublic.folderUtils import listFile
|
from appPublic.folderUtils import listFile
|
||||||
from appPublic.ExecFile import ExecFile
|
from appPublic.ExecFile import ExecFile
|
||||||
from ahserver.serverenv import ServerEnv
|
from ahserver.serverenv import ServerEnv
|
||||||
|
import appPublic
|
||||||
|
import sqlor
|
||||||
|
import ahserver
|
||||||
|
|
||||||
def load_plugins(p_dir):
|
def load_plugins(p_dir):
|
||||||
ef = ExecFile()
|
ef = ExecFile()
|
||||||
@ -15,8 +18,11 @@ def load_plugins(p_dir):
|
|||||||
ef.set('sys',sys)
|
ef.set('sys',sys)
|
||||||
ef.set('ServerEnv', ServerEnv)
|
ef.set('ServerEnv', ServerEnv)
|
||||||
for m in listFile(pdir, suffixs='.py'):
|
for m in listFile(pdir, suffixs='.py'):
|
||||||
if m.endswith('__init__.py'):
|
if m == '__init__.py':
|
||||||
continue
|
continue
|
||||||
|
if not m.endswith('.py'):
|
||||||
|
continue
|
||||||
|
print(f'{m=}')
|
||||||
module = os.path.basename(m[:-3])
|
module = os.path.basename(m[:-3])
|
||||||
print('module=', module)
|
print('module=', module)
|
||||||
__import__(module, locals(), globals())
|
__import__(module, locals(), globals())
|
||||||
|
@ -47,7 +47,7 @@ from .functionProcessor import FunctionProcessor
|
|||||||
from .proxyProcessor import ProxyProcessor
|
from .proxyProcessor import ProxyProcessor
|
||||||
from .serverenv import ServerEnv
|
from .serverenv import ServerEnv
|
||||||
from .url2file import Url2File
|
from .url2file import Url2File
|
||||||
from .filestorage import FileStorage
|
from .filestorage import FileStorage, file_realpath
|
||||||
from .restful import DBCrud
|
from .restful import DBCrud
|
||||||
from .dbadmin import DBAdmin
|
from .dbadmin import DBAdmin
|
||||||
from .filedownload import file_download, path_decode
|
from .filedownload import file_download, path_decode
|
||||||
@ -165,21 +165,6 @@ class ProcessorResource(AppLogger, StaticResource,Url2File):
|
|||||||
return ns
|
return ns
|
||||||
|
|
||||||
async def _handle(self,request:Request) -> StreamResponse:
|
async def _handle(self,request:Request) -> StreamResponse:
|
||||||
name = str(request.url)
|
|
||||||
t = TimeCost(name)
|
|
||||||
with t:
|
|
||||||
try:
|
|
||||||
x = await self._handle1(request)
|
|
||||||
except Exception as e:
|
|
||||||
self.error(f'{name}:error={e}')
|
|
||||||
print_exc()
|
|
||||||
x = e
|
|
||||||
self.info(f'{name}:time cost={t.end_time - t.begin_time}')
|
|
||||||
if not isinstance(x, StreamResponse):
|
|
||||||
return HTTPException
|
|
||||||
return x
|
|
||||||
|
|
||||||
async def _handle1(self,request:Request) -> StreamResponse:
|
|
||||||
clientkeys = {
|
clientkeys = {
|
||||||
"iPhone":"iphone",
|
"iPhone":"iphone",
|
||||||
"iPad":"ipad",
|
"iPad":"ipad",
|
||||||
@ -244,6 +229,7 @@ class ProcessorResource(AppLogger, StaticResource,Url2File):
|
|||||||
return await auth.get_auth(request)
|
return await auth.get_auth(request)
|
||||||
|
|
||||||
self.y_env.i18n = serveri18n
|
self.y_env.i18n = serveri18n
|
||||||
|
self.y_env.file_realpath = file_realpath
|
||||||
self.y_env.redirect = redirect
|
self.y_env.redirect = redirect
|
||||||
self.y_env.info = self.info
|
self.y_env.info = self.info
|
||||||
self.y_env.error = self.error
|
self.y_env.error = self.error
|
||||||
@ -327,6 +313,7 @@ class ProcessorResource(AppLogger, StaticResource,Url2File):
|
|||||||
async def html_handle(self,request,filepath):
|
async def html_handle(self,request,filepath):
|
||||||
async with aiofiles.open(filepath,'r', encoding='utf-8') as f:
|
async with aiofiles.open(filepath,'r', encoding='utf-8') as f:
|
||||||
txt = await f.read()
|
txt = await f.read()
|
||||||
|
utxt = txt.encode('utf-8')
|
||||||
headers = {
|
headers = {
|
||||||
'Content-Type': 'text/html; utf-8',
|
'Content-Type': 'text/html; utf-8',
|
||||||
'Accept-Ranges': 'bytes',
|
'Accept-Ranges': 'bytes',
|
||||||
|
Loading…
Reference in New Issue
Block a user