def _load(self): if not os.path.isfile(self._config.get(Setting.DATA_CACHE_FILE_PATH)): self._data = {KEY_SNPASHOTS: {}} else: with open(self._config.get(Setting.DATA_CACHE_FILE_PATH)) as f: self._data = json.load(f) # Check for an upgrade. if KEY_LAST_VERSION in self._data: self._last_version = Version.parse(self._data[KEY_LAST_VERSION]) if self.previousVersion != self.currentVersion: # add an upgrade marker if KEY_UPGRADES not in self._data: self._data[KEY_UPGRADES] = [] self._data[KEY_UPGRADES].append({ 'prev_version': str(self.previousVersion), 'new_version': str(self.currentVersion), 'date': self._time.now().isoformat() }) self._data[KEY_LAST_VERSION] = str(self.currentVersion) self.makeDirty() self.saveIfDirty() if self.previousVersion == Version.default( ) and self.currentVersion > Version.parse("0.103"): self._flags.add(UpgradeFlags.DONT_IGNORE_LEGACY_SNAPSHOTS)
async def authorize(self, request: Request): if 'redirectbacktoken' in request.query: version = Version.parse(request.query.get('version', "0")) token_url = request.query.get('redirectbacktoken') return_url = request.query.get('return', None) state = { 'v': str(version), 'token': token_url, 'return': return_url, 'bg': self.base_context(request).get('backgroundColor'), 'ac': self.base_context(request).get('accentColor'), } # Someone is trying to authenticate with the add-on, direct them to the google auth url raise HTTPSeeOther(await self.exchanger.getAuthorizationUrl( json.dumps(state))) elif 'state' in request.query and 'code' in request.query: state = json.loads(unquote(request.query.get('state'))) code = request.query.get('code') try: version = Version.parse(state["v"]) creds = (await self.exchanger.exchange(code)).serialize( include_secret=False) if version < NEW_AUTH_MINIMUM: # Redirect back to the addon, since this is the older addon url = URL(state['token']).with_query( {'creds': json.dumps(creds)}) raise HTTPSeeOther(url) serialized_creds = str( base64.b64encode(json.dumps(creds).encode("utf-8")), "utf-8") url = URL(state['token']).with_query({ 'creds': serialized_creds, 'host': state['return'] }) context = { **self.base_context(request), 'redirect_url': str(url), 'credentials_serialized': serialized_creds, } if 'bg' in state: context['backgroundColor'] = state['bg'] if 'ac' in state: context['accentColor'] = state['ac'] return aiohttp_jinja2.render_template("authorize.jinja2", request, context) except Exception as e: if isinstance(e, HTTPSeeOther): # expected, pass this thorugh raise self.logError(request, e) content = "The server encountered an error while processing this request: " + str( e) + "<br/>" content += "Please <a href='https://github.com/sabeechen/hassio-google-drive-backup/issues'>file an issue</a> on Home Assistant Google Backup's GitHub page so I'm aware of this problem or attempt authorizing with Google Drive again." return Response(status=500, body=content) else: raise HTTPBadRequest()
def _load(self): if not os.path.isfile(self._config.get(Setting.DATA_CACHE_FILE_PATH)): self._data = {NECESSARY_OLD_BACKUP_PLURAL_NAME: {}} else: with open(self._config.get(Setting.DATA_CACHE_FILE_PATH)) as f: self._data = json.load(f) # Check for an upgrade. if KEY_LAST_VERSION in self._data: self._last_version = Version.parse(self._data[KEY_LAST_VERSION]) if self.previousVersion != self.currentVersion: # add an upgrade marker if KEY_UPGRADES not in self._data: self._data[KEY_UPGRADES] = [] self._data[KEY_UPGRADES].append({ 'prev_version': str(self.previousVersion), 'new_version': str(self.currentVersion), 'date': self._time.now().isoformat() }) self._data[KEY_LAST_VERSION] = str(self.currentVersion) self.makeDirty() self.saveIfDirty()
async def picker(self, request: Request): version = Version.parse(request.query.get('version', "0")) bg = request.query.get('bg', self.config.get(Setting.BACKGROUND_COLOR)) ac = request.query.get('ac', self.config.get(Setting.ACCENT_COLOR)) return { **self.base_context(request), "client_id": self.config.get(Setting.DEFAULT_DRIVE_CLIENT_ID), "developer_key": self.config.get(Setting.DRIVE_PICKER_API_KEY), "app_id": self.config.get(Setting.DEFAULT_DRIVE_CLIENT_ID).split("-")[0], 'backgroundColor': bg, 'accentColor': ac, "do_redirect": str(version < NEW_AUTH_MINIMUM).lower() }
def getUpgradeTime(self, version: Version): for upgrade in self._data[KEY_UPGRADES]: if Version.parse(upgrade['new_version']) >= version: return self._time.parse(upgrade['date']) return self._time.now()
def currentVersion(self): return Version.parse(VERSION)
async def test_version_upgrades(time: Time, injector: Injector, config: Config) -> None: # Simluate upgrading from an un-tracked version assert not os.path.exists(config.get(Setting.DATA_CACHE_FILE_PATH)) cache = injector.get(DataCache) upgrade_time = time.now() assert cache.previousVersion == Version.default() assert cache.currentVersion == Version.parse(VERSION) assert cache.checkFlag(UpgradeFlags.DONT_IGNORE_LEGACY_SNAPSHOTS) assert os.path.exists(config.get(Setting.DATA_CACHE_FILE_PATH)) with open(config.get(Setting.DATA_CACHE_FILE_PATH)) as f: data = json.load(f) assert data["upgrades"] == [{ "prev_version": str(Version.default()), "new_version": VERSION, "date": upgrade_time.isoformat() }] # Reload the data cache, verify there is no upgrade. time.advance(days=1) cache = DataCache(config, time) assert cache.previousVersion == Version.parse(VERSION) assert cache.currentVersion == Version.parse(VERSION) assert not cache.checkFlag(UpgradeFlags.DONT_IGNORE_LEGACY_SNAPSHOTS) assert os.path.exists(config.get(Setting.DATA_CACHE_FILE_PATH)) with open(config.get(Setting.DATA_CACHE_FILE_PATH)) as f: data = json.load(f) assert data["upgrades"] == [{ "prev_version": str(Version.default()), "new_version": VERSION, "date": upgrade_time.isoformat() }] # simulate upgrading to a new version, verify an upgrade gets identified. upgrade_version = Version.parse("200") class UpgradeCache(DataCache): def __init__(self): super().__init__(config, time) @property def currentVersion(self): return upgrade_version cache = UpgradeCache() assert cache.previousVersion == Version.parse(VERSION) assert cache.currentVersion == upgrade_version assert os.path.exists(config.get(Setting.DATA_CACHE_FILE_PATH)) with open(config.get(Setting.DATA_CACHE_FILE_PATH)) as f: data = json.load(f) assert data["upgrades"] == [{ "prev_version": str(Version.default()), "new_version": VERSION, "date": upgrade_time.isoformat() }, { "prev_version": VERSION, "new_version": str(upgrade_version), "date": time.now().isoformat() }] next_upgrade_time = time.now() time.advance(days=1) # Verify version upgrade time queries work as expected assert cache.getUpgradeTime(Version.parse(VERSION)) == upgrade_time assert cache.getUpgradeTime(Version.default()) == upgrade_time assert cache.getUpgradeTime(upgrade_version) == next_upgrade_time # degenerate case, should never happen but a sensible value needs to be returned assert cache.getUpgradeTime(Version.parse("201")) == time.now()
def test_broken_versions(): assert Version.parse("") == Version.default() assert Version.parse(".") == Version.default() assert Version.parse("empty") == Version.default() assert Version.parse("no.version.here") == Version.default()
def test_junk_strings(): assert Version.parse("1-.2.3.1") == Version(1, 2, 3, 1) assert Version.parse("ignore-1.2.3.1") == Version(1, 2, 3, 1) assert Version.parse( "1.2.ignore.this.text.3.and...andhere.too.1") == Version(1, 2, 3, 1)
def test_parse_staging(): assert Version.parse("1.0.staging.1") == Version(1, 0, 1) assert Version.parse("1.0.staging.1").staging assert Version.parse("1.0.staging.1") > Version(1.0) assert Version.parse("1.2.3") == Version(1, 2, 3)
def test_parse(): assert Version.parse("1.0") == Version(1, 0) assert Version.parse("1.2.3") == Version(1, 2, 3)