tccli 3.0.1189.1__py2.py3-none-any.whl → 3.0.1191.1__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. tccli/__init__.py +1 -1
  2. tccli/command.py +7 -1
  3. tccli/configure.py +11 -3
  4. tccli/loaders.py +43 -8
  5. tccli/oauth.py +115 -0
  6. tccli/plugin.py +30 -0
  7. tccli/plugins/__init__.py +0 -0
  8. tccli/plugins/auth/__init__.py +59 -0
  9. tccli/plugins/auth/browser_flow.py +88 -0
  10. tccli/plugins/auth/login.py +130 -0
  11. tccli/plugins/auth/logout.py +21 -0
  12. tccli/plugins/auth/texts.py +30 -0
  13. tccli/plugins/test/__init__.py +87 -0
  14. tccli/plugins/test/add.py +31 -0
  15. tccli/services/__init__.py +2 -1
  16. tccli/services/apm/v20210622/api.json +19 -0
  17. tccli/services/cam/cam_client.py +138 -32
  18. tccli/services/cam/v20190116/api.json +203 -0
  19. tccli/services/cam/v20190116/examples.json +16 -0
  20. tccli/services/cdb/v20170320/api.json +4 -4
  21. tccli/services/cfg/v20210820/api.json +1 -1
  22. tccli/services/cfw/v20190904/api.json +82 -2
  23. tccli/services/dasb/dasb_client.py +53 -0
  24. tccli/services/dasb/v20191018/api.json +168 -0
  25. tccli/services/dasb/v20191018/examples.json +8 -0
  26. tccli/services/dlc/v20210125/api.json +287 -37
  27. tccli/services/dlc/v20210125/examples.json +18 -18
  28. tccli/services/dsgc/v20190723/api.json +13 -3
  29. tccli/services/emr/v20190103/api.json +1 -1
  30. tccli/services/ess/v20201111/api.json +56 -23
  31. tccli/services/ess/v20201111/examples.json +7 -1
  32. tccli/services/essbasic/v20210526/api.json +28 -1
  33. tccli/services/essbasic/v20210526/examples.json +6 -0
  34. tccli/services/iotexplorer/v20190423/api.json +2 -2
  35. tccli/services/iss/iss_client.py +118 -65
  36. tccli/services/iss/v20230517/api.json +78 -0
  37. tccli/services/iss/v20230517/examples.json +8 -0
  38. tccli/services/mps/v20190612/api.json +257 -8
  39. tccli/services/ocr/v20181119/api.json +10 -0
  40. tccli/services/region/v20220627/api.json +11 -11
  41. tccli/services/region/v20220627/examples.json +1 -1
  42. tccli/services/sms/v20210111/api.json +9 -9
  43. tccli/services/ssl/v20191205/api.json +3 -3
  44. tccli/services/ssl/v20191205/examples.json +2 -2
  45. tccli/services/tat/v20201028/api.json +10 -0
  46. tccli/services/tcb/v20180608/api.json +2 -2
  47. tccli/services/tdmq/v20200217/api.json +2 -2
  48. tccli/services/tdmq/v20200217/examples.json +2 -2
  49. tccli/services/tke/tke_client.py +364 -46
  50. tccli/services/tke/v20180525/api.json +9 -0
  51. tccli/services/tke/v20220501/api.json +525 -0
  52. tccli/services/tke/v20220501/examples.json +54 -0
  53. tccli/services/tmt/v20180321/api.json +2 -2
  54. tccli/services/vdb/v20230616/api.json +29 -1
  55. tccli/services/vod/v20180717/api.json +30 -9
  56. tccli/services/vod/v20180717/examples.json +1 -1
  57. tccli/services/vod/v20240718/api.json +121 -0
  58. tccli/services/vod/v20240718/examples.json +31 -0
  59. tccli/services/vod/vod_client.py +58 -0
  60. tccli/services/vpc/v20170312/api.json +9 -0
  61. tccli/services/wedata/v20210820/api.json +1 -1
  62. {tccli-3.0.1189.1.dist-info → tccli-3.0.1191.1.dist-info}/METADATA +2 -2
  63. {tccli-3.0.1189.1.dist-info → tccli-3.0.1191.1.dist-info}/RECORD +66 -54
  64. {tccli-3.0.1189.1.dist-info → tccli-3.0.1191.1.dist-info}/WHEEL +0 -0
  65. {tccli-3.0.1189.1.dist-info → tccli-3.0.1191.1.dist-info}/entry_points.txt +0 -0
  66. {tccli-3.0.1189.1.dist-info → tccli-3.0.1191.1.dist-info}/license_files/LICENSE +0 -0
