| Index: remoting/tools/me2me_virtual_host.py
|
| diff --git a/remoting/tools/me2me_virtual_host.py b/remoting/tools/me2me_virtual_host.py
|
| index bdb483720400a3b646e89d0aa6137723aea696cc..b9d072679bd42a6710f505b1ee0fb9e53e25bd67 100755
|
| --- a/remoting/tools/me2me_virtual_host.py
|
| +++ b/remoting/tools/me2me_virtual_host.py
|
| @@ -74,12 +74,62 @@ os.environ["XAUTHORITY"] = X_AUTH_FILE
|
| g_desktops = []
|
| g_pidfile = None
|
|
|
| +class Config:
|
| + def __init__(self, path):
|
| + self.path = path
|
| + self.data = {}
|
| + self.changed = False
|
| +
|
| + def load(self):
|
| + try:
|
| + settings_file = open(self.path, 'r')
|
| + self.data = json.load(settings_file)
|
| + self.changed = False
|
| + settings_file.close()
|
| + except:
|
| + return False
|
| + return True
|
| +
|
| + def save(self):
|
| + if not self.changed:
|
| + return True
|
| + try:
|
| + old_umask = os.umask(0066)
|
| + settings_file = open(self.path, 'w')
|
| + settings_file.write(json.dumps(self.data, indent=2))
|
| + settings_file.close()
|
| + os.umask(old_umask)
|
| + except:
|
| + return False
|
| + self.changed = False
|
| + return True
|
| +
|
| + def get(self, key):
|
| + return self.data.get(key)
|
| +
|
| + def __getitem__(self, key):
|
| + return self.data[key]
|
| +
|
| + def __setitem__(self, key, value):
|
| + self.data[key] = value
|
| + self.changed = True
|
| +
|
| + def clear_auth(self):
|
| + del self.data["xmpp_login"]
|
| + del self.data["chromoting_auth_token"]
|
| + del self.data["xmpp_auth_token"]
|
| +
|
| + def clear_host_info(self):
|
| + del self.data["host_id"]
|
| + del self.data["host_name"]
|
| + del self.data["host_secret_hash"]
|
| + del self.data["private_key"]
|
|
|
| class Authentication:
|
| """Manage authentication tokens for Chromoting/xmpp"""
|
|
|
| - def __init__(self, config_file):
|
| - self.config_file = config_file
|
| + def __init__(self):
|
| + pass
|
|
|
| def generate_tokens(self):
|
| """Prompt for username/password and use them to generate new authentication
|
| @@ -100,31 +150,19 @@ class Authentication:
|
| self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login,
|
| password)
|
|
|
| - def load_config(self):
|
| + def load_config(self, config):
|
| try:
|
| - settings_file = open(self.config_file, 'r')
|
| - data = json.load(settings_file)
|
| - settings_file.close()
|
| - self.login = data["xmpp_login"]
|
| - self.chromoting_auth_token = data["chromoting_auth_token"]
|
| - self.xmpp_auth_token = data["xmpp_auth_token"]
|
| - except:
|
| + self.login = config["xmpp_login"]
|
| + self.chromoting_auth_token = config["chromoting_auth_token"]
|
| + self.xmpp_auth_token = config["xmpp_auth_token"]
|
| + except KeyError:
|
| return False
|
| return True
|
|
|
| - def save_config(self):
|
| - data = {
|
| - "xmpp_login": self.login,
|
| - "chromoting_auth_token": self.chromoting_auth_token,
|
| - "xmpp_auth_token": self.xmpp_auth_token,
|
| - }
|
| - # File will contain private keys, so deny read/write access to others.
|
| - old_umask = os.umask(0066)
|
| - settings_file = open(self.config_file, 'w')
|
| - settings_file.write(json.dumps(data, indent=2))
|
| - settings_file.close()
|
| - os.umask(old_umask)
|
| -
|
| + def save_config(self, config):
|
| + config["xmpp_login"] = self.login
|
| + config["chromoting_auth_token"] = self.chromoting_auth_token
|
| + config["xmpp_auth_token"] = self.xmpp_auth_token
|
|
|
| class Host:
|
| """This manages the configuration for a host.
|
| @@ -145,14 +183,13 @@ class Host:
|
| server = 'www.googleapis.com'
|
| url = 'https://' + server + '/chromoting/v1/@me/hosts'
|
|
|
| - def __init__(self, config_file, auth):
|
| + def __init__(self, auth):
|
| """
|
| Args:
|
| - config_file: Host configuration file path
|
| + config: Host configuration object
|
| auth: Authentication object with credentials for authenticating with the
|
| Directory service.
|
| """
|
| - self.config_file = config_file
|
| self.auth = auth
|
| self.host_id = str(uuid.uuid1())
|
| self.host_name = socket.gethostname()
|
| @@ -222,36 +259,21 @@ class Host:
|
| def is_pin_set(self):
|
| return self.host_secret_hash
|
|
|
| - def load_config(self):
|
| + def load_config(self, config):
|
| try:
|
| - settings_file = open(self.config_file, 'r')
|
| - data = json.load(settings_file)
|
| - settings_file.close()
|
| - except:
|
| - logging.info("Failed to load: " + self.config_file)
|
| + self.host_id = config["host_id"]
|
| + self.host_name = config["host_name"]
|
| + self.host_secret_hash = config.get("host_secret_hash")
|
| + self.private_key = config["private_key"]
|
| + except KeyError:
|
| return False
|
| - self.host_id = data["host_id"]
|
| - self.host_name = data["host_name"]
|
| - self.host_secret_hash = data.get("host_secret_hash")
|
| - self.private_key = data["private_key"]
|
| return True
|
|
|
| - def save_config(self):
|
| - data = {
|
| - "host_id": self.host_id,
|
| - "host_name": self.host_name,
|
| - "host_secret_hash": self.host_secret_hash,
|
| - "private_key": self.private_key,
|
| - }
|
| - if self.host_secret_hash:
|
| - data["host_secret_hash"] = self.host_secret_hash
|
| -
|
| - old_umask = os.umask(0066)
|
| - settings_file = open(self.config_file, 'w')
|
| - settings_file.write(json.dumps(data, indent=2))
|
| - settings_file.close()
|
| - os.umask(old_umask)
|
| -
|
| + def save_config(self, config):
|
| + config["host_id"] = self.host_id
|
| + config["host_name"] = self.host_name
|
| + config["host_secret_hash"] = self.host_secret_hash
|
| + config["private_key"] = self.private_key
|
|
|
| class Desktop:
|
| """Manage a single virtual desktop"""
|
| @@ -380,12 +402,10 @@ class Desktop:
|
| if not self.session_proc.pid:
|
| raise Exception("Could not start X session")
|
|
|
| - def launch_host(self, host):
|
| + def launch_host(self, host_config):
|
| # Start remoting host
|
| args = [locate_executable(REMOTING_COMMAND),
|
| - "--host-config=%s" % (host.config_file)]
|
| - if host.auth.config_file != host.config_file:
|
| - args.append("--auth-config=%s" % (host.auth.config_file))
|
| + "--host-config=%s" % (host_config.path)]
|
| self.host_proc = subprocess.Popen(args, env=self.child_env)
|
| logging.info(args)
|
| if not self.host_proc.pid:
|
| @@ -702,21 +722,25 @@ def main():
|
| if not os.path.exists(CONFIG_DIR):
|
| os.makedirs(CONFIG_DIR, mode=0700)
|
|
|
| - host_config_file = os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)
|
| -
|
| - # --silent option is specified when we are started from WebApp UI. Don't use
|
| - # separate auth file in that case.
|
| - # TODO(sergeyu): Always use host config for auth parameters.
|
| - if options.silent:
|
| - auth_config_file = host_config_file
|
| - else:
|
| - auth_config_file = os.path.join(CONFIG_DIR, "auth.json")
|
| -
|
| - auth = Authentication(auth_config_file)
|
| - auth_config_loaded = auth.load_config()
|
| -
|
| - host = Host(host_config_file, auth)
|
| - host_config_loaded = host.load_config()
|
| + host_config = Config(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash))
|
| + host_config.load()
|
| +
|
| + auth = Authentication()
|
| + auth_config_loaded = auth.load_config(host_config)
|
| + if not auth_config_loaded and not options.silent:
|
| + # If we failed to load authentication parameters from the host config
|
| + # then try loading them from the legacy auth.json file.
|
| + auth_config = Config(os.path.join(CONFIG_DIR, "auth.json"))
|
| + if auth_config.load():
|
| + auth_config_loaded = auth.load_config(auth_config)
|
| + # If we were able to read auth.json then copy its content to the host
|
| + # config.
|
| + if auth_config_loaded:
|
| + auth.save_config(host_config)
|
| + host_config.save()
|
| +
|
| + host = Host(auth)
|
| + host_config_loaded = host.load_config(host_config)
|
|
|
| if options.silent:
|
| if not host_config_loaded or not auth_config_loaded:
|
| @@ -730,7 +754,7 @@ def main():
|
| host.ask_pin()
|
| elif options.new_pin or not host.is_pin_set():
|
| host.ask_pin()
|
| - host.save_config()
|
| + host.save_config(host_config)
|
| running, pid = PidFile(pid_filename).check()
|
| if running and pid != 0:
|
| os.kill(pid, signal.SIGUSR1)
|
| @@ -741,33 +765,41 @@ def main():
|
| # previously-saved auth tokens (from a previous run of this script), which
|
| # may require re-prompting for username & password.
|
| while True:
|
| - try:
|
| - if need_auth_tokens:
|
| + if need_auth_tokens:
|
| + try:
|
| auth.generate_tokens()
|
| - auth.save_config()
|
| need_auth_tokens = False
|
| - except Exception:
|
| - logging.error("Authentication failed")
|
| - return 1
|
| + except Exception:
|
| + logging.error("Authentication failed")
|
| + return 1
|
| + # Save the new auth tokens.
|
| + auth.save_config(host_config)
|
| + if not host_config.save():
|
| + logging.error("Failed to save host configuration.")
|
| + return 1
|
|
|
| - try:
|
| - if need_register_host:
|
| + if need_register_host:
|
| + try:
|
| host.register()
|
| - host.save_config()
|
| - except urllib2.HTTPError, err:
|
| - if err.getcode() == 401:
|
| - # Authentication failed - re-prompt for username & password.
|
| - need_auth_tokens = True
|
| - continue
|
| - else:
|
| - # Not an authentication error.
|
| - logging.error("Directory returned error: " + str(err))
|
| - logging.error(err.read())
|
| - return 1
|
| + host.save_config(host_config)
|
| + except urllib2.HTTPError, err:
|
| + if err.getcode() == 401:
|
| + # Authentication failed - re-prompt for username & password.
|
| + need_auth_tokens = True
|
| + continue
|
| + else:
|
| + # Not an authentication error.
|
| + logging.error("Directory returned error: " + str(err))
|
| + logging.error(err.read())
|
| + return 1
|
|
|
| # |auth| and |host| are both set up, so break out of the loop.
|
| break
|
|
|
| + if not host_config.save():
|
| + logging.error("Failed to save host configuration.")
|
| + return 1
|
| +
|
| global g_pidfile
|
| g_pidfile = PidFile(pid_filename)
|
| running, pid = g_pidfile.check()
|
| @@ -829,7 +861,7 @@ def main():
|
|
|
| if desktop.host_proc is None:
|
| logging.info("Launching host process")
|
| - desktop.launch_host(host)
|
| + desktop.launch_host(host_config)
|
|
|
| try:
|
| pid, status = os.wait()
|
| @@ -866,25 +898,19 @@ def main():
|
| # will be created and registered.
|
| if os.WEXITSTATUS(status) == 2:
|
| logging.info("Host configuration is invalid - exiting.")
|
| - try:
|
| - os.remove(host.config_file)
|
| - os.remove(auth.config_file)
|
| - except:
|
| - pass
|
| + host_config.clear_auth()
|
| + host_config.clear_host_info()
|
| + host_config.save()
|
| return 0
|
| elif os.WEXITSTATUS(status) == 3:
|
| logging.info("Host ID has been deleted - exiting.")
|
| - try:
|
| - os.remove(host.config_file)
|
| - except:
|
| - pass
|
| + host_config.clear_host_info()
|
| + host_config.save()
|
| return 0
|
| elif os.WEXITSTATUS(status) == 4:
|
| logging.info("OAuth credentials are invalid - exiting.")
|
| - try:
|
| - os.remove(auth.config_file)
|
| - except:
|
| - pass
|
| + host_config.clear_auth()
|
| + host_config.save()
|
| return 0
|
| elif os.WEXITSTATUS(status) == 5:
|
| logging.info("Host domain is blocked by policy - exiting.")
|
|
|