Skip to content

Commit 738f429

Browse files
committed
support login with AKSK input
1 parent 21ec176 commit 738f429

File tree

11 files changed

+294
-42
lines changed

11 files changed

+294
-42
lines changed

package.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@
6565
"views": {
6666
"tc-terraform-resources": [
6767
{
68-
"id": "tcTerraform.sessionExplorer",
69-
"name": "%TcTerraform.view.session.explorer%",
68+
"id": "tcTerraform.loginExplorer",
69+
"name": "%TcTerraform.view.login.explorer%",
7070
"contextualTitle": "Session Explorer",
7171
"visibility": "collapsed"
7272
},
@@ -83,6 +83,12 @@
8383
}
8484
]
8585
},
86+
"viewsWelcome": [
87+
{
88+
"view": "tcTerraform.loginExplorer",
89+
"contents": "%TcTerraform.view.login.welcome%"
90+
}
91+
],
8692
"languages": [
8793
{
8894
"id": "terraform",
@@ -101,8 +107,7 @@
101107
"commands": [
102108
{
103109
"command": "tcTerraform.login",
104-
"title": "Login",
105-
"category": "TencentCloud Terraform"
110+
"title": "%TcTerraform.view.login.welcome%"
106111
},
107112
{
108113
"command": "tcTerraform.init",

package.nls.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
2-
"TcTerraform.title": "TencentCloud Terraofrm",
3-
"TcTerraform.view.session.explorer": "Connect to TencentCloud",
2+
"TcTerraform.title": "TencentCloud Terraform",
3+
"TcTerraform.view.login.explorer": "Login TencentCloud",
4+
"TcTerraform.view.login.welcome": "Please login the TencentCloud. If you don't have a account, please [Sign up](https://cloud.tencent.com/register) first.\n[Login TencentCloud](command:tcTerraform.login)",
5+
"TcTerraform.view.welcome": "Welcome to use TencentCloud Terraform extension, please wait for the page loading...",
6+
"TcTerraform.view.login": "登录腾讯云",
47
"TcTerraform.view.template.explorer": "Tempaltes Management",
58
"TcTerraform.view.resource.explorer": "Resources Explorer",
69
"TcTerraform.view.resource.explorer.cvm": "Import Resouce: CVM",
@@ -9,5 +12,9 @@
912
"TcTerraform.view.help.explorer.provider": "TencentCloud Terraform Provider",
1013
"TcTerraform.view.help.explorer.doc": "Documentation",
1114
"TcTerraform.view.help.explorer.repo": "GitHub Repository",
15+
"TcTerraform.AKSK.title": "API AKSK Login",
16+
"TcTerraform.QR.title": "Scan code Login",
17+
"TcTerraform.AKSK.title.placeholder": "请输入腾讯云 API 密钥 {0}",
18+
"TcTerraform.AKSK.title.verify.empty": "{0}不能为空",
1219
"TcTerraform.productName": "TcTerraform Toolkit"
13-
}
20+
}

src/commons/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@
88
export class Constants {
99
// eslint-disable-next-line @typescript-eslint/naming-convention
1010
public static TerraformTerminalName = "TIAT-Terraform";
11-
1211
}

src/commons/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import * as customerCmd from "./customCmdRegister";
2+
import { registerTencent } from "./tencent";
23
export { tencent } from "./tencent";
34
export * from "./container";
45
export * from "./context";
56
export { cmds } from "./customCmdRegister";
67

78
export function registerCommon() {
8-
// registerTencent();
9+
registerTencent();
910
customerCmd.regHelpCommands();
1011
customerCmd.regResourceRelatedCommands();
12+
1113
}

src/commons/tencent/commands.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { commands } from "vscode";
2+
import user from "./user";
23

34
export function registerCommands() {
4-
// commands.registerCommand(command.TENCENT_LOGIN, user.login);
5+
commands.registerCommand(command.TENCENT_LOGIN, user.login);
56

6-
// commands.registerCommand(command.TENCENT_LOGINOUT, user.loginOut);
7+
// commands.registerCommand(command.TENCENT_LOGINOUT, user.loginOut);
78
}
89

910
export namespace command {
10-
export const TENCENT_LOGIN = "toolkit.tencent.login";
11-
/** 退出登录 */
12-
export const TENCENT_LOGINOUT = "toolkit.tencent.loginout";
11+
// login command
12+
export const TENCENT_LOGIN = "tcTerraform.login";
13+
// logout command
14+
export const TENCENT_LOGINOUT = "tcTerraform.logout";
1315
}

src/commons/tencent/index.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
import { commands } from "vscode";
22
import { registerCommands, command as _command } from "./commands";
33

4-
// import "./api.localfile";
5-
// import './api';
6-
// import _user from "./user";
4+
import _user from "./user";
75
import _tree from "./treeDataProvider";
86

97
export async function registerTencent() {
10-
registerCommands();
8+
registerCommands();
119

12-
await initialization();
10+
await initialization();
1311
}
1412

1513
async function initialization() {
16-
// commands.executeCommand(
17-
// "setContext",
18-
// "tencent.login",
19-
// !!(await _user.getInfo())
20-
// );
14+
commands.executeCommand(
15+
"setContext",
16+
"tencent.login",
17+
!!(await _user.getInfo())
18+
);
2119
}
2220

2321
export namespace tencent {
24-
// export import user = _user;
25-
export import tree = _tree;
26-
export import command = _command;
22+
export import user = _user;
23+
export import tree = _tree;
24+
export import command = _command;
2725
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import MultiStepInput from "../../../multiStepInput";
2+
import { Credential } from "tencentcloud-sdk-nodejs/tencentcloud/common/interface";
3+
import { localize } from "vscode-nls-i18n";
4+
import constant from "../index";
5+
6+
export async function getCredentailByInput() {
7+
const title = localize(constant.AKSK_TITLE);
8+
async function collectInputs() {
9+
const state = {} as Partial<Credential>;
10+
await MultiStepInput.run((input) => inputSecretId(input, state));
11+
return state as Required<Omit<Credential, "token">>;
12+
}
13+
14+
async function inputSecretId(
15+
input: MultiStepInput,
16+
state: Partial<Credential>
17+
) {
18+
state.secretId = await input.showInputBox({
19+
title,
20+
step: 1,
21+
totalSteps: 2,
22+
value: state.secretId || "",
23+
placeholder: localize(constant.AKSK_PLACEHOLD, "SecretId"),
24+
validate: validateInput.bind({ key: "SecretId " }),
25+
});
26+
return (input: MultiStepInput) => inpuSecretKey(input, state);
27+
}
28+
29+
async function inpuSecretKey(
30+
input: MultiStepInput,
31+
state: Partial<Credential>
32+
) {
33+
state.secretKey = await input.showInputBox({
34+
title,
35+
step: 2,
36+
totalSteps: 2,
37+
value: state.secretKey || "",
38+
placeholder: localize(constant.AKSK_PLACEHOLD, "SecretKey"),
39+
validate: validateInput.bind({ key: "SecretKey " }),
40+
});
41+
}
42+
43+
async function validateInput(this: { key: string }, value: string) {
44+
if (!value) {
45+
return Promise.reject(localize(constant.AKSK_EMPTY, this.key));
46+
}
47+
48+
return undefined;
49+
}
50+
51+
return collectInputs();
52+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { getCredentailByInput } from "./credentail";
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import axios from "axios";
2+
import { window } from "vscode";
3+
import { join } from "path";
4+
import { Credential } from "tencentcloud-sdk-nodejs/tencentcloud/common/interface";
5+
6+
const LOGIN_SERVER = "https://test.ide.cloud.tencent.com";
7+
8+
const client = axios.create({
9+
baseURL: LOGIN_SERVER,
10+
proxy: {
11+
protocol: "http",
12+
host: "9.135.97.58",
13+
port: 8899,
14+
},
15+
});
16+
17+
function getQrState() {
18+
return client.get("/api/public/qcloud/oauth/qrcode").then((res) => {
19+
return res.data.data;
20+
});
21+
}
22+
23+
function getQrUrl(state: string) {
24+
return `${LOGIN_SERVER}/api/public/qcloud/oauth/scan?state=${state}`;
25+
}
26+
27+
type State = "WAITING" | "SCANNED" | "OK";
28+
function getState(state: string): Promise<State> {
29+
return client
30+
.get("/api/public/qcloud/oauth/status", {
31+
params: {
32+
state,
33+
},
34+
})
35+
.then((res) => res.data.data)
36+
.catch((err) => {
37+
return "WAITING";
38+
});
39+
}
40+
41+
type TokenInfo = {
42+
scope: string;
43+
appId: string;
44+
requestId: string;
45+
userAccessToken: string;
46+
userOpenId: string;
47+
expiresAt: string;
48+
userRefreshToken: string;
49+
userUnionId: string;
50+
};
51+
52+
function getTokenInfo(state: string): Promise<TokenInfo> {
53+
return client
54+
.get("/api/public/qcloud/oauth/token", { params: { state } })
55+
.then((res) => res.data.data);
56+
}
57+
58+
interface CredentialsType {
59+
credentials: {
60+
token: string;
61+
tmpSecretId: string;
62+
tmpSecretKey: string;
63+
};
64+
expiredTime: string;
65+
}
66+
67+
function getSecret(
68+
userAccessToken: string,
69+
duration = 7200 * 24
70+
): Promise<CredentialsType> {
71+
return client
72+
.get("/api/public/qcloud/oauth/federationToken", {
73+
params: { userAccessToken, duration },
74+
})
75+
.then((res) => res.data.data);
76+
}
77+
78+
function refreshToken(userRefreshToken: string, userOpenId: string) {
79+
return client
80+
.get("/api/public/qcloud/oauth/refreshToken", {
81+
params: { userRefreshToken, userOpenId },
82+
})
83+
.then((res) => res.data.data);
84+
}
85+
86+
export async function getCredentailByQr() {
87+
const stateCode = await getQrState();
88+
const qrUrl = getQrUrl(stateCode);
89+
90+
const terminal = window.createTerminal({
91+
name: "登录腾讯云",
92+
shellPath: join(__dirname, "bin/qrcode.js"),
93+
shellArgs: [qrUrl],
94+
isTransient: true,
95+
});
96+
terminal.show();
97+
98+
let state: State = "WAITING";
99+
100+
while (state !== "OK") {
101+
// await delay(500);
102+
const newState = await getState(stateCode);
103+
104+
if (state !== "SCANNED" && newState === "SCANNED") {
105+
terminal.sendText("已扫码,等待授权... \n", true);
106+
}
107+
108+
state = newState;
109+
}
110+
111+
terminal.sendText("授权成功 \n", true);
112+
terminal.dispose();
113+
114+
const u = await getTokenInfo(stateCode);
115+
116+
const {
117+
credentials: { token, tmpSecretId, tmpSecretKey },
118+
} = await getSecret(u.userAccessToken);
119+
120+
return {
121+
token,
122+
secretId: tmpSecretId,
123+
secretKey: tmpSecretKey,
124+
};
125+
}

src/commons/tencent/user/index.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { localize } from "vscode-nls-i18n";
2+
import { ExtensionContext, ProgressLocation, window } from "vscode";
3+
import { AbstractClient } from "tencentcloud-sdk-nodejs/tencentcloud/common/abstract_client";
4+
import { Credential } from "tencentcloud-sdk-nodejs/tencentcloud/common/interface";
5+
6+
import { container } from "../../container";
7+
import { Context } from "../../context";
8+
import { tree } from "../treeDataProvider";
9+
import { getCredentailByInput } from "./auth";
10+
11+
export namespace user {
12+
interface UserInfo {
13+
secretId: string;
14+
secretKey: string;
15+
token?: string;
16+
uin: string;
17+
}
18+
19+
export const AKSK_TITLE = "TcTerraform.AKSK.title";
20+
export const AKSK_PLACEHOLD = "TcTerraform.AKSK.title.placeholder";
21+
export const AKSK_EMPTY = "TcTerraform.AKSK.title.verify.empty";
22+
23+
const USER_INFO = "USER_INFO";
24+
25+
export async function login() {
26+
const api = localize(AKSK_TITLE);
27+
const pick = await window.showQuickPick(["xxxxxxx", api]);
28+
29+
if (pick) {
30+
const credential = await getCredentailByInput();
31+
// api === pick ? await getCredentailByInput() : await getCredentailByQr();
32+
}
33+
}
34+
35+
export async function getInfo(): Promise<UserInfo | undefined> {
36+
const { secrets } = container.get<ExtensionContext>(Context);
37+
const userinfo = await secrets.get(USER_INFO);
38+
39+
if (userinfo) {
40+
return JSON.parse(userinfo) as UserInfo;
41+
}
42+
43+
return undefined;
44+
}
45+
46+
export async function loginOut() {
47+
const yes = localize("common.yes");
48+
const action = await window.showWarningMessage(
49+
localize("tencent.loginout.title"),
50+
{
51+
modal: true,
52+
detail: localize("tencent.loginout.detail"),
53+
},
54+
yes
55+
);
56+
if (action !== yes) {
57+
return;
58+
}
59+
60+
const { secrets } = container.get<ExtensionContext>(Context);
61+
await secrets.delete(USER_INFO);
62+
63+
tree.refreshTreeData();
64+
}
65+
}
66+
67+
export default user;

0 commit comments

Comments
 (0)