OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 # Virtual Me2Me implementation. This script runs and manages the processes | 6 # Virtual Me2Me implementation. This script runs and manages the processes |
7 # required for a Virtual Me2Me desktop, which are: X server, X desktop | 7 # required for a Virtual Me2Me desktop, which are: X server, X desktop |
8 # session, and Host process. | 8 # session, and Host process. |
9 # This script is intended to run continuously as a background daemon | 9 # This script is intended to run continuously as a background daemon |
10 # process, running under an ordinary (non-root) user account. | 10 # process, running under an ordinary (non-root) user account. |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 return self.data[key] | 111 return self.data[key] |
112 | 112 |
113 def __setitem__(self, key, value): | 113 def __setitem__(self, key, value): |
114 self.data[key] = value | 114 self.data[key] = value |
115 self.changed = True | 115 self.changed = True |
116 | 116 |
117 def clear_auth(self): | 117 def clear_auth(self): |
118 del self.data["xmpp_login"] | 118 del self.data["xmpp_login"] |
119 del self.data["chromoting_auth_token"] | 119 del self.data["chromoting_auth_token"] |
120 del self.data["xmpp_auth_token"] | 120 del self.data["xmpp_auth_token"] |
121 del self.data["oauth_refresh_token"] | |
121 | 122 |
122 def clear_host_info(self): | 123 def clear_host_info(self): |
123 del self.data["host_id"] | 124 del self.data["host_id"] |
124 del self.data["host_name"] | 125 del self.data["host_name"] |
125 del self.data["host_secret_hash"] | 126 del self.data["host_secret_hash"] |
126 del self.data["private_key"] | 127 del self.data["private_key"] |
127 | 128 |
128 class Authentication: | 129 class Authentication: |
129 """Manage authentication tokens for Chromoting/xmpp""" | 130 """Manage authentication tokens for Chromoting/xmpp""" |
130 | 131 |
131 def __init__(self): | 132 def __init__(self): |
132 pass | 133 self.login = None |
134 self.chromoting_auth_token = None | |
135 self.xmpp_auth_token = None | |
136 self.oauth_refresh_token = None | |
133 | 137 |
134 def generate_tokens(self): | 138 def generate_tokens(self): |
135 """Prompt for username/password and use them to generate new authentication | 139 """Prompt for username/password and use them to generate new authentication |
136 tokens. | 140 tokens. |
137 | 141 |
138 Raises: | 142 Raises: |
139 Exception: Failed to get new authentication tokens. | 143 Exception: Failed to get new authentication tokens. |
140 """ | 144 """ |
141 print "Email:", | 145 print "Email:", |
142 self.login = raw_input() | 146 self.login = raw_input() |
143 password = getpass.getpass("App-specific password: ") | 147 password = getpass.getpass("App-specific password: ") |
144 | 148 |
145 chromoting_auth = gaia_auth.GaiaAuthenticator('chromoting') | 149 chromoting_auth = gaia_auth.GaiaAuthenticator('chromoting') |
146 self.chromoting_auth_token = chromoting_auth.authenticate(self.login, | 150 self.chromoting_auth_token = chromoting_auth.authenticate(self.login, |
147 password) | 151 password) |
148 | 152 |
149 xmpp_authenticator = gaia_auth.GaiaAuthenticator('chromiumsync') | 153 xmpp_authenticator = gaia_auth.GaiaAuthenticator('chromiumsync') |
150 self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login, | 154 self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login, |
151 password) | 155 password) |
152 | 156 |
157 def has_chromoting_credentials(self): | |
158 """Returns True if we have credentials for the directory""" | |
159 return self.chromoting_auth_token != None | |
160 | |
161 def has_xmpp_credentials(self): | |
162 """Returns True if we have credentials to authenticate XMPP connection""" | |
163 # XMPP connection can be authenticated using either OAuth or XMPP token. | |
164 return self.oauth_refresh_token != None or self.xmpp_auth_token != None | |
165 | |
153 def load_config(self, config): | 166 def load_config(self, config): |
167 """Loads the config and returns false if the config is invalid. After | |
168 config is loaded caller must use has_xmpp_credentials() and | |
169 has_chromoting_credentials() to check that the credentials it needs are | |
170 present in the config.""" | |
171 | |
172 # Host can use different types of auth tokens depending on how the config | |
173 # was generated. E.g. if the config was created by the webapp it will have | |
174 # only oauth token. We still consider config to be valid in this case. | |
175 self.chromoting_auth_token = config.get("chromoting_auth_token") | |
176 self.oauth_refresh_token = config.get("oauth_refresh_token") | |
177 self.xmpp_auth_token = config.get("xmpp_auth_token") | |
178 | |
154 try: | 179 try: |
155 self.login = config["xmpp_login"] | 180 self.login = config["xmpp_login"] |
156 self.chromoting_auth_token = config["chromoting_auth_token"] | |
157 self.xmpp_auth_token = config["xmpp_auth_token"] | |
158 except KeyError: | 181 except KeyError: |
159 return False | 182 return False |
160 return True | 183 return True |
161 | 184 |
162 def save_config(self, config): | 185 def save_config(self, config): |
163 config["xmpp_login"] = self.login | 186 config["xmpp_login"] = self.login |
164 config["chromoting_auth_token"] = self.chromoting_auth_token | 187 if self.chromoting_auth_token: |
165 config["xmpp_auth_token"] = self.xmpp_auth_token | 188 config["chromoting_auth_token"] = self.chromoting_auth_token |
189 if self.xmpp_auth_token: | |
190 config["xmpp_auth_token"] = self.xmpp_auth_token | |
166 | 191 |
Lambros
2012/08/15 20:25:58
nit: two blank lines between top-levels (http://go
Sergey Ulanov
2012/08/16 00:52:45
Done.
| |
167 class Host: | 192 class Host: |
168 """This manages the configuration for a host. | 193 """This manages the configuration for a host. |
169 | 194 |
170 Callers should instantiate a Host object (passing in a filename where the | 195 Callers should instantiate a Host object (passing in a filename where the |
171 config will be kept), then should call either of the methods: | 196 config will be kept), then should call either of the methods: |
172 | 197 |
173 * register(auth): Create a new Host configuration and register it | 198 * register(auth): Create a new Host configuration and register it |
174 with the Directory Service (the "auth" parameter is used to | 199 with the Directory Service (the "auth" parameter is used to |
175 authenticate with the Service). | 200 authenticate with the Service). |
176 * load_config(): Load a config from disk, with details of an existing Host | 201 * load_config(): Load a config from disk, with details of an existing Host |
(...skipping 477 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
654 help="stop the daemon currently running") | 679 help="stop the daemon currently running") |
655 parser.add_option("-p", "--new-pin", dest="new_pin", default=False, | 680 parser.add_option("-p", "--new-pin", dest="new_pin", default=False, |
656 action="store_true", | 681 action="store_true", |
657 help="set new PIN before starting the host") | 682 help="set new PIN before starting the host") |
658 parser.add_option("", "--check-running", dest="check_running", default=False, | 683 parser.add_option("", "--check-running", dest="check_running", default=False, |
659 action="store_true", | 684 action="store_true", |
660 help="return 0 if the daemon is running, or 1 otherwise") | 685 help="return 0 if the daemon is running, or 1 otherwise") |
661 parser.add_option("", "--silent", dest="silent", default=False, | 686 parser.add_option("", "--silent", dest="silent", default=False, |
662 action="store_true", | 687 action="store_true", |
663 help="Start the host without trying to configure it.") | 688 help="Start the host without trying to configure it.") |
689 parser.add_option("", "--reload", dest="reload", default=False, | |
690 action="store_true", | |
691 help="Signal currently running host to reload the config.") | |
664 (options, args) = parser.parse_args() | 692 (options, args) = parser.parse_args() |
665 | 693 |
666 host_hash = hashlib.md5(socket.gethostname()).hexdigest() | 694 host_hash = hashlib.md5(socket.gethostname()).hexdigest() |
667 pid_filename = os.path.join(CONFIG_DIR, "host#%s.pid" % host_hash) | 695 pid_filename = os.path.join(CONFIG_DIR, "host#%s.pid" % host_hash) |
668 | 696 |
669 if options.check_running: | 697 if options.check_running: |
670 running, pid = PidFile(pid_filename).check() | 698 running, pid = PidFile(pid_filename).check() |
671 return 0 if (running and pid != 0) else 1 | 699 return 0 if (running and pid != 0) else 1 |
672 | 700 |
673 if options.stop: | 701 if options.stop: |
674 running, pid = PidFile(pid_filename).check() | 702 running, pid = PidFile(pid_filename).check() |
675 if not running: | 703 if not running: |
676 print "The daemon currently is not running" | 704 print "The daemon currently is not running" |
677 else: | 705 else: |
678 print "Killing process %s" % pid | 706 print "Killing process %s" % pid |
679 os.kill(pid, signal.SIGTERM) | 707 os.kill(pid, signal.SIGTERM) |
680 return 0 | 708 return 0 |
681 | 709 |
710 if options.reload: | |
711 running, pid = PidFile(pid_filename).check() | |
712 if not running: | |
713 return 1 | |
714 os.kill(pid, signal.SIGUSR1) | |
715 return 0 | |
716 | |
682 if not options.size: | 717 if not options.size: |
683 options.size = [DEFAULT_SIZE] | 718 options.size = [DEFAULT_SIZE] |
684 | 719 |
685 sizes = [] | 720 sizes = [] |
686 for size in options.size: | 721 for size in options.size: |
687 size_components = size.split("x") | 722 size_components = size.split("x") |
688 if len(size_components) != 2: | 723 if len(size_components) != 2: |
689 parser.error("Incorrect size format '%s', should be WIDTHxHEIGHT" % size) | 724 parser.error("Incorrect size format '%s', should be WIDTHxHEIGHT" % size) |
690 | 725 |
691 try: | 726 try: |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
736 # If we were able to read auth.json then copy its content to the host | 771 # If we were able to read auth.json then copy its content to the host |
737 # config. | 772 # config. |
738 if auth_config_loaded: | 773 if auth_config_loaded: |
739 auth.save_config(host_config) | 774 auth.save_config(host_config) |
740 host_config.save() | 775 host_config.save() |
741 | 776 |
742 host = Host(auth) | 777 host = Host(auth) |
743 host_config_loaded = host.load_config(host_config) | 778 host_config_loaded = host.load_config(host_config) |
744 | 779 |
745 if options.silent: | 780 if options.silent: |
781 # Just validate the config when run with --silent. | |
746 if not host_config_loaded or not auth_config_loaded: | 782 if not host_config_loaded or not auth_config_loaded: |
747 logging.error("Failed to load host configuration.") | 783 logging.error("Failed to load host configuration.") |
748 return 1 | 784 return 1 |
785 if not auth.has_xmpp_credentials(): | |
786 logging.error("Auth tokens are not configured.") | |
787 return 1 | |
749 else: | 788 else: |
750 need_auth_tokens = not auth_config_loaded | 789 need_auth_tokens = \ |
790 not auth_config_loaded or not auth.has_chromoting_credentials() | |
Lambros
2012/08/15 20:25:58
nit: Use round brackets instead of backslash (http
Sergey Ulanov
2012/08/16 00:52:45
Done.
| |
751 need_register_host = not host_config_loaded | 791 need_register_host = not host_config_loaded |
752 # Outside the loop so user doesn't get asked twice. | 792 # Outside the loop so user doesn't get asked twice. |
753 if need_register_host: | 793 if need_register_host: |
754 host.ask_pin() | 794 host.ask_pin() |
755 elif options.new_pin or not host.is_pin_set(): | 795 elif options.new_pin or not host.is_pin_set(): |
756 host.ask_pin() | 796 host.ask_pin() |
757 host.save_config(host_config) | 797 host.save_config(host_config) |
758 running, pid = PidFile(pid_filename).check() | 798 running, pid = PidFile(pid_filename).check() |
759 if running and pid != 0: | 799 if running and pid != 0: |
760 os.kill(pid, signal.SIGUSR1) | 800 os.kill(pid, signal.SIGUSR1) |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
913 host_config.save() | 953 host_config.save() |
914 return 0 | 954 return 0 |
915 elif os.WEXITSTATUS(status) == 5: | 955 elif os.WEXITSTATUS(status) == 5: |
916 logging.info("Host domain is blocked by policy - exiting.") | 956 logging.info("Host domain is blocked by policy - exiting.") |
917 os.remove(host.config_file) | 957 os.remove(host.config_file) |
918 return 0 | 958 return 0 |
919 | 959 |
920 if __name__ == "__main__": | 960 if __name__ == "__main__": |
921 logging.basicConfig(level=logging.DEBUG) | 961 logging.basicConfig(level=logging.DEBUG) |
922 sys.exit(main()) | 962 sys.exit(main()) |
OLD | NEW |