mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Update the Google Photos integration to limit scope to Home Assistant created content (#126398)
This commit is contained in:
parent
556deb4f77
commit
505fb3738f
@ -6,12 +6,9 @@ OAUTH2_AUTHORIZE = "https://accounts.google.com/o/oauth2/v2/auth"
|
||||
OAUTH2_TOKEN = "https://oauth2.googleapis.com/token"
|
||||
|
||||
UPLOAD_SCOPE = "https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
READ_SCOPES = [
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly",
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata",
|
||||
]
|
||||
READ_SCOPE = "https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
OAUTH2_SCOPES = [
|
||||
*READ_SCOPES,
|
||||
READ_SCOPE,
|
||||
UPLOAD_SCOPE,
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
]
|
||||
|
@ -19,11 +19,10 @@ from homeassistant.components.media_source import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import GooglePhotosConfigEntry
|
||||
from .const import DOMAIN, READ_SCOPES
|
||||
from .const import DOMAIN, READ_SCOPE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MAX_RECENT_PHOTOS = 100
|
||||
MEDIA_ITEMS_PAGE_SIZE = 100
|
||||
ALBUM_PAGE_SIZE = 50
|
||||
|
||||
@ -38,16 +37,12 @@ class SpecialAlbumDetails:
|
||||
path: str
|
||||
title: str
|
||||
list_args: dict[str, Any]
|
||||
max_photos: int | None
|
||||
|
||||
|
||||
class SpecialAlbum(Enum):
|
||||
"""Special Album types."""
|
||||
|
||||
RECENT = SpecialAlbumDetails("recent", "Recent Photos", {}, MAX_RECENT_PHOTOS)
|
||||
FAVORITE = SpecialAlbumDetails(
|
||||
"favorites", "Favorite Photos", {"favorites": True}, None
|
||||
)
|
||||
UPLOADED = SpecialAlbumDetails("uploaded", "Uploaded", {})
|
||||
|
||||
@classmethod
|
||||
def of(cls, path: str) -> Self | None:
|
||||
@ -247,12 +242,6 @@ class GooglePhotosMediaSource(MediaSource):
|
||||
**list_args, page_size=MEDIA_ITEMS_PAGE_SIZE
|
||||
):
|
||||
media_items.extend(media_item_result.media_items)
|
||||
if (
|
||||
special_album
|
||||
and (max_photos := special_album.value.max_photos)
|
||||
and len(media_items) > max_photos
|
||||
):
|
||||
break
|
||||
except GooglePhotosApiError as err:
|
||||
raise BrowseError(f"Error listing media items: {err}") from err
|
||||
|
||||
@ -270,7 +259,7 @@ class GooglePhotosMediaSource(MediaSource):
|
||||
entries = []
|
||||
for entry in self.hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
scopes = entry.data["token"]["scope"].split(" ")
|
||||
if any(scope in scopes for scope in READ_SCOPES):
|
||||
if READ_SCOPE in scopes:
|
||||
entries.append(entry)
|
||||
return entries
|
||||
|
||||
|
@ -92,8 +92,7 @@ async def test_full_flow(
|
||||
f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
"+https://www.googleapis.com/auth/userinfo.profile"
|
||||
"&access_type=offline&prompt=consent"
|
||||
@ -121,8 +120,7 @@ async def test_full_flow(
|
||||
"refresh_token": FAKE_REFRESH_TOKEN,
|
||||
"type": "Bearer",
|
||||
"scope": (
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
" https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
" https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
" https://www.googleapis.com/auth/userinfo.profile"
|
||||
),
|
||||
@ -163,8 +161,7 @@ async def test_api_not_enabled(
|
||||
f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
"+https://www.googleapis.com/auth/userinfo.profile"
|
||||
"&access_type=offline&prompt=consent"
|
||||
@ -203,8 +200,7 @@ async def test_general_exception(
|
||||
f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
"+https://www.googleapis.com/auth/userinfo.profile"
|
||||
"&access_type=offline&prompt=consent"
|
||||
@ -288,8 +284,7 @@ async def test_reauth(
|
||||
f"{OAUTH2_AUTHORIZE}?response_type=code&client_id={CLIENT_ID}"
|
||||
"&redirect_uri=https://example.com/auth/external/callback"
|
||||
f"&state={state}"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"&scope=https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"+https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
"+https://www.googleapis.com/auth/userinfo.profile"
|
||||
"&access_type=offline&prompt=consent"
|
||||
@ -321,8 +316,7 @@ async def test_reauth(
|
||||
"refresh_token": FAKE_REFRESH_TOKEN,
|
||||
"type": "Bearer",
|
||||
"scope": (
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly"
|
||||
" https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
"https://www.googleapis.com/auth/photoslibrary.readonly.appcreateddata"
|
||||
" https://www.googleapis.com/auth/photoslibrary.appendonly"
|
||||
" https://www.googleapis.com/auth/userinfo.profile"
|
||||
),
|
||||
|
@ -66,8 +66,7 @@ async def test_no_read_scopes(
|
||||
@pytest.mark.parametrize(
|
||||
("album_path", "expected_album_title"),
|
||||
[
|
||||
(f"{CONFIG_ENTRY_ID}/a/recent", "Recent Photos"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/favorites", "Favorite Photos"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/uploaded", "Uploaded Photos"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/album-media-id-1", "Album title"),
|
||||
],
|
||||
)
|
||||
@ -109,8 +108,7 @@ async def test_browse_albums(
|
||||
assert browse.identifier == CONFIG_ENTRY_ID
|
||||
assert browse.title == "Account Name"
|
||||
assert [(child.identifier, child.title) for child in browse.children] == [
|
||||
(f"{CONFIG_ENTRY_ID}/a/recent", "Recent Photos"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/favorites", "Favorite Photos"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/uploaded", "Uploaded"),
|
||||
(f"{CONFIG_ENTRY_ID}/a/album-media-id-1", "Album title"),
|
||||
]
|
||||
|
||||
|
@ -11,7 +11,7 @@ from google_photos_library_api.model import (
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.google_photos.const import DOMAIN, READ_SCOPES
|
||||
from homeassistant.components.google_photos.const import DOMAIN, READ_SCOPE
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
@ -225,7 +225,7 @@ async def test_upload_service_fails_create(
|
||||
@pytest.mark.parametrize(
|
||||
("scopes"),
|
||||
[
|
||||
READ_SCOPES,
|
||||
[READ_SCOPE],
|
||||
],
|
||||
)
|
||||
async def test_upload_service_no_scope(
|
||||
|
Loading…
x
Reference in New Issue
Block a user