add auth check and config docs

This commit is contained in:
yanyongyu 2020-08-20 16:34:07 +08:00
parent 02ca49f6d0
commit 26c697f5e4
5 changed files with 274 additions and 25 deletions

View File

@ -23,10 +23,12 @@ module.exports = {
themeConfig: {
repo: "nonebot/nonebot",
docsDir: "docs",
docsBranch: "dev2",
editLinks: true,
editLinkText: "在 GitHub 上编辑此页",
docsDir: "docs",
lastUpdated: "上次更新",
smoothScroll: true,
nav: [{ text: "API", link: "/api/" }],
sidebar: {
"/api/": [

View File

@ -1,5 +1,11 @@
# NoneBot.config 模块
## 配置
NoneBot 使用 [pydantic](https://pydantic-docs.helpmanual.io/) 以及 [python-dotenv](https://saurabh-kumar.com/python-dotenv/) 来读取配置。
配置项需符合特殊格式或 json 序列化格式。详情见 [pydantic Field Type](https://pydantic-docs.helpmanual.io/usage/types/) 文档
## _class_ `Env`
@ -29,7 +35,8 @@
NoneBot 主要配置。大小写不敏感。
除了 NoneBot 的配置项外,还可以自行添加配置项到 `.env.{environment}` 文件中。这些配置将会一起带入 `Config` 类中。
除了 NoneBot 的配置项外,还可以自行添加配置项到 `.env.{environment}` 文件中。
这些配置将会在 json 反序列化后一起带入 `Config` 类中。
### `driver`
@ -90,3 +97,143 @@ NoneBot 的 HTTP 和 WebSocket 服务端监听的端口。
POST /cqhttp/ HTTP/1.1
Authorization: Bearer kSLuTF2GC2Q4q4ugm3
```
### `debug`
* 类型: `bool`
* 默认值: `False`
* 说明:
是否以调试模式运行 NoneBot。
### `api_root`
* 类型: `Dict[str, str]`
* 默认值: `{}`
* 说明:
以机器人 ID 为键,上报地址为值的字典,环境变量或文件中应使用 json 序列化。
* 示例:
```plain
API_ROOT={"123456": "http://127.0.0.1:5700"}
```
### `api_timeout`
* 类型: `Optional[float]`
* 默认值: `60.`
* 说明:
API 请求超时时间,单位: 秒。
### `access_token`
* 类型: `Optional[str]`
* 默认值: `None`
* 说明:
API 请求所需密钥,会在调用 API 时在请求头中携带。
### `superusers`
* 类型: `Set[int]`
* 默认值: `set()`
* 说明:
机器人超级用户。
* 示例:
```plain
SUPER_USERS=[12345789]
```
### `nickname`
* 类型: `Union[str, Set[str]]`
* 默认值: `""`
* 说明:
机器人昵称。
### `command_start`
* 类型: `Set[str]`
* 默认值: `{"/"}`
* 说明:
命令的起始标记,用于判断一条消息是不是命令。
### `command_sep`
* 类型: `Set[str]`
* 默认值: `{"."}`
* 说明:
命令的分隔标记,用于将文本形式的命令切分为元组(实际的命令名)。
### `session_expire_timeout`
* 类型: `timedelta`
* 默认值: `timedelta(minutes=2)`
* 说明:
等待用户回复的超时时间。
* 示例:
```plain
SESSION_EXPIRE_TIMEOUT=120 # 单位: 秒
SESSION_EXPIRE_TIMEOUT=[DD ][HH:MM]SS[.ffffff]
SESSION_EXPIRE_TIMEOUT=P[DD]DT[HH]H[MM]M[SS]S # ISO 8601
```

View File

@ -67,7 +67,7 @@ class ResultStore:
future.set_result(result)
@classmethod
async def fetch(cls, seq: int, timeout: float) -> Dict[str, Any]:
async def fetch(cls, seq: int, timeout: Optional[float]) -> Dict[str, Any]:
future = asyncio.get_event_loop().create_future()
cls._futures[seq] = future
try:
@ -137,7 +137,7 @@ class Bot(BaseBot):
api_root += "/"
headers = {}
if self.config.access_token:
if self.config.access_token is not None:
headers["Authorization"] = "Bearer " + self.config.access_token
try:

View File

@ -1,5 +1,20 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置
====
NoneBot 使用 `pydantic`_ 以及 `python-dotenv`_ 来读取配置
配置项需符合特殊格式或 json 序列化格式详情见 `pydantic Field Type`_ 文档
.. _pydantic:
https://pydantic-docs.helpmanual.io/
.. _python-dotenv:
https://saurabh-kumar.com/python-dotenv/
.. _pydantic Field Type:
https://pydantic-docs.helpmanual.io/usage/types/
"""
import os
from pathlib import Path
@ -100,7 +115,8 @@ class Config(BaseConfig):
"""
NoneBot 主要配置大小写不敏感
除了 NoneBot 的配置项外还可以自行添加配置项到 ``.env.{environment}`` 文件中这些配置将会一起带入 ``Config`` 类中
除了 NoneBot 的配置项外还可以自行添加配置项到 ``.env.{environment}`` 文件中
这些配置将会在 json 反序列化后一起带入 ``Config`` 类中
"""
# nonebot configs
driver: str = "nonebot.drivers.fastapi"
@ -147,15 +163,80 @@ class Config(BaseConfig):
# bot connection configs
api_root: Dict[str, str] = {}
"""
- 类型: ``Dict[str, str]``
- 默认值: ``{}``
- 说明:
以机器人 ID 为键上报地址为值的字典环境变量或文件中应使用 json 序列化
- 示例:
.. code-block:: plain
API_ROOT={"123456": "http://127.0.0.1:5700"}
"""
api_timeout: Optional[float] = 60.
"""
- 类型: ``Optional[float]``
- 默认值: ``60.``
- 说明:
API 请求超时时间单位:
"""
access_token: Optional[str] = None
"""
- 类型: ``Optional[str]``
- 默认值: ``None``
- 说明:
API 请求所需密钥会在调用 API 时在请求头中携带
"""
# bot runtime configs
superusers: Set[int] = set()
"""
- 类型: ``Set[int]``
- 默认值: ``set()``
- 说明:
机器人超级用户
- 示例:
.. code-block:: plain
SUPER_USERS=[12345789]
"""
nickname: Union[str, Set[str]] = ""
"""
- 类型: ``Union[str, Set[str]]``
- 默认值: ``""``
- 说明:
机器人昵称
"""
command_start: Set[str] = {"/"}
"""
- 类型: ``Set[str]``
- 默认值: ``{"/"}``
- 说明:
命令的起始标记用于判断一条消息是不是命令
"""
command_sep: Set[str] = {"."}
"""
- 类型: ``Set[str]``
- 默认值: ``{"."}``
- 说明:
命令的分隔标记用于将文本形式的命令切分为元组实际的命令名
"""
session_expire_timeout: timedelta = timedelta(minutes=2)
"""
- 类型: ``timedelta``
- 默认值: ``timedelta(minutes=2)``
- 说明:
等待用户回复的超时时间
- 示例:
.. code-block:: plain
SESSION_EXPIRE_TIMEOUT=120 # 单位: 秒
SESSION_EXPIRE_TIMEOUT=[DD ][HH:MM]SS[.ffffff]
SESSION_EXPIRE_TIMEOUT=P[DD]DT[HH]H[MM]M[SS]S # ISO 8601
"""
# custom configs
# custom configs can be assigned during nonebot.init

View File

@ -5,10 +5,9 @@ import json
import logging
import uvicorn
from fastapi import FastAPI, status
from fastapi.security import OAuth2PasswordBearer
from starlette.websockets import WebSocketDisconnect
from fastapi import Body, Header, Response, WebSocket as FastAPIWebSocket
from fastapi import FastAPI, status, HTTPException
from fastapi import Body, Header, Response, Depends
from starlette.websockets import WebSocketDisconnect, WebSocket as FastAPIWebSocket
from nonebot.log import logger
from nonebot.config import Env, Config
@ -18,6 +17,18 @@ from nonebot.drivers import BaseDriver, BaseWebSocket
from nonebot.typing import Union, Optional, Callable, overrides
def get_auth_bearer(access_token: Optional[str] = Header(
None, alias="Authorization")):
if not access_token:
return None
scheme, _, param = access_token.partition(" ")
if scheme.lower() != "bearer":
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"})
return param
class Driver(BaseDriver):
def __init__(self, env: Env, config: Config):
@ -106,14 +117,18 @@ class Driver(BaseDriver):
**kwargs)
@overrides(BaseDriver)
async def _handle_http(self,
adapter: str,
response: Response,
data: dict = Body(...),
x_self_id: str = Header(None),
access_token: str = OAuth2PasswordBearer(
"/", auto_error=False)):
# TODO: Check authorization
async def _handle_http(
self,
adapter: str,
response: Response,
data: dict = Body(...),
x_self_id: str = Header(None),
access_token: Optional[str] = Depends(get_auth_bearer)):
secret = self.config.secret
if secret is not None and secret != access_token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"})
# Create Bot Object
if adapter in self._adapters:
@ -127,15 +142,19 @@ class Driver(BaseDriver):
return {"status": 200, "message": "success"}
@overrides(BaseDriver)
async def _handle_ws_reverse(self,
adapter: str,
websocket: FastAPIWebSocket,
x_self_id: str = Header(None),
access_token: str = OAuth2PasswordBearer(
"/", auto_error=False)):
websocket = WebSocket(websocket)
async def _handle_ws_reverse(
self,
adapter: str,
websocket: FastAPIWebSocket,
x_self_id: str = Header(None),
access_token: Optional[str] = Depends(get_auth_bearer)):
secret = self.config.secret
if secret is not None and secret != access_token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"})
# TODO: Check authorization
websocket = WebSocket(websocket)
# Create Bot Object
if adapter == "coolq":