Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle revoked oauth permissions by the user #4074

Merged
merged 10 commits into from Jun 14, 2018
17 changes: 13 additions & 4 deletions readthedocs/notifications/backends.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Pluggable backends for the delivery of notifications.

Expand All @@ -6,7 +7,9 @@
displayed on the site.
"""

from __future__ import absolute_import
from __future__ import (
absolute_import, division, print_function, unicode_literals)

from builtins import object
from django.conf import settings
from django.http import HttpRequest
Expand All @@ -15,7 +18,7 @@

from readthedocs.core.utils import send_email

from .constants import LEVEL_MAPPING, REQUIREMENT, HTML
from .constants import HTML, LEVEL_MAPPING, REQUIREMENT


def send_notification(request, notification):
Expand Down Expand Up @@ -53,14 +56,20 @@ class EmailBackend(Backend):
name = 'email'

def send(self, notification):
# FIXME: if the level is an ERROR an email is received and sometimes
# it's not necessary. This behavior should be clearly documented in the
# code
if notification.level >= REQUIREMENT:
send_email(
recipient=notification.user.email,
subject=notification.get_subject(),
template='core/email/common.txt',
template_html='core/email/common.html',
context={
'content': notification.render(self.name, source_format=HTML),
'content': notification.render(
self.name,
source_format=HTML,
),
},
request=self.request,
)
Expand Down Expand Up @@ -91,7 +100,7 @@ def send(self, notification):
level=LEVEL_MAPPING.get(notification.level, INFO_PERSISTENT),
message=notification.render(
backend_name=self.name,
source_format=HTML
source_format=HTML,
),
extra_tags='',
user=notification.user,
Expand Down
24 changes: 23 additions & 1 deletion readthedocs/oauth/services/base.py
Expand Up @@ -8,6 +8,7 @@
from datetime import datetime

from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.providers import registry
from builtins import object
from django.conf import settings
from oauthlib.oauth2.rfc6749.errors import InvalidClientIdError
Expand Down Expand Up @@ -56,6 +57,10 @@ def get_adapter(self):
def provider_id(self):
return self.get_adapter().provider_id

@property
def provider_name(self):
return registry.by_id(self.provider_id).name

def get_session(self):
if self.session is None:
self.create_session()
Expand Down Expand Up @@ -131,6 +136,22 @@ def paginate(self, url, **kwargs):
"""
try:
resp = self.get_session().get(url, data=kwargs)

# TODO: this check of the status_code would be better in the
# ``create_session`` method since it could be used from outside, but
# I didn't find a generic way to make a test request to each
# provider.
if resp.status_code == 401:
# Bad credentials: the token we have in our database is not
# valid. Probably the user has revoked the access to our App. He
# needs to reconnect his account
raise Exception(
'Our access to your {provider} account was revoked. '
'Please, reconnect it from your social account connections.'.format(
provider=self.provider_name,
),
)

next_url = self.get_next_url_to_paginate(resp)
results = self.get_paginated_results(resp)
if next_url:
Expand Down Expand Up @@ -202,4 +223,5 @@ def is_project_service(cls, project):
# TODO Replace this check by keying project to remote repos
return (
cls.url_pattern is not None and
cls.url_pattern.search(project.repo) is not None)
cls.url_pattern.search(project.repo) is not None
)
13 changes: 8 additions & 5 deletions readthedocs/oauth/tasks.py
@@ -1,11 +1,14 @@
"""Tasks for OAuth services"""
# -*- coding: utf-8 -*-
"""Tasks for OAuth services."""

from __future__ import (
absolute_import, division, print_function, unicode_literals)

from __future__ import absolute_import
from django.contrib.auth.models import User

from readthedocs.core.utils.tasks import PublicTask
from readthedocs.core.utils.tasks import permission_check
from readthedocs.core.utils.tasks import user_id_matches
from readthedocs.core.utils.tasks import (
PublicTask, permission_check, user_id_matches)

from .services import registry


Expand Down