mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
Initial OAuth2 (Google variant) support for dav storage
This commits adds 'oauth2_google' authentication mechanism to dav storage driver.
This commit is contained in:
parent
4cb7e6c1f5
commit
6d8db949fa
4 changed files with 95 additions and 5 deletions
|
|
@ -207,5 +207,28 @@ Vdirsyncer is continuously tested against the latest version of Baikal_.
|
|||
Google
|
||||
------
|
||||
|
||||
Vdirsyncer doesn't currently support Google accounts fully. For possible
|
||||
solutions see :gh:`202` and :gh:`8`.
|
||||
Using vdirsyncer with Google Calendar is possible, but it is not tested
|
||||
frequently.
|
||||
|
||||
::
|
||||
|
||||
[storage cal]
|
||||
type = caldav
|
||||
url = https://apidata.googleusercontent.com/caldav/v2/
|
||||
auth = oauth2_google
|
||||
|
||||
[storage card]
|
||||
type = carddav
|
||||
url = https://www.googleapis.com/carddav/v1/principals/EMAIL/lists/default
|
||||
auth = oauth2_google
|
||||
|
||||
At first run you will be asked to authorize application for google account
|
||||
access. Simply follow the instructions. You'll be asked to modify configuration
|
||||
file (save `refresh_token` as a password).
|
||||
|
||||
- Google's CardDav implementation is very limited, may lead to data loss, use
|
||||
with care.
|
||||
- You can select which calendars to sync on
|
||||
`CalDav settings page <https://calendar.google.com/calendar/syncselect>`_
|
||||
|
||||
For more information see :gh:`202` and :gh:`8`.
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -80,7 +80,8 @@ setup(
|
|||
},
|
||||
install_requires=requirements,
|
||||
extras_require={
|
||||
'remotestorage': ['requests-oauthlib']
|
||||
'remotestorage': ['requests-oauthlib'],
|
||||
'oauth2': ['requests-oauthlib'],
|
||||
},
|
||||
cmdclass={
|
||||
'minimal_requirements': PrintRequirements
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
import datetime
|
||||
import logging
|
||||
|
||||
import click
|
||||
|
||||
from lxml import etree
|
||||
|
||||
import requests
|
||||
|
|
@ -14,6 +16,15 @@ from .http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \
|
|||
from .. import exceptions, utils
|
||||
from ..utils.compat import text_type, to_native
|
||||
|
||||
OAUTH2_GOOGLE_TOKEN_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
|
||||
OAUTH2_GOOGLE_REFRESH_URL = 'https://www.googleapis.com/oauth2/v4/token'
|
||||
have_oauth2 = True
|
||||
try:
|
||||
from requests_oauthlib import OAuth2Session
|
||||
oauth2_client_id = 'FIXME'
|
||||
oauth2_client_secret = 'FIXME'
|
||||
except ImportError:
|
||||
have_oauth2 = False
|
||||
|
||||
dav_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -301,7 +312,6 @@ class DavSession(object):
|
|||
useragent=USERAGENT, verify_fingerprint=None,
|
||||
auth_cert=None):
|
||||
self._settings = {
|
||||
'auth': prepare_auth(auth, username, password),
|
||||
'cert': prepare_client_cert(auth_cert),
|
||||
}
|
||||
self._settings.update(prepare_verify(verify, verify_fingerprint))
|
||||
|
|
@ -310,6 +320,21 @@ class DavSession(object):
|
|||
self.url = url.rstrip('/') + '/'
|
||||
self.parsed_url = utils.compat.urlparse.urlparse(self.url)
|
||||
self._session = None
|
||||
self._token = None
|
||||
self._use_oauth2_google = False
|
||||
if auth == 'oauth2_google':
|
||||
if not have_oauth2:
|
||||
raise exceptions.UserError("requests-oauthlib not installed")
|
||||
if password:
|
||||
self._token = {
|
||||
'refresh_token': password,
|
||||
# Will be derived from refresh_token
|
||||
'access_token': 'dummy',
|
||||
'expires_in': -30
|
||||
}
|
||||
self._use_oauth2_google = True
|
||||
else:
|
||||
self._settings['auth'] = prepare_auth(auth, username, password)
|
||||
|
||||
def request(self, method, path, **kwargs):
|
||||
url = self.url
|
||||
|
|
@ -317,6 +342,44 @@ class DavSession(object):
|
|||
url = utils.compat.urlparse.urljoin(self.url, path)
|
||||
if self._session is None:
|
||||
self._session = requests.session()
|
||||
if self._use_oauth2_google:
|
||||
self._session = OAuth2Session(
|
||||
client_id=oauth2_client_id,
|
||||
token=self._token,
|
||||
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
|
||||
scope=['https://www.googleapis.com/auth/calendar',
|
||||
'https://www.googleapis.com/auth/carddav'],
|
||||
auto_refresh_url=OAUTH2_GOOGLE_REFRESH_URL,
|
||||
auto_refresh_kwargs={
|
||||
'client_id': oauth2_client_id,
|
||||
'client_secret': oauth2_client_secret,
|
||||
},
|
||||
token_updater=lambda x: None
|
||||
)
|
||||
if not self._token:
|
||||
authorization_url, state = self._session.authorization_url(
|
||||
OAUTH2_GOOGLE_TOKEN_URL,
|
||||
# access_type and approval_prompt are Google specific
|
||||
# extra parameters.
|
||||
access_type="offline", approval_prompt="force")
|
||||
click.echo('Opening {} ...'.format(authorization_url))
|
||||
try:
|
||||
utils.open_graphical_browser(authorization_url)
|
||||
except Exception as e:
|
||||
dav_logger.warning(str(e))
|
||||
|
||||
click.echo("Follow the instructions on the page.")
|
||||
code = click.prompt("Paste obtained code")
|
||||
self._token = self._session.fetch_token(
|
||||
OAUTH2_GOOGLE_REFRESH_URL,
|
||||
code=code,
|
||||
# Google specific extra parameter used for client
|
||||
# authentication
|
||||
client_secret=oauth2_client_secret,
|
||||
)
|
||||
raise exceptions.UserError(
|
||||
"Set the following token in a password field: {}".
|
||||
format(self._token['refresh_token']))
|
||||
|
||||
more = dict(self._settings)
|
||||
more.update(kwargs)
|
||||
|
|
|
|||
|
|
@ -82,7 +82,10 @@ HTTP_STORAGE_PARAMETERS = '''
|
|||
information.
|
||||
:param auth: Optional. Either ``basic``, ``digest`` or ``guess``. Default
|
||||
``guess``. If you know yours, consider setting it explicitly for
|
||||
performance.
|
||||
performance. For caldav and carddav, additionaly ``oauth2_google`` is
|
||||
supported. ``password`` setting should point a file for OAuth2 token
|
||||
storage (directory must already exists, but file itself will be created
|
||||
automatically).
|
||||
:param auth_cert: Optional. Either a path to a certificate with a client
|
||||
certificate and the key or a list of paths to the files with them.
|
||||
:param useragent: Default ``vdirsyncer``.
|
||||
|
|
|
|||
Loading…
Reference in a new issue