# What this PR does Follow up PR to https://github.com/grafana/oncall/pull/4792 Basically if when communicating with Google Calendar's API we encounter an HTTP 403, or the Google client throws a `google.auth.exceptions.RefreshError` this means one of three things: 1. the refresh token we have persisted for the user is missing the `https://www.googleapis.com/auth/calendar.events.readonly` scope (HTTP 403) 2. the Google user has been deleted (`google.auth.exceptions.RefreshError`) 3. the refresh token has expired (`google.auth.exceptions.RefreshError`) To prevent scenario 1 above from happening in the future we now will check that the token has been granted the required scopes. If the user doesn't grant us all the necessary scopes, we will show them an error message in the UI: https://www.loom.com/share/0055ef03192b4154b894c2221cecbd5f For tokens that were granted prior to this PR and which are missing the required scope, we will show the user a dismissible warning banner in the UI letting them know that they will need to reconnect their account and grant us the missing permissions (see [this second demo video](https://www.loom.com/share/bf2ee8b840864a64893165370a892bcd) showing this). ## 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. --------- Co-authored-by: Dominik <dominik.broj@grafana.com>
19 lines
622 B
Python
19 lines
622 B
Python
import datetime
|
|
|
|
from apps.google import constants
|
|
|
|
|
|
def user_granted_all_required_scopes(user_granted_scopes: str) -> bool:
|
|
"""
|
|
`user_granted_scopes` should be a space-separated string of scopes
|
|
"""
|
|
granted_scopes = user_granted_scopes.split(" ")
|
|
return all(scope in granted_scopes for scope in constants.REQUIRED_OAUTH_SCOPES)
|
|
|
|
|
|
def datetime_strftime(dt: datetime.datetime) -> str:
|
|
return dt.strftime(constants.GOOGLE_CALENDAR_EVENT_DATETIME_FORMAT)
|
|
|
|
|
|
def datetime_strptime(dt: str) -> datetime.datetime:
|
|
return datetime.datetime.strptime(dt, constants.GOOGLE_CALENDAR_EVENT_DATETIME_FORMAT)
|