Пример #1
0
    async def delete(self, request, *args, **kwargs):
        jwt_token = request.headers.get(
            config.app.auth.config.authorization_header(), None)
        if jwt_token is None:
            raise Forbidden("missing authentication")
        if request.json is None or "refresh_token" not in request.json:
            raise Forbidden("missing refresh_token")
        try:
            decoded = decode(
                jwt_token,
                config.app.auth.config.secret(),
                algorithms=config.app.auth.config.algorithm(),
            )
        except ExpiredSignatureError:
            raise Forbidden("token expired")

        if decoded is None or "name" not in decoded:
            raise Forbidden("malformed token")

        user = User.get_by_email(decoded["email"])
        if user is None:
            raise Forbidden("malformed token")

        await delete_refresh_token(user.email, request.json["refresh_token"])

        return response.json(None, status=204)
Пример #2
0
async def userCreate(request):
    session = request.ctx.session
    form = request.json

    authId = getattr(session, 'authId', None)
    if authId is None:
        raise Forbidden('anonymous')

    form['authorization'] = authId

    user = await User.get_or_none(authId=authId)
    if user is not None:
        raise Forbidden('exists')

    try:
        async with request.app.ctx.usermgrd.post('http://localhost/',
                                                 json=form) as resp:
            data = await resp.json()
            if data['status'] != 'ok':
                request.ctx.logger.error(__name__ + '.create.usermgrd_error',
                                         reason=data['status'])
                raise ServerError('backend')
    except aiohttp.ClientConnectionError:
        request.ctx.logger.error(__name__ + '.create.usermgrd_connect_failure')
        raise ServiceUnavailable('backend')

    user = User(authId=authId, name=data['user'])
    user.password = data['password']
    await user.save()

    request.ctx.logger.info(__name__ + '.create', user=user.name)

    return await makeUserResponse(user)
Пример #3
0
async def dialogue(request: Request) -> HTTPResponse:
    name, message, client_id = mzk.get_args(request, ('name|n', str, None, {
        'max_length': 32
    }), ('message|msg|m', str, None, {
        'max_length': 512
    }), ('client_id|id', str, None, {
        'max_length': 64
    }))
    if name is None:
        raise Forbidden('name parameter format error.')
    if message is None:
        raise Forbidden('message parameter format error.')
    bot = request.app.bots.get(name, None)
    bot_id = request.app.dict_cache['name'].get(name, 'unknown')
    if bot is None:
        raise Forbidden('Unknown bot name.')
    if message == '[el]#moca_bot_dict_count#':
        res = await request.app.mysql.execute_aio(core.GET_DICT_COUNT_QUERY,
                                                  (bot_id, ))
        return json({
            'res_type': 'system',
            'res_content': f'学習済みデータ数: {res[0][0]}'
        })
    res_type, res_content = bot.dialogue(message)
    await request.app.mysql.execute_aio(
        core.INSERT_CHAT_LOG_QUERY,
        (mzk.get_remote_address(request), bot_id, message, res_content,
         res_type, client_id), True)
    return json({'res_type': res_type, 'res_content': res_content})
def do_protection(request: Request, scoped: t.Optional[str] = None) -> None:
    """This method does all of our auth checks and raises exceptions upon failure"""

    if not is_authenticated(request):
        raise Unauthorized("Who are you?")

    if not is_authorized(request, scoped):
        raise Forbidden("You are not allowed")

    if not is_pass_csrf(request):
        raise Forbidden("You CSRF thief!")
