mirror of
https://github.com/ArcaneChat/arcanechat-tui.git
synced 2026-07-03 14:05:26 +02:00
Merge pull request #10 from mmoya/oauth2-support
Add support for OAuth2 accounts
This commit is contained in:
@@ -6,9 +6,11 @@ import os
|
||||
import sys
|
||||
|
||||
from .event import AccountPlugin
|
||||
from .oauth2 import is_oauth2, get_authz_code
|
||||
from .ui import CursedDelta
|
||||
|
||||
from deltachat import Account, eventlogger
|
||||
from deltachat import Account, events
|
||||
import deltachat.const
|
||||
|
||||
|
||||
__version__ = '0.1.0'
|
||||
@@ -262,6 +264,9 @@ def main():
|
||||
"--set-conf", action="store", help="set config option", nargs=2)
|
||||
parser.add_argument(
|
||||
"--get-conf", action="store", help="get config option")
|
||||
parser.add_argument(
|
||||
"--port", action="store", help="port to listen for oauth2 callback",
|
||||
type=int, default='8383')
|
||||
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
@@ -272,18 +277,35 @@ def main():
|
||||
return
|
||||
|
||||
if args.show_ffi:
|
||||
log = eventlogger.FFIEventLogger(ac, "CursedDelta")
|
||||
log = events.FFIEventLogger(ac, "CursedDelta")
|
||||
ac.add_account_plugin(log)
|
||||
|
||||
if not ac.is_configured():
|
||||
assert args.email and args.password, (
|
||||
"you must specify --email and --password once to"
|
||||
" configure this database/account"
|
||||
assert args.email, (
|
||||
"you must specify --email once"
|
||||
" to configure this database/account"
|
||||
)
|
||||
ac.set_config("addr", args.email)
|
||||
ac.set_config("mail_pw", args.password)
|
||||
ac.set_config("mvbox_watch", "0")
|
||||
ac.set_config("sentbox_watch", "0")
|
||||
|
||||
if is_oauth2(ac, args.email):
|
||||
authz_code = get_authz_code(ac, args.email, args.port)
|
||||
|
||||
ac.set_config("mail_pw", authz_code)
|
||||
|
||||
flags = ac.get_config('server_flags')
|
||||
flags = int(flags) if flags else 0
|
||||
flags |= deltachat.const.DC_LP_AUTH_OAUTH2
|
||||
ac.set_config('server_flags', str(flags))
|
||||
else:
|
||||
assert args.password, (
|
||||
"you must specify --password once"
|
||||
" to configure this database/account"
|
||||
)
|
||||
ac.set_config("mail_pw", args.email)
|
||||
ac.set_config("mvbox_watch", "0")
|
||||
ac.set_config("sentbox_watch", "0")
|
||||
|
||||
ac.configure()
|
||||
|
||||
if args.set_conf:
|
||||
ac.set_config(*args.set_conf)
|
||||
@@ -291,7 +313,7 @@ def main():
|
||||
account = AccountPlugin(ac)
|
||||
ac.add_account_plugin(account)
|
||||
|
||||
ac.start()
|
||||
ac.start_io()
|
||||
|
||||
keymap = get_keymap()
|
||||
theme = get_theme()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
from itertools import count
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
import webbrowser
|
||||
import wsgiref.simple_server
|
||||
|
||||
from deltachat.capi import ffi
|
||||
|
||||
|
||||
def is_oauth2(ac, addr):
|
||||
return get_oauth2_url(ac, addr, '') is not None
|
||||
|
||||
|
||||
def get_oauth2_url(ac, addr, redirect_uri):
|
||||
buf = ffi.dlopen(None).dc_get_oauth2_url(
|
||||
ac._dc_context,
|
||||
addr.encode('utf-8'),
|
||||
redirect_uri.encode('utf-8'))
|
||||
|
||||
if buf == ffi.NULL:
|
||||
return None
|
||||
|
||||
chars = []
|
||||
for i in count():
|
||||
if buf[i] == b'\x00':
|
||||
break
|
||||
chars.append(ord(buf[i]))
|
||||
|
||||
return bytes(chars).decode('utf-8')
|
||||
|
||||
|
||||
# borrowed from google-auth-library-python-oauthlib
|
||||
def get_authz_code(ac, addr, port,
|
||||
authorization_prompt_message=None,
|
||||
success_message=None,
|
||||
open_browser=True):
|
||||
"""Run a local server for receiving the authorization code and return it"""
|
||||
|
||||
if authorization_prompt_message is None:
|
||||
authorization_prompt_message = "Please visit this URL to authorize this application: {url}"
|
||||
|
||||
if success_message is None:
|
||||
success_message = "The authorization code was received. You may close this window."
|
||||
|
||||
auth_url = get_oauth2_url(ac, addr, f'http://127.0.0.1:{port}/')
|
||||
|
||||
wsgi_app = _RedirectWSGIApp(success_message)
|
||||
local_server = wsgiref.simple_server.make_server(
|
||||
'127.0.0.1', port, wsgi_app
|
||||
)
|
||||
|
||||
if open_browser:
|
||||
webbrowser.open(auth_url, new=1, autoraise=True)
|
||||
|
||||
print(authorization_prompt_message.format(url=auth_url))
|
||||
|
||||
local_server.handle_request()
|
||||
|
||||
authorization_response = wsgi_app.last_request_uri
|
||||
|
||||
res_params = parse_qs(urlparse(authorization_response).query)
|
||||
assert 'code' in res_params and res_params['code'], "authorization code not found in url"
|
||||
|
||||
return res_params['code'][0]
|
||||
|
||||
|
||||
class _RedirectWSGIApp(object):
|
||||
"""WSGI app to handle the authorization redirect.
|
||||
Stores the request URI and displays the given success message.
|
||||
"""
|
||||
|
||||
def __init__(self, success_message):
|
||||
"""
|
||||
Args:
|
||||
success_message (str): The message to display in the web browser
|
||||
the authorization flow is complete.
|
||||
"""
|
||||
self.last_request_uri = None
|
||||
self._success_message = success_message
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""WSGI Callable.
|
||||
Args:
|
||||
environ (Mapping[str, Any]): The WSGI environment.
|
||||
start_response (Callable[str, list]): The WSGI start_response
|
||||
callable.
|
||||
Returns:
|
||||
Iterable[bytes]: The response body.
|
||||
"""
|
||||
start_response("200 OK", [("Content-type", "text/plain")])
|
||||
self.last_request_uri = wsgiref.util.request_uri(environ)
|
||||
return [self._success_message.encode("utf-8")]
|
||||
Reference in New Issue
Block a user