async def checkaccesskey(request, api_key): if request.method == "OPTIONS": return HTTPResponse(None, 200, None) if api_key is None: raise Unauthorized("Please include API Key in query") valid, message = await check_apikey_valid(api_key) if not valid: if request.method == "HEAD": return HTTPResponse(None, 401, None) else: if message: raise Unauthorized( "API Key validation error: {}".format(message)) raise Unauthorized("API Key is not valid") works, message = await test_apikey(api_key) if not works: if request.method == "HEAD": return HTTPResponse(None, 401, None) else: if message: raise Unauthorized("API Key Test error: {}".format(message)) raise Unauthorized( "API Key is not valid but does not work. Perhaps the OAuth access key has been revoked." ) return HTTPResponse(message, 200)
async def verify_auth(route: str, headers: dict) -> None: kind, token = headers.get('Authorization', 'no auth').split() if kind != 'Bearer': raise Unauthorized('invalid token type', scheme='Bearer') # Use the shared secret to decrypt. Homefully we can generate this from a # single source of truth, ie. the name of the method, or the URL, or # something like that. Maybe the `Location` header? async with aiohttp.ClientSession() as sess: kms = KMS(API_PROJECT, API_SERVICE, route, session=sess) try: # If we can't decrypt the token, we know it was not encrypted with # the referenced KMS key, eg. the request was made by someone who # does not have access to the secret associated with this # project/service/route/method. payload = decode(await kms.decrypt(token)) except Exception: raise Unauthorized('access denied', scheme='Bearer') # Checking this value let's us avoid replay attacks, eg. by making sure # someone who intercepted a token can not use it for their own requests # later. if json.loads(payload).get('epoch', 0) < time.time() - TOKEN_TTL: raise Unauthorized('token expired', scheme='Bearer') # At the point, we've proven that the request has been generated recently # by a valid user who is whitelisted for accessing this endpoint. return
async def authorize(request: Request, roles: Optional[List[str]]): """ Authorize user role against roles @param request: @param roles: @return: """ if not roles: roles = ACTIVE_ROLES redis_client: Redis = request.app.redis_client db_client: DatabaseClient = request.app.db_client user_id: int = request.ctx.user_id cached_role: str = await redis_client.get(CacheKey.user_role(user_id), encoding="utf-8") if cached_role: if cached_role not in roles: raise Unauthorized("Unauthorized") return role_repo: RoleRepository = RoleRepository(db_client) role = role_repo.find_by_user_id(user_id) if not role or role["name"] not in roles: raise Unauthorized("Unauthorized") await redis_client.set(CacheKey.user_role(user_id), role["name"], expire=3600)
async def auth_server(session: aiohttp.ClientSession, username: str, password: str): """ 登录到 SDUT AuthServer(七天有效期) """ # 获取页面参数 async with session.get( 'http://authserver.sdut.edu.cn/authserver/login') as resp: text = await resp.text() cookies = resp.cookies soup = BeautifulSoup(text, 'html.parser') ipts = soup.form.find_all('input') data = { 'username': username, 'password': password, 'rememberMe': 'on', # 七天内记住我 } for ipt in ipts: if ipt.get('value'): data[ipt.get('name')] = ipt.get('value') JSESSIONID_auth = cookies.get('JSESSIONID_auth').value # 提交登录 # 山东理工大学统一登录平台有一处 Set-Cookie 错误,Python 没有对错误的格式进行兼容 # 手动处理第一次跳转,处理格式兼容 async with session.post( f'http://authserver.sdut.edu.cn/authserver/login;{JSESSIONID_auth}', data=data, allow_redirects=False) as resp: headers = resp.headers next_url = headers.get('Location') for key in headers: if key.lower() == 'set-cookie' and headers[key].startswith( 'CASTGC'): castgc = headers[key].split(';')[0][7:] session.cookie_jar.update_cookies( {'CASTGC': castgc}, URL('http://authserver.sdut.edu.cn/authserver')) break else: raise Unauthorized('获取 Cookie 失败,请检查用户名与密码。如果问题持续出现,请联系作者。') # 手动进行后续的跳转 async with session.get(next_url) as resp: text = await resp.text() url = str(resp.url) # 若页面跳转至首页,则说明登录成功 if url == 'http://authserver.sdut.edu.cn/authserver/index.do': return True # 若页面跳转回登录界面,则说明登录失败(用户名或密码错误) elif url == 'http://authserver.sdut.edu.cn/authserver/login': raise Unauthorized('用户名或密码错误') elif url == 'http://authserver.sdut.edu.cn/authserver/pcImproveInfo.do': raise Forbidden('需要修改初始密码后使用') else: print(url) raise ServerError('发生意料之外的错误,如果问题持续出现,请联系作者。')
async def update_user(request, id, user) -> HTTPResponse: """ Updates an already existing user """ if user: if user.id != int(id): raise Unauthorized('Unauthorized access.', status_code=400) with scoped_session() as session: ret_user = session.query(User).filter(User.id == int(id)).first() data = request.json or {} if 'username' in data and data[ 'username'] != ret_user.username and session.query( User).filter( User.username == data['username']).first(): return sanic_json( Response('Please use a different username.').__dict__, status=400) if 'email' in data and data[ 'email'] != ret_user.email and session.query(User).filter( User.email == data['email']).first(): return sanic_json( Response('Please use a different email address.').__dict__, status=400) if 'password' in data: user.set_password(data['password']) session.query(User).filter(User.id == int(id)).update({ User.username: data['username'], User.email: data['email'], User.password: user.password, User.modified_at: datetime.utcnow() }) session.commit() return sanic_json( Response('User successfully updated.').__dict__, status=200) else: session.query(User).filter(User.id == int(id)).update({ User.username: data['username'], User.email: data['email'], User.modified_at: datetime.utcnow() }) session.commit() return sanic_json( Response('User successfully updated.').__dict__, status=200) else: raise Unauthorized('Please provide credentials.', status_code=400)
async def get_user(request, id, user) -> HTTPResponse: """ Retrieves from the DB a particular user using his `id` """ if user: if user.id == int(id): with scoped_session() as session: user = session.query(User).filter(User.id == int(id)).first() return sanic_json(user.to_dict()) else: raise Unauthorized('Unauthorized access.', status_code=400) else: raise Unauthorized('Please provide credentials.', status_code=400)
async def delete_user(request, id, user) -> HTTPResponse: """ Deletes an existing user from the DB""" if user: if user.id == int(id): with scoped_session() as session: session.query(User).filter(User.id == int(id)).delete() return sanic_json( Response('User successfully removed.').__dict__, status=200) raise Unauthorized('Unauthorized access.', status_code=400) else: raise Unauthorized('Please provide credentials.', status_code=400)
def check_auth(self, request: Request) -> None: try: if request.ctx.request_user is None: logger.error("unauthorized") raise Unauthorized("Unauthorized") if self.required_scopes: if not (set(self.required_scopes) & set(request.ctx.request_user.scopes)): logger.error("forbidden") raise Forbidden("Forbidden") except AttributeError: logger.error("unauthorized") raise Unauthorized("Unauthorized")
async def customer(request): data = request.json if not request.headers.get('Authentication'): raise Unauthorized("It seems like you're not authenticated", 401) db = get_mongo_conn() name = data['namec'] data.pop('namec') data['name'] = name customer = { "defaultBank": data['defaultBank'], "name": data['name'], "email": data['email'] } headers = { 'Authorization': request.headers.get('Authorization'), 'X-CUSTOMER-ID': CUSTOMER_ID, "Accept": "application/json", "Content-Type": "application/json" } r = requests.post("https://api.paystand.co/v3/customers/accounts", json.dumps(data), headers=headers) j = r.json() if 'account' in j: customer['bank_id'] = j['id'] await db.customer.insert_one(customer) return response.json({"customerData": j}, 200) else: return response.json(j['error']['description'], int(j['error']['status']))
async def post(self, request): args = validate_required_params(request.args, 'idtoken') token = args['idtoken'][0].replace("'", "").replace('"', '') # example from https://developers.google.com/identity/sign-in/web/backend-auth try: session = requests.session() cached_session = cachecontrol.CacheControl(session) request = google.auth.transport.requests.Request( session=cached_session) id_info = id_token.verify_oauth2_token(token, request, CLIENT_ID) if id_info['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ]: raise ValueError('Wrong issuer.') user_id = id_info['sub'] user_nickname = id_info['name'] user_photo = str(id_info['picture']).replace('=s96-c', '') session_token = self.db.sign_in_or_create_oauth_user( user_id, user_nickname, user_photo) response = json({'session_id': session_token}) response.cookies['session_token'] = session_token return response except ValueError: raise Unauthorized('Token not accepted') pass
async def _check_token_redis(token): connection = await db.RedisEngine.create() item = await connection.get(str(token)) if item: return item else: raise Unauthorized('Need sign in')
async def checkapikey(request): if request.method == "OPTIONS": return HTTPResponse(None, 200, None) existing_apikey = request.headers.get("X-API-Key") if existing_apikey: return await checkaccesskey(request, existing_apikey) raise Unauthorized("Please include X-API-Key")
async def bank_payment(request): data = request.json db = get_mongo_conn() if not (request.headers.get('Authorization')): raise Unauthorized("It seems like you're not authenticated", None) headers = { 'Authorization': request.headers.get('Authorization'), 'X-CUSTOMER-ID': CUSTOMER_ID, "Accept": "application/json", "Content-Type": "application/json" } r = requests.post("api.paystand.com/v3/payments/secure", data=data, headers=headers) j = r.json() if 'id' in j: payer = await db.payer.find_one({'id': j['payer_id']}, {'_id': 0}) if not payer: await db.payer.insert_one(j['payer']) del j['_id'] return response.json({'paymentData': r.json()}, 200) else: j = r.json() return response.json(j['error']['description'], int(j['error']['status']))
async def transfer_role(self, request: Request, actor: "User", consume: TransferRoleRequest) -> OkListResult: """Transfer a role from the actor to the provided user""" url = self.get_url() role = f"{consume.role}@{url}" owner = await request.app._models.User.get(self._table, email=consume.owner) if role not in owner.roles: raise Unauthorized(f"{owner.name} has not {consume.role} @ {url}") newOwner = await request.app._models.User.get(self._table, email=consume.newOwner) if role in newOwner.roles: raise ValidationError( f"{newOwner.name} has already {consume.role} @ {url}") newOwner_roles = newOwner.roles newOwner_roles.append(role) owner_roles = owner.roles owner_roles.pop(owner_roles.index(role)) async with await self._table.database.client.start_session() as s: async with s.start_transaction(): await newOwner.update(request.app._models, roles=newOwner_roles) await owner.update(request.app._models, roles=owner_roles) return {"newOwner": newOwner_roles, "exOwner": owner_roles}
async def decorated(*args, **kwargs): request = None for arg in args: if hasattr(arg, "app") and hasattr(arg.app, "models"): request = arg break if request is None: raise InvalidRoute(func.__qualname__) if func.__decorators__["permission"]["name"] is None: permission = "call" if func.__name__ == '__call__' else func.__name__ else: permission = func.__decorators__["permission"]["name"] permRepo = await args[0].ancestors(request.app.models, check = lambda x: hasattr(x, "permissions") and x.permissions) if isinstance(permRepo, list) and len(permRepo): permRepo = permRepo[0] perm = await permRepo.get_permission(args[0].__class__.__name__, permission) actor = await (yAuth()._actor(request)) if await perm.can(actor, args[0], request): return await func(*args, **kwargs) raise Unauthorized("Not enought privileges")
async def inner(request, *args, req_args=None, **kwargs): # Validate the token before checking permission requester = await get_token_requester_from_request(request) # Invalidate the staff if he is disabled if "role_id" in requester and requester["disabled"]: raise Unauthorized( "Your account have been disabled. Please contact your supervisor." ) if not model: return await func(request, req_args=req_args, requester=requester, *args, **kwargs) # Role authorization permissions = model.permissions[request.method] if ROLES[requester["role_id"]] not in permissions: raise_permission_exception() return await func(request, req_args=req_args, requester=requester, *args, **kwargs)
async def verify_token_in_redis(token): connection = await RedisEngine.get_redis_engine() try: await connection.get(token) return token except TypeError: raise Unauthorized('Unauthorized user')
async def _sign_in_with_token(self, request): args = validate_required_params(request.args, 'idtoken') token = args['idtoken'][0].replace("'", "").replace('"', '') try: session = requests.session() cached_session = cachecontrol.CacheControl(session) request = google.auth.transport.requests.Request( session=cached_session) id_info = id_token.verify_oauth2_token(token, request, self.client_id) if id_info['iss'] not in ['accounts.google.com', 'https://accounts.google.com']: raise ValueError('Wrong issuer.') user_id = id_info['sub'] user_nickname = id_info['name'] user_photo = str(id_info['picture']).replace('=s96-c', '') session_token = self._db_manager.sign_in_or_create_oauth_user( user_id, user_nickname, user_photo) response = json({'session_token', session_token}) response.cookies['session_token'] = session_token return response except ValueError: raise Unauthorized('Token not accepted') pass
async def decorated(request, *args, **kwargs): nonlocal self, plug, reg, context for func in context._before_request_funcs: r = func() if isawaitable(r): r = await r request_context = context['request'][id(request)] _oauth = request_context.get('oauth', None) if _oauth: return f(request, *args, context=context, **kwargs) valid, req = plug.verify_request(request, scopes, self) for func in context._after_request_funcs: r = func(valid, req) if isawaitable(r): r = await r valid, req = r if not valid: if context._invalid_response: return context._invalid_response(req) raise Unauthorized("Unauthorized") request_context['oauth'] = req ret = f(request, *args, context=context, **kwargs) if isawaitable(ret): ret = await ret return ret
async def post(self, request, *args, **kwargs): if request.json is not None: user, errors = UserLoginSchema().load(request.json) if errors is not None and len(errors) > 0: raise InvalidUsage(errors) else: if user.exists(): clear_password = user.cleartext_passowrd user = user.get_by_email(user.email) if not user.password_matched(clear_password): raise Unauthorized( "You are not allowed into the secret garden!") jwt, output = await self.responses.get_access_token_output( request, user, self.config, self.instance) refresh_token = await self.instance.auth.generate_refresh_token( request, user) output.update( {self.config.refresh_token_name: refresh_token}) else: raise InvalidUsage("User do not exists") else: raise InvalidUsage("Missing JSON body") return response.json({ "jwt": jwt, "refresh_token": refresh_token }, status=201)
async def token(request: Request): """ 登录,获取 cookies,创建 token,存入数据库并返回 """ userid = request.args.get('userid') if userid is None: raise Unauthorized( '接到学校网络中心通知,本小程序涉及非法使用,因此主动停止提供服务,使用权限的申请请求已被拒绝,因此本程序将无限期停止服务。请不要继续尝试登录!也请务必不要再向外传播!谢谢同学们!' ) username = request.form.get('userid') or request.json.get('userid') password = request.form.get('password') or request.json.get('password') x_referer = request.headers.get('X-Referer', 'Unknown') async with aiohttp.ClientSession(headers=headers, loop=asyncio.get_event_loop()) as session: # 登录至 AuthServer,如果登录失败则会触发登录失败 401 await auth_server(session, username, password) cookies = auth_server_dump_cookies(session) # 生成 token now = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") token = f'SDUTAPI-{x_referer}-{username}-{now}-' + str(uuid.uuid4()) cookies_str = python_json.dumps(cookies) # 将 token 与 cookies 存入 redis,七天有效 await app.config.redis.setex(token, 7 * 24 * 60 * 60, cookies_str) # 返回 token return success(data={'token': token})
def handler_401_digest(request): raise Unauthorized("Unauthorized", scheme="Digest", realm="Sanic", qop="auth, auth-int", algorithm="MD5", nonce="abcdef", opaque="zyxwvu")
async def _actor(self, request, model): payload = model.verify(request.app.config["JWT_SECRET"]) if payload: model = request.app.models.User(request.app.table, exclude = ("password",)) await model.get(_id = ObjectId(payload["user_id"])) return model raise Unauthorized("No actor")
async def auth(self, request, model): user = await request.app.models.User.exists(self.table, model.email, True) if user and check_password_hash(user.password, model.password): token = AuthToken() token.generate({"user_id": str(user._id)}, request.app.config["JWT_SECRET"]) return token.to_plain_dict() else: raise Unauthorized("Authentication has failed")
async def delete_user(request, id, user): """ Deletes an existing user from the DB""" if user.id == int(id): with scoped_session() as session: session.query(User).filter(User.id == int(id)).delete() return sanic_json(Response('User successfully removed.').__dict__) raise Unauthorized('Unauthorized access.')
def handler_401_digest(request): challenge = { "qop": "auth, auth-int", "algorithm": "MD5", "nonce": "abcdef", "opaque": "zyxwvu", } raise Unauthorized("Unauthorized", "Digest", "Sanic", challenge)
def decode_token(token: str, token_type: str = "access"): try: payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]) if payload['token_type'] != token_type: raise JWTError return payload except JWTError: raise Unauthorized("Unauthorized", status_code=401)
async def get_user(request, id, user): """ Retrieves from the DB a particular user using his `id` """ if user.id == int(id): with scoped_session() as session: user = session.query(User).filter(User.id == int(id)).first() return sanic_json(user.to_dict()) else: raise Unauthorized('Unauthorized access.')
async def verify_token(request): token = request.headers.get('Authorization') if request.path in available_endpoints: return True stored_token = await verify_token_in_redis(token) if stored_token and stored_token == token: return True else: raise Unauthorized('Unauthorized user')
async def post(self, request): user_id = await authorization._check_token_redis( request.headers['Authorization']) result = await access.checker(request.form.get('project_id')) if result[user_id][0] == 'DELETE': response = await access.sharing(request.form, result) return response else: raise Unauthorized('Permision denied')