Пример #5
0
async def processRun(request, authenticatedUser):
    def substitute(s):
        return s.format(user=authenticatedUser.name)

    reqData = request.json
    actionToken = reqData.get('action', None)
    command = reqData.get('command', None)
    # client-defined extra data, forwarded to start notification
    extraData = reqData.get('extraData', None)
    token = reqData.get('token', None)
    isAction = bool(actionToken)
    isCommand = bool(command)

    user = None
    if isAction and isCommand:
        # cannot have both at the same time
        raise InvalidUsage('make_up_your_mind')
    elif isAction:
        action = await getAction(actionToken)
        args = action.arguments
        authId = args['user']
        user = await User.get_or_none(authId=authId)
        command = list(map(substitute, args['command']))
    elif isCommand:
        # use current user instead
        user = authenticatedUser

    if not user:
        # not connected to any authorized user
        raise Forbidden('forbidden')
    else:
        processes = perUserProcesses[authenticatedUser]

        if token in processes:
            raise InvalidUsage('process_exists')
        elif not token:
            raise InvalidUsage('missing_token')

        p = WebsocketProcess(token,
                             user,
                             broadcastFunc=partial(broadcast,
                                                   authenticatedUser),
                             command=command,
                             extraData=extraData)
        try:
            await p.run()
            processes[token] = p
            return jsonResponse({'status': 'ok', 'token': token})
        except asyncssh.misc.PermissionDenied:
            raise Forbidden('locked_out')
        except TermsNotAccepted:
            raise Forbidden('terms_of_service')
Пример #6
0
async def reset_user_database(request: Request) -> HTTPResponse:
    root_pass, api_key = get_args(request, 'root_pass', 'api_key')
    api_key_status = await request.app.moca_access.check_api_key(
        api_key, '-SM-', request)
    if api_key_status[0] != 0:
        raise Forbidden(api_key_status[1])
    if not (isinstance(root_pass, str) and root_pass.isascii() and
            (8 <= len(root_pass) <= 32)):
        raise Forbidden('root password format error.')
    if not compare_digest(root_pass, app.moca_config.get('root_pass', '')):
        raise Forbidden('invalid root password.')
    await request.app.moca_users.reset_user_database()
    return text('success.')
Пример #7
0
async def submit(request, user):
    # quota, allow 50 mails/day
    oldest = now() - timedelta(days=1)
    sent = await SentEmail.filter(sent__gte=oldest).count()
    quota = 50
    if sent >= quota:
        raise Forbidden('quota_reached')

    session = request.ctx.session
    values = dict(request.json)
    dryRun = values.get('dryRun')
    templateName = values.get('template')
    language = values.get('lang') or 'en'

    tpl = templates.get(templateName, {}).get(language, None)
    if not tpl:
        raise NotFound('template_not_found')
    subject = subjectTemplates[templateName][language]

    # input validation
    link = furl(values.get('link', None))
    recipientName = values.get('recipientName', None)
    recipientAddress = values.get('recipientAddress', None)
    senderAddress = session.oauthInfo.get('email', None)
    senderName = session.oauthInfo.get('name', None)
    # we’re lazy and defer actual validation to the sending SMTP server
    if not recipientName or not recipientAddress:
        raise SanicException('recipient_invalid', status_code=400)
    if not senderName or not senderAddress:
        raise Forbidden('sender_invalid')
    if link.host != request.server_name:
        raise Forbidden('link_invalid')

    values['senderName'] = senderName
    message = tpl.format(**values)
    try:
        if not dryRun:
            email = await sendEmail(request.app.config, recipientName,
                                    recipientAddress, senderAddress, subject,
                                    message)
            entry = SentEmail(messageId=email['message-id'],
                              sender=user,
                              message=str(email))
            await entry.save()
    except aiosmtplib.errors.SMTPException as e:
        if e.code == 501:
            raise SanicException('syntax_error', status_code=400)
        else:
            raise ServerError('error')

    return json(dict(status='ok', message=str(message)))
Пример #8
0
async def msg_delete(request: Request, message_id: int):
    request, user = await check_request(request, list(), True)
    msg = await Msg.find(msg_id=message_id)
    if msg.sender_id != user.id: raise Forbidden('You do not have permission to delete this message')

    await msg.update(is_deleted=True)
    return text('Success')
Пример #9
0
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 delete_jogging_result(request, *args, **kwargs):
    user_from_token = retrieve_user(request, args, kwargs)
    if user_from_token is None:
        raise InvalidUsage("invalid parameter (maybe expired?)")

    try:
        jogging_id = int(request.path.split("/")[2])
    except ValueError as e:
        raise InvalidUsage(e)

    if jogging_id < 0:
        raise InvalidUsage("invalid id")

    jog = JoggingResult.load_by_jogging_id(jogging_id)

    if jog is None:
        raise InvalidUsage("invalid id")

    user_id_from_token = retrieve_user(request, args, kwargs).user_id
    if user_id_from_token != jog.user_id:
        raise Forbidden("user can only access user jogs")

    jog.delete()

    return response.HTTPResponse(status=204)
