Unverified Commit 58db16cb authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #39 from jumpserver/dev

Dev
parents 1d0d0d6a 46744e8f
......@@ -16,4 +16,7 @@ log/
vendor/
config.yml
host_key
cmd/coco
cmd/koko
cmd/logs
cmd/kokodir
build
var __extends=this&&this.__extends||function(){var a=function(c,d){return a=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,c){a.__proto__=c}||function(a,c){for(var b in c)c.hasOwnProperty(b)&&(a[b]=c[b])},a(c,d)};return function(c,d){function b(){this.constructor=c}a(c,d),c.prototype=null===d?Object.create(d):(b.prototype=d.prototype,new b)}}(),__awaiter=this&&this.__awaiter||function(a,b,c,d){return new(c||(c=Promise))(function(e,f){function g(a){try{i(d.next(a))}catch(a){f(a)}}function h(a){try{i(d["throw"](a))}catch(a){f(a)}}function i(a){a.done?e(a.value):new c(function(b){b(a.value)}).then(g,h)}i((d=d.apply(a,b||[])).next())})},__generator=this&&this.__generator||function(a,b){function c(a){return function(b){return d([a,b])}}function d(c){if(e)throw new TypeError("Generator is already executing.");for(;k;)try{if(e=1,h&&(i=2&c[0]?h["return"]:c[0]?h["throw"]||((i=h["return"])&&i.call(h),0):h.next)&&!(i=i.call(h,c[1])).done)return i;switch((h=0,i)&&(c=[2&c[0],i.value]),c[0]){case 0:case 1:i=c;break;case 4:return k.label++,{value:c[1],done:!1};case 5:k.label++,h=c[1],c=[0];continue;case 7:c=k.ops.pop(),k.trys.pop();continue;default:if((i=k.trys,!(i=0<i.length&&i[i.length-1]))&&(6===c[0]||2===c[0])){k=0;continue}if(3===c[0]&&(!i||c[1]>i[0]&&c[1]<i[3])){k.label=c[1];break}if(6===c[0]&&k.label<i[1]){k.label=i[1],i=c;break}if(i&&k.label<i[2]){k.label=i[2],k.ops.push(c);break}i[2]&&k.ops.pop(),k.trys.pop();continue;}c=b.call(a,k)}catch(a){c=[6,a],h=0}finally{e=i=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}var e,h,i,j,k={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return j={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(j[Symbol.iterator]=function(){return this}),j},isBrowser="undefined"!=typeof window,_fetch="undefined"==typeof fetch?void 0:fetch;isBrowser?WebSocket=window.WebSocket:(WebSocket=require("ws"),_fetch=require("node-fetch"));var OnNamespaceConnect="_OnNamespaceConnect",OnNamespaceConnected="_OnNamespaceConnected",OnNamespaceDisconnect="_OnNamespaceDisconnect",OnRoomJoin="_OnRoomJoin",OnRoomJoined="_OnRoomJoined",OnRoomLeave="_OnRoomLeave",OnRoomLeft="_OnRoomLeft",OnAnyEvent="_OnAnyEvent",OnNativeMessage="_OnNativeMessage",ackBinary="M",ackIDBinary="A",ackNotOKBinary="H",waitIsConfirmationPrefix="#",waitComesFromClientPrefix="$";function isSystemEvent(a){return!(a!==OnNamespaceConnect&&a!==OnNamespaceConnected&&a!==OnNamespaceDisconnect&&a!==OnRoomJoin&&a!==OnRoomJoined&&a!==OnRoomLeave&&a!==OnRoomLeft)}function isEmpty(a){return!(void 0!==a)||!(null!==a)||(""==a||"string"==typeof a||a instanceof String?0===a.length||""===a:!!(a instanceof Error)&&isEmpty(a.message))}var Message=function(){function a(){}return a.prototype.isConnect=function(){return this.Event==OnNamespaceConnect||!1},a.prototype.isDisconnect=function(){return this.Event==OnNamespaceDisconnect||!1},a.prototype.isRoomJoin=function(){return this.Event==OnRoomJoin||!1},a.prototype.isRoomLeft=function(){return this.Event==OnRoomLeft||!1},a.prototype.isWait=function(){return!isEmpty(this.wait)&&(this.wait[0]==waitIsConfirmationPrefix||this.wait[0]==waitComesFromClientPrefix||!1)},a.prototype.unmarshal=function(){return JSON.parse(this.Body)},a}();function marshal(a){return JSON.stringify(a)}var messageSeparator=";",messageFieldSeparatorReplacement="@%!semicolon@%!",validMessageSepCount=7,trueString="1",falseString="0",escapeRegExp=/;/g;function escapeMessageField(a){return isEmpty(a)?"":a.replace(escapeRegExp,messageFieldSeparatorReplacement)}var unescapeRegExp=new RegExp(messageFieldSeparatorReplacement,"g");function unescapeMessageField(a){return isEmpty(a)?"":a.replace(unescapeRegExp,messageSeparator)}var replyError=function(a){function b(c){var d=a.call(this,c)||this;return d.name="replyError",Error.captureStackTrace(d,b),Object.setPrototypeOf(d,b.prototype),d}return __extends(b,a),b}(Error);function reply(a){return new replyError(a)}function isReply(a){return a instanceof replyError}function serializeMessage(a){if(a.IsNative&&isEmpty(a.wait))return a.Body;var b=falseString,c=falseString,d=a.Body||"";return isEmpty(a.Err)||(d=a.Err.message,!isReply(a.Err)&&(b=trueString)),a.isNoOp&&(c=trueString),[a.wait||"",escapeMessageField(a.Namespace),escapeMessageField(a.Room),escapeMessageField(a.Event),b,c,d].join(messageSeparator)}function splitN(a,b,c){if(0==c)return[a];var d=a.split(b,c);if(d.length==c){var e=d.join(b)+b;return d.push(a.substr(e.length)),d}return[a]}function deserializeMessage(a,b){var c=new Message;if(0==a.length)return c.isInvalid=!0,c;var d=splitN(a,messageSeparator,validMessageSepCount-1);if(d.length!=validMessageSepCount)return b?(c.Event=OnNativeMessage,c.Body=a):c.isInvalid=!0,c;c.wait=d[0],c.Namespace=unescapeMessageField(d[1]),c.Room=unescapeMessageField(d[2]),c.Event=unescapeMessageField(d[3]),c.isError=d[4]==trueString||!1,c.isNoOp=d[5]==trueString||!1;var e=d[6];return isEmpty(e)?c.Body="":c.isError?c.Err=new Error(e):c.Body=e,c.isInvalid=!1,c.IsForced=!1,c.IsLocal=!1,c.IsNative=b&&c.Event==OnNativeMessage||!1,c}function genWait(){if(!isBrowser){var a=process.hrtime();return waitComesFromClientPrefix+1e9*a[0]+a[1]}var b=window.performance.now();return waitComesFromClientPrefix+b.toString()}function genWaitConfirmation(a){return waitIsConfirmationPrefix+a}function genEmptyReplyToWait(a){return a+messageSeparator.repeat(validMessageSepCount-1)}var Room=function(){function a(a,b){this.nsConn=a,this.name=b}return a.prototype.emit=function(a,b){var c=new Message;return c.Namespace=this.nsConn.namespace,c.Room=this.name,c.Event=a,c.Body=b,this.nsConn.conn.write(c)},a.prototype.leave=function(){var a=new Message;return a.Namespace=this.nsConn.namespace,a.Room=this.name,a.Event=OnRoomLeave,this.nsConn.askRoomLeave(a)},a}(),NSConn=function(){function a(a,b,c){this.conn=a,this.namespace=b,this.events=c,this.rooms=new Map}return a.prototype.emit=function(a,b){var c=new Message;return c.Namespace=this.namespace,c.Event=a,c.Body=b,this.conn.write(c)},a.prototype.ask=function(a,b){var c=new Message;return c.Namespace=this.namespace,c.Event=a,c.Body=b,this.conn.ask(c)},a.prototype.joinRoom=function(a){return __awaiter(this,void 0,void 0,function(){return __generator(this,function(b){switch(b.label){case 0:return[4,this.askRoomJoin(a)];case 1:return[2,b.sent()];}})})},a.prototype.room=function(a){return this.rooms.get(a)},a.prototype.leaveAll=function(){return __awaiter(this,void 0,void 0,function(){var a,b=this;return __generator(this,function(){return a=new Message,a.Namespace=this.namespace,a.Event=OnRoomLeft,a.IsLocal=!0,this.rooms.forEach(function(c,d){return __awaiter(b,void 0,void 0,function(){var b;return __generator(this,function(c){switch(c.label){case 0:a.Room=d,c.label=1;case 1:return c.trys.push([1,3,,4]),[4,this.askRoomLeave(a)];case 2:return c.sent(),[3,4];case 3:return b=c.sent(),[2,b];case 4:return[2];}})})}),[2,null]})})},a.prototype.forceLeaveAll=function(a){var b=this,c=new Message;c.Namespace=this.namespace,c.Event=OnRoomLeave,c.IsForced=!0,c.IsLocal=a,this.rooms.forEach(function(a,d){c.Room=d,fireEvent(b,c),b.rooms.delete(d),c.Event=OnRoomLeft,fireEvent(b,c),c.Event=OnRoomLeave})},a.prototype.disconnect=function(){var a=new Message;return a.Namespace=this.namespace,a.Event=OnNamespaceDisconnect,this.conn.askDisconnect(a)},a.prototype.askRoomJoin=function(a){var b=this;return new Promise(function(c,d){return __awaiter(b,void 0,void 0,function(){var b,e,f,g;return __generator(this,function(h){switch(h.label){case 0:if(b=this.rooms.get(a),void 0!==b)return c(b),[2];e=new Message,e.Namespace=this.namespace,e.Room=a,e.Event=OnRoomJoin,e.IsLocal=!0,h.label=1;case 1:return h.trys.push([1,3,,4]),[4,this.conn.ask(e)];case 2:return h.sent(),[3,4];case 3:return f=h.sent(),d(f),[2];case 4:return(g=fireEvent(this,e),!isEmpty(g))?(d(g),[2]):(b=new Room(this,a),this.rooms.set(a,b),e.Event=OnRoomJoined,fireEvent(this,e),c(b),[2]);}})})})},a.prototype.askRoomLeave=function(a){return __awaiter(this,void 0,void 0,function(){var b,c;return __generator(this,function(d){switch(d.label){case 0:if(!this.rooms.has(a.Room))return[2,ErrBadRoom];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,this.conn.ask(a)];case 2:return d.sent(),[3,4];case 3:return b=d.sent(),[2,b];case 4:return(c=fireEvent(this,a),!isEmpty(c))?[2,c]:(this.rooms.delete(a.Room),a.Event=OnRoomLeft,fireEvent(this,a),[2,null]);}})})},a.prototype.replyRoomJoin=function(a){if(!(isEmpty(a.wait)||a.isNoOp)){if(!this.rooms.has(a.Room)){var b=fireEvent(this,a);if(!isEmpty(b))return a.Err=b,void this.conn.write(a);this.rooms.set(a.Room,new Room(this,a.Room)),a.Event=OnRoomJoined,fireEvent(this,a)}this.conn.writeEmptyReply(a.wait)}},a.prototype.replyRoomLeave=function(a){return isEmpty(a.wait)||a.isNoOp?void 0:this.rooms.has(a.Room)?void(fireEvent(this,a),this.rooms.delete(a.Room),this.conn.writeEmptyReply(a.wait),a.Event=OnRoomLeft,fireEvent(this,a)):void this.conn.writeEmptyReply(a.wait)},a}();function fireEvent(a,b){return a.events.has(b.Event)?a.events.get(b.Event)(a,b):a.events.has(OnAnyEvent)?a.events.get(OnAnyEvent)(a,b):null}function isNull(a){return null===a||a===void 0||"undefined"==typeof a}function resolveNamespaces(a,b){if(isNull(a))return isNull(b)||b("connHandler is empty."),null;var c=new Map,d=new Map,e=0;if(Object.keys(a).forEach(function(b){e++;var f=a[b];if(f instanceof Function)d.set(b,f);else if(f instanceof Map)c.set(b,f);else{var g=new Map;Object.keys(f).forEach(function(a){g.set(a,f[a])}),c.set(b,g)}}),0<d.size){if(e!=d.size)return isNull(b)||b("all keys of connHandler should be events, mix of namespaces and event callbacks is not supported "+d.size+" vs total "+e),null;c.set("",d)}return c}function getEvents(a,b){return a.has(b)?a.get(b):null}var URLParamAsHeaderPrefix="X-Websocket-Header-";function parseHeadersAsURLParameters(a,b){if(isNull(a))return b;for(var c in a)if(a.hasOwnProperty(c)){var d=a[c];c=encodeURIComponent(URLParamAsHeaderPrefix+c),d=encodeURIComponent(d);var e=c+"="+d;b=-1==b.indexOf("?")?-1==b.indexOf("#")?b+"?"+e:b.split("#")[0]+"?"+e+"#"+b.split("#")[1]:b.split("?")[0]+"?"+e+"&"+b.split("?")[1]}return b}function dial(a,b,c){return _dial(a,b,0,c)}var websocketReconnectHeaderKey="X-Websocket-Reconnect";function _dial(a,b,c,d){if(isBrowser&&0==a.indexOf("/")){var e="https:"==document.location.protocol?"wss":"ws",f=document.location.port?":"+document.location.port:"";a=e+"://"+document.location.hostname+f+a}return-1==a.indexOf("ws")&&(a="ws://"+a),new Promise(function(e,f){WebSocket||f("WebSocket is not accessible through this browser.");var g=resolveNamespaces(b,f);if(!isNull(g)){isNull(d)&&(d={}),isNull(d.headers)&&(d.headers={});var h=d.reconnect?d.reconnect:0;0<c&&0<h?d.headers[websocketReconnectHeaderKey]=c.toString():!isNull(d.headers[websocketReconnectHeaderKey])&&delete d.headers[websocketReconnectHeaderKey];var i=makeWebsocketConnection(a,d),j=new Conn(i,g);j.reconnectTries=c,i.binaryType="arraybuffer",i.onmessage=function(a){var b=j.handle(a);return isEmpty(b)?void(j.isAcknowledged()&&e(j)):void f(b)},i.onopen=function(){i.send(ackBinary)},i.onerror=function(a){j.close(),f(a)},i.onclose=function(){if(j.isClosed());else{if(i.onmessage=void 0,i.onopen=void 0,i.onerror=void 0,i.onclose=void 0,0>=h)return j.close(),null;var c=new Map;j.connectedNamespaces.forEach(function(a,b){var d=[];!isNull(a.rooms)&&0<a.rooms.size&&a.rooms.forEach(function(a,b){d.push(b)}),c.set(b,d)}),j.close(),whenResourceOnline(a,h,function(g){_dial(a,b,g,d).then(function(a){return isNull(e)||"function () { [native code] }"==e.toString()?void c.forEach(function(b,c){a.connect(c).then(function(a){return function(b){a.forEach(function(a){b.joinRoom(a)})}}(b))}):void e(a)}).catch(f)})}return null}}})}function makeWebsocketConnection(a,b){return isBrowser&&!isNull(b)?(b.headers&&(a=parseHeadersAsURLParameters(b.headers,a)),b.protocols?new WebSocket(a,b.protocols):new WebSocket(a)):new WebSocket(a,b)}function whenResourceOnline(a,b,c){var d=a.replace(/(ws)(s)?\:\/\//,"http$2://"),e=1,f={method:"HEAD",mode:"no-cors"},g=function(){_fetch(d,f).then(function(){c(e)}).catch(function(){e++,setTimeout(function(){g()},b)})};setTimeout(g,b)}var ErrInvalidPayload=new Error("invalid payload"),ErrBadNamespace=new Error("bad namespace"),ErrBadRoom=new Error("bad room"),ErrClosed=new Error("use of closed connection"),ErrWrite=new Error("write closed");function isCloseError(a){return!(!a||isEmpty(a.message))&&0<=a.message.indexOf("[-1] write closed")}var Conn=function(){function a(a,b){this.conn=a,this.reconnectTries=0,this._isAcknowledged=!1,this.namespaces=b;var c=b.has("");this.allowNativeMessages=c&&b.get("").has(OnNativeMessage),this.queue=[],this.waitingMessages=new Map,this.connectedNamespaces=new Map,this.closed=!1}return a.prototype.wasReconnected=function(){return 0<this.reconnectTries},a.prototype.isAcknowledged=function(){return this._isAcknowledged},a.prototype.handle=function(a){if(!this._isAcknowledged){var b=this.handleAck(a.data);return null==b?(this._isAcknowledged=!0,this.handleQueue()):this.conn.close(),b}return this.handleMessage(a.data)},a.prototype.handleAck=function(a){var b=a[0];switch(b){case ackIDBinary:var c=a.slice(1);this.ID=c;break;case ackNotOKBinary:var d=a.slice(1);return new Error(d);default:return this.queue.push(a),null;}},a.prototype.handleQueue=function(){var a=this;null==this.queue||0==this.queue.length||this.queue.forEach(function(b,c){a.queue.splice(c,1),a.handleMessage(b)})},a.prototype.handleMessage=function(a){var b=deserializeMessage(a,this.allowNativeMessages);if(b.isInvalid)return ErrInvalidPayload;if(b.IsNative&&this.allowNativeMessages){var c=this.namespace("");return fireEvent(c,b)}if(b.isWait()){var d=this.waitingMessages.get(b.wait);if(null!=d)return void d(b)}var e=this.namespace(b.Namespace);switch(b.Event){case OnNamespaceConnect:this.replyConnect(b);break;case OnNamespaceDisconnect:this.replyDisconnect(b);break;case OnRoomJoin:if(void 0!==e){e.replyRoomJoin(b);break}case OnRoomLeave:if(void 0!==e){e.replyRoomLeave(b);break}default:if(void 0===e)return ErrBadNamespace;b.IsLocal=!1;var f=fireEvent(e,b);if(!isEmpty(f))return b.Err=f,this.write(b),f;}return null},a.prototype.connect=function(a){return this.askConnect(a)},a.prototype.waitServerConnect=function(a){var b=this;return isNull(this.waitServerConnectNotifiers)&&(this.waitServerConnectNotifiers=new Map),new Promise(function(c){return __awaiter(b,void 0,void 0,function(){var b=this;return __generator(this,function(){return this.waitServerConnectNotifiers.set(a,function(){b.waitServerConnectNotifiers.delete(a),c(b.namespace(a))}),[2]})})})},a.prototype.namespace=function(a){return this.connectedNamespaces.get(a)},a.prototype.replyConnect=function(a){if(!(isEmpty(a.wait)||a.isNoOp)){var b=this.namespace(a.Namespace);if(void 0!==b)return void this.writeEmptyReply(a.wait);var c=getEvents(this.namespaces,a.Namespace);return isNull(c)?(a.Err=ErrBadNamespace,void this.write(a)):void(b=new NSConn(this,a.Namespace,c),this.connectedNamespaces.set(a.Namespace,b),this.writeEmptyReply(a.wait),a.Event=OnNamespaceConnected,fireEvent(b,a),!isNull(this.waitServerConnectNotifiers)&&0<this.waitServerConnectNotifiers.size&&this.waitServerConnectNotifiers.has(a.Namespace)&&this.waitServerConnectNotifiers.get(a.Namespace)())}},a.prototype.replyDisconnect=function(a){if(!(isEmpty(a.wait)||a.isNoOp)){var b=this.namespace(a.Namespace);return void 0===b?void this.writeEmptyReply(a.wait):void(b.forceLeaveAll(!0),this.connectedNamespaces.delete(a.Namespace),this.writeEmptyReply(a.wait),fireEvent(b,a))}},a.prototype.ask=function(a){var b=this;return new Promise(function(c,d){return b.isClosed()?void d(ErrClosed):(a.wait=genWait(),b.waitingMessages.set(a.wait,function(a){return a.isError?void d(a.Err):void c(a)}),!b.write(a))?void d(ErrWrite):void 0})},a.prototype.askConnect=function(a){var b=this;return new Promise(function(c,d){return __awaiter(b,void 0,void 0,function(){var b,e,f,g,h;return __generator(this,function(i){switch(i.label){case 0:if(b=this.namespace(a),void 0!==b)return c(b),[2];if(e=getEvents(this.namespaces,a),isNull(e))return d(ErrBadNamespace),[2];if(f=new Message,f.Namespace=a,f.Event=OnNamespaceConnect,f.IsLocal=!0,b=new NSConn(this,a,e),g=fireEvent(b,f),!isEmpty(g))return d(g),[2];i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.ask(f)];case 2:return i.sent(),[3,4];case 3:return h=i.sent(),d(h),[2];case 4:return this.connectedNamespaces.set(a,b),f.Event=OnNamespaceConnected,fireEvent(b,f),c(b),[2];}})})})},a.prototype.askDisconnect=function(a){return __awaiter(this,void 0,void 0,function(){var b,c;return __generator(this,function(d){switch(d.label){case 0:if(b=this.namespace(a.Namespace),void 0===b)return[2,ErrBadNamespace];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,this.ask(a)];case 2:return d.sent(),[3,4];case 3:return c=d.sent(),[2,c];case 4:return b.forceLeaveAll(!0),this.connectedNamespaces.delete(a.Namespace),a.IsLocal=!0,[2,fireEvent(b,a)];}})})},a.prototype.isClosed=function(){return this.closed},a.prototype.write=function(a){if(this.isClosed())return!1;if(!a.isConnect()&&!a.isDisconnect()){var b=this.namespace(a.Namespace);if(void 0===b)return!1;if(!isEmpty(a.Room)&&!a.isRoomJoin()&&!a.isRoomLeft()&&!b.rooms.has(a.Room))return!1}return this.conn.send(serializeMessage(a)),!0},a.prototype.writeEmptyReply=function(a){this.conn.send(genEmptyReplyToWait(a))},a.prototype.close=function(){var a=this;if(!this.closed){var b=new Message;b.Event=OnNamespaceDisconnect,b.IsForced=!0,b.IsLocal=!0,this.connectedNamespaces.forEach(function(c){c.forceLeaveAll(!0),b.Namespace=c.namespace,fireEvent(c,b),a.connectedNamespaces.delete(c.namespace)}),this.waitingMessages.clear(),this.closed=!0,this.conn.readyState===this.conn.OPEN&&this.conn.close()}},a}();(function(){var a={dial:dial,isSystemEvent:isSystemEvent,OnNamespaceConnect:OnNamespaceConnect,OnNamespaceConnected:OnNamespaceConnected,OnNamespaceDisconnect:OnNamespaceDisconnect,OnRoomJoin:OnRoomJoin,OnRoomJoined:OnRoomJoined,OnRoomLeave:OnRoomLeave,OnRoomLeft:OnRoomLeft,OnAnyEvent:OnAnyEvent,OnNativeMessage:OnNativeMessage,Message:Message,Room:Room,NSConn:NSConn,Conn:Conn,ErrInvalidPayload:ErrInvalidPayload,ErrBadNamespace:ErrBadNamespace,ErrBadRoom:ErrBadRoom,ErrClosed:ErrClosed,ErrWrite:ErrWrite,isCloseError:isCloseError,reply:reply,marshal:marshal};if("undefined"!=typeof exports)exports=a,module.exports=a;else{var b="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global;b.neffos=a}})();
\ No newline at end of file
......@@ -2,21 +2,39 @@
<body style="margin: 0">
<script type="text/javascript" src="/static/js/jquery-2.1.1.js"></script>
<script type="text/javascript" src="/static/js/jquery-ui-1.10.4.min.js"></script>
<script type="text/javascript" src="/static/js/socket.io.js"></script>
<script type="text/javascript" src="/static/js/neffos.min.js"></script>
<script type="text/javascript" src="/static/plugins/elfinder/elfinder.full.js"></script>
<script type="text/javascript" src="/static/plugins/elfinder/i18n/elfinder.pl.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="/static/plugins/elfinder/css/elfinder.min.css">
<link rel="stylesheet" type="text/css" media="screen" href="/static/plugins/elfinder/css/theme-gray.css">
<script type="text/javascript" charset="utf-8">
var socket = io.connect('/elfinder');
socket.on('connect', function () {
console.log("Connect websocket done")
});
socket.on('data', function (msg) {
var sid = msg.sid;
init_elfinder(sid);
var scheme = document.location.protocol == "https:" ? "wss" : "ws";
var port = document.location.port ? ":" + document.location.port : "";
var wsURL = scheme + "://" + document.location.hostname + port + "/socket.io/";
dial(wsURL, {
"elfinder": {
_OnNamespaceConnected: function (nsConn, msg) {
console.log("Connect websocket done")
},
data: function (nsConn, msg) {
var data = msg.unmarshal();
var sid = data.sid;
initElfinder(sid);
}
}
}).then(conn => {
conn.connect("elfinder");
});
function init_elfinder(sid) {
// var socket = io.connect('/elfinder');
// socket.on('connect', function () {
// console.log("Connect websocket done")
// });
// socket.on('data', function (msg) {
// var sid = msg.sid;
// initElfinder(sid);
// });
function initElfinder(sid) {
var elf;
var opts = {
uiOptions : {
......
......@@ -3,40 +3,41 @@ module github.com/jumpserver/koko
go 1.12
require (
github.com/Azure/azure-pipeline-go v0.1.9
github.com/Azure/azure-pipeline-go v0.1.9 // indirect
github.com/Azure/azure-storage-blob-go v0.6.0
github.com/LeeEirc/elfinder v0.0.0-20190604073433-f4f8357e9220
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/LeeEirc/elfinder v0.0.0-20190718023636-5679c8bdb7bf
github.com/aliyun/aliyun-oss-go-sdk v1.9.8
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
github.com/aws/aws-sdk-go v1.19.46
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/elastic/go-elasticsearch v0.0.0
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gliderlabs/ssh v0.2.3-0.20190711180243-866d0ddf7991
github.com/go-playground/form v3.1.4+incompatible
github.com/googollee/go-socket.io v1.4.2-0.20190317095603-ed07a7212e28
github.com/go-playground/form v3.1.4+incompatible // indirect
github.com/gorilla/mux v1.7.2
github.com/gorilla/websocket v1.4.0
github.com/jarcoal/httpmock v1.0.4
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
github.com/konsorten/go-windows-terminal-sequences v1.0.2
github.com/kr/fs v0.1.0
github.com/kr/pty v1.1.4
github.com/kataras/neffos v0.0.7
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/leonelquinteros/gotext v1.4.0
github.com/mattn/go-runewidth v0.0.4
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/olekukonko/tablewriter v0.0.1
github.com/pkg/errors v0.8.1
github.com/pkg/sftp v1.10.0
github.com/satori/go.uuid v1.2.0
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.3.0 // indirect
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3
gopkg.in/yaml.v2 v2.2.2
)
replace (
github.com/gliderlabs/ssh v0.2.3-0.20190711180243-866d0ddf7991 => github.com/ibuler/ssh v0.1.6-0.20190509065047-1c00c8e8b607
github.com/googollee/go-engine.io v1.4.1 => github.com/ibuler/go-engine.io v1.4.2-0.20190529094538-7786d3a289b9
github.com/googollee/go-socket.io v1.4.2-0.20190317095603-ed07a7212e28 => github.com/LeeEirc/go-socket.io v1.4.2-0.20190610105739-e344e8b5a55a
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 => github.com/ibuler/crypto v0.0.0-20190509101200-a7099eef26a7
)
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-pipeline-go v0.1.9 h1:u7JFb9fFTE6Y/j8ae2VK33ePrRqJqoCM/IWkQdAZ+rg=
github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
github.com/Azure/azure-storage-blob-go v0.6.0 h1:SEATKb3LIHcaSIX+E6/K4kJpwfuozFEsmt5rS56N6CE=
github.com/Azure/azure-storage-blob-go v0.6.0/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/LeeEirc/elfinder v0.0.0-20190604073433-f4f8357e9220 h1:U865EO8YNrjZIGNp7O8QEYLZFDJ8zcooD73BiAqgDg4=
github.com/LeeEirc/elfinder v0.0.0-20190604073433-f4f8357e9220/go.mod h1:ApL/XFs34Gvqinex9Z1sZdsp3Jeu26nNuEsf1wQFx8s=
github.com/LeeEirc/go-socket.io v1.4.2-0.20190610105739-e344e8b5a55a h1:l5fhBUD24xuw9S4yCUd6hD7OFjYXiJiALT1aqQ4LLfA=
github.com/LeeEirc/go-socket.io v1.4.2-0.20190610105739-e344e8b5a55a/go.mod h1:yjlQxKcAZXZjpGwQVW/y1sgyL1ou+DdCpkswURDCRrU=
github.com/LeeEirc/elfinder v0.0.0-20190718023636-5679c8bdb7bf h1:dZipr1cwienSKNTXsveMmyd7VFY3v/eMHNl/vueN10s=
github.com/LeeEirc/elfinder v0.0.0-20190718023636-5679c8bdb7bf/go.mod h1:ApL/XFs34Gvqinex9Z1sZdsp3Jeu26nNuEsf1wQFx8s=
github.com/aliyun/aliyun-oss-go-sdk v1.9.8 h1:BOflvK0Zs/zGmoabyFIzTg5c3kguktWTXEwewwbuba0=
github.com/aliyun/aliyun-oss-go-sdk v1.9.8/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/aws/aws-sdk-go v1.19.46 h1:lRqljzjkGmEeiawkw4z1QgtCnU/S5Jw8lNeUuvmydUQ=
github.com/aws/aws-sdk-go v1.19.46/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elastic/go-elasticsearch v0.0.0 h1:Pd5fqOuBxKxv83b0+xOAJDAkziWYwFinWnBO0y+TZaA=
github.com/elastic/go-elasticsearch v0.0.0/go.mod h1:TkBSJBuTyFdBnrNqoPc54FN0vKf5c04IdM4zuStJ7xg=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-playground/form v3.1.4+incompatible h1:lvKiHVxE2WvzDIoyMnWcjyiBxKt2+uFJyZcPYWsLnjI=
github.com/go-playground/form v3.1.4+incompatible/go.mod h1:lhcKXfTuhRtIZCIKUeJ0b5F207aeQCPbZU09ScKjwWg=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/ibuler/crypto v0.0.0-20190509101200-a7099eef26a7 h1:1wAr7VKNYJw3mhTTU1Ztu5lyJKHzHmjlA9n+LV09z8E=
github.com/ibuler/crypto v0.0.0-20190509101200-a7099eef26a7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
github.com/ibuler/go-engine.io v1.4.2-0.20190529094538-7786d3a289b9 h1:8vjRBcvQ50mYr5y9jQWbYXOew3nn3+eF2YNpaAcFrAU=
github.com/ibuler/go-engine.io v1.4.2-0.20190529094538-7786d3a289b9/go.mod h1:26oFqHsnuWIzNOM0T08x21eQOydBosKOCgK3tyhzPPI=
github.com/ibuler/ssh v0.1.6-0.20190509065047-1c00c8e8b607 h1:FMGGB1fr8jroqo0LUUVRcgxkPlczXaWdmv9BSB+PhGo=
github.com/ibuler/ssh v0.1.6-0.20190509065047-1c00c8e8b607/go.mod h1:OC6P2JzT5kNhTLlU1N+zEw9Nxj+23dj/OhE72g8JyD8=
github.com/iris-contrib/go.uuid v2.0.0+incompatible h1:XZubAYg61/JwnJNbZilGjf3b3pB80+OQg2qf6c8BfWE=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/jarcoal/httpmock v1.0.4 h1:jp+dy/+nonJE4g4xbVtl9QdrUNbn6/3hDT5R4nDIZnA=
github.com/jarcoal/httpmock v1.0.4/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kataras/neffos v0.0.7 h1:o7zNRcN4fazOzi5j9kKG4IykgKZ37Y7gh0xcrY59wug=
github.com/kataras/neffos v0.0.7/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leonelquinteros/gotext v1.4.0 h1:2NHPCto5IoMXbrT0bldPrxj0qM5asOCwtb1aUQZ1tys=
github.com/leonelquinteros/gotext v1.4.0/go.mod h1:yZGXREmoGTtBvZHNcc+Yfug49G/2spuF/i/Qlsvz1Us=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.0 h1:DGA1KlA9esU6WcicH+P8PxFZOl15O6GYtab1cIJdOlE=
github.com/pkg/sftp v1.10.0/go.mod h1:NxmoDg/QLVWluQDUYG7XBZTLUpKeFa8e3aMf1BfjyHk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190214204934-8dcb7bc8c7fe/go.mod h1:E6PF97AdD6v0s+fPshSmumCW1S1Ne85RbPQxELkKa44=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405 h1:829vOVxxusYHC+IqBtkX5mbKtsY9fheQiQn0MZRVLfQ=
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hTWHhDTQDk/m2gorfVHUEa9vo3tp3D7TzwjI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
......@@ -167,7 +167,7 @@ func (h *interactiveHandler) Dispatch(ctx cctx.Context) {
if err != nil {
if err != io.EOF {
logger.Debug("user disconnected")
logger.Debug("User disconnected")
} else {
logger.Error("Read from user err: ", err)
}
......@@ -323,7 +323,7 @@ func (h *interactiveHandler) refreshAssetsAndNodesData() {
}
func (h *interactiveHandler) loadUserAssets(cachePolicy string) {
assets := service.GetUserAssets(h.user.ID, cachePolicy)
assets := service.GetUserAssets(h.user.ID, cachePolicy, "")
userAssetsCached.SetValue(h.user.ID, assets)
h.mu.Lock()
h.assets = assets
......
package handler
import (
"fmt"
"io"
"net"
"os"
"strings"
"sync"
"syscall"
"time"
......@@ -14,8 +12,6 @@ import (
"github.com/pkg/sftp"
"github.com/jumpserver/koko/pkg/cctx"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/service"
......@@ -26,498 +22,94 @@ func SftpHandler(sess ssh.Session) {
ctx, cancel := cctx.NewContext(sess)
defer cancel()
host, _, _ := net.SplitHostPort(sess.RemoteAddr().String())
handler := &sftpHandler{user: ctx.User(), addr: host}
handler.initial()
userSftp := NewSFTPHandler(ctx.User(), host)
handlers := sftp.Handlers{
FileGet: handler,
FilePut: handler,
FileCmd: handler,
FileList: handler,
FileGet: userSftp,
FilePut: userSftp,
FileCmd: userSftp,
FileList: userSftp,
}
req := sftp.NewRequestServer(sess, handlers)
if err := req.Serve(); err == io.EOF {
_ = req.Close()
handler.Close()
userSftp.Close()
logger.Info("sftp client exited session.")
} else if err != nil {
logger.Error("sftp server completed with error:", err)
}
}
type sftpHandler struct {
user *model.User
addr string
assets model.AssetList
rootPath string // tmp || home || ~
hosts map[string]*HostNameDir
permCache map[string]bool
func NewSFTPHandler(user *model.User, addr string) *sftpHandler {
assets := service.GetUserAssets(user.ID, "1", "")
return &sftpHandler{srvconn.NewUserSFTP(user, addr, assets...)}
}
func (fs *sftpHandler) initial() {
fs.loadAssets()
fs.hosts = make(map[string]*HostNameDir)
fs.rootPath = config.GetConf().SftpRoot
fs.permCache = make(map[string]bool)
for i, item := range fs.assets {
tmpDir := &HostNameDir{
rootPath: fs.rootPath,
hostname: item.Hostname,
asset: &fs.assets[i],
time: time.Now().UTC(),
}
fs.hosts[item.Hostname] = tmpDir
}
}
func (fs *sftpHandler) loadAssets() {
fs.assets = service.GetUserAssets(fs.user.ID, "1")
type sftpHandler struct {
*srvconn.UserSftp
}
func (fs *sftpHandler) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
var fileInfos = listerat{}
var err error
logger.Debug("list path: ", r.Filepath)
if r.Filepath == "/" {
for _, v := range fs.hosts {
fileInfos = append(fileInfos, v)
}
logger.Debug(fileInfos)
return fileInfos, err
}
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
hostDir, ok := fs.hosts[pathNames[0]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if hostDir.suMaps == nil {
hostDir.suMaps = make(map[string]*SysUserDir)
systemUsers := hostDir.asset.SystemUsers
for i, sysUser := range systemUsers {
hostDir.suMaps[sysUser.Name] = &SysUserDir{
time: time.Now().UTC(),
rootPath: fs.rootPath,
systemUser: &systemUsers[i],
prefix: fmt.Sprintf("/%s/%s", hostDir.asset.Hostname, sysUser.Name),
}
}
}
if len(pathNames) == 1 {
for _, v := range hostDir.suMaps {
fileInfos = append(fileInfos, v)
}
return fileInfos, err
}
var realPath string
var sysUserDir *SysUserDir
sysUserDir, ok = hostDir.suMaps[pathNames[1]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if !fs.validatePermission(hostDir.asset.ID, sysUserDir.systemUser.ID, model.ConnectAction) {
return nil, sftp.ErrSshFxPermissionDenied
}
if sysUserDir.client == nil {
client, conn, err := fs.GetSftpClient(hostDir.asset, sysUserDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
sysUserDir.homeDirPath, err = client.Getwd()
if err != nil {
return nil, err
}
sysUserDir.client = client
sysUserDir.conn = conn
}
realPath = sysUserDir.ParsePath(r.Filepath)
switch r.Method {
case "List":
logger.Debug("List method")
fileInfos, err = sysUserDir.client.ReadDir(realPath)
wraperFiles := make([]os.FileInfo, 0, len(fileInfos))
for i := 0; i < len(fileInfos); i++ {
wraperFiles = append(wraperFiles, &wrapperFileInfo{f: fileInfos[i]})
logger.Debug("List method: ", r.Filepath)
res, err := fs.ReadDir(r.Filepath)
fileInfos := make(listerat, 0, len(res))
for i := 0; i < len(res); i++ {
fileInfos = append(fileInfos, &wrapperSFTPFileInfo{f: res[i]})
}
return listerat(wraperFiles), err
return fileInfos, err
case "Stat":
logger.Debug("stat method")
fsInfo, err := sysUserDir.client.Stat(realPath)
return listerat([]os.FileInfo{&wrapperFileInfo{f: fsInfo}}), err
case "Readlink":
logger.Debug("Readlink method")
filename, err := sysUserDir.client.ReadLink(realPath)
fsInfo := &FakeFile{name: filename, modtime: time.Now().UTC()}
logger.Debug("stat method: ", r.Filepath)
fsInfo, err := fs.Stat(r.Filepath)
return listerat([]os.FileInfo{fsInfo}), err
case "Readlink":
logger.Debug("Readlink method", r.Filepath)
filename, err := fs.ReadLink(r.Filepath)
fsInfo := srvconn.NewFakeSymFile(filename)
return listerat([]os.FileInfo{&wrapperSFTPFileInfo{f: fsInfo}}), err
}
return fileInfos, err
return nil, sftp.ErrSshFxOpUnsupported
}
func (fs *sftpHandler) Filecmd(r *sftp.Request) (err error) {
logger.Debug("File cmd: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
return sftp.ErrSshFxPermissionDenied
}
hostDir, ok := fs.hosts[pathNames[0]]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if hostDir.suMaps == nil {
hostDir.suMaps = make(map[string]*SysUserDir)
systemUsers := hostDir.asset.SystemUsers
for i, sysUser := range systemUsers {
hostDir.suMaps[sysUser.Name] = &SysUserDir{
time: time.Now().UTC(),
rootPath: fs.rootPath,
systemUser: &systemUsers[i],
prefix: fmt.Sprintf("/%s/%s", hostDir.asset.Hostname, sysUser.Name),
}
}
}
suDir, ok := hostDir.suMaps[pathNames[1]]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !fs.validatePermission(hostDir.asset.ID, suDir.systemUser.ID, model.ConnectAction) {
return sftp.ErrSshFxPermissionDenied
}
if suDir.client == nil {
client, conn, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return sftp.ErrSshFxPermissionDenied
}
suDir.homeDirPath, err = client.Getwd()
if err != nil {
return err
}
suDir.client = client
suDir.conn = conn
}
realPathName := suDir.ParsePath(r.Filepath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", fs.user.Name, fs.user.Username),
Hostname: hostDir.asset.Hostname,
OrgID: hostDir.asset.OrgID,
SystemUser: suDir.systemUser.Name,
RemoteAddr: fs.addr,
Operate: r.Method,
Path: realPathName,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer fs.CreateFTPLog(logData)
switch r.Method {
case "Setstat":
return
case "Rename":
realNewName := suDir.ParsePath(r.Target)
logData.Path = fmt.Sprintf("%s=>%s", realPathName, realNewName)
err = suDir.client.Rename(realPathName, realNewName)
logger.Debug("%s=>%s", r.Filepath, r.Target)
return fs.Rename(r.Filepath, r.Target)
case "Rmdir":
err = suDir.client.RemoveDirectory(realPathName)
err = fs.RemoveDirectory(r.Filepath)
case "Remove":
err = suDir.client.Remove(realPathName)
err = fs.Remove(r.Filepath)
case "Mkdir":
err = suDir.client.MkdirAll(realPathName)
err = fs.MkdirAll(r.Filepath)
case "Symlink":
realNewName := suDir.ParsePath(r.Target)
logData.Path = fmt.Sprintf("%s=>%s", realPathName, realNewName)
err = suDir.client.Symlink(realPathName, realNewName)
logger.Debug("%s=>%s", r.Filepath, r.Target)
err = fs.Symlink(r.Filepath, r.Target)
default:
return
}
if err == nil {
logData.IsSuccess = true
}
return
}
func (fs *sftpHandler) Filewrite(r *sftp.Request) (io.WriterAt, error) {
logger.Debug("File write: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
return nil, sftp.ErrSshFxPermissionDenied
}
hostDir, ok := fs.hosts[pathNames[0]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if hostDir.suMaps == nil {
hostDir.suMaps = make(map[string]*SysUserDir)
systemUsers := hostDir.asset.SystemUsers
for i, sysUser := range systemUsers {
hostDir.suMaps[sysUser.Name] = &SysUserDir{
time: time.Now().UTC(),
rootPath: fs.rootPath,
systemUser: &systemUsers[i],
prefix: fmt.Sprintf("/%s/%s", hostDir.asset.Hostname, sysUser.Name),
}
}
}
suDir, ok := hostDir.suMaps[pathNames[1]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if !fs.validatePermission(hostDir.asset.ID, suDir.systemUser.ID, model.UploadAction) {
return nil, sftp.ErrSshFxPermissionDenied
}
if suDir.client == nil {
client, conn, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
suDir.homeDirPath, err = client.Getwd()
if err != nil {
return nil, err
}
suDir.client = client
suDir.conn = conn
}
realPathName := suDir.ParsePath(r.Filepath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", fs.user.Name, fs.user.Username),
Hostname: hostDir.asset.Hostname,
OrgID: hostDir.asset.OrgID,
SystemUser: suDir.systemUser.Name,
RemoteAddr: fs.addr,
Operate: "Upload",
Path: realPathName,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer fs.CreateFTPLog(logData)
f, err := suDir.client.Create(realPathName)
if err == nil {
logData.IsSuccess = true
}
f, err := fs.Create(r.Filepath)
return NewWriterAt(f), err
}
func (fs *sftpHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
logger.Debug("File read: ", r.Filepath)
pathNames := strings.Split(strings.TrimPrefix(r.Filepath, "/"), "/")
if len(pathNames) <= 2 {
return nil, sftp.ErrSshFxPermissionDenied
}
hostDir, ok := fs.hosts[pathNames[0]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if hostDir.suMaps == nil {
hostDir.suMaps = make(map[string]*SysUserDir)
systemUsers := hostDir.asset.SystemUsers
for i, sysUser := range systemUsers {
hostDir.suMaps[sysUser.Name] = &SysUserDir{
time: time.Now().UTC(),
rootPath: fs.rootPath,
systemUser: &systemUsers[i],
prefix: fmt.Sprintf("/%s/%s", hostDir.asset.Hostname, sysUser.Name),
}
}
}
suDir, ok := hostDir.suMaps[pathNames[1]]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if !fs.validatePermission(hostDir.asset.ID, suDir.systemUser.ID, model.DownloadAction) {
return nil, sftp.ErrSshFxPermissionDenied
}
if suDir.client == nil {
ftpClient, client, err := fs.GetSftpClient(hostDir.asset, suDir.systemUser)
if err != nil {
return nil, sftp.ErrSshFxPermissionDenied
}
suDir.homeDirPath, err = ftpClient.Getwd()
if err != nil {
return nil, err
}
suDir.client = ftpClient
suDir.conn = client
}
realPathName := suDir.ParsePath(r.Filepath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", fs.user.Name, fs.user.Username),
Hostname: hostDir.asset.Hostname,
OrgID: hostDir.asset.OrgID,
SystemUser: suDir.systemUser.Name,
RemoteAddr: fs.addr,
Operate: "Download",
Path: realPathName,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer fs.CreateFTPLog(logData)
f, err := suDir.client.Open(realPathName)
if err != nil {
return nil, err
}
logData.IsSuccess = true
f, err := fs.Open(r.Filepath)
return NewReaderAt(f), err
}
func (fs *sftpHandler) GetSftpClient(asset *model.Asset, sysUser *model.SystemUser) (sftpClient *sftp.Client, sshClient *srvconn.SSHClient, err error) {
sshClient, err = srvconn.NewClient(fs.user, asset, sysUser, config.GetConf().SSHTimeout*time.Second)
if err != nil {
return
}
sftpClient, err = sftp.NewClient(sshClient.Client)
if err != nil {
return
}
return sftpClient, sshClient, err
}
func (fs *sftpHandler) CreateFTPLog(data *model.FTPLog) {
for i := 0; i < 4; i++ {
err := service.PushFTPLog(data)
if err == nil {
break
}
logger.Debugf("create FTP log err: %s", err.Error())
}
}
func (fs *sftpHandler) Close() {
for _, dir := range fs.hosts {
if dir.suMaps == nil {
continue
}
for _, d := range dir.suMaps {
if d.client != nil {
_ = d.client.Close()
srvconn.RecycleClient(d.conn)
}
}
}
}
func (fs *sftpHandler) validatePermission(aid, suid, operate string) bool {
permKey := fmt.Sprintf("%s_%s_%s", aid, suid, operate)
permission, ok := fs.permCache[permKey]
if ok {
return permission
}
permission = service.ValidateUserAssetPermission(
fs.user.ID, aid, suid, operate,
)
fs.permCache[permKey] = permission
return permission
}
type HostNameDir struct {
rootPath string
hostname string
time time.Time
asset *model.Asset
suMaps map[string]*SysUserDir
}
func (h *HostNameDir) Name() string { return h.hostname }
func (h *HostNameDir) Size() int64 { return int64(0) }
func (h *HostNameDir) Mode() os.FileMode { return os.FileMode(0400) | os.ModeDir }
func (h *HostNameDir) ModTime() time.Time { return h.time }
func (h *HostNameDir) IsDir() bool { return true }
func (h *HostNameDir) Sys() interface{} {
// fake current dir sys info
fakeInfo, _ := os.Stat(".")
return fakeInfo.Sys()
}
type SysUserDir struct {
ID string
prefix string
rootPath string
systemUser *model.SystemUser
time time.Time
homeDirPath string
client *sftp.Client
conn *srvconn.SSHClient
}
func (su *SysUserDir) Name() string { return su.systemUser.Name }
func (su *SysUserDir) Size() int64 { return int64(0) }
func (su *SysUserDir) Mode() os.FileMode { return os.FileMode(0400) | os.ModeDir }
func (su *SysUserDir) ModTime() time.Time { return su.time }
func (su *SysUserDir) IsDir() bool { return true }
func (su *SysUserDir) Sys() interface{} {
// fake current dir sys info
fakeInfo, _ := os.Stat(".")
return fakeInfo.Sys()
}
func (su *SysUserDir) ParsePath(path string) string {
var realPath string
switch strings.ToLower(su.rootPath) {
case "home", "~", "":
realPath = strings.ReplaceAll(path, su.prefix, su.homeDirPath)
default:
realPath = strings.ReplaceAll(path, su.prefix, su.rootPath)
}
logger.Debug("real path: ", realPath)
return realPath
}
type FakeFile struct {
name string
modtime time.Time
symlink string
}
func (f *FakeFile) Name() string { return f.name }
func (f *FakeFile) Size() int64 { return int64(0) }
func (f *FakeFile) Mode() os.FileMode {
ret := os.FileMode(0644)
if f.symlink != "" {
ret = os.FileMode(0777) | os.ModeSymlink
}
return ret
}
func (f *FakeFile) ModTime() time.Time { return f.modtime }
func (f *FakeFile) IsDir() bool { return false }
func (f *FakeFile) Sys() interface{} {
fakeInfo, _ := os.Stat(".")
return fakeInfo.Sys()
}
type wrapperFileInfo struct {
f os.FileInfo
}
func (w *wrapperFileInfo) Name() string { return w.f.Name() }
func (w *wrapperFileInfo) Size() int64 { return w.f.Size() }
func (w *wrapperFileInfo) Mode() os.FileMode {
return w.f.Mode()
}
func (w *wrapperFileInfo) ModTime() time.Time { return w.f.ModTime() }
func (w *wrapperFileInfo) IsDir() bool { return w.f.IsDir() }
func (w *wrapperFileInfo) Sys() interface{} {
if statInfo, ok := w.f.Sys().(*sftp.FileStat); ok {
return &syscall.Stat_t{Uid: statInfo.UID, Gid: statInfo.GID}
} else {
fakeInfo, _ := os.Stat(".")
return fakeInfo.Sys()
}
fs.UserSftp.Close()
}
type listerat []os.FileInfo
......@@ -553,7 +145,6 @@ func (c *clientReadWritAt) WriteAt(p []byte, off int64) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
logger.Debug("WriteAt: ", off)
return 0, c.firstErr
}
if _, err = c.f.Seek(off, 0); err != nil {
......@@ -575,7 +166,6 @@ func (c *clientReadWritAt) ReadAt(p []byte, off int64) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
logger.Debug("ReadAt: ", off)
return 0, c.firstErr
}
if _, err = c.f.Seek(off, 0); err != nil {
......@@ -590,6 +180,28 @@ func (c *clientReadWritAt) ReadAt(p []byte, off int64) (n int, err error) {
c.closed = true
_ = c.f.Close()
}
return nr, err
}
type wrapperSFTPFileInfo struct {
f os.FileInfo
}
func (w *wrapperSFTPFileInfo) Name() string {
return w.f.Name()
}
func (w *wrapperSFTPFileInfo) Size() int64 { return w.f.Size() }
func (w *wrapperSFTPFileInfo) Mode() os.FileMode {
return w.f.Mode()
}
func (w *wrapperSFTPFileInfo) ModTime() time.Time { return w.f.ModTime() }
func (w *wrapperSFTPFileInfo) IsDir() bool { return w.f.IsDir() }
func (w *wrapperSFTPFileInfo) Sys() interface{} {
if statInfo, ok := w.f.Sys().(*sftp.FileStat); ok {
return &syscall.Stat_t{Uid: statInfo.UID, Gid: statInfo.GID}
}
if statInfo, ok := w.f.Sys().(*syscall.Stat_t); ok {
return &syscall.Stat_t{Uid: statInfo.Uid, Gid: statInfo.Gid}
}
return &syscall.Stat_t{Uid: 0, Gid: 0}
}
package httpd
import (
"encoding/json"
"io"
"sync"
"github.com/gliderlabs/ssh"
socketio "github.com/googollee/go-socket.io"
"github.com/kataras/neffos"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
)
type Client struct {
Uuid string
Cid string
......@@ -18,10 +21,10 @@ type Client struct {
WinChan chan ssh.Window
UserRead io.Reader
UserWrite io.WriteCloser
Conn socketio.Conn
Conn *neffos.NSConn
Closed bool
pty ssh.Pty
lock *sync.RWMutex
mu *sync.RWMutex
}
func (c *Client) WinCh() <-chan ssh.Window {
......@@ -41,14 +44,23 @@ func (c *Client) Read(p []byte) (n int, err error) {
}
func (c *Client) Write(p []byte) (n int, err error) {
c.lock.RLock()
defer c.lock.RUnlock()
c.mu.RLock()
defer c.mu.RUnlock()
if c.Closed {
return
}
data := DataMsg{Data: string(p), Room: c.Uuid}
data := DataMsg{Data: string(p)}
msg, err := json.Marshal(data)
if err != nil {
return
}
n = len(p)
c.Conn.Emit("data", data)
room := c.Conn.Room(c.Uuid)
if room == nil {
logger.Error("room not found: ", c.Uuid)
return
}
room.Emit("data", msg)
return
}
......@@ -57,11 +69,17 @@ func (c *Client) Pty() ssh.Pty {
}
func (c *Client) Close() (err error) {
c.lock.Lock()
defer c.lock.Unlock()
c.mu.Lock()
defer c.mu.Unlock()
if c.Closed {
return
}
c.Closed = true
return c.UserWrite.Close()
}
func (c *Client) SetWinSize(size ssh.Window) {
c.mu.RLock()
defer c.mu.RUnlock()
c.WinChan <- size
}
package httpd
import (
"fmt"
"sync"
"github.com/jumpserver/koko/pkg/logger"
)
var conns = &Connections{container: make(map[string][]string), mu: new(sync.RWMutex)}
var clients = &Clients{container: make(map[string]*Client), mu: new(sync.RWMutex)}
type Clients struct {
container map[string]*Client
mu *sync.RWMutex
}
func (c *Clients) GetClient(cID string) (client *Client) {
c.mu.RLock()
defer c.mu.RUnlock()
client = c.container[cID]
return
}
func (c *Clients) DeleteClient(cID string) {
c.mu.RLock()
client, ok := c.container[cID]
c.mu.RUnlock()
if !ok {
return
}
_ = client.Close()
c.mu.Lock()
defer c.mu.Unlock()
delete(c.container, cID)
logger.Debug("Remain clients count: ", len(c.container))
}
func (c *Clients) AddClient(cID string, conn *Client) {
fmt.Println("Add Client id: ", cID)
c.mu.Lock()
defer c.mu.Unlock()
c.container[cID] = conn
logger.Debug("Now clients count: ", len(c.container))
}
type Connections struct {
container map[string][]string
mu *sync.RWMutex
}
func (c *Connections) AddClient(cID, clientID string) {
c.mu.Lock()
defer c.mu.Unlock()
clients, ok := c.container[cID]
if ok {
clients = append(clients, clientID)
} else {
clients = []string{clientID}
}
c.container[cID] = clients
}
func (c *Connections) GetClients(cID string) (clients []string) {
c.mu.Lock()
defer c.mu.Unlock()
return c.container[cID]
}
func (c *Connections) DeleteClients(cID string) {
if clientIDs := c.GetClients(cID); clientIDs != nil{
for _, clientID := range clientIDs {
clients.DeleteClient(clientID)
}
}
c.mu.Lock()
defer c.mu.Unlock()
delete(c.container, cID)
}
package httpd
import (
"sync"
"github.com/gliderlabs/ssh"
socketio "github.com/googollee/go-socket.io"
"github.com/jumpserver/koko/pkg/model"
)
var conns = &connections{container: make(map[string]*WebConn), mu: new(sync.RWMutex)}
type connections struct {
container map[string]*WebConn
mu *sync.RWMutex
}
func (c *connections) GetWebConn(conID string) (conn *WebConn) {
c.mu.RLock()
defer c.mu.RUnlock()
conn = c.container[conID]
return
}
func (c *connections) DeleteWebConn(conID string) {
c.mu.RLock()
webC, ok := c.container[conID]
c.mu.RUnlock()
if !ok {
return
}
webC.Close()
c.mu.Lock()
defer c.mu.Unlock()
delete(c.container, conID)
}
func (c *connections) AddWebConn(conID string, conn *WebConn) {
c.mu.Lock()
defer c.mu.Unlock()
c.container[conID] = conn
}
func newWebConn(id string, sock socketio.Conn, addr string, user *model.User) *WebConn {
conn := &WebConn{Cid: id, Sock: sock, Addr: addr, User: user, mu: new(sync.RWMutex), Clients: make(map[string]*Client)}
return conn
}
type WebConn struct {
Cid string
Sock socketio.Conn
Addr string
User *model.User
Clients map[string]*Client
mu *sync.RWMutex
}
func (w *WebConn) GetClient(clientID string) (conn *Client) {
w.mu.RLock()
defer w.mu.RUnlock()
return w.Clients[clientID]
}
func (w *WebConn) DeleteClient(clientID string) {
w.mu.Lock()
defer w.mu.Unlock()
delete(w.Clients, clientID)
}
func (w *WebConn) AddClient(clientID string, conn *Client) {
w.mu.Lock()
defer w.mu.Unlock()
w.Clients[clientID] = conn
}
func (w *WebConn) GetAllClients() (clients []string) {
clients = make([]string, 0)
w.mu.RLock()
defer w.mu.RUnlock()
for k := range w.Clients {
clients = append(clients, k)
}
return clients
}
func (w *WebConn) SetWinSize(winSize ssh.Window) {
w.mu.RLock()
defer w.mu.RUnlock()
for _, client := range w.Clients {
client.WinChan <- winSize
}
}
func (w *WebConn) Close() {
w.mu.Lock()
defer w.mu.Unlock()
clientsCopy := make(map[string]*Client)
for k, v := range w.Clients {
clientsCopy[k] = v
}
for k, client := range clientsCopy {
_ = client.Close()
delete(w.Clients, k)
}
}
......@@ -4,6 +4,6 @@ import "github.com/jumpserver/koko/pkg/model"
type WebContext struct {
User *model.User
Connection *WebConn
Connection *Client
Client *Client
}
......@@ -20,7 +20,6 @@ type TokenMsg struct {
type DataMsg struct {
Data string `json:"data"`
Room string `json:"room"`
}
type RoomMsg struct {
......
......@@ -8,7 +8,6 @@ import (
"strings"
"github.com/LeeEirc/elfinder"
socketio "github.com/googollee/go-socket.io"
"github.com/gorilla/mux"
"github.com/jumpserver/koko/pkg/cctx"
......@@ -49,17 +48,7 @@ func AuthDecorator(handler http.HandlerFunc) http.HandlerFunc {
}
}
func OnELFinderConnect(s socketio.Conn) error {
data := EmitSidMsg{Sid: s.ID()}
s.Emit("data", data)
return nil
}
func OnELFinderDisconnect(s socketio.Conn, msg string) {
logger.Debug("disconnect: ", s.ID())
logger.Debug("disconnect msg ", msg)
removeUserVolume(s.ID())
}
func sftpHostFinder(wr http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
......@@ -74,6 +63,8 @@ func sftpFinder(wr http.ResponseWriter, req *http.Request) {
}
func sftpHostConnectorView(wr http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
hostID := vars["host"]
user := req.Context().Value(cctx.ContextKeyUser).(*model.User)
remoteIP := req.Context().Value(cctx.ContextKeyRemoteAddr).(string)
switch req.Method {
......@@ -92,10 +83,15 @@ func sftpHostConnectorView(wr http.ResponseWriter, req *http.Request) {
sid := req.Form.Get("sid")
userV, ok := GetUserVolume(sid)
if !ok {
userV = NewUserVolume(user, remoteIP)
switch strings.TrimSpace(hostID) {
case "_":
userV = NewUserVolume(user, remoteIP,"")
default:
userV = NewUserVolume(user, remoteIP, hostID)
}
addUserVolume(sid, userV)
}
logger.Debugf("sid: %s", sid)
con := elfinder.NewElFinderConnector([]elfinder.Volume{userV})
con.ServeHTTP(wr, req)
logger.Debugf("Elfinder connector sid: %s", sid)
conn := elfinder.NewElFinderConnector([]elfinder.Volume{userV})
conn.ServeHTTP(wr, req)
}
package httpd
import (
"github.com/kataras/neffos"
"github.com/jumpserver/koko/pkg/logger"
)
func OnELFinderConnect(c *neffos.NSConn, msg neffos.Message) error {
data := EmitSidMsg{Sid: c.Conn.ID()}
c.Emit("data", neffos.Marshal(data))
return nil
}
func OnELFinderDisconnect(c *neffos.NSConn, msg neffos.Message) (error) {
logger.Debug("disconnect: ", c.Conn.ID())
removeUserVolume(c.Conn.ID())
return nil
}
......@@ -5,8 +5,9 @@ import (
"net/http"
"path/filepath"
"github.com/googollee/go-socket.io"
"github.com/gorilla/mux"
"github.com/kataras/neffos"
"github.com/kataras/neffos/gorilla"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/logger"
......@@ -16,35 +17,43 @@ var (
httpServer *http.Server
)
var wsEvents = neffos.Namespaces{
"ssh": neffos.Events{
neffos.OnNamespaceConnected: OnNamespaceConnected,
neffos.OnNamespaceDisconnect: OnNamespaceDisconnect,
neffos.OnRoomJoined: func(c *neffos.NSConn, msg neffos.Message) error {
return nil
},
neffos.OnRoomLeft: func(c *neffos.NSConn, msg neffos.Message) error {
return nil
},
"data": OnDataHandler,
"resize": OnResizeHandler,
"host": OnHostHandler,
"logout": OnLogoutHandler,
"token": OnTokenHandler,
},
"elfinder": neffos.Events{
neffos.OnNamespaceConnected: OnELFinderConnect,
neffos.OnNamespaceDisconnect: OnELFinderDisconnect,
},
}
func StartHTTPServer() {
conf := config.GetConf()
server, err := socketio.NewServer(nil)
if err != nil {
logger.Fatal(err)
sshWs := neffos.New(gorilla.DefaultUpgrader, wsEvents)
sshWs.IDGenerator = func(w http.ResponseWriter, r *http.Request) string {
return neffos.DefaultIDGenerator(w, r)
}
sshWs.OnUpgradeError = func(err error) {
}
server.OnConnect("/ssh", OnConnectHandler)
server.OnDisconnect("/ssh", OnDisconnect)
server.OnError("/ssh", OnErrorHandler)
server.OnEvent("/ssh", "host", OnHostHandler)
server.OnEvent("/ssh", "token", OnTokenHandler)
server.OnEvent("/ssh", "data", OnDataHandler)
server.OnEvent("/ssh", "resize", OnResizeHandler)
server.OnEvent("/ssh", "logout", OnLogoutHandler)
server.OnConnect("/elfinder", OnELFinderConnect)
server.OnDisconnect("/elfinder", OnELFinderDisconnect)
server.OnError("/elfiner", OnErrorHandler)
server.OnDisconnect("", SocketDisconnect)
server.OnError("", OnErrorHandler)
go server.Serve()
defer server.Close()
router := mux.NewRouter()
fs := http.FileServer(http.Dir(filepath.Join(conf.RootPath, "static")))
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
router.Handle("/socket.io/", server)
router.Handle("/socket.io/", sshWs)
router.HandleFunc("/coco/elfinder/sftp/{host}/", AuthDecorator(sftpHostFinder))
router.HandleFunc("/coco/elfinder/sftp/", AuthDecorator(sftpFinder))
router.HandleFunc("/coco/elfinder/sftp/connector/{host}/",
......@@ -60,8 +69,4 @@ func StopHTTPServer() {
_ = httpServer.Close()
}
func SocketDisconnect(s socketio.Conn, msg string) {
removeUserVolume(s.ID())
conns.DeleteWebConn(s.ID())
logger.Debug("clean disconnect")
}
......@@ -5,11 +5,8 @@ import (
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/LeeEirc/elfinder"
"github.com/pkg/sftp"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
......@@ -19,383 +16,138 @@ import (
"github.com/jumpserver/koko/pkg/srvconn"
)
var (
defaultHomeName = "Home"
)
func NewUserVolume(user *model.User, addr string) *UserVolume {
rawID := fmt.Sprintf("'%s@%s", user.Username, addr)
func NewUserVolume(user *model.User, addr, hostId string) *UserVolume {
var assets []model.Asset
var homename string
switch hostId {
case "":
assets = service.GetUserAssets(user.ID, "1", "")
homename = "Home"
default:
assets = service.GetUserAssets(user.ID, "1", hostId)
if len(assets) == 1 {
homename = assets[0].Hostname
if assets[0].OrgID != "" {
homename = fmt.Sprintf("%s.%s", assets[0].Hostname, assets[0].OrgName)
}
}
}
conf := config.GetConf()
rawID := fmt.Sprintf("%s@%s", user.Username, addr)
uVolume := &UserVolume{
Uuid: elfinder.GenerateID(rawID),
Addr: addr,
user: user,
Name: defaultHomeName,
basePath: fmt.Sprintf("/%s", defaultHomeName),
Uuid: elfinder.GenerateID(rawID),
UserSftp: srvconn.NewUserSFTP(user, addr, assets...),
Homename: homename,
basePath: filepath.Join("/", homename),
localTmpPath: filepath.Join(conf.RootPath, "data", "tmp"),
}
uVolume.initial()
return uVolume
}
type UserVolume struct {
Uuid string
Addr string
Name string
basePath string
user *model.User
assets model.AssetList
rootPath string // tmp || home || ~
hosts map[string]*hostnameVolume
Uuid string
*srvconn.UserSftp
localTmpPath string
permCache map[string]bool
}
func (u *UserVolume) initial() {
conf := config.GetConf()
u.loadAssets()
u.rootPath = conf.SftpRoot
u.localTmpPath = filepath.Join(conf.RootPath, "data", "tmp")
u.permCache = make(map[string]bool)
_ = common.EnsureDirExist(u.localTmpPath)
u.hosts = make(map[string]*hostnameVolume)
for i, item := range u.assets {
tmpDir := &hostnameVolume{
VID: u.ID(),
homePath: u.basePath,
hostPath: filepath.Join(u.basePath, item.Hostname),
asset: &u.assets[i],
time: time.Now().UTC(),
}
u.hosts[item.Hostname] = tmpDir
}
}
func (u *UserVolume) loadAssets() {
u.assets = service.GetUserAssets(u.user.ID, "1")
Homename string
basePath string
}
func (u *UserVolume) ID() string {
return u.Uuid
}
func (u *UserVolume) Info(path string) (elfinder.FileDir, error) {
var rest elfinder.FileDir
if path == "" || path == "/" {
path = u.basePath
}
if path == u.basePath {
return u.RootFileDir(), nil
}
pathNames := strings.Split(strings.TrimPrefix(path, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == path {
return hostVol.info(), nil
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if path == sysUserVol.suPath {
return sysUserVol.info(), nil
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.ConnectAction) {
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
logger.Debug("volume Info: ", path)
if path == "/" {
return u.RootFileDir(), nil
}
realPath := sysUserVol.ParsePath(path)
dirname := filepath.Dir(path)
fileInfos, err := sysUserVol.client.Stat(realPath)
var rest elfinder.FileDir
originFileInfo, err := u.Stat(path)
if err != nil {
return rest, err
}
rest.Name = fileInfos.Name()
rest.Hash = hashPath(u.ID(), path)
rest.Phash = hashPath(u.ID(), dirname)
rest.Size = fileInfos.Size()
rest.Volumeid = u.ID()
if fileInfos.IsDir() {
dirPath := filepath.Dir(path)
filename := filepath.Base(path)
rest.Read, rest.Write = elfinder.ReadWritePem(originFileInfo.Mode())
if filename != originFileInfo.Name() {
rest.Read, rest.Write = 1, 1
}
if filename == "." {
filename = originFileInfo.Name()
fmt.Println("askldkasdlala")
}
rest.Name = filename
rest.Hash = hashPath(u.Uuid, filepath.Join(dirPath, filename))
rest.Phash = hashPath(u.Uuid, dirPath)
if rest.Hash == rest.Phash {
rest.Phash = ""
}
rest.Size = originFileInfo.Size()
rest.Volumeid = u.Uuid
if originFileInfo.IsDir() {
rest.Mime = "directory"
rest.Dirs = 1
} else {
rest.Mime = "file"
rest.Dirs = 0
}
rest.Read, rest.Write = elfinder.ReadWritePem(fileInfos.Mode())
return rest, nil
return rest, err
}
func (u *UserVolume) List(path string) []elfinder.FileDir {
var dirs []elfinder.FileDir
if path == "" || path == "/" {
path = u.basePath
}
if path == u.basePath {
dirs = make([]elfinder.FileDir, 0, len(u.hosts))
for _, item := range u.hosts {
dirs = append(dirs, item.info())
}
return dirs
}
pathNames := strings.Split(strings.TrimPrefix(path, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return dirs
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
if hostVol.hostPath == path {
dirs = make([]elfinder.FileDir, 0, len(hostVol.suMaps))
for _, item := range hostVol.suMaps {
dirs = append(dirs, item.info())
}
return dirs
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
dirs := make([]elfinder.FileDir, 0)
logger.Debug("volume List: ", path)
dirInfo, err := u.Info(path)
if err != nil {
return dirs
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return dirs
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return dirs
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(path)
subFiles, err := sysUserVol.client.ReadDir(realPath)
dirs = append(dirs, dirInfo)
originFileInfolist, err := u.UserSftp.ReadDir(path)
if err != nil {
return dirs
}
dirs = make([]elfinder.FileDir, 0, len(subFiles))
for _, fInfo := range subFiles {
fileDir, err := u.Info(filepath.Join(path, fInfo.Name()))
if err != nil {
continue
}
dirs = append(dirs, fileDir)
for i := 0; i < len(originFileInfolist); i++ {
dirs = append(dirs, NewElfinderFileInfo(u.Uuid, path, originFileInfolist[i]))
}
return dirs
}
func (u *UserVolume) Parents(path string, dep int) []elfinder.FileDir {
relativepath := strings.TrimPrefix(strings.TrimPrefix(path, u.basePath), "/")
relativePaths := strings.Split(relativepath, "/")
dirs := make([]elfinder.FileDir, 0, len(relativePaths))
for i := range relativePaths {
realDirPath := filepath.Join(u.basePath, filepath.Join(relativePaths[:i]...))
result, err := u.Info(realDirPath)
if err != nil {
continue
}
dirs = append(dirs, result)
tmpDir := u.List(realDirPath)
for j, item := range tmpDir {
if item.Dirs == 1 {
dirs = append(dirs, tmpDir[j])
}
logger.Debug("volume Parents: ", path)
dirs := make([]elfinder.FileDir, 0)
dirPath := path
for {
tmps := u.List(dirPath)
dirs = append(dirs, tmps...)
if dirPath == "/" {
break
}
dirPath = filepath.Dir(dirPath)
}
return dirs
}
func (u *UserVolume) GetFile(path string) (reader io.ReadCloser, err error) {
pathNames := strings.Split(strings.TrimPrefix(path, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return nil, os.ErrNotExist
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return nil, os.ErrNotExist
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.DownloadAction) {
return nil, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return nil, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return nil, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(path)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Download",
Path: realPath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
reader, err = sysUserVol.client.Open(realPath)
if err != nil {
return
}
logData.IsSuccess = true
return
return u.UserSftp.Open(path)
}
func (u *UserVolume) UploadFile(dir, filename string, reader io.Reader) (elfinder.FileDir, error) {
path := filepath.Join(dir, filename)
logger.Debug("Volume upload file path: ", path)
var rest elfinder.FileDir
var err error
if dir == "" || dir == "/" {
dir = u.basePath
}
if dir == u.basePath {
return rest, os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(dir, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == dir {
return rest, os.ErrPermission
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(dir)
realFilenamePath := filepath.Join(realPath, filename)
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.UploadAction) {
return rest, os.ErrPermission
}
fd, err := sysUserVol.client.Create(realFilenamePath)
fd, err := u.UserSftp.Create(path)
if err != nil {
return rest, err
}
defer fd.Close()
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Upload",
Path: realFilenamePath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
_, err = io.Copy(fd, reader)
if err != nil {
return rest, err
}
logData.IsSuccess = true
return u.Info(filepath.Join(dir, filename))
return u.Info(path)
}
func (u *UserVolume) UploadChunk(cid int, dirPath, chunkName string, reader io.Reader) error {
......@@ -419,76 +171,16 @@ func (u *UserVolume) UploadChunk(cid int, dirPath, chunkName string, reader io.R
}
func (u *UserVolume) MergeChunk(cid, total int, dirPath, filename string) (elfinder.FileDir, error) {
path := filepath.Join(dirPath, filename)
logger.Debug("merge chunk path: ",path)
var rest elfinder.FileDir
if u.basePath == dirPath {
return rest, os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(dirPath, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == dirPath {
return rest, os.ErrPermission
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.UploadAction) {
fd, err := u.UserSftp.Create(path)
if err != nil {
for i := 0; i <= total; i++ {
partPath := fmt.Sprintf("%s.%d_%d.part_%d",
filepath.Join(u.localTmpPath, dirPath, filename), i, total, cid)
_ = os.Remove(partPath)
}
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realDirPath := sysUserVol.ParsePath(dirPath)
filenamePath := filepath.Join(realDirPath, filename)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Upload",
Path: filenamePath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
fd, err := sysUserVol.client.OpenFile(filenamePath, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC)
if err != nil {
return rest, err
}
defer fd.Close()
......@@ -510,8 +202,7 @@ func (u *UserVolume) MergeChunk(cid, total int, dirPath, filename string) (elfin
_ = partFD.Close()
_ = os.Remove(partPath)
}
logData.IsSuccess = true
return u.Info(filepath.Join(dirPath, filename))
return u.Info(path)
}
func (u *UserVolume) CompleteChunk(cid, total int, dirPath, filename string) bool {
......@@ -527,362 +218,48 @@ func (u *UserVolume) CompleteChunk(cid, total int, dirPath, filename string) boo
}
func (u *UserVolume) MakeDir(dir, newDirname string) (elfinder.FileDir, error) {
path := filepath.Join(dir, newDirname)
var rest elfinder.FileDir
if dir == "" || dir == "/" {
dir = u.basePath
}
if dir == u.basePath {
return rest, os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(dir, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == dir {
return rest, os.ErrPermission
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.ConnectAction) {
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(dir)
realDirPath := filepath.Join(realPath, newDirname)
err := sysUserVol.client.MkdirAll(realDirPath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Mkdir",
Path: realDirPath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
err := u.UserSftp.MkdirAll(path)
if err != nil {
return rest, err
}
logData.IsSuccess = true
return u.Info(filepath.Join(dir, newDirname))
return u.Info(path)
}
func (u *UserVolume) MakeFile(dir, newFilename string) (elfinder.FileDir, error) {
path := filepath.Join(dir, newFilename)
var rest elfinder.FileDir
if dir == "" || dir == "/" {
dir = u.basePath
}
if dir == u.basePath {
return rest, os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(dir, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == dir {
return rest, os.ErrPermission
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.ConnectAction) {
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(dir)
realFilePath := filepath.Join(realPath, newFilename)
_, err := sysUserVol.client.Create(realFilePath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Append",
Path: realFilePath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
fd, err := u.UserSftp.Create(path)
if err != nil {
return rest, err
}
logData.IsSuccess = true
return u.Info(filepath.Join(dir, newFilename))
defer fd.Close()
return u.Info(path)
}
func (u *UserVolume) Rename(oldNamePath, newName string) (elfinder.FileDir, error) {
var rest elfinder.FileDir
pathNames := strings.Split(strings.TrimPrefix(oldNamePath, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if sysUserVol.suPath == oldNamePath {
return rest, os.ErrPermission
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.ConnectAction) {
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(oldNamePath)
dirpath := filepath.Dir(realPath)
newFilePath := filepath.Join(dirpath, newName)
err := sysUserVol.client.Rename(oldNamePath, newFilePath)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Rename",
Path: fmt.Sprintf("%s=>%s", oldNamePath, newFilePath),
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
newNamePath := filepath.Join(filepath.Dir(oldNamePath), newName)
err := u.UserSftp.Rename(oldNamePath, newNamePath)
if err != nil {
return rest, err
}
logData.IsSuccess = true
return u.Info(newFilePath)
return u.Info(newNamePath)
}
func (u *UserVolume) Remove(path string) error {
if path == "" || path == "/" {
path = u.basePath
}
if path == u.basePath {
return os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(path, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return os.ErrNotExist
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return os.ErrNotExist
}
if sysUserVol.suPath == path {
return os.ErrPermission
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.ConnectAction) {
return os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(path)
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Delete",
Path: realPath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
}
defer u.CreateFTPLog(logData)
err := sysUserVol.client.Remove(realPath)
if err == nil {
logData.IsSuccess = true
}
return err
return u.UserSftp.Remove(path)
}
func (u *UserVolume) Paste(dir, filename, suffix string, reader io.ReadCloser) (elfinder.FileDir, error) {
var rest elfinder.FileDir
if dir == "" || dir == "/" {
dir = u.basePath
}
if dir == u.basePath {
return rest, os.ErrPermission
}
pathNames := strings.Split(strings.TrimPrefix(dir, "/"), "/")
hostVol, ok := u.hosts[pathNames[1]]
if !ok {
return rest, os.ErrNotExist
}
if hostVol.hostPath == dir {
return rest, os.ErrPermission
}
if hostVol.suMaps == nil {
hostVol.suMaps = make(map[string]*sysUserVolume)
systemUsers := hostVol.asset.SystemUsers
for i, sysUser := range systemUsers {
hostVol.suMaps[sysUser.Name] = &sysUserVolume{
VID: u.ID(),
hostpath: hostVol.hostPath,
suPath: filepath.Join(hostVol.hostPath, sysUser.Name),
systemUser: &systemUsers[i],
rootPath: u.rootPath,
}
}
}
sysUserVol, ok := hostVol.suMaps[pathNames[2]]
if !ok {
return rest, os.ErrNotExist
}
if !u.validatePermission(hostVol.asset.ID, sysUserVol.systemUser.ID, model.UploadAction) {
return rest, os.ErrPermission
}
if sysUserVol.client == nil {
sftClient, conn, err := u.GetSftpClient(hostVol.asset, sysUserVol.systemUser)
if err != nil {
return rest, os.ErrPermission
}
sysUserVol.homeDirPath, err = sftClient.Getwd()
if err != nil {
return rest, err
}
sysUserVol.client = sftClient
sysUserVol.conn = conn
}
realPath := sysUserVol.ParsePath(dir)
realFilePath := filepath.Join(realPath, filename)
_, err := sysUserVol.client.Stat(realFilePath)
path := filepath.Join(dir, filename)
rest, err := u.Info(path)
if err != nil {
realFilePath += suffix
}
logData := &model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.user.Name, u.user.Username),
Hostname: hostVol.asset.Hostname,
OrgID: hostVol.asset.OrgID,
SystemUser: sysUserVol.systemUser.Name,
RemoteAddr: u.Addr,
Operate: "Append",
Path: realFilePath,
DataStart: common.CurrentUTCTime(),
IsSuccess: false,
path += suffix
}
defer u.CreateFTPLog(logData)
fd, err := sysUserVol.client.OpenFile(realPath, os.O_RDWR|os.O_CREATE)
fd, err := u.UserSftp.Create(path)
if err != nil {
return rest, err
}
......@@ -891,130 +268,47 @@ func (u *UserVolume) Paste(dir, filename, suffix string, reader io.ReadCloser) (
if err != nil {
return rest, err
}
logData.IsSuccess = true
return u.Info(realPath)
return u.Info(path)
}
func (u *UserVolume) RootFileDir() elfinder.FileDir {
var resFDir = elfinder.FileDir{}
resFDir.Name = u.Name
resFDir.Hash = hashPath(u.Uuid, u.basePath)
resFDir.Mime = "directory"
resFDir.Volumeid = u.Uuid
resFDir.Dirs = 1
resFDir.Read, resFDir.Write = 1, 1
resFDir.Locked = 1
return resFDir
}
func (u *UserVolume) GetSftpClient(asset *model.Asset, sysUser *model.SystemUser) (sftpClient *sftp.Client, sshClient *srvconn.SSHClient, err error) {
sshClient, err = srvconn.NewClient(u.user, asset, sysUser, config.GetConf().SSHTimeout*time.Second)
if err != nil {
return
}
sftpClient, err = sftp.NewClient(sshClient.Client)
if err != nil {
return
}
return sftpClient, sshClient, nil
logger.Debug("Root File Dir")
fInfo, _ := u.UserSftp.Info()
var rest elfinder.FileDir
rest.Name = u.Homename
rest.Hash = hashPath(u.Uuid, "/")
rest.Size = fInfo.Size()
rest.Volumeid = u.Uuid
rest.Mime = "directory"
rest.Dirs = 1
rest.Read, rest.Write = 1, 1
rest.Locked = 1
return rest
}
func (u *UserVolume) Close() {
for _, host := range u.hosts {
if host.suMaps == nil {
continue
}
for _, su := range host.suMaps {
su.Close()
}
}
}
func (u *UserVolume) CreateFTPLog(data *model.FTPLog) {
for i := 0; i < 4; i++ {
err := service.PushFTPLog(data)
if err == nil {
break
}
logger.Debugf("create FTP log err: %s", err.Error())
}
}
func (u *UserVolume) validatePermission(aid, suid, operate string) bool {
permKey := fmt.Sprintf("%s_%s_%s", aid, suid, operate)
permission, ok := u.permCache[permKey]
if ok {
return permission
}
permission = service.ValidateUserAssetPermission(
u.user.ID, aid, suid, operate,
)
u.permCache[permKey] = permission
return permission
}
type hostnameVolume struct {
VID string
homePath string
hostPath string // /home/hostname/
time time.Time
asset *model.Asset
suMaps map[string]*sysUserVolume
}
func (h *hostnameVolume) info() elfinder.FileDir {
var resFDir = elfinder.FileDir{}
resFDir.Name = h.asset.Hostname
resFDir.Hash = hashPath(h.VID, h.hostPath)
resFDir.Phash = hashPath(h.VID, h.homePath)
resFDir.Mime = "directory"
resFDir.Volumeid = h.VID
resFDir.Dirs = 1
resFDir.Read, resFDir.Write = 1, 1
return resFDir
}
type sysUserVolume struct {
VID string
hostpath string
suPath string
rootPath string
systemUser *model.SystemUser
homeDirPath string
client *sftp.Client
conn *srvconn.SSHClient
}
func (su *sysUserVolume) info() elfinder.FileDir {
var resFDir = elfinder.FileDir{}
resFDir.Name = su.systemUser.Name
resFDir.Hash = hashPath(su.VID, su.suPath)
resFDir.Phash = hashPath(su.VID, su.hostpath)
resFDir.Mime = "directory"
resFDir.Volumeid = su.VID
resFDir.Dirs = 1
resFDir.Read, resFDir.Write = 1, 1
return resFDir
}
func (su *sysUserVolume) ParsePath(path string) string {
var realPath string
switch strings.ToLower(su.rootPath) {
case "home", "~", "":
realPath = strings.ReplaceAll(path, su.suPath, su.homeDirPath)
default:
realPath = strings.ReplaceAll(path, su.suPath, su.rootPath)
}
logger.Debug("real path: ", realPath)
return realPath
u.UserSftp.Close()
}
func (su *sysUserVolume) Close() {
if su.client != nil {
_ = su.client.Close()
func NewElfinderFileInfo(id, dirPath string, originFileInfo os.FileInfo) elfinder.FileDir {
var rest elfinder.FileDir
rest.Name = originFileInfo.Name()
rest.Hash = hashPath(id, filepath.Join(dirPath, originFileInfo.Name()))
rest.Phash = hashPath(id, dirPath)
if rest.Hash == rest.Phash {
rest.Phash = ""
}
rest.Size = originFileInfo.Size()
rest.Volumeid = id
if originFileInfo.IsDir() {
rest.Mime = "directory"
rest.Dirs = 1
} else {
rest.Mime = "file"
rest.Dirs = 0
}
srvconn.RecycleClient(su.conn)
rest.Read, rest.Write = elfinder.ReadWritePem(originFileInfo.Mode())
return rest
}
func hashPath(id, path string) string {
......
......@@ -2,12 +2,19 @@ package httpd
import (
"sync"
"github.com/LeeEirc/elfinder"
)
var userVolumes = make(map[string]*UserVolume)
type VolumeCloser interface {
elfinder.Volume
Close()
}
var userVolumes = make(map[string]VolumeCloser)
var volumeLock = new(sync.RWMutex)
func addUserVolume(sid string, v *UserVolume) {
func addUserVolume(sid string, v VolumeCloser) {
volumeLock.Lock()
defer volumeLock.Unlock()
userVolumes[sid] = v
......@@ -27,7 +34,7 @@ func removeUserVolume(sid string) {
}
func GetUserVolume(sid string) (*UserVolume, bool) {
func GetUserVolume(sid string) (VolumeCloser, bool) {
volumeLock.RLock()
defer volumeLock.RUnlock()
v, ok := userVolumes[sid]
......
package httpd
import (
"encoding/json"
"errors"
"fmt"
"github.com/jumpserver/koko/pkg/model"
"io"
"net"
"strings"
"sync"
"github.com/gliderlabs/ssh"
socketio "github.com/googollee/go-socket.io"
uuid "github.com/satori/go.uuid"
"github.com/kataras/neffos"
"github.com/satori/go.uuid"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/proxy"
"github.com/jumpserver/koko/pkg/service"
)
// OnConnectHandler 当websocket连接后触发
func OnConnectHandler(s socketio.Conn) error {
func OnNamespaceConnected(c *neffos.NSConn, msg neffos.Message) error {
// 首次连接 1.获取当前用户的信息
logger.Debug("Web terminal on connect event trigger")
cookies := strings.Split(s.RemoteHeader().Get("Cookie"), ";")
cc := c.Conn
if cc.WasReconnected() {
logger.Debugf("Web terminal redirected, with tries: %d", cc.ID(), cc.ReconnectTries)
} else {
logger.Debug("Web terminal on connect event trigger")
}
request := cc.Socket().Request()
header := request.Header
cookies := strings.Split(header.Get("Cookie"), ";")
var csrfToken, sessionID, remoteIP string
for _, line := range cookies {
if strings.Contains(line, "csrftoken") {
......@@ -37,17 +51,21 @@ func OnConnectHandler(s socketio.Conn) error {
logger.Error(msg)
return errors.New(strings.ToLower(msg))
}
remoteAddr := s.RemoteHeader().Get("X-Forwarded-For")
cc.Set("currentUser", user)
remoteAddr := header.Get("X-Forwarded-For")
if remoteAddr == "" {
remoteIP = s.RemoteAddr().String()
} else {
remoteIP = strings.Split(remoteAddr, ",")[0]
remoteAddr = request.RemoteAddr
}
remoteIP = strings.Split(remoteAddr, ",")[0]
logger.Infof("Accepted %s connect websocket from %s", user.Username, remoteIP)
conn := newWebConn(s.ID(), s, remoteIP, user)
ctx := WebContext{User: user, Connection: conn}
s.SetContext(ctx)
conns.AddWebConn(s.ID(), conn)
return nil
}
// OnDisconnect websocket断开后触发
func OnNamespaceDisconnect(c *neffos.NSConn, msg neffos.Message) (err error){
logger.Debug("On disconnect event trigger")
conns.DeleteClients(c.Conn.ID())
return nil
}
......@@ -57,8 +75,15 @@ func OnErrorHandler(e error) {
}
// OnHostHandler 当用户连接Host时触发
func OnHostHandler(s socketio.Conn, message HostMsg) {
func OnHostHandler(c *neffos.NSConn, msg neffos.Message) (err error) {
logger.Debug("Web terminal on host event trigger")
cc := c.Conn
var message HostMsg
err = msg.Unmarshal(&message)
if err != nil {
return
}
fmt.Println("Host msg: ", message)
win := ssh.Window{Height: 24, Width: 80}
assetID := message.Uuid
systemUserID := message.UserID
......@@ -70,141 +95,151 @@ func OnHostHandler(s socketio.Conn, message HostMsg) {
if height != 0 {
win.Height = height
}
clientID := uuid.NewV4().String()
emitMsg := RoomMsg{clientID, secret}
s.Emit("room", emitMsg)
roomID := uuid.NewV4().String()
emitMsg := RoomMsg{roomID, secret}
joinRoomMsg, _ := json.Marshal(emitMsg)
c.Emit("room", joinRoomMsg)
if err != nil {
logger.Debug("Join room error occur: ", err)
return
}
asset := service.GetAsset(assetID)
systemUser := service.GetSystemUser(systemUserID)
if asset.ID == "" || systemUser.ID == "" {
logger.Debug("No asset id or system user id found, exit")
return
}
logger.Debug("Web terminal want to connect host: ", asset.Hostname)
currentUser, ok := cc.Get("currentUser").(*model.User)
if !ok {
return errors.New("not found current user")
}
ctx := s.Context().(WebContext)
userR, userW := io.Pipe()
conn := conns.GetWebConn(s.ID())
addr, _, _ := net.SplitHostPort(s.RemoteAddr().String())
addr, _, _ := net.SplitHostPort(cc.Socket().Request().RemoteAddr)
client := &Client{
Uuid: clientID, Cid: conn.Cid, user: conn.User, addr: addr,
WinChan: make(chan ssh.Window, 100), Conn: s,
UserRead: userR, UserWrite: userW, lock: new(sync.RWMutex),
Uuid: roomID, user: currentUser, addr: addr,
WinChan: make(chan ssh.Window, 100), Conn: c,
UserRead: userR, UserWrite: userW, mu: new(sync.RWMutex),
pty: ssh.Pty{Term: "xterm", Window: win},
}
user := cc.Get("currentUser").(*model.User)
client.WinChan <- win
conn.AddClient(clientID, client)
clients.AddClient(roomID, client)
conns.AddClient(cc.ID(), roomID)
proxySrv := proxy.ProxyServer{
UserConn: client, User: ctx.User,
UserConn: client, User: user,
Asset: &asset, SystemUser: &systemUser,
}
go func() {
defer logger.Debug("web proxy end")
logger.Debug("Start proxy")
proxySrv.Proxy()
s.Emit("logout", RoomMsg{Room: clientID})
logoutMsg, _ := json.Marshal(RoomMsg{Room: roomID})
c.Emit("logout", logoutMsg)
clients.DeleteClient(roomID)
}()
return nil
}
// OnTokenHandler 当使用token连接时触发
func OnTokenHandler(s socketio.Conn, message TokenMsg) {
func OnTokenHandler(c *neffos.NSConn, msg neffos.Message) (err error) {
logger.Debug("Web terminal on token event trigger")
win := ssh.Window{Height: 24, Width: 80}
cc := c.Conn
var message TokenMsg
err = msg.Unmarshal(&message)
if err != nil {
return
}
token := message.Token
secret := message.Secret
width, height := message.Size[0], message.Size[1]
if width != 0 {
win.Width = width
}
if height != 0 {
win.Height = height
}
clientID := uuid.NewV4().String()
emitMs := RoomMsg{clientID, secret}
s.Emit("room", emitMs)
roomMsg := RoomMsg{clientID, secret}
c.Emit("room", neffos.Marshal(roomMsg))
// check token
if token == "" || secret == "" {
msg := fmt.Sprintf("Token or secret is None: %s %s", token, secret)
dataMsg := EmitDataMsg{Data: msg, Room: clientID}
s.Emit("data", dataMsg)
s.Emit("disconnect")
c.Emit("data", neffos.Marshal(dataMsg))
c.Emit("disconnect", nil)
}
tokenUser := service.GetTokenAsset(token)
if tokenUser.UserID == "" {
msg := "Token info is none, maybe token expired"
dataMsg := EmitDataMsg{Data: msg, Room: clientID}
s.Emit("data", dataMsg)
s.Emit("disconnect")
c.Emit("data", neffos.Marshal(dataMsg))
c.Emit("disconnect", nil)
}
currentUser := service.GetUserDetail(tokenUser.UserID)
asset := service.GetAsset(tokenUser.AssetID)
systemUser := service.GetSystemUser(tokenUser.SystemUserID)
if asset.ID == "" || systemUser.ID == "" {
return
if currentUser == nil {
msg := "User id error"
dataMsg := EmitDataMsg{Data: msg, Room: clientID}
c.Emit("data", neffos.Marshal(dataMsg))
c.Emit("disconnect", nil)
}
userR, userW := io.Pipe()
conn := conns.GetWebConn(s.ID())
conn.User = currentUser
client := Client{
Uuid: clientID, Cid: conn.Cid, user: conn.User,
WinChan: make(chan ssh.Window, 100), Conn: s,
UserRead: userR, UserWrite: userW, lock: new(sync.RWMutex),
pty: ssh.Pty{Term: "xterm", Window: win},
cc.Set("currentUser", currentUser)
hostMsg := HostMsg{
Uuid: tokenUser.AssetID, UserID: tokenUser.SystemUserID,
Size: message.Size, Secret:secret,
}
client.WinChan <- win
conn.AddClient(clientID, &client)
proxySrv := proxy.ProxyServer{
UserConn: &client, User: currentUser,
Asset: &asset, SystemUser: &systemUser,
fmt.Println("Host msg: ", hostMsg)
hostWsMsg := neffos.Message{
Body:neffos.Marshal(hostMsg),
}
go func() {
defer logger.Debug("web proxy end")
proxySrv.Proxy()
s.Emit("logout", RoomMsg{Room: clientID})
}()
return OnHostHandler(c, hostWsMsg)
}
// OnDataHandler 收发数据时触发
func OnDataHandler(s socketio.Conn, message DataMsg) {
cid := message.Room
conn := conns.GetWebConn(s.ID())
client := conn.GetClient(cid)
func OnDataHandler(c *neffos.NSConn, msg neffos.Message) (err error) {
roomID := msg.Room
client := clients.GetClient(roomID)
if client == nil {
return
}
_, _ = client.UserWrite.Write([]byte(message.Data))
var message DataMsg
err = msg.Unmarshal(&message)
if err != nil {
return
}
_, err = client.UserWrite.Write([]byte(message.Data))
return err
}
// OnResizeHandler 用户窗口改变时触发
func OnResizeHandler(s socketio.Conn, message ResizeMsg) {
func OnResizeHandler(c *neffos.NSConn, msg neffos.Message) (err error) {
var message ResizeMsg
err = msg.Unmarshal(&message)
if err != nil {
return
}
logger.Debugf("Web terminal on resize event trigger: %d*%d", message.Width, message.Height)
winSize := ssh.Window{Height: message.Height, Width: message.Width}
conn := conns.GetWebConn(s.ID())
conn.SetWinSize(winSize)
for _, room := range c.Rooms() {
roomID := room.Name
client := clients.GetClient(roomID)
if client != nil {
client.SetWinSize(winSize)
}
}
return nil
}
// OnLogoutHandler 用户登出一个会话时触发
func OnLogoutHandler(s socketio.Conn, message string) {
logger.Debug("Web terminal on logout event trigger")
conn := conns.GetWebConn(s.ID())
if conn == nil {
logger.Error("No conn found")
return
}
client := conn.GetClient(message)
if client == nil {
logger.Error("No client found")
func OnLogoutHandler(c *neffos.NSConn, msg neffos.Message) (err error){
logger.Debug("Web terminal on logout event trigger: ", msg.Room)
var message RoomMsg
err = msg.Unmarshal(&message)
if err != nil {
return
}
_ = client.Close()
roomID := message.Room
clients.DeleteClient(roomID)
return
}
// OnDisconnect websocket断开后触发
func OnDisconnect(s socketio.Conn, msg string) {
logger.Debug("On disconnect event trigger")
conn := conns.GetWebConn(s.ID())
conn.Close()
}
......@@ -70,7 +70,7 @@ func uploadRemainReplay(rootPath string) {
}
_ = os.Remove(path)
}
relayRecord := &proxy.ReplyRecorder{}
relayRecord := &proxy.ReplyRecorder{SessionID:sid}
relayRecord.AbsGzFilePath = absGzPath
relayRecord.Target, _ = filepath.Rel(path, rootPath)
relayRecord.UploadGzipFile(3)
......
......@@ -220,6 +220,7 @@ func SortAssetNodesByKey(assetNodes []Node) {
const LoginModeManual = "manual"
const (
AllAction = "all"
ConnectAction = "connect"
UploadAction = "upload_file"
DownloadAction = "download_file"
......
package model
const (
OperateRemoveDir = "Rmdir"
OperateDownaload = "Download"
OperateUpload = "Upload"
OperateRename = "Rename"
OperateMkdir = "Mkdir"
OperateDelete = "Delete"
OperateSymlink = "Symlink"
)
......@@ -22,7 +22,7 @@ func NewCommandRecorder(sid string) (recorder *CommandRecorder) {
}
func NewReplyRecord(sid string) (recorder *ReplyRecorder) {
recorder = &ReplyRecorder{sessionID: sid}
recorder = &ReplyRecorder{SessionID: sid}
recorder.initial()
return recorder
}
......@@ -93,7 +93,7 @@ func (c *CommandRecorder) record() {
}
type ReplyRecorder struct {
sessionID string
SessionID string
absFilePath string
AbsGzFilePath string
......@@ -119,7 +119,7 @@ func (r *ReplyRecorder) Record(b []byte) {
}
func (r *ReplyRecorder) prepare() {
sessionID := r.sessionID
sessionID := r.SessionID
rootPath := config.GetConf().RootPath
today := time.Now().UTC().Format("2006-01-02")
gzFileName := sessionID + ".replay.gz"
......@@ -179,7 +179,7 @@ func (r *ReplyRecorder) UploadGzipFile(maxRetry int) {
err := r.storage.Upload(r.AbsGzFilePath, r.Target)
if err == nil {
_ = os.Remove(r.AbsGzFilePath)
service.FinishReply(r.sessionID)
service.FinishReply(r.SessionID)
break
}
// 如果还是失败,使用备用storage再传一次
......
......@@ -57,7 +57,7 @@ func GetSystemUserFilterRules(systemUserID string) (rules []model.SystemUserFilt
"filter": "de7693ca-75d5-4639-986b-44ed390260a0"
}
]`*/
Url := fmt.Sprintf(SystemUserCmdFilterRules, systemUserID)
Url := fmt.Sprintf(SystemUserCmdFilterRulesListURL, systemUserID)
err = authClient.Get(Url, &rules)
if err != nil {
......@@ -94,7 +94,7 @@ func GetDomainWithGateway(gID string) (domain model.Domain) {
}
func GetTokenAsset(token string) (tokenUser model.TokenUser) {
Url := fmt.Sprintf(TokenAssetUrl, token)
Url := fmt.Sprintf(TokenAssetURL, token)
err := authClient.Get(Url, &tokenUser)
if err != nil {
logger.Error("Get Token Asset info failed: ", err)
......
......@@ -7,11 +7,14 @@ import (
"github.com/jumpserver/koko/pkg/model"
)
func GetUserAssets(userID, cachePolicy string) (assets model.AssetList) {
func GetUserAssets(userID, cachePolicy, assetId string) (assets model.AssetList) {
if cachePolicy == "" {
cachePolicy = "1"
}
payload := map[string]string{"cache_policy": cachePolicy}
if assetId != "" {
payload["id"] = assetId
}
Url := fmt.Sprintf(UserAssetsURL, userID)
err := authClient.Get(Url, &assets, payload)
if err != nil {
......
......@@ -46,12 +46,12 @@ func FinishSession(data map[string]interface{}) {
var res map[string]interface{}
if sid, ok := data["id"]; ok {
playborad := map[string]interface{}{
payload := map[string]interface{}{
"is_finished": true,
"date_end": data["date_end"],
}
Url := fmt.Sprintf(SessionDetailURL, sid)
err := authClient.Patch(Url, playborad, &res)
err := authClient.Patch(Url, payload, &res)
if err != nil {
logger.Error(err)
}
......@@ -102,7 +102,7 @@ func PushSessionCommand(commands []*model.Command) (err error) {
}
func PushFTPLog(data *model.FTPLog) (err error) {
err = authClient.Post(FTPLogList, data, nil)
err = authClient.Post(FTPLogListURL, data, nil)
if err != nil {
logger.Error(err)
}
......
......@@ -3,16 +3,16 @@ package service
const (
UserAuthURL = "/api/authentication/v1/auth/" // post 验证用户登陆
UserProfileURL = "/api/users/v1/profile/" // 获取当前用户的基本信息
UserListUrl = "/api/users/v1/users/" // 用户列表地址
UserListURL = "/api/users/v1/users/" // 用户列表地址
UserDetailURL = "/api/users/v1/users/%s/" // 获取用户信息
UserAuthOTPURL = "/api/authentication/v1/otp/auth/" // 验证OTP
TokenAssetUrl = "/api/authentication/v1/connection-token/?token=%s" // Token name
TokenAssetURL = "/api/authentication/v1/connection-token/?token=%s" // Token name
SystemUserAssetAuthURL = "/api/assets/v1/system-user/%s/asset/%s/auth-info/" // 该系统用户对某资产的授权
SystemUserCmdFilterRules = "/api/assets/v1/system-user/%s/cmd-filter-rules/" // 过滤规则url
SystemUserDetailURL = "/api/assets/v1/system-user/%s/" // 某个系统用户的信息
AssetDetailURL = "/api/assets/v1/assets/%s/" // 某一个资产信息
DomainDetailURL = "/api/assets/v1/domain/%s/?gateway=1"
SystemUserAssetAuthURL = "/api/assets/v1/system-user/%s/asset/%s/auth-info/" // 该系统用户对某资产的授权
SystemUserCmdFilterRulesListURL = "/api/assets/v1/system-user/%s/cmd-filter-rules/" // 过滤规则url
SystemUserDetailURL = "/api/assets/v1/system-user/%s/" // 某个系统用户的信息
AssetDetailURL = "/api/assets/v1/assets/%s/" // 某一个资产信息
DomainDetailURL = "/api/assets/v1/domain/%s/?gateway=1"
TerminalRegisterURL = "/api/terminal/v2/terminal-registrations/" // 注册当前coco
TerminalConfigURL = "/api/terminal/v1/terminal/config/" // 从jumpserver获取coco的配置
......@@ -22,11 +22,11 @@ const (
SessionDetailURL = "/api/terminal/v1/sessions/%s/" // finish session的时候发送
SessionReplayURL = "/api/terminal/v1/sessions/%s/replay/" //上传录像
SessionCommandURL = "/api/terminal/v1/command/" //上传批量命令
FTPLogList = "/api/audits/v1/ftp-log/" // 上传 ftp日志
FinishTaskURL = "/api/terminal/v1/tasks/%s/"
FinishTaskURL = "/api/terminal/v1/tasks/%s/"
FTPLogListURL = "/api/audits/v1/ftp-log/" // 上传 ftp日志
UserAssetsURL = "/api/perms/v1/user/%s/assets/" //获取用户授权的所有资产
UserNodesAssetsURL = "/api/perms/v1/user/%s/nodes-assets/" // 获取用户授权的所有节点信息 节点分组
ValidateUserAssetPermissionURL = "/api/perms/v1/asset-permission/user/validate/" //0不使用缓存 1 使用缓存 2 刷新缓存
UserAssetsURL = "/api/perms/v1/users/%s/assets/" //获取用户授权的所有资产
UserNodesAssetsURL = "/api/perms/v1/users/%s/nodes-assets/" // 获取用户授权的所有节点信息 节点分组
ValidateUserAssetPermissionURL = "/api/perms/v1/asset-permissions/user/validate/" //0不使用缓存 1 使用缓存 2 刷新缓存
)
......@@ -44,7 +44,7 @@ func GetProfile() (user *model.User, err error) {
func GetUserByUsername(username string) (user *model.User, err error) {
var users []*model.User
payload := map[string]string{"username": username}
err = authClient.Get(UserListUrl, &users, payload)
err = authClient.Get(UserListURL, &users, payload)
if err != nil {
return
}
......
package srvconn
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"syscall"
"time"
"github.com/pkg/sftp"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/service"
)
func NewUserSFTP(user *model.User, addr string, assets ...model.Asset) *UserSftp {
u := UserSftp{
User: user, Addr: addr,
}
u.initial(assets)
return &u
}
type UserSftp struct {
User *model.User
Addr string
RootPath string
hosts map[string]*HostnameDir // key hostname or hostname.orgName
sftpClients map[string]*SftpConn // key %s@%s suName hostName
LogChan chan *model.FTPLog
}
func (u *UserSftp) initial(assets []model.Asset) {
u.RootPath = config.GetConf().SftpRoot
u.hosts = make(map[string]*HostnameDir)
u.sftpClients = make(map[string]*SftpConn)
u.LogChan = make(chan *model.FTPLog, 10)
for i := 0; i < len(assets); i++ {
if !assets[i].IsSupportProtocol("ssh") {
continue
}
key := assets[i].Hostname
if assets[i].OrgID != "" {
key = fmt.Sprintf("%s.%s", assets[i].Hostname, assets[i].OrgName)
}
u.hosts[key] = NewHostnameDir(&assets[i])
}
go u.LoopPushFTPLog()
}
func (u *UserSftp) ReadDir(path string) (res []os.FileInfo, err error) {
req := u.ParsePath(path)
if req.host == "" {
return u.RootDirInfo()
}
host, ok := u.hosts[req.host]
if !ok {
return res, sftp.ErrSshFxNoSuchFile
}
if req.su == "" {
for _, su := range host.GetSystemUsers() {
res = append(res, NewFakeFile(su.Name, true))
}
return
}
su, ok := host.suMaps[req.su]
if !ok {
return res, sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.ConnectAction) {
return res, sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return res, sftp.ErrSshFxPermissionDenied
}
logger.Debug("intersftp read dir real path: ", realPath)
res, err = conn.client.ReadDir(realPath)
return res, err
}
func (u *UserSftp) Stat(path string) (res os.FileInfo, err error) {
req := u.ParsePath(path)
if req.host == "" {
return u.Info()
}
host, ok := u.hosts[req.host]
if !ok {
return res, sftp.ErrSshFxNoSuchFile
}
if req.su == "" {
res = NewFakeFile(req.host, true)
return
}
su, ok := host.suMaps[req.su]
if !ok {
return res, sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.ConnectAction) {
return res, sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return res, sftp.ErrSshFxPermissionDenied
}
return conn.client.Stat(realPath)
}
func (u *UserSftp) ReadLink(path string) (res string, err error) {
req := u.ParsePath(path)
if req.host == "" {
return res, sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return res, sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return res, sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return res, sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.ConnectAction) {
return res, sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return res, sftp.ErrSshFxPermissionDenied
}
return conn.client.ReadLink(realPath)
}
func (u *UserSftp) RemoveDirectory(path string) error {
req := u.ParsePath(path)
if req.host == "" {
return sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return sftp.ErrSshFxPermissionDenied
}
err := conn.client.RemoveDirectory(realPath)
filename := realPath
isSucess := false
operate := model.OperateRemoveDir
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return err
}
func (u *UserSftp) Remove(path string) error {
req := u.ParsePath(path)
if req.host == "" {
return sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return sftp.ErrSshFxPermissionDenied
}
err := conn.client.Remove(realPath)
filename := realPath
isSucess := false
operate := model.OperateDelete
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return err
}
func (u *UserSftp) MkdirAll(path string) error {
req := u.ParsePath(path)
if req.host == "" {
return sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return sftp.ErrSshFxPermissionDenied
}
err := conn.client.MkdirAll(realPath)
filename := realPath
isSucess := false
operate := model.OperateMkdir
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return err
}
func (u *UserSftp) Rename(oldNamePath, newNamePath string) error {
req1 := u.ParsePath(oldNamePath)
req2 := u.ParsePath(newNamePath)
if req1.host == "" || req2.host == "" || req1.su == "" || req2.su == "" {
return sftp.ErrSshFxPermissionDenied
} else if req1.host != req2.host || req1.su != req2.su {
return sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req1.host]
if !ok {
return sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req1.su]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return sftp.ErrSshFxPermissionDenied
}
conn1, oldRealPath := u.GetSFTPAndRealPath(req1)
conn2, newRealPath := u.GetSFTPAndRealPath(req2)
if conn1 != conn2 {
return sftp.ErrSshFxOpUnsupported
}
err := conn1.client.Rename(oldRealPath, newRealPath)
filename := fmt.Sprintf("%s=>%s", oldRealPath, newRealPath)
isSucess := false
operate := model.OperateRename
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return err
}
func (u *UserSftp) Symlink(oldNamePath, newNamePath string) error {
req1 := u.ParsePath(oldNamePath)
req2 := u.ParsePath(newNamePath)
if req1.host == "" || req2.host == "" || req1.su == "" || req2.su == "" {
return sftp.ErrSshFxPermissionDenied
} else if req1.host != req2.host || req1.su != req2.su {
return sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req1.host]
if !ok {
return sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req1.su]
if !ok {
return sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return sftp.ErrSshFxPermissionDenied
}
conn1, oldRealPath := u.GetSFTPAndRealPath(req1)
conn2, newRealPath := u.GetSFTPAndRealPath(req2)
if conn1 != conn2 {
return sftp.ErrSshFxOpUnsupported
}
err := conn1.client.Symlink(oldRealPath, newRealPath)
filename := fmt.Sprintf("%s=>%s", oldRealPath, newRealPath)
isSucess := false
operate := model.OperateSymlink
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return err
}
func (u *UserSftp) Create(path string) (*sftp.File, error) {
req := u.ParsePath(path)
if req.host == "" {
return nil, sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return nil, sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return nil, sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.UploadAction) {
return nil, sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return nil, sftp.ErrSshFxPermissionDenied
}
sf, err := conn.client.Create(realPath)
filename := realPath
isSucess := false
operate := model.OperateUpload
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return sf, err
}
func (u *UserSftp) Open(path string) (*sftp.File, error) {
req := u.ParsePath(path)
if req.host == "" {
return nil, sftp.ErrSshFxPermissionDenied
}
host, ok := u.hosts[req.host]
if !ok {
return nil, sftp.ErrSshFxPermissionDenied
}
if req.su == "" {
return nil, sftp.ErrSshFxPermissionDenied
}
su, ok := host.suMaps[req.su]
if !ok {
return nil, sftp.ErrSshFxNoSuchFile
}
if !u.validatePermission(su, model.DownloadAction) {
return nil, sftp.ErrSshFxPermissionDenied
}
conn, realPath := u.GetSFTPAndRealPath(req)
if conn == nil {
return nil, sftp.ErrSshFxPermissionDenied
}
sf, err := conn.client.Open(realPath)
filename := realPath
isSucess := false
operate := model.OperateDownaload
if err == nil {
isSucess = true
}
u.CreateFTPLog(host.asset, su, operate, filename, isSucess)
return sf, err
}
func (u *UserSftp) Info() (os.FileInfo, error) {
return NewFakeFile("/", true), nil
}
func (u *UserSftp) RootDirInfo() ([]os.FileInfo, error) {
hostDirs := make([]os.FileInfo, 0, len(u.hosts))
for key := range u.hosts {
hostDirs = append(hostDirs, NewFakeFile(key, true))
}
sort.Sort(FileInfoList(hostDirs))
return hostDirs, nil
}
func (u *UserSftp) ParsePath(path string) (req requestMessage) {
data := strings.Split(strings.TrimPrefix(path, "/"), "/")
if len(data) == 0 {
return
}
host, pathArray := data[0], data[1:]
req.host = host
if suName, unique := u.HostHasUniqueSu(host); unique {
req.suUnique = true
req.su = suName
} else {
if len(pathArray) == 0 {
req.su = ""
} else {
req.su, pathArray = pathArray[0], pathArray[1:]
}
}
req.dpath = strings.Join(pathArray, "/")
return
}
func (u *UserSftp) GetSFTPAndRealPath(req requestMessage) (conn *SftpConn, realPath string) {
if host, ok := u.hosts[req.host]; ok {
if su, ok := host.suMaps[req.su]; ok {
key := fmt.Sprintf("%s@%s", su.Name, req.host)
conn, ok := u.sftpClients[key]
if !ok {
var err error
conn, err = u.GetSftpClient(host.asset, su)
if err != nil {
logger.Debug("Get Sftp Client err: ", err.Error())
return nil, ""
}
u.sftpClients[key] = conn
}
switch strings.ToLower(u.RootPath) {
case "home", "~", "":
realPath = filepath.Join(conn.HomeDirPath, strings.TrimPrefix(req.dpath, "/"))
default:
realPath = filepath.Join(u.RootPath, strings.TrimPrefix(req.dpath, "/"))
}
return conn, realPath
}
}
return
}
func (u *UserSftp) HostHasUniqueSu(hostKey string) (string, bool) {
if host, ok := u.hosts[hostKey]; ok {
return host.HasUniqueSu()
}
return "", false
}
func (u *UserSftp) validatePermission(su *model.SystemUser, action string) bool {
for _, pemAction := range su.Actions {
if pemAction == action || pemAction == model.AllAction {
return true
}
}
return false
}
func (u *UserSftp) CreateFTPLog(asset *model.Asset, su *model.SystemUser, operate, filename string, isSuccess bool) {
data := model.FTPLog{
User: fmt.Sprintf("%s (%s)", u.User.Name, u.User.Username),
Hostname: asset.Hostname,
OrgID: asset.OrgID,
SystemUser: su.Name,
RemoteAddr: u.Addr,
Operate: operate,
Path: filename,
DataStart: common.CurrentUTCTime(),
IsSuccess: isSuccess,
}
u.LogChan <- &data
}
func (u *UserSftp) LoopPushFTPLog() {
ftpLogList := make([]*model.FTPLog, 0, 1024)
dataChan := make(chan *model.FTPLog)
go u.SendFTPLog(dataChan)
defer close(dataChan)
for {
select {
case <-time.After(time.Second * 5):
case logData, ok := <-u.LogChan:
if !ok {
return
}
ftpLogList = append(ftpLogList, logData)
}
if len(ftpLogList) > 0 {
select {
case dataChan <- ftpLogList[len(ftpLogList)-1]:
ftpLogList = ftpLogList[:len(ftpLogList)-1]
default:
}
}
}
}
func (u *UserSftp) SendFTPLog(dataChan <-chan *model.FTPLog) {
for data := range dataChan {
for i := 0; i < 4; i++ {
err := service.PushFTPLog(data)
if err == nil {
break
}
logger.Debugf("create FTP log err: %s", err.Error())
}
}
}
func (u *UserSftp) GetSftpClient(asset *model.Asset, sysUser *model.SystemUser) (conn *SftpConn, err error) {
sshClient, err := NewClient(u.User, asset, sysUser, config.GetConf().SSHTimeout*time.Second)
if err != nil {
return
}
sftpClient, err := sftp.NewClient(sshClient.Client)
if err != nil {
return
}
HomeDirPath, err := sftpClient.Getwd()
if err != nil {
return
}
conn = &SftpConn{client: sftpClient, conn: sshClient, HomeDirPath: HomeDirPath}
return conn, err
}
func (u *UserSftp) Close() {
for _, client := range u.sftpClients {
client.Close()
}
close(u.LogChan)
}
type requestMessage struct {
host string
su string
dpath string
suUnique bool
}
func NewHostnameDir(asset *model.Asset) *HostnameDir {
sus := make(map[string]*model.SystemUser)
for i := 0; i < len(asset.SystemUsers); i++ {
if asset.SystemUsers[i].Protocol == "ssh" {
sus[asset.SystemUsers[i].Name] = &asset.SystemUsers[i]
}
}
h := HostnameDir{asset: asset, suMaps: sus}
return &h
}
type HostnameDir struct {
asset *model.Asset
suMaps map[string]*model.SystemUser
}
func (h *HostnameDir) HasUniqueSu() (string, bool) {
sus := h.GetSystemUsers()
if len(sus) == 1 {
return sus[0].Name, true
}
return "", false
}
func (h *HostnameDir) GetSystemUsers() (sus []model.SystemUser) {
sus = make([] model.SystemUser, 0, len(h.suMaps))
for _, item := range h.suMaps {
sus = append(sus, *item)
}
model.SortSystemUserByPriority(sus)
return sus
}
type SftpConn struct {
HomeDirPath string
client *sftp.Client
conn *SSHClient
}
func (s *SftpConn) Close() {
_ = s.client.Close()
RecycleClient(s.conn)
}
func NewFakeFile(name string, isDir bool) *FakeFileInfo {
return &FakeFileInfo{
name: name,
modtime: time.Now().UTC(),
isDir: isDir,
size: int64(0),
}
}
func NewFakeSymFile(name string) *FakeFileInfo{
return &FakeFileInfo{
name: name,
modtime: time.Now().UTC(),
size: int64(0),
symlink: name,
}
}
type FakeFileInfo struct {
name string
isDir bool
size int64
modtime time.Time
symlink string
}
func (f *FakeFileInfo) Name() string { return f.name }
func (f *FakeFileInfo) Size() int64 { return f.size }
func (f *FakeFileInfo) Mode() os.FileMode {
ret := os.FileMode(0644)
if f.isDir {
ret = os.FileMode(0755) | os.ModeDir
}
if f.symlink != "" {
ret = os.FileMode(0777) | os.ModeSymlink
}
return ret
}
func (f *FakeFileInfo) ModTime() time.Time { return f.modtime }
func (f *FakeFileInfo) IsDir() bool { return f.isDir }
func (f *FakeFileInfo) Sys() interface{} {
return &syscall.Stat_t{Uid: 0, Gid: 0}
}
type FileInfoList []os.FileInfo
func (fl FileInfoList) Len() int {
return len(fl)
}
func (fl FileInfoList) Swap(i, j int) { fl[i], fl[j] = fl[j], fl[i] }
func (fl FileInfoList) Less(i, j int) bool { return fl[i].Name() < fl[j].Name() }
package srvconn
import (
"golang.org/x/crypto/ssh"
"io/ioutil"
"golang.org/x/crypto/ssh"
)
func GetPubKeyFromFile(keypath string) (ssh.Signer, error) {
......
......@@ -23,7 +23,7 @@ func StartServer() {
}
addr := net.JoinHostPort(conf.BindHost, conf.SSHPort)
logger.Infof("Start ssh server at %s", addr)
logger.Infof("Start SSH server at %s", addr)
sshServer = &ssh.Server{
Addr: addr,
KeyboardInteractiveHandler: auth.CheckMFA,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment