|
@@ -0,0 +1,371 @@
|
|
|
+<template>
|
|
|
+ <v-layout
|
|
|
+ column
|
|
|
+ fill-height
|
|
|
+ >
|
|
|
+ <v-toolbar color="grey">
|
|
|
+ <v-toolbar-title flex>
|
|
|
+ {{ title }}
|
|
|
+ </v-toolbar-title>
|
|
|
+ <v-spacer />
|
|
|
+ <v-text-field
|
|
|
+ v-if="this.hideUrl == undefined"
|
|
|
+ label="Telws URL"
|
|
|
+ single-line
|
|
|
+ prepend-icon="mdi-link-variant"
|
|
|
+ hide-details
|
|
|
+ v-model="telwsurl"
|
|
|
+ />
|
|
|
+ <v-btn
|
|
|
+ icon
|
|
|
+ @click="connect"
|
|
|
+ >
|
|
|
+ <v-icon>mdi-lan-connect</v-icon>
|
|
|
+ </v-btn>
|
|
|
+ <v-btn
|
|
|
+ icon
|
|
|
+ @click="disconnect"
|
|
|
+ >
|
|
|
+ <v-icon>mdi-lan-disconnect</v-icon>
|
|
|
+ </v-btn>
|
|
|
+ </v-toolbar>
|
|
|
+ <v-layout fill-height>
|
|
|
+ <xterm-vue
|
|
|
+ ref="term"
|
|
|
+ v-on:title-change="titleChange"
|
|
|
+ />
|
|
|
+ </v-layout>
|
|
|
+ <v-toolbar color="grey">
|
|
|
+ <v-text-field
|
|
|
+ placeholder="Write here and press Enter to send..."
|
|
|
+ single-line
|
|
|
+ prepend-icon="mdi-typewriter"
|
|
|
+ hide-details
|
|
|
+ v-model="writeBarText"
|
|
|
+ @keydown.enter="pasteWriteBar"
|
|
|
+ />
|
|
|
+ </v-toolbar>
|
|
|
+ <v-dialog
|
|
|
+ v-model="userAuthDialog"
|
|
|
+ max-width="290"
|
|
|
+ >
|
|
|
+ <v-card>
|
|
|
+ <v-card-title class="headline">Simple AAA Auth</v-card-title>
|
|
|
+
|
|
|
+ <v-card-text>
|
|
|
+ <v-form>
|
|
|
+ <v-text-field
|
|
|
+ ref="authUsername"
|
|
|
+ v-model="authDialogUsername"
|
|
|
+ label="Username"
|
|
|
+ @keydown.enter="focusPasswordTextfield"
|
|
|
+ />
|
|
|
+ <v-text-field
|
|
|
+ ref="authPassword"
|
|
|
+ v-model="authDialogPassword"
|
|
|
+ label="Password"
|
|
|
+ type="password"
|
|
|
+ @keydown.enter="doUserLogin"
|
|
|
+ />
|
|
|
+ </v-form>
|
|
|
+ </v-card-text>
|
|
|
+
|
|
|
+ <v-card-actions>
|
|
|
+ <v-spacer></v-spacer>
|
|
|
+
|
|
|
+ <v-btn
|
|
|
+ color="danger"
|
|
|
+ text
|
|
|
+ @click="cancelUserLogin"
|
|
|
+ >
|
|
|
+ Cancel
|
|
|
+ </v-btn>
|
|
|
+
|
|
|
+ <v-btn
|
|
|
+ color="success"
|
|
|
+ text
|
|
|
+ @click="doUserLogin"
|
|
|
+ >
|
|
|
+ Login
|
|
|
+ </v-btn>
|
|
|
+ </v-card-actions>
|
|
|
+ </v-card>
|
|
|
+ </v-dialog>
|
|
|
+ </v-layout>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+//import XtermVuePlugin from "@swzry/xterm-vue";
|
|
|
+import axios from "axios";
|
|
|
+import smcrypto from "sm-crypto";
|
|
|
+const ConnState = {
|
|
|
+ IDLE: 0,
|
|
|
+ GET_INFO: 1,
|
|
|
+ USER_AUTH: 2,
|
|
|
+ SERVER_AUTH: 3,
|
|
|
+ AUTH_OK: 4,
|
|
|
+ CONNECTED: 5
|
|
|
+};
|
|
|
+export default {
|
|
|
+ name: "telws_view",
|
|
|
+ props: ["hideUrl", "defaultUrl", "autoConnect"],
|
|
|
+ data: () => ({
|
|
|
+ writeBarText: "",
|
|
|
+ title: "Untitled",
|
|
|
+ telwsurl: "",
|
|
|
+ userAuthDialog: false,
|
|
|
+ authDialogUsername: "",
|
|
|
+ authDialogPassword: "",
|
|
|
+ connSM: {
|
|
|
+ state: ConnState.IDLE,
|
|
|
+ axiosCancel: undefined,
|
|
|
+ continueDo: false,
|
|
|
+ url_prefix: "",
|
|
|
+ auth_jwt: "",
|
|
|
+ wsurl: "",
|
|
|
+ wsconn: undefined
|
|
|
+ },
|
|
|
+ encryptConfig: undefined
|
|
|
+ }),
|
|
|
+ mounted() {
|
|
|
+ this.$refs.term.fit();
|
|
|
+ if (this.defaultUrl) {
|
|
|
+ this.telwsurl = this.defaultUrl;
|
|
|
+ }
|
|
|
+ if (this.autoConnect != undefined) {
|
|
|
+ this.connect();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ destroyed() {
|
|
|
+ this.disconnect();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ focusUsernameTextfield(){
|
|
|
+ this.$refs.authUsername.focus()
|
|
|
+ },
|
|
|
+ focusPasswordTextfield(){
|
|
|
+ this.$refs.authPassword.focus()
|
|
|
+ },
|
|
|
+ pasteWriteBar(event) {
|
|
|
+ this.$refs.term.paste(this.writeBarText);
|
|
|
+ this.writeBarText = "";
|
|
|
+ },
|
|
|
+ titleChange(title) {
|
|
|
+ this.title = title;
|
|
|
+ },
|
|
|
+ popUserLoginDialog() {
|
|
|
+ this.authDialogUsername = "";
|
|
|
+ this.authDialogPassword = "";
|
|
|
+ this.userAuthDialog = true;
|
|
|
+ var self = this
|
|
|
+ this.$nextTick(() => {
|
|
|
+ self.focusUsernameTextfield();
|
|
|
+ })
|
|
|
+ },
|
|
|
+ doUserLogin() {
|
|
|
+ this.userAuthDialog = false;
|
|
|
+ var authData = {
|
|
|
+ method: "simple-aaa",
|
|
|
+ username: this.authDialogUsername,
|
|
|
+ password: this.authDialogPassword
|
|
|
+ };
|
|
|
+ var jsondata = JSON.stringify(authData);
|
|
|
+ var cm = 1;
|
|
|
+ if (this.encryptConfig.cipherMode === "C1C3C2") {
|
|
|
+ cm = 1;
|
|
|
+ } else {
|
|
|
+ cm = 0;
|
|
|
+ }
|
|
|
+ if (this.encryptConfig) {
|
|
|
+ var ct = smcrypto.sm2.doEncrypt(
|
|
|
+ jsondata,
|
|
|
+ this.encryptConfig.pubkey,
|
|
|
+ cm
|
|
|
+ );
|
|
|
+ var tureCipher = this.encryptConfig.header + ct;
|
|
|
+ this.doLoginAuth(tureCipher);
|
|
|
+ } else {
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ this.$refs.term.$term.writeln("Password encypt failed.");
|
|
|
+ this.$refs.term.$term.onData(function(data) {});
|
|
|
+ }
|
|
|
+ },
|
|
|
+ doWebsocketConnect() {
|
|
|
+ if (this.connSM.wsconn) {
|
|
|
+ this.connSM.wsconn.close();
|
|
|
+ }
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Connecting Websocket '" + this.connSM.wsurl + "'..."
|
|
|
+ );
|
|
|
+ var trueUrl = this.connSM.wsurl + "?token=" + this.connSM.auth_jwt
|
|
|
+ this.connSM.wsconn = new WebSocket(trueUrl);
|
|
|
+ var tty = this.$refs.term.$term;
|
|
|
+ var wsconn = this.connSM.wsconn;
|
|
|
+ var self = this
|
|
|
+ wsconn.onopen = function() {
|
|
|
+ //wsconn.binaryType = "arrayBuffer"
|
|
|
+ wsconn.binaryType = "blob"
|
|
|
+ tty.writeln("Connected.\r\n");
|
|
|
+ self.connSM.state = ConnState.CONNECTED
|
|
|
+ };
|
|
|
+ wsconn.onmessage = function(event) {
|
|
|
+ var er = new FileReader();
|
|
|
+ er.onload = function(e) {
|
|
|
+ var buf = new Uint8Array(er.result);
|
|
|
+ tty.write(buf);
|
|
|
+ };
|
|
|
+ er.readAsArrayBuffer(event.data);
|
|
|
+ };
|
|
|
+ wsconn.onclose = function() {
|
|
|
+ tty.writeln("\r\n\r\nConnection Closed.");
|
|
|
+ };
|
|
|
+ wsconn.onerror = function() {
|
|
|
+ tty.writeln("\r\n\r\nConnection Error.");
|
|
|
+ };
|
|
|
+ this.$refs.term.$term.onData(function(data) {
|
|
|
+ if (wsconn) {
|
|
|
+ var bd = Buffer.from(data, "utf8");
|
|
|
+ wsconn.send(bd);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ doLoginAuth(authData) {
|
|
|
+ var adjson = { authData: authData };
|
|
|
+ var self = this;
|
|
|
+ this.connSM.state = ConnState.SERVER_AUTH;
|
|
|
+ axios
|
|
|
+ .post(this.connSM.url_prefix + "login.satori", adjson, {
|
|
|
+ cancelToken: new axios.CancelToken(function executor(c) {
|
|
|
+ self.connSM.cancelToken = c;
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .then(resp => {
|
|
|
+ if (resp.data.suc) {
|
|
|
+ this.connSM.auth_jwt = resp.data.jwt;
|
|
|
+ this.connSM.state = ConnState.AUTH_OK;
|
|
|
+ this.doWebsocketConnect();
|
|
|
+ } else {
|
|
|
+ if (resp.data.etype == "auth_failed") {
|
|
|
+ this.$refs.term.$term.writeln("Simple AAA Auth Failed.");
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Reason: " + resp.data.auth_error_msg
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ this.$refs.term.$term.writeln("Failed to do remote auth.");
|
|
|
+ this.$refs.term.$term.writeln("Error: " + resp.data.etype);
|
|
|
+ }
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(errmsg => {
|
|
|
+ this.$refs.term.$term.writeln("Failed to do remote auth.");
|
|
|
+ console.warn(errmsg);
|
|
|
+ if (errmsg.response) {
|
|
|
+ console.warn(errmsg.response);
|
|
|
+ this.$refs.term.$term.writeln("Error: " + errmsg.response.data);
|
|
|
+ } else if (errmsg.request) {
|
|
|
+ console.warn(errmsg.request);
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Error: request failed. See javascript console for detail."
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Error: unknown. See javascript console for more detail."
|
|
|
+ );
|
|
|
+ }
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ cancelUserLogin() {
|
|
|
+ this.userAuthDialog = false;
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ this.$refs.term.$term.writeln("Auth cancel by user.");
|
|
|
+ this.$refs.term.$term.onData(function(data) {});
|
|
|
+ },
|
|
|
+ disconnect() {
|
|
|
+ if (this.connSM.state == ConnState.IDLE) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.connSM.continueDo = false;
|
|
|
+ if (this.connSM.cancelToken) {
|
|
|
+ this.connSM.cancelToken();
|
|
|
+ }
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ this.$refs.term.$term.writeln("");
|
|
|
+ this.$refs.term.$term.writeln("Disconnected.");
|
|
|
+ this.$refs.term.$term.onData(function(data) {});
|
|
|
+ if(this.connSM.wsconn){
|
|
|
+ this.connSM.wsconn.close()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ connect() {
|
|
|
+ if (this.connSM.state != ConnState.IDLE) {
|
|
|
+ this.disconnect();
|
|
|
+ }
|
|
|
+ this.$refs.term.$term.clear();
|
|
|
+ this.connSM.url_prefix = this.telwsurl + "/";
|
|
|
+ var turl = new URL(this.connSM.url_prefix);
|
|
|
+ var schpre = "ws:";
|
|
|
+ if (turl.protocol === "https:") {
|
|
|
+ schpre = "wss:";
|
|
|
+ }
|
|
|
+ this.connSM.wsurl = schpre + "//" + turl.host + turl.pathname + "ws.satori"
|
|
|
+ this.$refs.term.$term.writeln("Connecting " + this.telwsurl + "...");
|
|
|
+ var self = this;
|
|
|
+ this.connSM.continueDo = true;
|
|
|
+ this.connSM.state = ConnState.GET_INFO;
|
|
|
+ axios
|
|
|
+ .get(this.telwsurl, {
|
|
|
+ cancelToken: new axios.CancelToken(function executor(c) {
|
|
|
+ self.connSM.cancelToken = c;
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .then(resp => {
|
|
|
+ if (self.connSM.continueDo) {
|
|
|
+ this.$refs.term.$term.writeln("Telws server info fetched.");
|
|
|
+ console.log(resp.data);
|
|
|
+ if (resp.data.auth_methods) {
|
|
|
+ if (resp.data.auth_methods.indexOf("simple-aaa") > -1) {
|
|
|
+ this.encryptConfig = resp.data.encrypt;
|
|
|
+ this.connSM.state = ConnState.USER_AUTH;
|
|
|
+ this.popUserLoginDialog();
|
|
|
+ } else {
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Failed to connect to telws server."
|
|
|
+ );
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Error: no auth method supported by this client."
|
|
|
+ );
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Failed to connect to telws server."
|
|
|
+ );
|
|
|
+ this.$refs.term.$term.writeln("Error: not a valid telws server.");
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(errmsg => {
|
|
|
+ this.$refs.term.$term.writeln("Failed to connect to telws server.");
|
|
|
+ console.warn(errmsg);
|
|
|
+ if (errmsg.response) {
|
|
|
+ console.warn(errmsg.response);
|
|
|
+ this.$refs.term.$term.writeln("Error: " + errmsg.response.data);
|
|
|
+ } else if (errmsg.request) {
|
|
|
+ console.warn(errmsg.request);
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Error: request failed. See javascript console for detail."
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ this.$refs.term.$term.writeln(
|
|
|
+ "Error: unknown. See javascript console for more detail."
|
|
|
+ );
|
|
|
+ }
|
|
|
+ this.connSM.state = ConnState.IDLE;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|