Пример #11
0
async def notification_staff_route(request,
                                   *,
                                   req_args=None,
                                   req_body=None,
                                   query_params=None,
                                   requester=None,
                                   **kwargs):
    if "role_id" not in requester:
        raise Forbidden("Only staffs can receive notifications")
    staff_id = requester["id"]

    call_funcs = {
        "GET": noti_staff_retrieve,
        # "POST": noti_staff_create,
        "PUT": noti_staff_refresh,
        # "PATCH": visitor_update,
        # "DELETE": user_delete,
    }

    response = await call_funcs[request.method](request,
                                                req_args={
                                                    "staff_id": staff_id
                                                },
                                                req_body=req_body,
                                                query_params=query_params,
                                                **kwargs)
    return json(response)
Пример #12
0
async def show_bot_dict(request: Request) -> HTTPResponse:
    check_root_pass(request)
    name, *_ = mzk.get_args(
        request,
        ('name|n', str, None, {
            'max_length': 32
        }),
    )
    if name is None:
        raise Forbidden('name parameter format error.')
    bot_id = request.app.dict_cache['name'].get(name)
    if bot_id is None:
        raise Forbidden('unknown bot name.')
    res = await request.app.mysql.execute_aio(core.GET_BOT_DATA_QUERY,
                                              (bot_id, ))
    return json(res)
Пример #13
0
    def decorator(request, *args, **kwargs):
        prefix, token = _get_token(request)

        if not token or token != valid_token:
            raise Forbidden("You don't have access to requested resource")

        return handler(request, *args, **kwargs)
Пример #14
0
async def msg_read(request: Request, message_id: int):
    request, user = await check_request(request, list(), True)
    msg = await Msg.find(msg_id=message_id, is_deleted=False)

    if user.id not in (msg.sender_id, msg.recipient_id):
        raise Forbidden('You do not have permission to view this message')
    return json(await msg.dump())
Пример #15
0
async def user_update_avatar(request: Request, user_id: int):
    user = (await check_request(request, list(), True))[1]

    if user.id != user_id:
        raise Forbidden('You do not have permission to update this avatar')

    upload_file = request.files.get('photo')
    if not upload_file: raise InvalidUsage('You need to upload a photo')
    if len(upload_file.body) > MAX_FILE_SIZE:
        raise InvalidUsage(
            f'Photo size must be less than {MAX_FILE_SIZE} bytes')

    if imghdr.what(None, upload_file.body) == 'jpeg': file_type = 'jpg'
    elif imghdr.what(None, upload_file.body) == 'png': file_type = 'png'
    elif imghdr.what(None, upload_file.body) == 'gif': file_type = 'gif'
    else: raise InvalidUsage('Photo must be .jpg, .png or .gif')

    upload = await Upload.save_upload(user_id=user_id, file_type=file_type)
    async with aiofiles.open(f'{UPLOAD_DIR}\\{upload.id}.{file_type}',
                             'wb') as f:
        await f.write(upload_file.body)
    f.close()
    await user.update(photo=upload.id)

    return text('Success')
Пример #16
0
async def extract_quiz_questions_from_quiz(quiz_id,
                                           requester=None,
                                           allow_readonly=False,
                                           query_params=None):
    quiz = await Quiz.get(id=quiz_id)
    quiz_question_ids = quiz["questions"]
    query_params = query_params or {}

    # Check if the requester is the Quiz's owner
    if requester and requester["id"] != quiz["creator_id"]:
        raise Forbidden("You are not allowed to perform this action")

    questions = []
    if quiz_question_ids:
        quiz_questions = await QuizQuestion.get(
            in_column="id",
            in_values=quiz_question_ids,
            many=True,
            allow_readonly=allow_readonly,
            **query_params,
        )
        questions_dict = {
            question["id"]: question
            for question in quiz_questions
        }
        questions = [
            questions_dict[question_id] for question_id in quiz_question_ids
        ]

    return quiz_question_ids, questions_dict, questions
