Merge pull request #10 from mmoya/oauth2-support

Add support for OAuth2 accounts
This commit is contained in:
Asiel Díaz Benítez
2020-06-01 11:44:59 -04:00
committed by GitHub
2 changed files with 122 additions and 9 deletions
+31 -9
View File
@@ -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()
+91
View File
@@ -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")]