Feature/google calendar read only support (#52790)

* feat(google): Added support for read only access in google calendar.

By default the current read/write access will be given, but the user has the option to set read only access which stops the add event service from registering

* fix(google): Updated documentation link

* docs(google-calendar): Added style fixes

* feat(calendar-google): Updated scopes to be defined on enum property.

This was done as a MR suggestion to simplify the code.

* feat(calendar-google):Removed constants no longer needed.

* feat(calendar-google): Reduced scope check to minimum.

* style: Fixed style issues
This commit is contained in:
David Kendall 2021-07-25 20:33:21 +01:00 committed by GitHub
parent 5e6853b9e1
commit f3ba71748b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 11 deletions

View file

@ -1,5 +1,6 @@
"""Support for Google - Calendar Event Devices."""
from datetime import datetime, timedelta
from enum import Enum
import logging
import os
@ -41,6 +42,7 @@ CONF_TRACK = "track"
CONF_SEARCH = "search"
CONF_IGNORE_AVAILABILITY = "ignore_availability"
CONF_MAX_RESULTS = "max_results"
CONF_CALENDAR_ACCESS = "calendar_access"
DEFAULT_CONF_TRACK_NEW = True
DEFAULT_CONF_OFFSET = "!!"
@ -70,10 +72,26 @@ SERVICE_ADD_EVENT = "add_event"
DATA_INDEX = "google_calendars"
YAML_DEVICES = f"{DOMAIN}_calendars.yaml"
SCOPES = "https://www.googleapis.com/auth/calendar"
TOKEN_FILE = f".{DOMAIN}.token"
class FeatureAccess(Enum):
"""Class to represent different access scopes."""
read_only = "https://www.googleapis.com/auth/calendar.readonly"
read_write = "https://www.googleapis.com/auth/calendar"
def __init__(self, scope: str) -> None:
"""Init instance."""
self._scope = scope
@property
def scope(self) -> str:
"""Google calendar scope for the feature."""
return self._scope
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
@ -81,6 +99,9 @@ CONFIG_SCHEMA = vol.Schema(
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_TRACK_NEW): cv.boolean,
vol.Optional(CONF_CALENDAR_ACCESS, default="read_write"): cv.enum(
FeatureAccess
),
}
)
},
@ -139,7 +160,7 @@ def do_authentication(hass, hass_config, config):
oauth = OAuth2WebServerFlow(
client_id=config[CONF_CLIENT_ID],
client_secret=config[CONF_CLIENT_SECRET],
scope="https://www.googleapis.com/auth/calendar",
scope=config[CONF_CALENDAR_ACCESS].scope,
redirect_uri="Home-Assistant.io",
)
try:
@ -213,7 +234,7 @@ def setup(hass, config):
if not os.path.isfile(token_file):
do_authentication(hass, config, conf)
else:
if not check_correct_scopes(token_file):
if not check_correct_scopes(token_file, conf):
do_authentication(hass, config, conf)
else:
do_setup(hass, config, conf)
@ -221,16 +242,22 @@ def setup(hass, config):
return True
def check_correct_scopes(token_file):
def check_correct_scopes(token_file, config):
"""Check for the correct scopes in file."""
with open(token_file) as tokenfile:
if "readonly" in tokenfile.read():
contents = tokenfile.read()
# Check for quoted scope as our scopes can be subsets of other scopes
target_scope = f'"{config.get(CONF_CALENDAR_ACCESS).scope}"'
if target_scope not in contents:
_LOGGER.warning("Please re-authenticate with Google")
return False
return True
def setup_services(hass, hass_config, track_new_found_calendars, calendar_service):
def setup_services(
hass, hass_config, config, track_new_found_calendars, calendar_service
):
"""Set up the service listeners."""
def _found_calendar(call):
@ -312,9 +339,11 @@ def setup_services(hass, hass_config, track_new_found_calendars, calendar_servic
service_data = {"calendarId": call.data[EVENT_CALENDAR_ID], "body": event}
event = service.events().insert(**service_data).execute()
hass.services.register(
DOMAIN, SERVICE_ADD_EVENT, _add_event, schema=ADD_EVENT_SERVICE_SCHEMA
)
# Only expose the add event service if we have the correct permissions
if config.get(CONF_CALENDAR_ACCESS) is FeatureAccess.read_write:
hass.services.register(
DOMAIN, SERVICE_ADD_EVENT, _add_event, schema=ADD_EVENT_SERVICE_SCHEMA
)
return True
@ -327,7 +356,9 @@ def do_setup(hass, hass_config, config):
track_new_found_calendars = convert(
config.get(CONF_TRACK_NEW), bool, DEFAULT_CONF_TRACK_NEW
)
setup_services(hass, hass_config, track_new_found_calendars, calendar_service)
setup_services(
hass, hass_config, config, track_new_found_calendars, calendar_service
)
for calendar in hass.data[DATA_INDEX].values():
discovery.load_platform(hass, "calendar", DOMAIN, calendar, hass_config)

View file

@ -1,7 +1,7 @@
{
"domain": "google",
"name": "Google Calendars",
"documentation": "https://www.home-assistant.io/integrations/google",
"documentation": "https://www.home-assistant.io/integrations/calendar.google/",
"requirements": [
"google-api-python-client==1.6.4",
"httplib2==0.19.0",