diff --git a/src/home.ts b/src/home.ts index 0cacf2f..eefa77b 100644 --- a/src/home.ts +++ b/src/home.ts @@ -85,6 +85,25 @@ export default class DSHome { panel.webview.postMessage({command: 'setUsername', data: {ok: false}}); break; } + case 'getUserPublicKey': + var userPublicKey = ''; + if (this.user.existUserPrivateKey()) { + userPublicKey = this.user.getUserPublicKey(); + panel.webview.postMessage({command: 'getUserPublicKey', data: {userPublicKey: userPublicKey}}) + break; + } else { + panel.webview.postMessage({command: 'getUserPublicKey', data: {userPublicKey: userPublicKey}}) + break; + } + case 'createUserPublicKey': + this.user.createUserSSHKey(); + if (this.user.existUserPublicKey()) { + panel.webview.postMessage({command: 'createUserPublicKey', data: {ok: true}}) + break; + } else { + panel.webview.postMessage({command: 'createUserPublicKey', data: {ok: false}}) + break; + } case 'getDefaultPublicKey': var defaultPublicKey; if (utils.existDefaultPublicKey()) { diff --git a/src/user.ts b/src/user.ts index aaa0f60..4176e7e 100644 --- a/src/user.ts +++ b/src/user.ts @@ -1,6 +1,10 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as os from 'os'; +import * as fs from 'fs'; +const { + generateKeyPairSync, +} = require('node:crypto') export default class User { private context:vscode.ExtensionContext; @@ -15,6 +19,23 @@ export default class User { this.userToken = this.context.globalState.get(this.userTokenKey); } + private isLogged() { + var existUsername = false; + var existUserToken = false; + if(this.username != undefined && this.username != '') { + existUsername = true; + } + if(this.userToken != undefined && this.userToken != '') { + existUserToken = true; + } + + if (existUsername && existUserToken) { + return true; + } else { + return false; + } + } + public getUsernameFromLocal(): string|undefined { return this.username; } @@ -33,4 +54,73 @@ export default class User { this.userToken = userToken } + public getUserPrivateKeyPath() : string{ + if (!this.isLogged) { + return ''; + } + + return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}`) + } + + public getUserPublicKeyPath() :string{ + if (!this.isLogged) { + return ''; + } + + return path.join(os.homedir(), '.ssh', `id_rsa_${this.username}.pub`) + } + + public existUserPublicKey() :boolean{ + const userPublicKeyPath = this.getUserPublicKeyPath(); + return fs.existsSync(userPublicKeyPath) + } + + public existUserPrivateKey() :boolean{ + const userPrivateKeyPath = this.getUserPrivateKeyPath(); + return fs.existsSync(userPrivateKeyPath) + } + + public getUserPublicKey(): string { + const userPublicKeyPath = this.getUserPublicKeyPath(); + const userPublicKey = fs.readFileSync(userPublicKeyPath, 'utf-8'); + // remove `\r` `\n` + const trimmedDefaultPublicKey = userPublicKey.replace(/[\r\n]/g, ""); + return trimmedDefaultPublicKey; + } + + public getUserPrivateKey(): string { + const userPrivateKey = this.getUserPrivateKeyPath(); + return fs.readFileSync(userPrivateKey, 'utf-8'); + } + + public createUserSSHKey() { + if (this.existUserPublicKey() && this.existUserPrivateKey()) { + // if both public and private key exists, stop + return; + } + + const { + publicKey, + privateKey, + } = generateKeyPairSync('rsa', { + modulusLength: 4096, + publicKeyEncoding: { + type: 'spki', + format: 'pem', + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: 'aes-256-cbc', + passphrase: 'devstar' + }, + }); + + try { + fs.writeFileSync(this.getUserPublicKeyPath(), publicKey); + fs.writeFileSync(this.getUserPrivateKeyPath(), privateKey); + } catch(error) { + console.error("Failed to write public/private key into the default ssh public/key file: ", error); + } + } } \ No newline at end of file diff --git a/test/home.html b/test/home.html index db5bac3..308205a 100644 --- a/test/home.html +++ b/test/home.html @@ -314,7 +314,7 @@ header("Allow: GET, POST, OPTIONS, PUT, DELETE"); document.getElementById('loginModal').style.display = 'none'; } - function login() { + async function login() { var username = document.getElementById('username').value; var password = document.getElementById('password').value; const url = DEVSTAR_HOME + `/api/v1/users/${username}/tokens`; @@ -343,7 +343,7 @@ header("Allow: GET, POST, OPTIONS, PUT, DELETE"); } return response.json(); }) - .then(data => { + .then(async data => { // store token in global variable and vscode global state USERTOKEN = data.sha1; USERNAME = username; @@ -351,6 +351,21 @@ header("Allow: GET, POST, OPTIONS, PUT, DELETE"); setUsernameToVSCode(username); loadPageModules() + // if user public key exist, meaning that public key has been uploaded + await getUserPublicKeyFromVSCode() + .then(async userPublicKey => { + if (userPublicKey == '') { + await createUserPublicKeyByVSCode() + await getUserPublicKeyFromVSCode() + .then(async userPublicKey => { + // upload new created public key + + }) + + } + }) + + closeLoginModal() }) .catch(error => { @@ -418,6 +433,33 @@ header("Allow: GET, POST, OPTIONS, PUT, DELETE"); }) } + async function getUserPublicKeyFromVSCode() { + return new Promise(async (resolve, reject) => { + await communicateVSCodeByWebview('getUserPublicKey', {}) + .then(data => { + const publicKey = data.userPublicKey; + resolve(publicKey) + }) + .catch(error => { + reject(error) + }) + }) + } + + async function createUserPublicKeyByVSCode() { + await communicateVSCodeByWebview('createUserPublicKey', {}) + .then(res => { + if (res.ok) { + console.log('User public key has been created') + } else { + console.error('Failed to create user public key') + } + }) + .catch(error => { + console.error('Failed to request to create user public key: ', erro) + }) + } + // ===================================== Repo =========================== function loadRepositories() { // clear old data