ahserver_g/ahserver/auth_api.py

155 lines
4.8 KiB
Python
Raw Normal View History

2023-07-07 09:53:56 +08:00
import time
2023-10-26 11:50:18 +08:00
import uuid
2019-07-10 17:34:45 +08:00
from aiohttp_auth import auth
2023-07-07 09:53:56 +08:00
from aiohttp_auth.auth.ticket_auth import TktAuthentication
2023-10-26 11:50:18 +08:00
from aiohttp_session.redis_storage import RedisStorage
2019-07-10 17:34:45 +08:00
from os import urandom
from aiohttp import web
import aiohttp_session
2023-07-18 18:25:59 +08:00
import aioredis
2023-04-05 23:28:17 +08:00
import base64
2023-10-26 11:50:18 +08:00
import binascii
2019-07-10 17:34:45 +08:00
2023-10-26 11:50:18 +08:00
from aiohttp_session import get_session, session_middleware, Session
2019-07-10 17:34:45 +08:00
from aiohttp_session.cookie_storage import EncryptedCookieStorage
2023-07-18 18:25:59 +08:00
from aiohttp_session.redis_storage import RedisStorage
2019-07-10 17:34:45 +08:00
2020-02-26 17:24:07 +08:00
from appPublic.jsonConfig import getConfig
2022-09-27 14:03:16 +08:00
from appPublic.rsawrap import RSA
2022-06-23 14:58:03 +08:00
from appPublic.app_logger import AppLogger
2023-07-18 18:25:59 +08:00
2023-10-25 11:48:11 +08:00
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
2023-10-26 11:50:18 +08:00
class MyRedisStorage(RedisStorage):
def key_gen(self, request):
2023-11-01 15:53:20 +08:00
key = request.headers.get('client_uuid')
if not key:
2023-11-01 16:34:14 +08:00
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
2023-10-26 11:50:18 +08:00
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,
)
2022-06-23 14:58:03 +08:00
class AuthAPI(AppLogger):
def __init__(self):
super().__init__()
2023-06-12 14:05:23 +08:00
self.conf = getConfig()
2020-02-26 17:24:07 +08:00
2023-07-04 12:02:13 +08:00
async def checkUserPermission(self, user, path):
2023-12-18 18:54:15 +08:00
# print('************* checkUserPermission() use default one ****************')
2023-07-04 12:02:13 +08:00
return True
2020-02-26 17:24:07 +08:00
def getPrivateKey(self):
if not hasattr(self,'rsaEngine'):
self.rsaEngine = RSA()
fname = self.conf.website.rsakey.privatekey
self.privatekey = self.rsaEngine.read_privatekey(fname)
return self.privatekey
def rsaDecode(self,cdata):
self.getPrivateKey()
return self.rsaEngine.decode(self.privatekey,cdata)
2023-07-19 11:38:09 +08:00
async def setupAuth(self,app):
2019-07-10 17:34:45 +08:00
# setup session middleware in aiohttp fashion
2024-01-28 11:14:28 +08:00
b = str(self.conf.website.port).encode('utf-8')
cnt = 32 - len(b)
secret = b + b'iqwertyuiopasdfghjklzxcvbnm12345'[:cnt]
2023-10-26 11:50:18 +08:00
storage = EncryptedCookieStorage(secret)
2023-07-18 18:25:59 +08:00
if self.conf.website.session_redis:
url = self.conf.website.session_redis.url
# redis = await aioredis.from_url("redis://127.0.0.1:6379")
redis = await aioredis.from_url(url)
2023-10-26 11:50:18 +08:00
storage = MyRedisStorage(redis)
2019-07-10 17:34:45 +08:00
aiohttp_session.setup(app, storage)
# Create an auth ticket mechanism that expires after 1 minute (60
# seconds), and has a randomly generated secret. Also includes the
# optional inclusion of the users IP address in the hash
2023-06-12 14:05:23 +08:00
session_max_time = 120
session_reissue_time = 30
if self.conf.website.session_max_time:
session_max_time = self.conf.website.session_max_time
if self.conf.website.session_reissue_time:
session_reissue_time = self.conf.website.session_reissue_time
2023-07-07 09:53:56 +08:00
def _new_ticket(self, request, user_id):
client_uuid = request.headers.get('client_uuid')
2023-10-25 11:48:11 +08:00
ip = self._get_ip(request)
2023-07-07 09:53:56 +08:00
valid_until = int(time.time()) + self._max_age
2024-02-18 15:52:11 +08:00
# print(f'hack: my _new_ticket() called ... remote {ip=}, {client_uuid=}')
2023-10-26 11:50:18 +08:00
return self._ticket.new(user_id,
valid_until=valid_until,
client_ip=ip,
user_data=client_uuid)
2023-07-07 09:53:56 +08:00
2023-10-25 11:48:11 +08:00
TktAuthentication._get_ip = get_client_ip
2023-07-07 09:53:56 +08:00
TktAuthentication._new_ticket = _new_ticket
2023-10-26 11:50:18 +08:00
policy = auth.SessionTktAuthentication(secret,
session_max_time,
reissue_time=session_reissue_time,
include_ip=True)
2019-07-10 17:34:45 +08:00
# setup aiohttp_auth.auth middleware in aiohttp fashion
2023-04-06 16:20:19 +08:00
# print('policy = ', policy)
2019-07-10 17:34:45 +08:00
auth.setup(app, policy)
app.middlewares.append(self.checkAuth)
@web.middleware
async def checkAuth(self,request,handler):
2024-02-18 15:52:11 +08:00
self.info(f'checkAuth() called ... {request.path=}')
2023-10-23 17:32:35 +08:00
t1 = time.time()
2019-07-10 17:34:45 +08:00
path = request.path
user = await auth.get_auth(request)
2023-04-04 10:34:02 +08:00
is_ok = await self.checkUserPermission(user, path)
2023-10-23 17:32:35 +08:00
t2 = time.time()
2023-10-25 11:48:11 +08:00
ip = get_client_ip(None, request)
2023-04-04 10:34:02 +08:00
if is_ok:
2023-10-23 17:32:35 +08:00
try:
ret = await handler(request)
t3 = time.time()
2023-10-24 11:57:14 +08:00
self.info(f'timecost=client({ip}) {user} access {path} cost {t3-t1}, ({t2-t1})')
2023-10-23 17:32:35 +08:00
return ret
except Exception as e:
2023-10-26 11:50:18 +08:00
t3 = time.time()
2023-10-24 11:57:14 +08:00
self.info(f'timecost=client({ip}) {user} access {path} cost {t3-t1}, ({t2-t1}), except={e}')
2023-10-23 17:32:35 +08:00
raise e
2023-06-15 10:38:32 +08:00
if user is None:
2023-10-24 11:57:14 +08:00
self.info(f'timecost=client({ip}) {user} access need login to access {path} ({t2-t1})')
2023-06-15 10:38:32 +08:00
raise web.HTTPUnauthorized
2023-10-24 11:57:14 +08:00
self.info(f'timecost=client({ip}) {user} access {path} forbidden ({t2-t1})')
2019-07-10 17:34:45 +08:00
raise web.HTTPForbidden()
async def needAuth(self,path):
return False