# What this PR does
Introduces a new class,
`apps.grafana_plugin.ui_url_builder.UIURLBuilder`, which is responsible
for... building UI URLs (😄). The class mainly does two things:
- it will decide if the URL should point to `grafana-oncall-app` or
`grafana-irm-app` based on the value of
`organization.is_grafana_irm_enabled` (**NOTE**: this value isn't yet
being set + defaults to `False`; logic for setting this value will be
done in a subsequent PR)
- Adds `enum`s, `OnCallPage` and `IncidentPage` to DRYify hardcoded UI
URLs (in case we decide to change these slightly in the near future)
## Checklist
- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
show up in the autogenerated release notes.
89 lines
3.2 KiB
Python
89 lines
3.2 KiB
Python
import logging
|
|
import typing
|
|
|
|
import requests
|
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
|
from django.http import HttpResponse
|
|
from rest_framework import status
|
|
from social_core.backends.base import BaseAuth
|
|
|
|
from apps.google.utils import user_granted_all_required_scopes
|
|
from apps.grafana_plugin.ui_url_builder import UIURLBuilder
|
|
from apps.social_auth.exceptions import GOOGLE_AUTH_MISSING_GRANTED_SCOPE_ERROR
|
|
from apps.social_auth.types import GoogleOauth2Response
|
|
from apps.user_management.models import Organization, User
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def connect_user_to_google(
|
|
strategy,
|
|
response: GoogleOauth2Response,
|
|
user: User,
|
|
organization: Organization,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
granted_scopes = response.get("scope", "")
|
|
|
|
if not user_granted_all_required_scopes(granted_scopes):
|
|
logger.warning(
|
|
f"User {user.pk} did not grant all required scopes, redirecting w/ error message "
|
|
f"granted_scopes={granted_scopes}"
|
|
)
|
|
|
|
strategy.session[REDIRECT_FIELD_NAME] = UIURLBuilder(organization).user_profile(
|
|
f"?google_error={GOOGLE_AUTH_MISSING_GRANTED_SCOPE_ERROR}"
|
|
)
|
|
|
|
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
# at this point everything is correct and we can persist the Google OAuth2 token + generate any other relevant
|
|
# config for the user
|
|
#
|
|
# be sure to clear any pre-existing sessions, in case the user previously enecountered errors we want
|
|
# to be sure to clear these so they do not see them again
|
|
strategy.session.flush()
|
|
|
|
user.save_google_oauth2_settings(response)
|
|
|
|
|
|
def disconnect_user_google_oauth2_settings(backend: typing.Type[BaseAuth], user: User, *args, **kwargs):
|
|
"""
|
|
Don't use `google_oauth2_user.access_token` when revoking token, use `refresh_token` instead. If we use
|
|
the access token, we may get an HTTP 400 from Google because the token may be invalid or revoked.
|
|
|
|
https://stackoverflow.com/a/18578660/3902555
|
|
"""
|
|
user_pk = user.pk
|
|
google_oauth2_user = user.google_oauth2_user
|
|
|
|
logger.info(f"Disconnecting user {user_pk} from Google OAuth2")
|
|
|
|
try:
|
|
backend.revoke_token(google_oauth2_user.refresh_token, google_oauth2_user.google_user_id)
|
|
except requests.exceptions.HTTPError as e:
|
|
response = e.response
|
|
|
|
logger.error(f"There was an HTTP error when trying to revoke Google OAuth2 token for user={user_pk}")
|
|
|
|
if response.status_code == 400:
|
|
error_details = response.json()
|
|
error_code = error_details["error"]
|
|
error_description = error_details["error_description"]
|
|
|
|
logger.error(
|
|
f"There was an HTTP 400 error when trying to revoke Google OAuth2 token for user={user_pk} "
|
|
f"error_code={error_code} error_description={error_description}"
|
|
)
|
|
|
|
error_codes_to_ignore = ["invalid_token"]
|
|
|
|
if error_code not in error_codes_to_ignore:
|
|
raise e
|
|
else:
|
|
logger.info(f"Google OAuth2 token for user {user_pk} is already invalid or revoked, ignoring error")
|
|
|
|
user.reset_google_oauth2_settings()
|
|
|
|
logger.info(f"Successfully disconnected user {user.pk} from Google OAuth2")
|