tccli/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '3.0.1189.1'
1
+ __version__ = '3.0.1191.1'
tccli/command.py CHANGED
@@ -6,6 +6,8 @@ import copy
6
6
  import tccli.services as Services
7
7
  import tccli.options_define as Options_define
8
8
  from collections import OrderedDict
9
+
10
+ from tccli import oauth
9
11
  from tccli.utils import Utils
10
12
  from tccli.argument import CLIArgument, CustomArgument, ListArgument, BooleanArgument
11
13
  from tccli.exceptions import UnknownArgumentError
@@ -176,12 +178,15 @@ class ServiceCommand(BaseCommand):
176
178
  service_model = self._get_service_model()
177
179
  for action in service_model["actions"]:
178
180
  action_model = service_model["actions"][action]
181
+ action_caller = action_model.get("action_caller", None)
182
+ if not action_caller:
183
+ action_caller = Services.action_caller(self._service_name)()[action]
179
184
  command_map[action] = ActionCommand(
180
185
  service_name=self._service_name,
181
186
  version=self._version,
182
187
  action_name=action,
183
188
  action_model=action_model,
184
- action_caller=Services.action_caller(self._service_name)()[action],
189
+ action_caller=action_caller,
185
190
  )
186
191
  return command_map
187
192
 
@@ -286,6 +291,7 @@ class ActionCommand(BaseCommand):
286
291
  action_parameters = self.cli_unfold_argument.build_action_parameters(parsed_args)
287
292
  else:
288
293
  action_parameters = self._build_action_parameters(parsed_args, self.argument_map)
294
+ oauth.maybe_refresh_credential(parsed_globals.profile if parsed_globals.profile else "default")
289
295
  return self._action_caller(action_parameters, vars(parsed_globals))
290
296
 
291
297
  def create_help_command(self):
tccli/configure.py CHANGED
@@ -416,16 +416,24 @@ class ConfigureCommand(BasicConfigure):
416
416
 
417
417
  def init_configures(self):
418
418
  config = {}
419
- if not self._profile_existed("default.configure")[0]:
419
+ import argparse
420
+ parser = argparse.ArgumentParser()
421
+ parser.add_argument("--profile", type=str)
422
+ args, _ = parser.parse_known_args()
423
+ profile = args.profile or "default"
424
+ profile_file = "%s.configure" % profile
425
+
426
+ if not self._profile_existed(profile_file)[0]:
420
427
  config = {
421
428
  "region": "ap-guangzhou",
422
429
  "output": "json",
423
430
  "arrayCount": 10,
424
431
  "warning": "off"
425
432
  }
426
- self._init_configure("default.configure", config)
433
+ self._init_configure(profile_file, config)
434
+
427
435
  for profile_name in os.listdir(self.cli_path):
428
- if profile_name == "default.configure":
436
+ if profile_name == profile_file:
429
437
  continue
430
438
  if profile_name.endswith(".configure"):
431
439
  self._init_configure(profile_name, {})
tccli/loaders.py CHANGED
@@ -9,6 +9,7 @@ from tccli.utils import Utils
9
9
  from tccli import __version__
10
10
  from tccli.services import SERVICE_VERSIONS
11
11
  from collections import OrderedDict
12
+ import tccli.plugin as plugin
12
13
 