Пример #17
0
async def login_(req, login, password):
    """Authenticate a user, on success create a new JSON Web Token"""
    try:
        user = await RDB.get_user_by_login(login)
    except NotFoundError as exc:
        logger.log(45, "User not found (IP: %s)", req.ip)
        raise NotFound("User not found") from exc

    try:
        scrypt.decrypt(user.password, password, encoding=None)
    except scrypt.error:
        logger.log(45, "Wrong password for user %s (IP: %s)", user.name,
                   req.ip)
        raise Forbidden("Wrong password")

    jwt = jwtlib.encode(
        {
            "iss": ClientType.WEBAPI.value,
            "sub": "webgames",
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + JWT_EXPIRATION_TIME,
            "jti": str(uuid4()),
            "typ": ClientType.ADMIN.value
            if user.isadmin else ClientType.PLAYER.value,
            "uid": str(user.userid),
            "nic": user.name
        },
        config.webapi.JWT_SECRET,
        algorithm='HS256')

    logger.info("User connected: %s", user.userid)
    return json({"token": jwt})
Пример #18
0
def raise_role_authorization_exception(target_role_id, action: str = None):
    # action = action or "perform this action"
    # if ROLES[target_role_id] == "admin":
    #     raise Forbidden("Please contact the service provider" " to {}.".format(action))
    action = action or "create"
    raise Forbidden("Only {} and upper are allowed to {} {} accounts.".format(
        ROLES[target_role_id - 1], action, ROLES[target_role_id]))
