hyperpocket 0.1.9__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hyperpocket/__init__.py +4 -4
- hyperpocket/auth/__init__.py +12 -7
- hyperpocket/auth/calendly/oauth2_handler.py +24 -17
- hyperpocket/auth/calendly/oauth2_schema.py +3 -1
- hyperpocket/auth/context.py +2 -1
- hyperpocket/auth/github/oauth2_handler.py +13 -8
- hyperpocket/auth/github/token_handler.py +27 -21
- hyperpocket/auth/google/context.py +1 -3
- hyperpocket/auth/google/oauth2_context.py +1 -1
- hyperpocket/auth/google/oauth2_handler.py +22 -17
- hyperpocket/auth/gumloop/token_context.py +1 -4
- hyperpocket/auth/gumloop/token_handler.py +48 -20
- hyperpocket/auth/gumloop/token_schema.py +2 -1
- hyperpocket/auth/handler.py +21 -6
- hyperpocket/auth/linear/token_context.py +2 -5
- hyperpocket/auth/linear/token_handler.py +45 -21
- hyperpocket/auth/notion/context.py +2 -2
- hyperpocket/auth/notion/token_context.py +2 -4
- hyperpocket/auth/notion/token_handler.py +45 -21
- hyperpocket/auth/notion/token_schema.py +0 -1
- hyperpocket/auth/reddit/oauth2_handler.py +9 -10
- hyperpocket/auth/reddit/oauth2_schema.py +0 -2
- hyperpocket/auth/schema.py +4 -1
- hyperpocket/auth/slack/oauth2_context.py +3 -1
- hyperpocket/auth/slack/oauth2_handler.py +55 -35
- hyperpocket/auth/slack/token_context.py +2 -4
- hyperpocket/auth/slack/token_handler.py +42 -19
- hyperpocket/builtin.py +4 -2
- hyperpocket/cli/__main__.py +4 -2
- hyperpocket/cli/auth.py +59 -28
- hyperpocket/cli/codegen/auth/auth_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_handler_template.py +6 -5
- hyperpocket/cli/codegen/auth/auth_token_schema_template.py +3 -2
- hyperpocket/cli/codegen/auth/server_auth_template.py +3 -2
- hyperpocket/cli/pull.py +5 -5
- hyperpocket/config/__init__.py +3 -8
- hyperpocket/config/auth.py +3 -1
- hyperpocket/config/logger.py +20 -15
- hyperpocket/config/session.py +4 -2
- hyperpocket/config/settings.py +19 -2
- hyperpocket/futures/__init__.py +1 -1
- hyperpocket/futures/futurestore.py +3 -2
- hyperpocket/pocket_auth.py +171 -84
- hyperpocket/pocket_core.py +51 -33
- hyperpocket/pocket_main.py +122 -93
- hyperpocket/prompts.py +2 -2
- hyperpocket/repository/__init__.py +1 -1
- hyperpocket/repository/lock.py +47 -33
- hyperpocket/repository/lockfile.py +2 -2
- hyperpocket/repository/repository.py +1 -1
- hyperpocket/server/__init__.py +1 -1
- hyperpocket/server/auth/github.py +2 -1
- hyperpocket/server/auth/linear.py +1 -3
- hyperpocket/server/auth/notion.py +2 -5
- hyperpocket/server/auth/slack.py +1 -3
- hyperpocket/server/auth/token.py +17 -11
- hyperpocket/server/proxy.py +29 -13
- hyperpocket/server/server.py +75 -31
- hyperpocket/server/tool/dto/script.py +15 -10
- hyperpocket/server/tool/wasm.py +14 -11
- hyperpocket/session/__init__.py +6 -2
- hyperpocket/session/in_memory.py +44 -24
- hyperpocket/session/interface.py +42 -24
- hyperpocket/session/redis.py +48 -31
- hyperpocket/tool/__init__.py +10 -10
- hyperpocket/tool/function/__init__.py +1 -5
- hyperpocket/tool/function/annotation.py +11 -9
- hyperpocket/tool/function/tool.py +37 -27
- hyperpocket/tool/tool.py +59 -36
- hyperpocket/tool/wasm/__init__.py +1 -1
- hyperpocket/tool/wasm/browser.py +15 -10
- hyperpocket/tool/wasm/invoker.py +16 -16
- hyperpocket/tool/wasm/script.py +27 -14
- hyperpocket/tool/wasm/templates/__init__.py +22 -15
- hyperpocket/tool/wasm/templates/node.py +2 -2
- hyperpocket/tool/wasm/templates/python.py +2 -2
- hyperpocket/tool/wasm/tool.py +27 -14
- hyperpocket/tool_like.py +3 -3
- hyperpocket/util/__init__.py +1 -1
- hyperpocket/util/extract_func_param_desc_from_docstring.py +33 -14
- hyperpocket/util/find_all_leaf_class_in_package.py +4 -3
- hyperpocket/util/find_all_subclass_in_package.py +4 -2
- hyperpocket/util/flatten_json_schema.py +10 -6
- hyperpocket/util/function_to_model.py +33 -12
- hyperpocket/util/get_objects_from_subpackage.py +1 -1
- hyperpocket/util/json_schema_to_model.py +14 -5
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/METADATA +29 -24
- hyperpocket-0.2.0.dist-info/RECORD +137 -0
- hyperpocket-0.1.9.dist-info/RECORD +0 -137
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/entry_points.txt +0 -0
@@ -1,21 +1,26 @@
|
|
1
1
|
from typing import Optional
|
2
|
-
from urllib.parse import
|
2
|
+
from urllib.parse import urlencode, urljoin
|
3
3
|
|
4
4
|
from hyperpocket.auth import AuthProvider
|
5
5
|
from hyperpocket.auth.context import AuthContext
|
6
6
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
7
7
|
from hyperpocket.auth.linear.token_context import LinearTokenAuthContext
|
8
|
-
from hyperpocket.auth.linear.token_schema import
|
8
|
+
from hyperpocket.auth.linear.token_schema import LinearTokenRequest, LinearTokenResponse
|
9
9
|
from hyperpocket.config import config
|
10
10
|
from hyperpocket.futures import FutureStore
|
11
11
|
|
12
12
|
|
13
13
|
class LinearTokenAuthHandler(AuthHandlerInterface):
|
14
14
|
name: str = "linear-token"
|
15
|
-
description: str =
|
15
|
+
description: str = (
|
16
|
+
"This handler is used to authenticate users using the Linear token."
|
17
|
+
)
|
16
18
|
scoped: bool = False
|
17
19
|
|
18
|
-
_TOKEN_URL: str = urljoin(
|
20
|
+
_TOKEN_URL: str = urljoin(
|
21
|
+
config().public_base_url + "/",
|
22
|
+
f"{config().callback_url_rewrite_prefix}/auth/token",
|
23
|
+
)
|
19
24
|
|
20
25
|
@staticmethod
|
21
26
|
def provider() -> AuthProvider:
|
@@ -29,24 +34,37 @@ class LinearTokenAuthHandler(AuthHandlerInterface):
|
|
29
34
|
def recommended_scopes() -> set[str]:
|
30
35
|
return set()
|
31
36
|
|
32
|
-
def prepare(
|
33
|
-
|
37
|
+
def prepare(
|
38
|
+
self,
|
39
|
+
auth_req: LinearTokenRequest,
|
40
|
+
thread_id: str,
|
41
|
+
profile: str,
|
42
|
+
future_uid: str,
|
43
|
+
*args,
|
44
|
+
**kwargs,
|
45
|
+
) -> str:
|
34
46
|
redirect_uri = urljoin(
|
35
|
-
config.public_base_url + "/",
|
36
|
-
f"{config.callback_url_rewrite_prefix}/auth/linear/token/callback",
|
47
|
+
config().public_base_url + "/",
|
48
|
+
f"{config().callback_url_rewrite_prefix}/auth/linear/token/callback",
|
49
|
+
)
|
50
|
+
auth_url = self._make_auth_url(
|
51
|
+
auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid
|
52
|
+
)
|
53
|
+
FutureStore.create_future(
|
54
|
+
future_uid,
|
55
|
+
data={
|
56
|
+
"redirect_uri": redirect_uri,
|
57
|
+
"thread_id": thread_id,
|
58
|
+
"profile": profile,
|
59
|
+
},
|
37
60
|
)
|
38
|
-
auth_url = self._make_auth_url(auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid)
|
39
|
-
FutureStore.create_future(future_uid, data={
|
40
|
-
"redirect_uri": redirect_uri,
|
41
|
-
"thread_id": thread_id,
|
42
|
-
"profile": profile,
|
43
|
-
})
|
44
61
|
|
45
|
-
return f
|
62
|
+
return f"User needs to authenticate using the following URL: {auth_url}"
|
46
63
|
|
47
|
-
async def authenticate(
|
48
|
-
|
49
|
-
|
64
|
+
async def authenticate(
|
65
|
+
self, auth_req: LinearTokenResponse, future_uid: str, *args, **kwargs
|
66
|
+
) -> LinearTokenAuthContext:
|
67
|
+
future_data = FutureStore.get_future(future_uid)
|
50
68
|
access_token = await future_data.future
|
51
69
|
|
52
70
|
response = LinearTokenResponse(access_token=access_token)
|
@@ -54,15 +72,21 @@ class LinearTokenAuthHandler(AuthHandlerInterface):
|
|
54
72
|
|
55
73
|
return context
|
56
74
|
|
57
|
-
async def refresh(
|
75
|
+
async def refresh(
|
76
|
+
self, auth_req: LinearTokenRequest, context: AuthContext, *args, **kwargs
|
77
|
+
) -> AuthContext:
|
58
78
|
raise Exception("Linear token doesn't support refresh")
|
59
79
|
|
60
|
-
def _make_auth_url(
|
80
|
+
def _make_auth_url(
|
81
|
+
self, auth_req: LinearTokenRequest, redirect_uri: str, state: str
|
82
|
+
):
|
61
83
|
params = {
|
62
84
|
"redirect_uri": redirect_uri,
|
63
85
|
"state": state,
|
64
86
|
}
|
65
87
|
return f"{self._TOKEN_URL}?{urlencode(params)}"
|
66
88
|
|
67
|
-
def make_request(
|
89
|
+
def make_request(
|
90
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
91
|
+
) -> LinearTokenRequest:
|
68
92
|
return LinearTokenRequest()
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
1
|
from hyperpocket.auth.context import AuthContext
|
3
2
|
|
3
|
+
|
4
4
|
class NotionAuthContext(AuthContext):
|
5
5
|
_ACCESS_TOKEN_KEY: str = "NOTION_TOKEN"
|
6
6
|
|
@@ -12,4 +12,4 @@ class NotionAuthContext(AuthContext):
|
|
12
12
|
def to_profiled_dict(self, profile: str) -> dict[str, str]:
|
13
13
|
return {
|
14
14
|
f"{profile.upper()}_{self._ACCESS_TOKEN_KEY}": self.access_token,
|
15
|
-
}
|
15
|
+
}
|
@@ -5,10 +5,8 @@ from hyperpocket.auth.notion.token_schema import NotionTokenResponse
|
|
5
5
|
class NotionTokenAuthContext(NotionAuthContext):
|
6
6
|
@classmethod
|
7
7
|
def from_notion_token_response(cls, response: NotionTokenResponse):
|
8
|
-
description =
|
8
|
+
description = "Notion Token Context logged in"
|
9
9
|
|
10
10
|
return cls(
|
11
|
-
access_token=response.access_token,
|
12
|
-
description=description,
|
13
|
-
expires_at=None
|
11
|
+
access_token=response.access_token, description=description, expires_at=None
|
14
12
|
)
|
@@ -1,22 +1,26 @@
|
|
1
|
-
|
2
1
|
from typing import Optional
|
3
|
-
from urllib.parse import
|
2
|
+
from urllib.parse import urlencode, urljoin
|
4
3
|
|
5
4
|
from hyperpocket.auth import AuthProvider
|
6
5
|
from hyperpocket.auth.context import AuthContext
|
7
6
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
8
7
|
from hyperpocket.auth.notion.token_context import NotionTokenAuthContext
|
9
|
-
from hyperpocket.auth.notion.token_schema import
|
8
|
+
from hyperpocket.auth.notion.token_schema import NotionTokenRequest, NotionTokenResponse
|
10
9
|
from hyperpocket.config import config
|
11
10
|
from hyperpocket.futures import FutureStore
|
12
11
|
|
13
12
|
|
14
13
|
class NotionTokenAuthHandler(AuthHandlerInterface):
|
15
14
|
name: str = "notion-token"
|
16
|
-
description: str =
|
15
|
+
description: str = (
|
16
|
+
"This handler is used to authenticate users using the Notion token."
|
17
|
+
)
|
17
18
|
scoped: bool = False
|
18
19
|
|
19
|
-
_TOKEN_URL: str = urljoin(
|
20
|
+
_TOKEN_URL: str = urljoin(
|
21
|
+
config().public_base_url + "/",
|
22
|
+
f"{config().callback_url_rewrite_prefix}/auth/token",
|
23
|
+
)
|
20
24
|
|
21
25
|
@staticmethod
|
22
26
|
def provider() -> AuthProvider:
|
@@ -26,22 +30,36 @@ class NotionTokenAuthHandler(AuthHandlerInterface):
|
|
26
30
|
def recommended_scopes() -> set[str]:
|
27
31
|
return set()
|
28
32
|
|
29
|
-
def prepare(
|
30
|
-
|
33
|
+
def prepare(
|
34
|
+
self,
|
35
|
+
auth_req: NotionTokenRequest,
|
36
|
+
thread_id: str,
|
37
|
+
profile: str,
|
38
|
+
future_uid: str,
|
39
|
+
*args,
|
40
|
+
**kwargs,
|
41
|
+
) -> str:
|
31
42
|
redirect_uri = urljoin(
|
32
|
-
config.public_base_url + "/",
|
33
|
-
f"{config.callback_url_rewrite_prefix}/auth/notion/token/callback",
|
43
|
+
config().public_base_url + "/",
|
44
|
+
f"{config().callback_url_rewrite_prefix}/auth/notion/token/callback",
|
45
|
+
)
|
46
|
+
url = self._make_auth_url(
|
47
|
+
auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid
|
48
|
+
)
|
49
|
+
FutureStore.create_future(
|
50
|
+
future_uid,
|
51
|
+
data={
|
52
|
+
"redirect_uri": redirect_uri,
|
53
|
+
"thread_id": thread_id,
|
54
|
+
"profile": profile,
|
55
|
+
},
|
34
56
|
)
|
35
|
-
url = self._make_auth_url(auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid)
|
36
|
-
FutureStore.create_future(future_uid, data={
|
37
|
-
"redirect_uri": redirect_uri,
|
38
|
-
"thread_id": thread_id,
|
39
|
-
"profile": profile,
|
40
|
-
})
|
41
57
|
|
42
|
-
return f
|
58
|
+
return f"User needs to authenticate using the following URL: {url}"
|
43
59
|
|
44
|
-
async def authenticate(
|
60
|
+
async def authenticate(
|
61
|
+
self, auth_req: NotionTokenRequest, future_uid: str, *args, **kwargs
|
62
|
+
) -> AuthContext:
|
45
63
|
future_data = FutureStore.get_future(future_uid)
|
46
64
|
access_token = await future_data.future
|
47
65
|
|
@@ -50,10 +68,14 @@ class NotionTokenAuthHandler(AuthHandlerInterface):
|
|
50
68
|
|
51
69
|
return context
|
52
70
|
|
53
|
-
async def refresh(
|
71
|
+
async def refresh(
|
72
|
+
self, auth_req: NotionTokenRequest, context: AuthContext, *args, **kwargs
|
73
|
+
) -> AuthContext:
|
54
74
|
raise Exception("Notion token doesn't support refresh")
|
55
75
|
|
56
|
-
def _make_auth_url(
|
76
|
+
def _make_auth_url(
|
77
|
+
self, auth_req: NotionTokenRequest, redirect_uri: str, state: str
|
78
|
+
):
|
57
79
|
params = {
|
58
80
|
"redirect_uri": redirect_uri,
|
59
81
|
"state": state,
|
@@ -61,5 +83,7 @@ class NotionTokenAuthHandler(AuthHandlerInterface):
|
|
61
83
|
auth_url = f"{self._TOKEN_URL}?{urlencode(params)}"
|
62
84
|
return auth_url
|
63
85
|
|
64
|
-
def make_request(
|
65
|
-
|
86
|
+
def make_request(
|
87
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
88
|
+
) -> NotionTokenRequest:
|
89
|
+
return NotionTokenRequest()
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import base64
|
2
|
-
from os import access
|
3
2
|
from typing import Optional
|
4
|
-
from urllib.parse import
|
3
|
+
from urllib.parse import urlencode, urljoin
|
5
4
|
|
6
5
|
import httpx
|
7
6
|
|
@@ -10,8 +9,8 @@ from hyperpocket.auth.context import AuthContext
|
|
10
9
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
11
10
|
from hyperpocket.auth.reddit.oauth2_context import RedditOAuth2AuthContext
|
12
11
|
from hyperpocket.auth.reddit.oauth2_schema import (
|
13
|
-
RedditOAuth2Response,
|
14
12
|
RedditOAuth2Request,
|
13
|
+
RedditOAuth2Response,
|
15
14
|
)
|
16
15
|
from hyperpocket.config import config as config
|
17
16
|
from hyperpocket.futures import FutureStore
|
@@ -35,7 +34,7 @@ class RedditOAuth2AuthHandler(AuthHandlerInterface):
|
|
35
34
|
|
36
35
|
@staticmethod
|
37
36
|
def recommended_scopes() -> set[str]:
|
38
|
-
if config.auth.reddit.use_recommended_scope:
|
37
|
+
if config().auth.reddit.use_recommended_scope:
|
39
38
|
recommended_scopes = {"account", "identity", "read"}
|
40
39
|
else:
|
41
40
|
recommended_scopes = {}
|
@@ -51,8 +50,8 @@ class RedditOAuth2AuthHandler(AuthHandlerInterface):
|
|
51
50
|
**kwargs,
|
52
51
|
) -> str:
|
53
52
|
redirect_uri = urljoin(
|
54
|
-
config.public_base_url + "/",
|
55
|
-
f"{config.callback_url_rewrite_prefix}/auth/reddit/oauth2/callback",
|
53
|
+
config().public_base_url + "/",
|
54
|
+
f"{config().callback_url_rewrite_prefix}/auth/reddit/oauth2/callback",
|
56
55
|
)
|
57
56
|
print(f"redirect_uri: {redirect_uri}")
|
58
57
|
auth_url = self._make_auth_url(
|
@@ -108,8 +107,8 @@ class RedditOAuth2AuthHandler(AuthHandlerInterface):
|
|
108
107
|
resp = await client.post(
|
109
108
|
url=self._REDDIT_TOKEN_URL,
|
110
109
|
data={
|
111
|
-
"client_id": config.auth.reddit.client_id,
|
112
|
-
"client_secret": config.auth.reddit.client_secret,
|
110
|
+
"client_id": config().auth.reddit.client_id,
|
111
|
+
"client_secret": config().auth.reddit.client_secret,
|
113
112
|
"grant_type": "refresh_token",
|
114
113
|
"refresh_token": refresh_token,
|
115
114
|
},
|
@@ -146,6 +145,6 @@ class RedditOAuth2AuthHandler(AuthHandlerInterface):
|
|
146
145
|
) -> RedditOAuth2Request:
|
147
146
|
return RedditOAuth2Request(
|
148
147
|
auth_scopes=auth_scopes,
|
149
|
-
client_id=config.auth.reddit.client_id,
|
150
|
-
client_secret=config.auth.reddit.client_secret,
|
148
|
+
client_id=config().auth.reddit.client_id,
|
149
|
+
client_secret=config().auth.reddit.client_secret,
|
151
150
|
)
|
hyperpocket/auth/schema.py
CHANGED
@@ -7,13 +7,16 @@ class AuthenticateRequest(BaseModel):
|
|
7
7
|
"""
|
8
8
|
This class is used to define the interface of the authentication request.
|
9
9
|
"""
|
10
|
+
|
10
11
|
auth_scopes: Optional[list[str]] = Field(
|
11
12
|
default_factory=list,
|
12
|
-
description="authentication scopes. if the authentication handler is non scoped, it isn't needed"
|
13
|
+
description="authentication scopes. if the authentication handler is non scoped, it isn't needed",
|
14
|
+
)
|
13
15
|
|
14
16
|
|
15
17
|
class AuthenticateResponse(BaseModel):
|
16
18
|
"""
|
17
19
|
This class is used to define the interface of the authentication response.
|
18
20
|
"""
|
21
|
+
|
19
22
|
pass
|
@@ -12,7 +12,9 @@ class SlackOAuth2AuthContext(SlackAuthContext):
|
|
12
12
|
|
13
13
|
@classmethod
|
14
14
|
def from_slack_oauth2_response(cls, response: SlackOAuth2Response):
|
15
|
-
description =
|
15
|
+
description = (
|
16
|
+
f"Slack OAuth2 Context logged in as a user {response.authed_user.id}"
|
17
|
+
)
|
16
18
|
now = datetime.now(tz=timezone.utc)
|
17
19
|
|
18
20
|
# user token
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from typing import Optional
|
2
|
-
from urllib.parse import
|
2
|
+
from urllib.parse import urlencode, urljoin
|
3
3
|
|
4
4
|
import httpx
|
5
5
|
|
@@ -7,7 +7,7 @@ from hyperpocket.auth import AuthProvider
|
|
7
7
|
from hyperpocket.auth.context import AuthContext
|
8
8
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
9
9
|
from hyperpocket.auth.slack.oauth2_context import SlackOAuth2AuthContext
|
10
|
-
from hyperpocket.auth.slack.oauth2_schema import
|
10
|
+
from hyperpocket.auth.slack.oauth2_schema import SlackOAuth2Request, SlackOAuth2Response
|
11
11
|
from hyperpocket.config import config as config
|
12
12
|
from hyperpocket.futures import FutureStore
|
13
13
|
|
@@ -30,7 +30,7 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
30
30
|
|
31
31
|
@staticmethod
|
32
32
|
def recommended_scopes() -> set[str]:
|
33
|
-
if config.auth.slack.use_recommended_scope:
|
33
|
+
if config().auth.slack.use_recommended_scope:
|
34
34
|
recommended_scopes = {
|
35
35
|
"channels:history",
|
36
36
|
"channels:read",
|
@@ -46,24 +46,38 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
46
46
|
recommended_scopes = {}
|
47
47
|
return recommended_scopes
|
48
48
|
|
49
|
-
def prepare(
|
50
|
-
|
49
|
+
def prepare(
|
50
|
+
self,
|
51
|
+
auth_req: SlackOAuth2Request,
|
52
|
+
thread_id: str,
|
53
|
+
profile: str,
|
54
|
+
future_uid: str,
|
55
|
+
*args,
|
56
|
+
**kwargs,
|
57
|
+
) -> str:
|
51
58
|
redirect_uri = urljoin(
|
52
|
-
config.public_base_url + "/",
|
53
|
-
f"{config.callback_url_rewrite_prefix}/auth/slack/oauth2/callback",
|
59
|
+
config().public_base_url + "/",
|
60
|
+
f"{config().callback_url_rewrite_prefix}/auth/slack/oauth2/callback",
|
54
61
|
)
|
55
62
|
print(f"redirect_uri: {redirect_uri}")
|
56
|
-
auth_url = self._make_auth_url(
|
63
|
+
auth_url = self._make_auth_url(
|
64
|
+
req=auth_req, redirect_uri=redirect_uri, state=future_uid
|
65
|
+
)
|
57
66
|
|
58
|
-
FutureStore.create_future(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
FutureStore.create_future(
|
68
|
+
future_uid,
|
69
|
+
data={
|
70
|
+
"redirect_uri": redirect_uri,
|
71
|
+
"thread_id": thread_id,
|
72
|
+
"profile": profile,
|
73
|
+
},
|
74
|
+
)
|
63
75
|
|
64
|
-
return f
|
76
|
+
return f"User needs to authenticate using the following URL: {auth_url}"
|
65
77
|
|
66
|
-
async def authenticate(
|
78
|
+
async def authenticate(
|
79
|
+
self, auth_req: SlackOAuth2Request, future_uid: str, *args, **kwargs
|
80
|
+
) -> AuthContext:
|
67
81
|
future_data = FutureStore.get_future(future_uid)
|
68
82
|
auth_code = await future_data.future
|
69
83
|
|
@@ -71,11 +85,11 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
71
85
|
resp = await client.post(
|
72
86
|
url=self._SLACK_TOKEN_URL,
|
73
87
|
data={
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
}
|
88
|
+
"client_id": auth_req.client_id,
|
89
|
+
"client_secret": auth_req.client_secret,
|
90
|
+
"code": auth_code,
|
91
|
+
"redirect_uri": future_data.data["redirect_uri"],
|
92
|
+
},
|
79
93
|
)
|
80
94
|
if resp.status_code != 200:
|
81
95
|
raise Exception(f"failed to authenticate. status_code : {resp.status_code}")
|
@@ -87,7 +101,9 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
87
101
|
resp_typed = SlackOAuth2Response(**resp_json)
|
88
102
|
return SlackOAuth2AuthContext.from_slack_oauth2_response(resp_typed)
|
89
103
|
|
90
|
-
async def refresh(
|
104
|
+
async def refresh(
|
105
|
+
self, auth_req: SlackOAuth2Request, context: AuthContext, *args, **kwargs
|
106
|
+
) -> AuthContext:
|
91
107
|
slack_context: SlackOAuth2AuthContext = context
|
92
108
|
last_oauth2_resp: SlackOAuth2Response = slack_context.detail
|
93
109
|
refresh_token = slack_context.refresh_token
|
@@ -96,10 +112,10 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
96
112
|
resp = await client.post(
|
97
113
|
url=self._SLACK_TOKEN_URL,
|
98
114
|
data={
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
115
|
+
"client_id": config().auth.slack.client_id,
|
116
|
+
"client_secret": config().auth.slack.client_secret,
|
117
|
+
"grant_type": "refresh_token",
|
118
|
+
"refresh_token": refresh_token,
|
103
119
|
},
|
104
120
|
)
|
105
121
|
|
@@ -113,12 +129,14 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
113
129
|
if last_oauth2_resp.authed_user:
|
114
130
|
new_resp = last_oauth2_resp.model_copy(
|
115
131
|
update={
|
116
|
-
"authed_user": SlackOAuth2Response.AuthedUser(
|
117
|
-
**
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
132
|
+
"authed_user": SlackOAuth2Response.AuthedUser(
|
133
|
+
**{
|
134
|
+
**last_oauth2_resp.authed_user.model_dump(),
|
135
|
+
"access_token": resp_json["access_token"],
|
136
|
+
"refresh_token": resp_json["refresh_token"],
|
137
|
+
"expires_in": resp_json["expires_in"],
|
138
|
+
}
|
139
|
+
)
|
122
140
|
}
|
123
141
|
)
|
124
142
|
else:
|
@@ -135,7 +153,7 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
135
153
|
|
136
154
|
def _make_auth_url(self, req: SlackOAuth2Request, redirect_uri: str, state: str):
|
137
155
|
params = {
|
138
|
-
"user_scope":
|
156
|
+
"user_scope": ",".join(req.auth_scopes),
|
139
157
|
"client_id": req.client_id,
|
140
158
|
"redirect_uri": redirect_uri,
|
141
159
|
"state": state,
|
@@ -143,9 +161,11 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
|
|
143
161
|
auth_url = f"{self._SLACK_OAUTH_URL}?{urlencode(params)}"
|
144
162
|
return auth_url
|
145
163
|
|
146
|
-
def make_request(
|
164
|
+
def make_request(
|
165
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
166
|
+
) -> SlackOAuth2Request:
|
147
167
|
return SlackOAuth2Request(
|
148
168
|
auth_scopes=auth_scopes,
|
149
|
-
client_id=config.auth.slack.client_id,
|
150
|
-
client_secret=config.auth.slack.client_secret,
|
169
|
+
client_id=config().auth.slack.client_id,
|
170
|
+
client_secret=config().auth.slack.client_secret,
|
151
171
|
)
|
@@ -5,10 +5,8 @@ from hyperpocket.auth.slack.token_schema import SlackTokenResponse
|
|
5
5
|
class SlackTokenAuthContext(SlackAuthContext):
|
6
6
|
@classmethod
|
7
7
|
def from_slack_token_response(cls, response: SlackTokenResponse):
|
8
|
-
description =
|
8
|
+
description = "Slack Token Context logged in"
|
9
9
|
|
10
10
|
return cls(
|
11
|
-
access_token=response.access_token,
|
12
|
-
description=description,
|
13
|
-
expires_at=None
|
11
|
+
access_token=response.access_token, description=description, expires_at=None
|
14
12
|
)
|
@@ -1,21 +1,26 @@
|
|
1
1
|
from typing import Optional
|
2
|
-
from urllib.parse import
|
2
|
+
from urllib.parse import urlencode, urljoin
|
3
3
|
|
4
4
|
from hyperpocket.auth import AuthProvider
|
5
5
|
from hyperpocket.auth.context import AuthContext
|
6
6
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
7
7
|
from hyperpocket.auth.slack.token_context import SlackTokenAuthContext
|
8
|
-
from hyperpocket.auth.slack.token_schema import
|
8
|
+
from hyperpocket.auth.slack.token_schema import SlackTokenRequest, SlackTokenResponse
|
9
9
|
from hyperpocket.config import config
|
10
10
|
from hyperpocket.futures import FutureStore
|
11
11
|
|
12
12
|
|
13
13
|
class SlackTokenAuthHandler(AuthHandlerInterface):
|
14
14
|
name: str = "slack-token"
|
15
|
-
description: str =
|
15
|
+
description: str = (
|
16
|
+
"This handler is used to authenticate users using the Slack token."
|
17
|
+
)
|
16
18
|
scoped: bool = False
|
17
19
|
|
18
|
-
_TOKEN_URL: str = urljoin(
|
20
|
+
_TOKEN_URL: str = urljoin(
|
21
|
+
config().public_base_url + "/",
|
22
|
+
f"{config().callback_url_rewrite_prefix}/auth/token",
|
23
|
+
)
|
19
24
|
|
20
25
|
@staticmethod
|
21
26
|
def provider() -> AuthProvider:
|
@@ -25,23 +30,37 @@ class SlackTokenAuthHandler(AuthHandlerInterface):
|
|
25
30
|
def recommended_scopes() -> set[str]:
|
26
31
|
return set()
|
27
32
|
|
28
|
-
def prepare(
|
29
|
-
|
33
|
+
def prepare(
|
34
|
+
self,
|
35
|
+
auth_req: SlackTokenRequest,
|
36
|
+
thread_id: str,
|
37
|
+
profile: str,
|
38
|
+
future_uid: str,
|
39
|
+
*args,
|
40
|
+
**kwargs,
|
41
|
+
) -> str:
|
30
42
|
redirect_uri = urljoin(
|
31
|
-
config.public_base_url + "/",
|
32
|
-
f"{config.callback_url_rewrite_prefix}/auth/slack/token/callback",
|
43
|
+
config().public_base_url + "/",
|
44
|
+
f"{config().callback_url_rewrite_prefix}/auth/slack/token/callback",
|
45
|
+
)
|
46
|
+
url = self._make_auth_url(
|
47
|
+
req=auth_req, redirect_uri=redirect_uri, state=future_uid
|
48
|
+
)
|
49
|
+
FutureStore.create_future(
|
50
|
+
future_uid,
|
51
|
+
data={
|
52
|
+
"redirect_uri": redirect_uri,
|
53
|
+
"thread_id": thread_id,
|
54
|
+
"profile": profile,
|
55
|
+
},
|
33
56
|
)
|
34
|
-
url = self._make_auth_url(req=auth_req, redirect_uri=redirect_uri, state=future_uid)
|
35
|
-
FutureStore.create_future(future_uid, data={
|
36
|
-
"redirect_uri": redirect_uri,
|
37
|
-
"thread_id": thread_id,
|
38
|
-
"profile": profile,
|
39
|
-
})
|
40
57
|
|
41
|
-
return f
|
58
|
+
return f"User needs to authenticate using the following URL: {url}"
|
42
59
|
|
43
|
-
async def authenticate(
|
44
|
-
|
60
|
+
async def authenticate(
|
61
|
+
self, auth_req: SlackTokenRequest, future_uid: str, *args, **kwargs
|
62
|
+
) -> AuthContext:
|
63
|
+
future_data = FutureStore.get_future(future_uid)
|
45
64
|
access_token = await future_data.future
|
46
65
|
|
47
66
|
response = SlackTokenResponse(access_token=access_token)
|
@@ -49,7 +68,9 @@ class SlackTokenAuthHandler(AuthHandlerInterface):
|
|
49
68
|
|
50
69
|
return context
|
51
70
|
|
52
|
-
async def refresh(
|
71
|
+
async def refresh(
|
72
|
+
self, auth_req: SlackTokenRequest, context: AuthContext, *args, **kwargs
|
73
|
+
) -> AuthContext:
|
53
74
|
raise Exception("Slack token doesn't support refresh")
|
54
75
|
|
55
76
|
def _make_auth_url(self, req: SlackTokenRequest, redirect_uri: str, state: str):
|
@@ -60,5 +81,7 @@ class SlackTokenAuthHandler(AuthHandlerInterface):
|
|
60
81
|
auth_url = f"{self._TOKEN_URL}?{urlencode(params)}"
|
61
82
|
return auth_url
|
62
83
|
|
63
|
-
def make_request(
|
84
|
+
def make_request(
|
85
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
86
|
+
) -> SlackTokenRequest:
|
64
87
|
return SlackTokenRequest()
|
hyperpocket/builtin.py
CHANGED
@@ -2,7 +2,7 @@ from typing import List
|
|
2
2
|
|
3
3
|
from hyperpocket.auth import AuthProvider
|
4
4
|
from hyperpocket.pocket_auth import PocketAuth
|
5
|
-
from hyperpocket.tool import
|
5
|
+
from hyperpocket.tool import Tool, from_func
|
6
6
|
|
7
7
|
|
8
8
|
def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
|
@@ -34,7 +34,9 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
|
|
34
34
|
session_list = pocket_auth.list_session_state(thread_id)
|
35
35
|
return str(session_list)
|
36
36
|
|
37
|
-
def __delete_session(
|
37
|
+
def __delete_session(
|
38
|
+
auth_provider_name: str, thread_id: str = "default", profile: str = "default"
|
39
|
+
) -> str:
|
38
40
|
"""
|
39
41
|
This tool deletes a saved session for a specified authentication provider in the given thread and profile.
|
40
42
|
|