13
14
  BASE_TYPE = ["int64", "uint64", "string", "float", "bool", "date", "datetime", "datetime_iso", "binary"]
14
15
  CLI_BASE_TYPE = ["Integer", "String", "Float", "Timestamp", "Boolean", "Binary"]
@@ -175,7 +176,15 @@ class Loader(object):
175
176
  return version[1:5] + "-" + version[5:7] + "-" + version[7:9]
176
177
 
177
178
  def get_available_services(self):
178
- return SERVICE_VERSIONS
179
+ services = copy.deepcopy(SERVICE_VERSIONS)
180
+ for name, vers in plugin.import_plugins().items():
181
+ if name not in services:
182
+ services[name] = []
183
+ for ver, spec in vers.items():
184
+ api_ver = spec["metadata"]["apiVersion"]
185
+ if api_ver not in services[name]:
186
+ services[name].append(api_ver)
187
+ return services
179
188
 
180
189
  def get_service_default_version(self, service):
181
190
  args = sys.argv[1:]
@@ -194,15 +203,41 @@ class Loader(object):
194
203
  services_path = self.get_services_path()
195
204
  version = "v" + version.replace('-', '')
196
205
  apis_path = os.path.join(services_path, service, version, "api.json")
197
- if not os.path.exists(apis_path):
206
+ model = {
207
+ "metadata": {},
208
+ "actions": {},
209
+ "objects": {},
210
+ }
211
+ if os.path.exists(apis_path):
212
+ if six.PY2:
213
+ with open(apis_path, 'r') as f:
214
+ model = json.load(f)
215
+ else:
216
+ with open(apis_path, 'r', encoding='utf-8') as f:
217
+ model = json.load(f)
218
+
219
+ # merge plugins
220
+ for plugin_name, vers in plugin.import_plugins().items():
221
+
222
+ if plugin_name != service:
223
+ continue
224
+
225
+ for ver, spec in vers.items():
226
+
227
+ # 2017-03-12 -> v20170312
228
+ compact_ver = 'v' + ver.replace('-', '')
229
+
230
+ if compact_ver != version:
231
+ continue
232
+
233
+ model["metadata"].update(spec["metadata"])
234
+ model["actions"].update(spec["actions"])
235
+ model["objects"].update(spec["objects"])
236
+
237
+ if not model:
198
238
  raise Exception("Not find service:%s version:%s model" % (service, version))
199
239
 
200
- if six.PY2:
201
- with open(apis_path, 'r') as f:
202
- return json.load(f)
203
- else:
204
- with open(apis_path, 'r', encoding='utf-8') as f:
205
- return json.load(f)
240
+ return model
206
241
 
207
242
  def get_service_description(self, service, version):
208
243
  service_model = self.get_service_model(service, version)