Пример #19
0
    async def method_delete(
        self,
        request: Request,
        body: dict,
        session: DBSession,
        message_id: int,
        token: dict,
        *args,
        **kwargs,
    ) -> BaseHTTPResponse:
        """
        Delete message by id
        :param request:
        :param body:
        :param session:
        :param message_id:
        :param token:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            db_message = delete_message(session, message_id=message_id)
        except DBMessageNotExists as e:
            raise NotFound(f"Message {message_id} not found") from e

        if token["sub"] not in (db_message.sender_id, db_message.recipient_id):
            raise Forbidden("user is not recipient or sender")

        try:
            session.commit_session()
        except (DBDataException, DBIntegrityException) as e:
            raise SanicDBException(str(e))

        return await self.make_response_json(status=204)
Пример #20
0
    async def method_get(
        self,
        request: Request,
        body: dict,
        session: DBSession,
        message_id: int,
        token: dict,
        *args,
        **kwargs,
    ) -> BaseHTTPResponse:
        """
        Get message by id
        :param request:
        :param body:
        :param session:
        :param message_id:
        :param token:
        :param args:
        :param kwargs:
        :return:
        """
        try:
            db_message = get_message_by_id(session, message_id=message_id)
        except DBMessageNotExists as e:
            raise NotFound(f"Message {message_id} not found") from e

        if token["sub"] not in (db_message.sender_id, db_message.recipient_id):
            raise Forbidden("user is not recipient or sender")

        response_model = ResponseMessageDto(db_message)

        return await self.make_response_json(status=200,
                                             body=response_model.dump())
Пример #21
0
    async def sendmail(request, user_id, account):
        """ Send an email """

        if account.name != user_id:
            raise Forbidden("You can't consult another person account.")

        data = request.json

        from_addr = mailutils.parse_email(account.address)
        all_addrs = [
            mailutils.parse_email(a["address"]) for a in data["recipients"]
        ]
        tos = [
            mailutils.parse_email(a["address"]) for a in data["recipients"]
            if a["type"] == "to"
        ]
        ccs = [
            mailutils.parse_email(a["address"]) for a in data["recipients"]
            if a["type"] == "cc"
        ]

        attachments = data.get("attachments", [])

        msg = mailutils.make_msg(data["subject"], data["content"], from_addr,
                                 tos, ccs, attachments)

        result = await send_mail(account, msg, from_addr, all_addrs)

        return json(result)
Пример #22
0
    async def unreads(request, user_id, account):
        if account.name != user_id:
            raise Forbidden("You can't consult another person account.")

        mbxs = await storage.get_unreads(account)

        return json(mbxs)
Пример #23
0
 async def mailbox(request, mailbox_id, account):
     mailbox_to_return = await storage.get_mailbox(mailbox_id)
     if mailbox_to_return['account'] != account.name:
         raise Forbidden("You don't have permission to see this mailbox.")
     for m in mailbox_to_return['messages']:
         if isinstance(m['date'], datetime.datetime): # Also strange hack
             m['date'] = m['date'].isoformat()
     return json(mailbox_to_return)
Пример #24
0
async def get_locked_user_number(request: Request) -> HTTPResponse:
    api_key = get_args(request, 'api_key')[0]
    api_key_status = await request.app.moca_access.check_api_key(
        api_key, '-SM-', request)
    if api_key_status[0] != 0:
        raise Forbidden(api_key_status[1])
    count = await request.app.moca_users.get_locked_user_number()
    return text(str(count))
Пример #25
0
async def get_user_phone_number(request: Request) -> HTTPResponse:
    api_key, userid_list = get_args(request, 'api_key', 'userid_list')
    api_key_status = await request.app.moca_access.check_api_key(
        api_key, '-SM-', request)
    if api_key_status[0] != 0:
        raise Forbidden(api_key_status[1])
    data = await request.app.moca_users.get_user_phone_number(userid_list)
    return json(data)
Пример #26
0
async def ip_blacklist_filter(request: Request):
    """
    Block all IPs in configs/ip_blacklist.json
    """
    if request.app.ip_blacklist.is_in(mzk.get_remote_address(request)):
        raise Forbidden("Your access was blocked by ip filter.")
    else:
        pass  # do nothing
Пример #27
0
async def search_users(request: Request) -> HTTPResponse:
    api_key, keywords = get_args(request, 'api_key', 'keywords')
    api_key_status = await request.app.moca_access.check_api_key(
        api_key, '-NR-', request)
    if api_key_status[0] != 0:
        raise Forbidden(api_key_status[1])
    data = await request.app.moca_users.search_users(keywords)
    return json(data)
Пример #28
0
async def visitor_update(req, *, req_args, req_body, requester, **kwargs):
    visitor_id = req_args["id"]

    # Only the visitor himself can modify
    if requester["id"] != visitor_id:
        raise Forbidden("Only the visitor himself can modify.")

    return {"data": await Visitor.modify(req_args, req_body)}
Пример #29
0
async def get_chat_logs(request: Request) -> HTTPResponse:
    check_root_pass(request)
    name, *_ = mzk.get_args(
        request,
        ('name|n', str, None, {
            'max_length': 32
        }),
    )
    if name is None:
        raise Forbidden('name parameter format error.')
    bot_id = request.app.dict_cache['name'].get(name)
    if bot_id is None:
        raise Forbidden('unknown bot name.')
    res = await request.app.mysql.execute_aio(core.GET_CHAT_LOGS_QUERY,
                                              (bot_id, ))
    return json([(item[0], item[1], item[2], item[3], str(item[4]), item[5],
                  item[6]) for item in res])
Пример #30
0
async def update_user(request, *args, **kwargs):
    if request.json is None:
        raise InvalidUsage("invalid payload (empty payload not allowed)")
    try:
        requested_user_id = int(request.path.split("/")[2])
    except ValueError as e:
        raise InvalidUsage(e)

    user_from_token = retrieve_user(request, args, kwargs)
    if user_from_token is None:
        raise InvalidUsage("invalid parameter (maybe expired?)")

    if (
        "admin" not in user_from_token.scopes
        and "manager" not in user_from_token.scopes
    ):
        if requested_user_id != user_from_token.user_id:
            raise Forbidden(f"user can only update self")

    user = User.get_by_user_id(requested_user_id)
    if not user:
        raise InvalidUsage("invalid parameter")

    if (
        "manager" in user_from_token.scopes
        and "admin" not in user_from_token.scopes
        and ("manager" in user.scopes or "admin" in user.scopes)
    ):
        if requested_user_id != user_from_token.user_id:
            raise Forbidden(f"manager can only update manager")

    if "password" in request.json:
        password = request.json["password"]
        if not password_validator(password):
            raise InvalidUsage("password does not match minimum requirements")

        user.update_password(encrypt(password))
    if "email" in request.json:
        user.update_email(request.json["email"])
    if "name" in request.json:
        user.update_name(request.json["name"])

    user.save(modifying_user_id=user_from_token.user_id)

    return response.HTTPResponse(status=204)