Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make authentication work better with multiple hubs #25

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions cozify/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@
from .Error import APIError, AuthenticationError, ConnectionError


def resolve_local_ip(hub_keys, local_hubs):
"""Try to figure out which hub is behind local ip.

Connect to every IP address returned by cloud.lan_ip API call with every hub keys available in account.
If can connect to IP with hub_token, verify that hubId matches and store hubId<->ip pair into dict
"""
hub_id_ip_pairs = dict()
for local_ip in local_hubs:
print(f'Trying to resolve which hub is {local_ip}')
for hub_id, hub_token in hub_keys.items():
print(f'Trying to connect {local_ip} with hub_id {hub_id}')
try:
hub_info = hub_api.hub(host=local_ip, hub_token=hub_token, remote=False)
if 'hubId' in hub_info and hub_info['hubId'] == hub_id:
print(f'Assign {local_ip} to {hub_id}')
hub_id_ip_pairs[hub_id] = local_ip
except Exception as e:
pass
return hub_id_ip_pairs

def authenticate(trustCloud=True, trustHub=True, remote=False, autoremote=True):
"""Authenticate with the Cozify Cloud and Hub.

Expand Down Expand Up @@ -70,27 +90,33 @@ def authenticate(trustCloud=True, trustHub=True, remote=False, autoremote=True):
localHubs = (
cloud_api.lan_ip()
) # will only work if we're local to the Hub, otherwise None
# TODO(artanicus): unknown what will happen if there is a local hub but another one remote. Needs testing by someone with multiple hubs. Issue #7
hubkeys = cloud_api.hubkeys(
cloud_token
) # get all registered hubs and their keys from the cloud.
if not hubkeys:
logging.fatal(
"You have not registered any hubs to the Cozify Cloud, hence a hub cannot be used yet."
)

# Get dict which contains local hubs that has ip address.
hub_id_ip_pairs = resolve_local_ip(hub_keys=hubkeys, local_hubs=localHubs)
# evaluate all returned Hubs and store them
for hub_id, hub_token in hubkeys.items():
logging.debug("hub: {0} token: {1}".format(hub_id, hub_token))
local = False
hub_info = None
hub_ip = None

if hub_id in hub_id_ip_pairs:
print(f'hub_id: {hub_id} is local')
local = True

if not hub.exists(hub_id):
autoremote = True
else:
autoremote = hub.autoremote(hub_id=hub_id)
# if we're remote, we didn't get a valid ip
if not localHubs:
if not local:
print(f'Hub {hub_id} is remote')
logging.info("No local Hubs detected, changing to remote mode.")
hub_info = hub_api.hub(
remote=True, cloud_token=cloud_token, hub_token=hub_token
Expand All @@ -104,10 +130,8 @@ def authenticate(trustCloud=True, trustHub=True, remote=False, autoremote=True):
)
remote = True
else:
# localHubs is valid so a hub is in the lan. A mixed environment cannot yet be detected.
# cloud_api.lan_ip cannot provide a map as to which ip is which hub. Thus we actually need to determine the right one.
# TODO(artanicus): Need to truly test how multihub works before implementing ip to hub resolution. See issue #7
hub_ip = localHubs[0]
print(f'Hub {hub_id} is local')
hub_ip = hub_id_ip_pairs[hub_id]
hub_info = hub_api.hub(host=hub_ip, remote=False)
# if the hub wants autoremote we flip the state. If this is the first time the hub is seen, act as if autoremote=True, remote=False
if not hub.exists(hub_id) or (
Expand Down Expand Up @@ -136,10 +160,12 @@ def authenticate(trustCloud=True, trustHub=True, remote=False, autoremote=True):
config.state.add_section(hubSection)
# if default hub not set, set this hub as the first as the default
if "default" not in config.state["Hubs"]:
config.state["Hubs"]["default"] = hub_id
if local:
config.state["Hubs"]["default"] = hub_id

# store Hub data under it's named section
hub._setAttr(hub_id, "host", hub_ip, commit=False)
if hub_ip:
hub._setAttr(hub_id, "host", hub_ip, commit=False)
hub._setAttr(hub_id, "hubName", hub_name, commit=False)
hub.token(hub_id, hub_token)
hub.remote(hub_id, remote)
Expand Down