tccli/oauth.py ADDED
@@ -0,0 +1,115 @@
1
+ import json
2
+ import os
3
+ import time
4
+
5
+ import requests
6
+ import uuid
7
+
8
+ _API_ENDPOINT = "https://cli.cloud.tencent.com"
9
+ _CRED_REFRESH_SAFE_DUR = 60 * 5
10
+ _ACCESS_REFRESH_SAFE_DUR = 60 * 5
11
+
12
+
13
+ def maybe_refresh_credential(profile):
14
+ cred_path = cred_path_of_profile(profile)
15
+ try:
16
+ with open(cred_path, "r") as cred_file:
17
+ cred = json.load(cred_file)
18
+ except IOError:
19
+ # file not found, don't check
20
+ return
21
+
22
+ if cred.get("type") != "oauth":
23
+ return
24
+
25
+ try:
26
+ now = time.time()
27
+
28
+ expires_at = cred["expiresAt"]
29
+ if expires_at - now > _CRED_REFRESH_SAFE_DUR:
30
+ return
31
+
32
+ token_info = cred["oauth"]
33
+ site = token_info["site"]
34
+ access_expires = token_info["expiresAt"]
35
+ if access_expires - now < _ACCESS_REFRESH_SAFE_DUR:
36
+ refresh_token = token_info["refreshToken"]
37
+ open_id = token_info["openId"]
38
+ new_token = refresh_user_token(refresh_token, open_id, site)
39
+ token_info.update(new_token)
40
+
41
+ access_token = token_info["accessToken"]
42
+ new_cred = get_temp_cred(access_token, site)
43
+ save_credential(token_info, new_cred, profile)
44
+
45
+ except KeyError as e:
46
+ print("failed to refresh credential, your credential file(%s) is corrupted, %s" % (cred_path, e))
47
+
48
+ except Exception as e:
49
+ print("failed to refresh credential, %s" % e)
50
+
51
+
52
+ def refresh_user_token(ref_token, open_id, site):
53
+ api_endpoint = _API_ENDPOINT + "/refresh_user_token"
54
+ body = {
55
+ "TraceId": str(uuid.uuid4()),
56
+ "RefreshToken": ref_token,
57
+ "OpenId": open_id,
58
+ "Site": site,
59
+ }
60
+ http_response = requests.post(api_endpoint, json=body, verify=False)
61
+ resp = http_response.json()
62
+
63
+ if "Error" in resp:
64
+ raise ValueError("refresh_user_token: %s" % json.dumps(resp))
65
+
66
+ return {
67
+ "accessToken": resp["AccessToken"],
68
+ "expiresAt": resp["ExpiresAt"],
69
+ }
70
+
71
+
72
+ def get_temp_cred(access_token, site):
73
+ api_endpoint = _API_ENDPOINT + "/get_temp_cred"
74
+ body = {
75
+ "TraceId": str(uuid.uuid4()),
76
+ "AccessToken": access_token,
77
+ "Site": site,
78
+ }
79
+ http_response = requests.post(api_endpoint, json=body, verify=False)
80
+ resp = http_response.json()
81
+
82
+ if "Error" in resp:
83
+ raise ValueError("get_temp_key: %s" % json.dumps(resp))
84
+
85
+ return {
86
+ "secretId": resp["SecretId"],
87
+ "secretKey": resp["SecretKey"],
88
+ "token": resp["Token"],
89
+ "expiresAt": resp["ExpiresAt"],
90
+ }
91
+
92
+
93
+ def cred_path_of_profile(profile):
94
+ return os.path.join(os.path.expanduser("~"), ".tccli", profile + ".credential")
95
+
96
+
97
+ def save_credential(token, new_cred, profile):
98
+ cred_path = cred_path_of_profile(profile)
99
+
100
+ cred = {
101
+ "type": "oauth",
102
+ "secretId": new_cred["secretId"],
103
+ "secretKey": new_cred["secretKey"],
104
+ "token": new_cred["token"],
105
+ "expiresAt": new_cred["expiresAt"],
106
+ "oauth": {
107
+ "openId": token["openId"],
108
+ "accessToken": token["accessToken"],
109
+ "expiresAt": token["expiresAt"],
110
+ "refreshToken": token["refreshToken"],
111
+ "site": token["site"],
112
+ },
113
+ }
114
+ with open(cred_path, "w") as cred_file:
115
+ json.dump(cred, cred_file, indent=4)
tccli/plugin.py ADDED
@@ -0,0 +1,30 @@
1
+ import importlib
2
+ import logging
3
+ import pkgutil
4
+
5
+ import tccli.plugins as plugins
6
+
7
+ _plugins = {}
8
+ _imported = False
9
+
10
+
11
+ def import_plugins():
12
+ global _imported
13
+
14
+ if not _imported:
15
+ _reimport_plugins()
16
+ _imported = True
17
+
18
+ return _plugins
19
+
20
+
21
+ def _reimport_plugins():
22
+ for _, name, _ in pkgutil.iter_modules(plugins.__path__, plugins.__name__ + "."):
23
+ mod = importlib.import_module(name)
24
+ register_service = getattr(mod, "register_service", None)
25
+ if not register_service:
26
+ logging.warning("invalid module %s" % name)
27
+ continue
28
+ register_service(_plugins)
29
+
30
+ return _plugins
File without changes
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+ from tccli.plugins.auth.login import login_command_entrypoint
3
+ from tccli.plugins.auth.logout import logout_command_entrypoint
4
+
5
+ service_name = "auth"
6
+ service_version = "2024-08-20"
7
+
8
+ _spec = {
9
+ "metadata": {
10
+ "serviceShortName": service_name,
11
+ "apiVersion": service_version,
12
+ "description": "auth related commands",
13
+ },
14
+ "actions": {
15
+ "login": {
16
+ "name": "登陆",
17
+ "document": "login through sso",
18
+ "input": "loginRequest",
19
+ "output": "loginResponse",
20
+ "action_caller": login_command_entrypoint,
21
+ },
22
+ "logout": {
23
+ "name": "登出",
24
+ "document": "remove local credential file",
25
+ "input": "logoutRequest",
26
+ "output": "logoutResponse",
27
+ "action_caller": logout_command_entrypoint,
28
+ },
29
+ },
30
+ "objects": {
31
+ "loginRequest": {
32
+ "members": [
33
+ {
34
+ "name": "browser",
35
+ "member": "string",
36
+ "type": "string",
37
+ "required": False,
38
+ "document": "use browser=no to indicate no browser login mode",
39
+ },
40
+ ],
41
+ },
42
+ "loginResponse": {
43
+ "members": [],
44
+ },
45
+ "logoutRequest": {
46
+ "members": [],
47
+ },
48
+ "logoutResponse": {
49
+ "members": [],
50
+ },
51
+ },
52
+ "version": "1.0",
53
+ }
54
+
55
+
56
+ def register_service(specs):
57
+ specs[service_name] = {
58
+ service_version: _spec,
59
+ }
@@ -0,0 +1,88 @@
1
+ import time
2
+ import traceback
3
+ import socket
4
+ from threading import Thread
5
+ from tccli import oauth
6
+
7
+ try:
8
+ from urlparse import urlparse, parse_qs
9
+ from Queue import Queue
10
+ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
11
+ from SocketServer import ThreadingTCPServer, TCPServer
12
+ except ImportError:
13
+ from urllib.parse import urlparse, parse_qs
14
+ from queue import Queue
15
+ from http.server import HTTPServer, BaseHTTPRequestHandler
16
+ from socketserver import ThreadingTCPServer, TCPServer
17
+
18
+
19
+ # chrome keeps previous connection alive, so use threading to avoid blocking
20
+ class ThreadingHTTPServer(ThreadingTCPServer):
21
+ allow_reuse_address = 1
22
+
23
+ def server_bind(self):
24
+ """Override server_bind to store the server name."""
25
+ TCPServer.server_bind(self)
26
+ host, port = self.socket.getsockname()[:2]
27
+ self.server_name = socket.getfqdn(host)
28
+ self.server_port = port
29
+
30
+
31
+ class HTTPHandler(BaseHTTPRequestHandler):
32
+ result_queue = Queue(1)
33
+
34
+ def do_GET(self):
35
+ try:
36
+ parsed_url = urlparse(self.path)
37
+ query_vals = parse_qs(parsed_url.query)
38
+
39
+ open_id = query_vals.get("open_id")[0]
40
+ access_token = query_vals.get("access_token")[0]
41
+ refresh_token = query_vals.get("refresh_token")[0]
42
+ expires_at = int(query_vals.get("expires_at")[0])
43
+ state = query_vals.get("state")[0]
44
+ redirect_url = query_vals.get("redirect_url")[0]
45
+ site = query_vals.get("site")[0]
46
+ token = {
47
+ "openId": open_id,
48
+ "accessToken": access_token,
49
+ "refreshToken": refresh_token,
50
+ "expiresAt": expires_at,
51
+ "state": state,
52
+ "site": site,
53
+ }
54
+ cred = oauth.get_temp_cred(token["accessToken"], token["site"])
55
+ self.result_queue.put((token, cred))
56
+ self.send_response(307)
57
+ self.send_header("Location", redirect_url)
58
+ self.end_headers()
59
+ except Exception:
60
+ err = traceback.format_exc()
61
+ print(err)
62
+ self.send_response(400)
63
+ self.end_headers()
64
+ self.wfile.write("login failed due to the following error:\n\n".encode("utf-8"))
65
+ self.wfile.write(err.encode("utf-8"))
66
+ self.wfile.flush()
67
+
68
+ # suppress debug message
69
+ def log_message(self, format, *args):
70
+ pass
71
+
72
+
73
+ def try_run(start_search_port, end_search_port):
74
+ port = start_search_port
75
+
76
+ while port <= end_search_port:
77
+ server_address = ('', port)
78
+ try:
79
+ ThreadingHTTPServer.daemon_threads = True
80
+ httpd = ThreadingHTTPServer(server_address, HTTPHandler)
81
+ t = Thread(target=httpd.serve_forever)
82
+ t.setDaemon(True)
83
+ t.start()
84
+ return port, HTTPHandler.result_queue
85
+ except socket.error:
86
+ port += 1
87
+
88
+ raise socket.error("no port available from range [%d, %d]" % (start_search_port, end_search_port))
@@ -0,0 +1,130 @@
1
+ # coding: utf-8
2
+ import base64
3
+ import json
4
+ import random
5
+ import string
6
+ import sys
7
+ import time
8
+ from six.moves.urllib.parse import urlencode
9
+ import webbrowser
10
+
11
+ from tccli import oauth
12
+ from tccli.plugins.auth import texts
13
+
14
+ _APP_ID = 100038427476
15
+ _AUTH_URL = "https://cloud.tencent.com/open/authorize"
16
+ _REDIRECT_URL = "https://cli.cloud.tencent.com/oauth"
17
+ _SITE = "cn"
18
+ _DEFAULT_LANG = "zh-CN"
19
+
20
+ _START_SEARCH_PORT = 9000
21
+ _END_SEARCH_PORT = _START_SEARCH_PORT + 100
22
+
23
+
24
+ def print_message(msg):
25
+ print(msg)
26
+ sys.stdout.flush()
27
+
28
+
29
+ def login_command_entrypoint(args, parsed_globals):
30
+ language = parsed_globals.get("language")
31
+ if not language:
32
+ language = _DEFAULT_LANG
33
+ texts.set_lang(language)
34
+
35
+ profile = parsed_globals.get("profile", "default")
36
+ if not profile:
37
+ profile = "default"
38
+
39
+ browser = args.get("browser")
40
+
41
+ login(browser != "no", profile, language)
42
+
43
+
44
+ def login(use_browser, profile, language):
45
+ characters = string.ascii_letters + string.digits
46
+ state = ''.join(random.choice(characters) for _ in range(10))
47
+
48
+ if use_browser:
49
+ token, cred = _get_token(state, language)
50
+ else:
51
+ token, cred = _get_token_no_browser(state, language)
52
+
53
+ if token["state"] != state:
54
+ raise ValueError("invalid state %s" % token["state"])
55
+
56
+ oauth.save_credential(token, cred, profile)
57
+
58
+ print_message("")
59
+ print_message(texts.get("login_success") % oauth.cred_path_of_profile(profile))
60
+
61
+
62
+ def _get_token(state, language):
63
+ from tccli.plugins.auth import browser_flow
64
+
65
+ port, result_queue = browser_flow.try_run(_START_SEARCH_PORT, _END_SEARCH_PORT)
66
+
67
+ redirect_params = {
68
+ "redirect_url": "http://localhost:%d" % port,
69
+ "lang": language,
70
+ "site": _SITE,
71
+ }
72
+ redirect_query = urlencode(redirect_params)
73
+ redirect_url = _REDIRECT_URL + "?" + redirect_query
74
+ url_params = {
75
+ "scope": "login",
76
+ "app_id": _APP_ID,
77
+ "redirect_url": redirect_url,
78
+ "state": state,
79
+ }
80
+ url_query = urlencode(url_params)
81
+ auth_url = _AUTH_URL + "?" + url_query
82
+
83
+ if not webbrowser.open(auth_url):
84
+ print_message(texts.get("login_failed_due_to_no_browser"))
85
+ sys.exit(1)
86
+
87
+ print_message(texts.get("login_prompt"))
88
+ print_message(auth_url)
89
+
90
+ # use polling to avoid being unresponsive in python2
91
+ while result_queue.empty():
92
+ time.sleep(1)
93
+
94
+ result = result_queue.get()
95
+ if isinstance(result, Exception):
96
+ raise result
97
+
98
+ return result
99
+
100
+
101
+ def _get_token_no_browser(state, language):
102
+ redirect_params = {
103
+ "browser": "no",
104
+ "lang": language,
105
+ "site": _SITE,
106
+ }
107
+ redirect_query = urlencode(redirect_params)
108
+ redirect_url = _REDIRECT_URL + "?" + redirect_query
109
+ url_params = {
110
+ "scope": "login",
111
+ "app_id": _APP_ID,
112
+ "redirect_url": redirect_url,
113
+ "state": state,
114
+ }
115
+ url_query = urlencode(url_params)
116
+ auth_url = _AUTH_URL + "?" + url_query
117
+
118
+ print_message(texts.get("login_prompt_no_browser"))
119
+ print_message("")
120
+ print_message(auth_url)
121
+
122
+ try:
123
+ input_func = raw_input
124
+ except NameError:
125
+ input_func = input
126
+
127
+ user_input = input_func(texts.get("login_prompt_code_no_browser"))
128
+ token = json.loads(base64.b64decode(user_input))
129
+ cred = oauth.get_temp_cred(token["accessToken"], token["site"])
130
+ return token, cred
@@ -0,0 +1,21 @@
1
+ # coding: utf-8
2
+ import os
3
+
4
+ from tccli import oauth
5
+ from tccli.plugins.auth import texts
6
+
7
+
8
+ def logout_command_entrypoint(args, parsed_globals):
9
+ language = parsed_globals.get("language")
10
+ if not language:
11
+ language = "zh-CN"
12
+ texts.set_lang(language)
13
+
14
+ profile = parsed_globals.get("profile", "default")
15
+ if not profile:
16
+ profile = "default"
17
+
18
+ cred_path = oauth.cred_path_of_profile(profile)
19
+ if os.path.exists(cred_path):
20
+ os.remove(cred_path)
21
+ print(texts.get("logout") % cred_path)
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ _lang = "zh-CN"
3
+
4
+ texts = {
5
+ "zh-CN": {
6
+ "login_prompt": "您的浏览器已打开, 请根据提示完成登录",
7
+ "login_prompt_no_browser": "在浏览器中转到以下链接, 并根据提示完成登录:",
8
+ "login_prompt_code_no_browser": "完成后,输入浏览器中提供的验证码:",
9
+ "login_failed_due_to_no_browser": "无法打开浏览器, 请尝试添加 '--browser no' 选项",
10
+ "login_success": "登陆成功, 密钥凭证已被写入: %s",
11
+ "logout": "登出成功, 密钥凭证已被删除: %s",
12
+ },
13
+ "en-US": {
14
+ "login_prompt": "Your browser is open, please complete the login according to the prompts",
15
+ "login_prompt_no_browser": "Go to the following link in your browser, and complete the sign-in prompts:",
16
+ "login_prompt_code_no_browser": "Once finished, enter the verification code provided in your browser:",
17
+ "login_failed_due_to_no_browser": "Failed to launch browser, please try option '--browser no'",
18
+ "login_success": "Login succeed, credential has been written to %s",
19
+ "logout": "Logout succeed, credential has been removed: %s",
20
+ }
21
+ }
22
+
23
+
24
+ def set_lang(lang):
25
+ global _lang
26
+ _lang = lang
27
+
28
+
29
+ def get(key):
30
+ return texts[_lang][key]