hyperpocket 0.1.10__py3-none-any.whl → 0.2.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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 +23 -7
- 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.10.dist-info → hyperpocket-0.2.1.dist-info}/METADATA +11 -5
- hyperpocket-0.2.1.dist-info/RECORD +137 -0
- hyperpocket-0.1.10.dist-info/RECORD +0 -137
- {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.1.dist-info}/WHEEL +0 -0
- {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.1.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
|
|