Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
koko
Commits
00fd5d8a
Unverified
Commit
00fd5d8a
authored
Dec 05, 2019
by
Eric_Lee
Committed by
GitHub
Dec 05, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
155dev (#163)
sftp node tree
parent
700af064
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1077 additions
and
639 deletions
+1077
-639
sftp.go
pkg/handler/sftp.go
+3
-5
sftpvolume.go
pkg/httpd/sftpvolume.go
+8
-7
nodetree.go
pkg/model/nodetree.go
+24
-0
perms.go
pkg/service/perms.go
+17
-0
urls.go
pkg/service/urls.go
+2
-0
sftpconn.go
pkg/srvconn/sftpconn.go
+258
-627
sftpfile.go
pkg/srvconn/sftpfile.go
+765
-0
No files found.
pkg/handler/sftp.go
View file @
00fd5d8a
...
...
@@ -14,7 +14,6 @@ import (
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/service"
"github.com/jumpserver/koko/pkg/srvconn"
)
...
...
@@ -46,12 +45,11 @@ func SftpHandler(sess ssh.Session) {
}
func
NewSFTPHandler
(
user
*
model
.
User
,
addr
string
)
*
sftpHandler
{
assets
:=
service
.
GetUserAllAssets
(
user
.
ID
)
return
&
sftpHandler
{
srvconn
.
NewUserSFTP
(
user
,
addr
,
assets
...
)}
return
&
sftpHandler
{
srvconn
.
NewUserSftpConn
(
user
,
addr
)}
}
type
sftpHandler
struct
{
*
srvconn
.
UserSftp
*
srvconn
.
UserSftp
Conn
}
func
(
fs
*
sftpHandler
)
Filelist
(
r
*
sftp
.
Request
)
(
sftp
.
ListerAt
,
error
)
{
...
...
@@ -122,7 +120,7 @@ func (fs *sftpHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) {
}
func
(
fs
*
sftpHandler
)
Close
()
{
fs
.
UserSftp
.
Close
()
fs
.
UserSftp
Conn
.
Close
()
}
type
listerat
[]
os
.
FileInfo
...
...
pkg/httpd/sftpvolume.go
View file @
00fd5d8a
...
...
@@ -18,14 +18,14 @@ import (
)
func
NewUserVolume
(
user
*
model
.
User
,
addr
,
hostId
string
)
*
UserVolume
{
var
assets
[]
model
.
Asset
var
userSftp
*
srvconn
.
UserSftpConn
homename
:=
"Home"
basePath
:=
"/"
switch
hostId
{
case
""
:
assets
=
service
.
GetUserAllAssets
(
user
.
ID
)
userSftp
=
srvconn
.
NewUserSftpConn
(
user
,
addr
)
default
:
assets
=
service
.
GetUserAssetByID
(
user
.
ID
,
hostId
)
assets
:
=
service
.
GetUserAssetByID
(
user
.
ID
,
hostId
)
if
len
(
assets
)
==
1
{
homename
=
assets
[
0
]
.
Hostname
if
assets
[
0
]
.
OrgID
!=
""
{
...
...
@@ -33,11 +33,12 @@ func NewUserVolume(user *model.User, addr, hostId string) *UserVolume {
}
basePath
=
filepath
.
Join
(
"/"
,
homename
)
}
userSftp
=
srvconn
.
NewUserSftpConnWithAssets
(
user
,
addr
,
assets
...
)
}
rawID
:=
fmt
.
Sprintf
(
"%s@%s"
,
user
.
Username
,
addr
)
uVolume
:=
&
UserVolume
{
Uuid
:
elfinder
.
GenerateID
(
rawID
),
UserSftp
:
srvconn
.
NewUserSFTP
(
user
,
addr
,
assets
...
)
,
UserSftp
:
userSftp
,
Homename
:
homename
,
basePath
:
basePath
,
chunkFilesMap
:
make
(
map
[
int
]
*
sftp
.
File
),
...
...
@@ -47,8 +48,8 @@ func NewUserVolume(user *model.User, addr, hostId string) *UserVolume {
}
type
UserVolume
struct
{
Uuid
string
*
srvconn
.
UserSftp
Uuid
string
UserSftp
*
srvconn
.
UserSftpConn
Homename
string
basePath
string
...
...
@@ -66,7 +67,7 @@ func (u *UserVolume) Info(path string) (elfinder.FileDir, error) {
if
path
==
"/"
{
return
u
.
RootFileDir
(),
nil
}
originFileInfo
,
err
:=
u
.
Stat
(
filepath
.
Join
(
u
.
basePath
,
path
))
originFileInfo
,
err
:=
u
.
UserSftp
.
Stat
(
filepath
.
Join
(
u
.
basePath
,
path
))
if
err
!=
nil
{
return
rest
,
err
}
...
...
pkg/model/nodetree.go
0 → 100644
View file @
00fd5d8a
package
model
import
"encoding/json"
type
NodeTreeList
[]
NodeTreeAsset
type
NodeTreeAsset
struct
{
ID
string
`json:"id"`
Name
string
`json:"name"`
Title
string
`json:"title"`
Meta
map
[
string
]
interface
{}
`json:"meta"`
}
func
ConvertMetaToNode
(
body
[]
byte
)
(
node
Node
,
err
error
)
{
err
=
json
.
Unmarshal
(
body
,
&
node
)
return
}
func
ConvertMetaToAsset
(
body
[]
byte
)
(
asset
Asset
,
err
error
)
{
err
=
json
.
Unmarshal
(
body
,
&
asset
)
return
}
pkg/service/perms.go
View file @
00fd5d8a
...
...
@@ -165,3 +165,20 @@ func ValidateUserAssetPermission(userID, assetID, systemUserID, action string) b
return
res
.
Msg
}
func
GetUserNodeTreeWithAsset
(
userID
,
nodeID
,
cachePolicy
string
)
(
nodeTrees
model
.
NodeTreeList
)
{
if
cachePolicy
==
""
{
cachePolicy
=
"1"
}
payload
:=
map
[
string
]
string
{
"cache_policy"
:
cachePolicy
}
if
nodeID
!=
""
{
payload
[
"id"
]
=
nodeID
}
Url
:=
fmt
.
Sprintf
(
NodeTreeWithAssetURL
,
userID
)
_
,
err
:=
authClient
.
Get
(
Url
,
&
nodeTrees
,
payload
)
if
err
!=
nil
{
logger
.
Error
(
"Get user node tree error: "
,
err
)
}
return
}
pkg/service/urls.go
View file @
00fd5d8a
...
...
@@ -43,4 +43,6 @@ const (
const
(
UserTokenAuthURL
=
"/api/v1/authentication/tokens/"
// 用户登录验证
UserConfirmAuthURL
=
"/api/v1/authentication/login-confirm-ticket/status/"
NodeTreeWithAssetURL
=
"/api/v1/perms/users/%s/nodes/children-with-assets/tree/"
// 资产树
)
pkg/srvconn/sftpconn.go
View file @
00fd5d8a
package
srvconn
import
(
"encoding/json"
"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
{
type
UserSftpConn
struct
{
User
*
model
.
User
Addr
string
Dirs
map
[
string
]
os
.
FileInfo
RootPath
string
ShowHidden
bool
ReuseConnection
bool
Overtime
time
.
Duration
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
)
{
conf
:=
config
.
GetConf
()
u
.
RootPath
=
conf
.
SftpRoot
u
.
ShowHidden
=
conf
.
ShowHiddenFile
u
.
ReuseConnection
=
conf
.
ReuseConnection
u
.
Overtime
=
conf
.
SSHTimeout
*
time
.
Second
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
])
}
modeTime
time
.
Time
logChan
chan
*
model
.
FTPLog
go
u
.
LoopPushFTPLog
()
closed
chan
struct
{}
}
func
(
u
*
UserSftp
)
ReadDir
(
path
string
)
(
res
[]
os
.
FileInfo
,
err
error
)
{
req
:=
u
.
ParsePath
(
path
)
if
r
eq
.
host
==
""
{
return
u
.
RootDirInfo
()
func
(
u
*
UserSftp
Conn
)
ReadDir
(
path
string
)
(
res
[]
os
.
FileInfo
,
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
r
ootDir
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
{
return
rootDir
.
List
()
}
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
res
,
sftp
.
ErrSshFxNoSuchFile
if
nodeDir
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
nodeDir
.
List
()
}
if
!
u
.
validatePermission
(
su
,
model
.
ConnectAction
)
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
ReadDir
(
restPath
)
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
}
logger
.
Debug
(
"inter sftp read dir real path: "
,
realPath
)
res
,
err
=
conn
.
client
.
ReadDir
(
realPath
)
if
!
u
.
ShowHidden
{
noHiddenFiles
:=
make
([]
os
.
FileInfo
,
0
,
len
(
res
))
for
i
:=
0
;
i
<
len
(
res
);
i
++
{
if
!
strings
.
HasPrefix
(
res
[
i
]
.
Name
(),
"."
)
{
noHiddenFiles
=
append
(
noHiddenFiles
,
res
[
i
])
}
}
return
noHiddenFiles
,
err
}
return
res
,
err
return
nil
,
sftp
.
ErrSSHFxNoSuchFile
}
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
func
(
u
*
UserSftpConn
)
Stat
(
path
string
)
(
res
os
.
FileInfo
,
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
rootDir
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
{
return
rootDir
,
nil
}
if
req
.
su
==
""
{
res
=
NewFakeFile
(
req
.
host
,
true
)
return
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
res
,
sftp
.
ErrSshFxNoSuchFile
if
nodeDir
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
nodeDir
,
nil
}
if
!
u
.
validatePermission
(
su
,
model
.
ConnectAction
)
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
Stat
(
restPath
)
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
}
return
conn
.
client
.
Stat
(
realPath
)
return
nil
,
sftp
.
ErrSSHFxNoSuchFile
}
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
func
(
u
*
UserSftpConn
)
ReadLink
(
path
string
)
(
name
string
,
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
&&
restPath
==
""
{
return
""
,
sftp
.
ErrSshFxOpUnsupported
}
if
req
.
su
==
""
{
return
res
,
sftp
.
ErrSshFxPermissionDeni
ed
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
""
,
sftp
.
ErrSshFxOpUnsupport
ed
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
res
,
sftp
.
ErrSshFxNoSuchFile
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
ReadLink
(
restPath
)
}
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
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
:=
u
.
removeDirectoryAll
(
conn
.
client
,
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateRemoveDir
if
err
==
nil
{
isSuccess
=
true
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
err
return
""
,
sftp
.
ErrSshFxOpUnsupported
}
func
(
u
*
UserSftp
)
removeDirectoryAll
(
conn
*
sftp
.
Client
,
path
string
)
error
{
var
err
error
var
files
[]
os
.
FileInfo
files
,
err
=
conn
.
ReadDir
(
path
)
if
err
!=
nil
{
return
err
}
for
_
,
item
:=
range
files
{
realPath
:=
filepath
.
Join
(
path
,
item
.
Name
())
if
item
.
IsDir
()
{
err
=
u
.
removeDirectoryAll
(
conn
,
realPath
)
if
err
!=
nil
{
return
err
func
(
u
*
UserSftpConn
)
Rename
(
oldNamePath
,
newNamePath
string
)
(
err
error
)
{
oldFi
,
oldRestPath
:=
u
.
ParsePath
(
oldNamePath
)
newFi
,
newRestPath
:=
u
.
ParsePath
(
newNamePath
)
if
oldAssetDir
,
ok
:=
oldFi
.
(
*
AssetDir
);
ok
{
if
newAssetDir
,
newOk
:=
newFi
.
(
*
AssetDir
);
newOk
{
if
oldAssetDir
==
newAssetDir
{
return
oldAssetDir
.
Rename
(
oldRestPath
,
newRestPath
)
}
continue
}
err
=
conn
.
Remove
(
realPath
)
if
err
!=
nil
{
return
err
}
}
return
conn
.
RemoveDirectory
(
path
)
return
sftp
.
ErrSshFxOpUnsupported
}
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
u
.
validatePermission
(
su
,
model
.
UploadAction
)
{
func
(
u
*
UserSftpConn
)
RemoveDirectory
(
path
string
)
(
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
&&
restPath
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
sftp
.
ErrSshFxPermissionDenied
}
logger
.
Debug
(
"remove file path"
,
realPath
)
err
:=
conn
.
client
.
Remove
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateDelete
if
err
==
nil
{
isSuccess
=
true
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
RemoveDirectory
(
restPath
)
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
err
return
sftp
.
ErrSshFxPermissionDenied
}
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
u
.
validatePermission
(
su
,
model
.
UploadAction
)
{
func
(
u
*
UserSftpConn
)
Remove
(
path
string
)
(
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
&&
restPath
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
sftp
.
ErrSshFxPermissionDenied
}
err
:=
conn
.
client
.
MkdirAll
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateMkdir
if
err
==
nil
{
isSuccess
=
true
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
Remove
(
restPath
)
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
err
return
sftp
.
ErrSshFxPermissionDenied
}
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
{
func
(
u
*
UserSftpConn
)
MkdirAll
(
path
string
)
(
err
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
&&
restPath
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
host
,
ok
:=
u
.
hosts
[
req1
.
host
]
if
!
ok
{
return
sftp
.
ErrSshFxPermissionDenied
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
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
)
isSuccess
:=
false
operate
:=
model
.
OperateRename
if
err
==
nil
{
isSuccess
=
true
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req1
.
su
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
u
.
validatePermission
(
su
,
model
.
UploadAction
)
{
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
sftp
.
ErrSshFxPermissionDenied
}
conn1
,
oldRealPath
:=
u
.
GetSFTPAndRealPath
(
req1
)
conn2
,
newRealPath
:=
u
.
GetSFTPAndRealPath
(
req2
)
if
conn1
!=
conn2
{
return
sftp
.
ErrSshFxOpUnsupported
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
MkdirAll
(
restPath
)
}
err
:=
conn1
.
client
.
Symlink
(
oldRealPath
,
newRealPath
)
return
sftp
.
ErrSshFxPermissionDenied
}
filename
:=
fmt
.
Sprintf
(
"%s=>%s"
,
oldRealPath
,
newRealPath
)
isSuccess
:=
false
operate
:=
model
.
OperateSymlink
if
err
==
nil
{
isSuccess
=
true
func
(
u
*
UserSftpConn
)
Symlink
(
oldNamePath
,
newNamePath
string
)
(
err
error
)
{
oldFi
,
oldRestPath
:=
u
.
ParsePath
(
oldNamePath
)
newFi
,
newRestPath
:=
u
.
ParsePath
(
newNamePath
)
if
oldAssetDir
,
ok
:=
oldFi
.
(
*
AssetDir
);
ok
{
if
newAssetDir
,
newOk
:=
newFi
.
(
*
AssetDir
);
newOk
{
if
oldAssetDir
==
newAssetDir
{
return
oldAssetDir
.
Symlink
(
oldRestPath
,
newRestPath
)
}
}
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
err
return
sftp
.
ErrSshFxPermissionDenied
}
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
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
u
.
validatePermission
(
su
,
model
.
UploadAction
)
{
func
(
u
*
UserSftpConn
)
Create
(
path
string
)
(
*
sftp
.
File
,
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
logger
.
Debug
(
"create path:"
,
realPath
)
sf
,
err
:=
conn
.
client
.
Create
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateUpload
if
err
==
nil
{
isSuccess
=
true
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
Create
(
restPath
)
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
sf
,
err
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
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
{
func
(
u
*
UserSftpConn
)
Open
(
path
string
)
(
*
sftp
.
File
,
error
)
{
fi
,
restPath
:=
u
.
ParsePath
(
path
)
if
_
,
ok
:=
fi
.
(
*
UserSftpConn
);
ok
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
if
req
.
su
==
""
{
if
_
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
host
.
loadSystemUsers
(
u
.
User
.
ID
)
su
,
ok
:=
host
.
suMaps
[
req
.
su
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
u
.
validatePermission
(
su
,
model
.
DownloadAction
)
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
return
assetDir
.
Open
(
restPath
)
}
conn
,
realPath
:=
u
.
GetSFTPAndRealPath
(
req
)
if
conn
==
nil
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
logger
.
Debug
(
"Open path:"
,
realPath
)
sf
,
err
:=
conn
.
client
.
Open
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateDownaload
if
err
==
nil
{
isSuccess
=
true
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
func
(
u
*
UserSftpConn
)
Close
()
{
for
_
,
dir
:=
range
u
.
Dirs
{
if
nodeDir
,
ok
:=
dir
.
(
*
NodeDir
);
ok
{
nodeDir
.
close
()
continue
}
if
assetDir
,
ok
:=
dir
.
(
*
AssetDir
);
ok
{
assetDir
.
close
()
continue
}
}
u
.
CreateFTPLog
(
host
.
asset
,
su
,
operate
,
filename
,
isSuccess
)
return
sf
,
err
close
(
u
.
closed
)
}
func
(
u
*
UserSftpConn
)
Name
()
string
{
return
"/"
}
func
(
u
*
UserSftpConn
)
Size
()
int64
{
return
0
}
func
(
u
*
UserSftpConn
)
Mode
()
os
.
FileMode
{
return
os
.
ModePerm
|
os
.
ModeDir
}
func
(
u
*
UserSftp
)
Info
()
(
os
.
FileInfo
,
error
)
{
return
NewFakeFile
(
"/"
,
true
),
nil
func
(
u
*
UserSftpConn
)
ModTime
()
time
.
Time
{
return
u
.
modeTime
}
func
(
u
*
UserSftpConn
)
IsDir
()
bool
{
return
true
}
func
(
u
*
UserSftpConn
)
Sys
()
interface
{}
{
return
&
syscall
.
Stat_t
{
Uid
:
0
,
Gid
:
0
}
}
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
))
func
(
u
*
UserSftpConn
)
List
()
(
res
[]
os
.
FileInfo
,
err
error
)
{
for
_
,
item
:=
range
u
.
Dirs
{
res
=
append
(
res
,
item
)
}
sort
.
Sort
(
FileInfoList
(
hostDirs
))
return
hostDirs
,
nil
return
}
func
(
u
*
UserSftp
)
ParsePath
(
path
string
)
(
req
requestMessage
)
{
data
:=
strings
.
Split
(
strings
.
TrimPrefix
(
path
,
"/"
),
"/"
)
if
len
(
data
)
==
0
{
func
(
u
*
UserSftpConn
)
ParsePath
(
path
string
)
(
fi
os
.
FileInfo
,
restPath
string
)
{
path
=
strings
.
TrimPrefix
(
path
,
"/"
)
data
:=
strings
.
Split
(
path
,
"/"
)
if
len
(
data
)
==
1
&&
data
[
0
]
==
""
{
fi
=
u
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
:
]
dirs
:=
u
.
Dirs
var
ok
bool
for
i
:=
0
;
i
<
len
(
data
);
i
++
{
fi
,
ok
=
dirs
[
data
[
i
]]
if
!
ok
{
restPath
=
strings
.
Join
(
data
[
i
+
1
:
],
"/"
)
break
}
if
nodeDir
,
ok
:=
fi
.
(
*
NodeDir
);
ok
{
nodeDir
.
loadNodeAsset
(
u
)
dirs
=
nodeDir
.
subDirs
continue
}
if
assetDir
,
ok
:=
fi
.
(
*
AssetDir
);
ok
{
assetDir
.
loadSystemUsers
()
restPath
=
strings
.
Join
(
data
[
i
+
1
:
],
"/"
)
break
}
}
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
.
Info
(
"Get Sftp Client err: "
,
err
.
Error
())
return
nil
,
""
func
(
u
*
UserSftpConn
)
initial
()
{
nodeTrees
:=
service
.
GetUserNodeTreeWithAsset
(
u
.
User
.
ID
,
""
,
""
)
if
u
.
Dirs
==
nil
{
u
.
Dirs
=
map
[
string
]
os
.
FileInfo
{}
}
for
_
,
item
:=
range
nodeTrees
{
typeName
,
ok
:=
item
.
Meta
[
"type"
]
.
(
string
)
if
!
ok
{
continue
}
body
,
err
:=
json
.
Marshal
(
item
.
Meta
[
typeName
])
if
err
!=
nil
{
logger
.
Errorf
(
"Json Marshal err: %s"
,
err
)
continue
}
switch
typeName
{
case
"node"
:
node
,
err
:=
model
.
ConvertMetaToNode
(
body
)
if
err
!=
nil
{
logger
.
Errorf
(
"convert to node err: %s"
,
err
)
continue
}
nodeDir
:=
NewNodeDir
(
node
)
folderName
:=
nodeDir
.
folderName
for
{
_
,
ok
:=
u
.
Dirs
[
folderName
]
if
!
ok
{
break
}
u
.
sftpClients
[
key
]
=
conn
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
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
,
"/"
))
if
folderName
!=
nodeDir
.
folderName
{
nodeDir
.
folderName
=
folderName
}
return
conn
,
realPath
}
}
return
}
func
(
u
*
UserSftp
)
HostHasUniqueSu
(
hostKey
string
)
(
string
,
bool
)
{
if
host
,
ok
:=
u
.
hosts
[
hostKey
];
ok
{
host
.
loadSystemUsers
(
u
.
User
.
ID
)
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
u
.
Dirs
[
folderName
]
=
&
nodeDir
case
"asset"
:
asset
,
err
:=
model
.
ConvertMetaToAsset
(
body
)
if
err
!=
nil
{
logger
.
Errorf
(
"convert to asset err: %s"
,
err
)
continue
}
if
!
asset
.
IsSupportProtocol
(
"ssh"
)
{
continue
}
assetDir
:=
NewAssetDir
(
u
.
User
,
asset
,
u
.
Addr
,
u
.
logChan
)
folderName
:=
assetDir
.
folderName
for
{
_
,
ok
:=
u
.
Dirs
[
folderName
]
if
!
ok
{
break
}
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
if
folderName
!=
assetDir
.
folderName
{
assetDir
.
folderName
=
folderName
}
u
.
Dirs
[
folderName
]
=
&
assetDir
}
}
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
)
L
oopPushFTPLog
()
{
func
(
u
*
UserSftp
Conn
)
l
oopPushFTPLog
()
{
ftpLogList
:=
make
([]
*
model
.
FTPLog
,
0
,
1024
)
dataChan
:=
make
(
chan
*
model
.
FTPLog
)
go
u
.
SendFTPLog
(
dataChan
)
defer
close
(
dataChan
)
tick
:=
time
.
NewTicker
(
time
.
Second
*
10
)
maxRetry
:=
0
var
err
error
tick
:=
time
.
NewTicker
(
time
.
Second
*
20
)
defer
tick
.
Stop
()
for
{
select
{
case
<-
u
.
closed
:
if
len
(
ftpLogList
)
==
0
{
return
}
case
<-
tick
.
C
:
case
logData
,
ok
:=
<-
u
.
LogChan
:
if
len
(
ftpLogList
)
==
0
{
continue
}
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
.
Errorf
(
"Create FTP log err: %s"
,
err
.
Error
())
}
}
}
func
(
u
*
UserSftp
)
GetSftpClient
(
asset
*
model
.
Asset
,
sysUser
*
model
.
SystemUser
)
(
conn
*
SftpConn
,
err
error
)
{
var
(
sshClient
*
SSHClient
ok
bool
)
if
u
.
ReuseConnection
{
key
:=
MakeReuseSSHClientKey
(
u
.
User
,
asset
,
sysUser
)
switch
sysUser
.
Username
{
case
""
:
sshClient
,
ok
=
searchSSHClientFromCache
(
key
)
if
ok
{
sysUser
.
Username
=
sshClient
.
username
}
default
:
sshClient
,
ok
=
GetClientFromCache
(
key
)
}
if
!
ok
{
sshClient
,
err
=
NewClient
(
u
.
User
,
asset
,
sysUser
,
u
.
Overtime
,
u
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
}
data
:=
ftpLogList
[
len
(
ftpLogList
)
-
1
]
err
=
service
.
PushFTPLog
(
data
)
if
err
==
nil
{
ftpLogList
=
ftpLogList
[
:
len
(
ftpLogList
)
-
1
]
maxRetry
=
0
continue
}
else
{
logger
.
Infof
(
"Reuse connection for SFTP: %s->%s@%s. SSH client %p current ref: %d"
,
u
.
User
.
Username
,
sshClient
.
username
,
asset
.
IP
,
sshClient
,
sshClient
.
RefCount
())
logger
.
Errorf
(
"Create FTP log err: %s"
,
err
.
Error
())
}
}
else
{
sshClient
,
err
=
NewClient
(
u
.
User
,
asset
,
sysUser
,
u
.
Overtime
,
u
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
if
maxRetry
>
5
{
ftpLogList
=
ftpLogList
[
1
:
]
}
maxRetry
++
}
sftpClient
,
err
:=
sftp
.
NewClient
(
sshClient
.
client
)
if
err
!=
nil
{
logger
.
Errorf
(
"SSH client %p start sftp client session err %s"
,
sshClient
,
err
)
RecycleClient
(
sshClient
)
return
nil
,
err
}
HomeDirPath
,
err
:=
sftpClient
.
Getwd
()
if
err
!=
nil
{
logger
.
Errorf
(
"SSH client %p get home dir err %s"
,
sshClient
,
err
)
_
=
sftpClient
.
Close
()
RecycleClient
(
sshClient
)
return
nil
,
err
}
logger
.
Infof
(
"SSH client %p start sftp client session success"
,
sshClient
)
conn
=
&
SftpConn
{
client
:
sftpClient
,
conn
:
sshClient
,
HomeDirPath
:
HomeDirPath
}
return
conn
,
err
}
func
(
u
*
UserSftp
)
Close
()
{
for
_
,
client
:=
range
u
.
sftpClients
{
if
client
==
nil
{
continue
}
client
.
Close
()
func
NewUserSftpConn
(
user
*
model
.
User
,
addr
string
)
*
UserSftpConn
{
u
:=
UserSftpConn
{
User
:
user
,
Addr
:
addr
,
Dirs
:
map
[
string
]
os
.
FileInfo
{},
modeTime
:
time
.
Now
()
.
UTC
(),
logChan
:
make
(
chan
*
model
.
FTPLog
,
1024
),
closed
:
make
(
chan
struct
{}),
}
close
(
u
.
LogChan
)
}
type
requestMessage
struct
{
host
string
su
string
dpath
string
suUnique
bool
}
func
NewHostnameDir
(
asset
*
model
.
Asset
)
*
HostnameDir
{
h
:=
HostnameDir
{
asset
:
asset
}
return
&
h
}
type
HostnameDir
struct
{
asset
*
model
.
Asset
suMaps
map
[
string
]
*
model
.
SystemUser
u
.
initial
()
go
u
.
loopPushFTPLog
()
return
&
u
}
func
(
h
*
HostnameDir
)
loadSystemUsers
(
userID
string
)
{
if
h
.
suMaps
==
nil
{
sus
:=
make
(
map
[
string
]
*
model
.
SystemUser
)
SystemUsers
:=
service
.
GetUserAssetSystemUsers
(
userID
,
h
.
asset
.
ID
)
for
i
:=
0
;
i
<
len
(
SystemUsers
);
i
++
{
if
SystemUsers
[
i
]
.
Protocol
==
"ssh"
{
sus
[
SystemUsers
[
i
]
.
Name
]
=
&
SystemUsers
[
i
]
func
NewUserSftpConnWithAssets
(
user
*
model
.
User
,
addr
string
,
assets
...
model
.
Asset
)
*
UserSftpConn
{
u
:=
UserSftpConn
{
User
:
user
,
Addr
:
addr
,
Dirs
:
map
[
string
]
os
.
FileInfo
{},
modeTime
:
time
.
Now
()
.
UTC
(),
logChan
:
make
(
chan
*
model
.
FTPLog
,
1024
),
closed
:
make
(
chan
struct
{}),
}
for
_
,
asset
:=
range
assets
{
if
asset
.
IsSupportProtocol
(
"ssh"
)
{
assetDir
:=
NewAssetDir
(
u
.
User
,
asset
,
u
.
Addr
,
u
.
logChan
)
folderName
:=
assetDir
.
folderName
for
{
_
,
ok
:=
u
.
Dirs
[
folderName
]
if
!
ok
{
break
}
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
if
folderName
!=
assetDir
.
folderName
{
assetDir
.
folderName
=
folderName
}
u
.
Dirs
[
assetDir
.
folderName
]
=
&
assetDir
}
h
.
suMaps
=
sus
}
}
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
()
{
if
s
.
client
==
nil
{
return
}
_
=
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
)
go
u
.
loopPushFTPLog
()
return
&
u
}
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
()
}
pkg/srvconn/sftpfile.go
0 → 100644
View file @
00fd5d8a
package
srvconn
import
(
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"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"
)
type
NodeDir
struct
{
node
*
model
.
Node
subDirs
map
[
string
]
os
.
FileInfo
folderName
string
modeTime
time
.
Time
once
*
sync
.
Once
}
func
(
nd
*
NodeDir
)
Name
()
string
{
return
nd
.
folderName
}
func
(
nd
*
NodeDir
)
Size
()
int64
{
return
0
}
func
(
nd
*
NodeDir
)
Mode
()
os
.
FileMode
{
return
os
.
ModePerm
|
os
.
ModeDir
}
func
(
nd
*
NodeDir
)
ModTime
()
time
.
Time
{
return
nd
.
modeTime
}
func
(
nd
*
NodeDir
)
IsDir
()
bool
{
return
true
}
func
(
nd
*
NodeDir
)
Sys
()
interface
{}
{
return
&
syscall
.
Stat_t
{
Uid
:
0
,
Gid
:
0
}
}
func
(
nd
*
NodeDir
)
List
()
(
res
[]
os
.
FileInfo
,
err
error
)
{
for
_
,
item
:=
range
nd
.
subDirs
{
res
=
append
(
res
,
item
)
}
return
}
func
(
nd
*
NodeDir
)
loadNodeAsset
(
uSftp
*
UserSftpConn
)
{
nd
.
once
.
Do
(
func
()
{
nodeTrees
:=
service
.
GetUserNodeTreeWithAsset
(
uSftp
.
User
.
ID
,
nd
.
node
.
ID
,
"1"
)
dirs
:=
map
[
string
]
os
.
FileInfo
{}
for
_
,
item
:=
range
nodeTrees
{
typeName
,
ok
:=
item
.
Meta
[
"type"
]
.
(
string
)
if
!
ok
{
continue
}
body
,
err
:=
json
.
Marshal
(
item
.
Meta
[
typeName
])
if
err
!=
nil
{
continue
}
switch
typeName
{
case
"node"
:
node
,
err
:=
model
.
ConvertMetaToNode
(
body
)
if
err
!=
nil
{
logger
.
Errorf
(
"convert node err: %s"
,
err
)
continue
}
nodeDir
:=
NewNodeDir
(
node
)
folderName
:=
nodeDir
.
folderName
for
{
_
,
ok
:=
dirs
[
folderName
]
if
!
ok
{
break
}
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
if
folderName
!=
nodeDir
.
folderName
{
nodeDir
.
folderName
=
folderName
}
dirs
[
folderName
]
=
&
nodeDir
case
"asset"
:
asset
,
err
:=
model
.
ConvertMetaToAsset
(
body
)
if
err
!=
nil
{
logger
.
Errorf
(
"convert asset err: %s"
,
err
)
continue
}
if
!
asset
.
IsSupportProtocol
(
"ssh"
)
{
continue
}
assetDir
:=
NewAssetDir
(
uSftp
.
User
,
asset
,
uSftp
.
Addr
,
uSftp
.
logChan
)
folderName
:=
assetDir
.
folderName
for
{
_
,
ok
:=
dirs
[
folderName
]
if
!
ok
{
break
}
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
if
folderName
!=
assetDir
.
folderName
{
assetDir
.
folderName
=
folderName
}
dirs
[
folderName
]
=
&
assetDir
}
}
nd
.
subDirs
=
dirs
})
}
func
(
nd
*
NodeDir
)
close
()
{
for
_
,
dir
:=
range
nd
.
subDirs
{
if
nodeDir
,
ok
:=
dir
.
(
*
NodeDir
);
ok
{
nodeDir
.
close
()
continue
}
if
assetDir
,
ok
:=
dir
.
(
*
AssetDir
);
ok
{
assetDir
.
close
()
}
}
}
func
NewNodeDir
(
node
model
.
Node
)
NodeDir
{
folderName
:=
node
.
Value
if
strings
.
Contains
(
node
.
Value
,
"/"
)
{
folderName
=
strings
.
ReplaceAll
(
node
.
Value
,
"/"
,
"_"
)
}
return
NodeDir
{
node
:
&
node
,
folderName
:
folderName
,
subDirs
:
map
[
string
]
os
.
FileInfo
{},
modeTime
:
time
.
Now
()
.
UTC
(),
once
:
new
(
sync
.
Once
),
}
}
func
NewAssetDir
(
user
*
model
.
User
,
asset
model
.
Asset
,
addr
string
,
logChan
chan
<-
*
model
.
FTPLog
)
AssetDir
{
folderName
:=
asset
.
Hostname
if
strings
.
Contains
(
folderName
,
"/"
)
{
folderName
=
strings
.
ReplaceAll
(
folderName
,
"/"
,
"_"
)
}
conf
:=
config
.
GetConf
()
return
AssetDir
{
user
:
user
,
asset
:
&
asset
,
folderName
:
folderName
,
modeTime
:
time
.
Now
()
.
UTC
(),
addr
:
addr
,
suMaps
:
nil
,
logChan
:
logChan
,
Overtime
:
conf
.
SSHTimeout
*
time
.
Second
,
RootPath
:
conf
.
SftpRoot
,
ShowHidden
:
conf
.
ShowHiddenFile
,
reuse
:
conf
.
ReuseConnection
,
sftpClients
:
map
[
string
]
*
SftpConn
{},
}
}
type
AssetDir
struct
{
user
*
model
.
User
asset
*
model
.
Asset
folderName
string
modeTime
time
.
Time
addr
string
suMaps
map
[
string
]
*
model
.
SystemUser
logChan
chan
<-
*
model
.
FTPLog
sftpClients
map
[
string
]
*
SftpConn
// systemUser_id
once
sync
.
Once
RootPath
string
reuse
bool
ShowHidden
bool
Overtime
time
.
Duration
}
func
(
ad
*
AssetDir
)
Name
()
string
{
return
ad
.
folderName
}
func
(
ad
*
AssetDir
)
Size
()
int64
{
return
0
}
func
(
ad
*
AssetDir
)
Mode
()
os
.
FileMode
{
return
os
.
ModePerm
|
os
.
ModeDir
}
func
(
ad
*
AssetDir
)
ModTime
()
time
.
Time
{
return
ad
.
modeTime
}
func
(
ad
*
AssetDir
)
IsDir
()
bool
{
return
true
}
func
(
ad
*
AssetDir
)
Sys
()
interface
{}
{
return
&
syscall
.
Stat_t
{
Uid
:
0
,
Gid
:
0
}
}
func
(
ad
*
AssetDir
)
loadSystemUsers
()
{
ad
.
once
.
Do
(
func
()
{
sus
:=
make
(
map
[
string
]
*
model
.
SystemUser
)
SystemUsers
:=
service
.
GetUserAssetSystemUsers
(
ad
.
user
.
ID
,
ad
.
asset
.
ID
)
for
i
:=
0
;
i
<
len
(
SystemUsers
);
i
++
{
if
SystemUsers
[
i
]
.
Protocol
==
"ssh"
{
ok
:=
true
folderName
:=
SystemUsers
[
i
]
.
Name
for
ok
{
if
_
,
ok
=
sus
[
folderName
];
ok
{
folderName
=
fmt
.
Sprintf
(
"%s_"
,
folderName
)
}
}
sus
[
folderName
]
=
&
SystemUsers
[
i
]
}
}
ad
.
suMaps
=
sus
})
}
func
(
ad
*
AssetDir
)
Create
(
path
string
)
(
*
sftp
.
File
,
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
folderName
=
pathData
[
0
]
pathData
=
pathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
UploadAction
)
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
,
"/"
))
if
con
==
nil
{
return
nil
,
sftp
.
ErrSshFxConnectionLost
}
sf
,
err
:=
con
.
client
.
Create
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateUpload
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
sf
,
err
}
func
(
ad
*
AssetDir
)
MkdirAll
(
path
string
)
(
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
folderName
=
pathData
[
0
]
pathData
=
pathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
UploadAction
)
{
return
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
,
"/"
))
if
con
==
nil
{
return
sftp
.
ErrSshFxConnectionLost
}
err
=
con
.
client
.
MkdirAll
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateMkdir
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
}
func
(
ad
*
AssetDir
)
Open
(
path
string
)
(
*
sftp
.
File
,
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
folderName
=
pathData
[
0
]
pathData
=
pathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
DownloadAction
)
{
return
nil
,
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
,
"/"
))
if
con
==
nil
{
return
nil
,
sftp
.
ErrSshFxConnectionLost
}
sf
,
err
:=
con
.
client
.
Open
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateDownaload
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
sf
,
err
}
func
(
ad
*
AssetDir
)
ReadDir
(
path
string
)
(
res
[]
os
.
FileInfo
,
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
for
folderName
:=
range
ad
.
suMaps
{
res
=
append
(
res
,
NewFakeFile
(
folderName
,
true
))
}
return
}
folderName
=
pathData
[
0
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
ConnectAction
)
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
[
1
:
],
"/"
))
if
con
==
nil
{
return
nil
,
sftp
.
ErrSshFxConnectionLost
}
res
,
err
=
con
.
client
.
ReadDir
(
realPath
)
if
!
ad
.
ShowHidden
{
noHiddenFiles
:=
make
([]
os
.
FileInfo
,
0
,
len
(
res
))
for
i
:=
0
;
i
<
len
(
res
);
i
++
{
if
!
strings
.
HasPrefix
(
res
[
i
]
.
Name
(),
"."
)
{
noHiddenFiles
=
append
(
noHiddenFiles
,
res
[
i
])
}
}
return
noHiddenFiles
,
err
}
return
}
func
(
ad
*
AssetDir
)
ReadLink
(
path
string
)
(
res
string
,
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
""
,
sftp
.
ErrSshFxOpUnsupported
}
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
folderName
=
pathData
[
0
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
""
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
ConnectAction
)
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
[
1
:
],
"/"
))
if
con
==
nil
{
return
""
,
sftp
.
ErrSshFxConnectionLost
}
res
,
err
=
con
.
client
.
ReadLink
(
realPath
)
return
}
func
(
ad
*
AssetDir
)
RemoveDirectory
(
path
string
)
(
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
folderName
=
pathData
[
0
]
pathData
=
pathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
UploadAction
)
{
return
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
,
"/"
))
if
con
==
nil
{
return
sftp
.
ErrSshFxConnectionLost
}
err
=
ad
.
removeDirectoryAll
(
con
.
client
,
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateRemoveDir
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
}
func
(
ad
*
AssetDir
)
Rename
(
oldNamePath
,
newNamePath
string
)
(
err
error
)
{
oldPathData
:=
ad
.
parsePath
(
oldNamePath
)
newPathData
:=
ad
.
parsePath
(
newNamePath
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
oldPathData
[
0
]
!=
newPathData
[
0
]
{
return
sftp
.
ErrSshFxNoSuchFile
}
folderName
=
oldPathData
[
0
]
oldPathData
=
oldPathData
[
1
:
]
newPathData
=
newPathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
conn1
,
oldRealPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
oldPathData
,
"/"
))
conn2
,
newRealPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
newPathData
,
"/"
))
if
conn1
!=
conn2
{
return
sftp
.
ErrSshFxOpUnsupported
}
err
=
conn1
.
client
.
Rename
(
oldRealPath
,
newRealPath
)
filename
:=
fmt
.
Sprintf
(
"%s=>%s"
,
oldRealPath
,
newRealPath
)
isSuccess
:=
false
operate
:=
model
.
OperateRename
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
}
func
(
ad
*
AssetDir
)
Remove
(
path
string
)
(
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
sftp
.
ErrSshFxPermissionDenied
}
folderName
=
pathData
[
0
]
pathData
=
pathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
UploadAction
)
{
return
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
,
"/"
))
if
con
==
nil
{
return
sftp
.
ErrSshFxConnectionLost
}
err
=
con
.
client
.
Remove
(
realPath
)
filename
:=
realPath
isSuccess
:=
false
operate
:=
model
.
OperateDelete
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
}
func
(
ad
*
AssetDir
)
Stat
(
path
string
)
(
res
os
.
FileInfo
,
err
error
)
{
pathData
:=
ad
.
parsePath
(
path
)
if
len
(
pathData
)
==
1
&&
pathData
[
0
]
==
""
{
return
ad
,
nil
}
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
folderName
=
pathData
[
0
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
nil
,
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
ConnectAction
)
{
return
res
,
sftp
.
ErrSshFxPermissionDenied
}
con
,
realPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
pathData
[
1
:
],
"/"
))
if
con
==
nil
{
return
nil
,
sftp
.
ErrSshFxConnectionLost
}
res
,
err
=
con
.
client
.
Stat
(
realPath
)
return
}
func
(
ad
*
AssetDir
)
Symlink
(
oldNamePath
,
newNamePath
string
)
(
err
error
)
{
oldPathData
:=
ad
.
parsePath
(
oldNamePath
)
newPathData
:=
ad
.
parsePath
(
newNamePath
)
folderName
,
ok
:=
ad
.
IsUniqueSu
()
if
!
ok
{
if
oldPathData
[
0
]
!=
newPathData
[
0
]
{
return
sftp
.
ErrSshFxNoSuchFile
}
folderName
=
oldPathData
[
0
]
oldPathData
=
oldPathData
[
1
:
]
newPathData
=
newPathData
[
1
:
]
}
su
,
ok
:=
ad
.
suMaps
[
folderName
]
if
!
ok
{
return
sftp
.
ErrSshFxNoSuchFile
}
if
!
ad
.
validatePermission
(
su
,
model
.
UploadAction
)
{
return
sftp
.
ErrSshFxPermissionDenied
}
conn1
,
oldRealPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
oldPathData
,
"/"
))
conn2
,
newRealPath
:=
ad
.
GetSFTPAndRealPath
(
su
,
strings
.
Join
(
newPathData
,
"/"
))
if
conn1
!=
conn2
{
return
sftp
.
ErrSshFxOpUnsupported
}
err
=
conn1
.
client
.
Symlink
(
oldRealPath
,
newRealPath
)
filename
:=
fmt
.
Sprintf
(
"%s=>%s"
,
oldRealPath
,
newRealPath
)
isSuccess
:=
false
operate
:=
model
.
OperateSymlink
if
err
==
nil
{
isSuccess
=
true
}
ad
.
CreateFTPLog
(
su
,
operate
,
filename
,
isSuccess
)
return
}
func
(
ad
*
AssetDir
)
removeDirectoryAll
(
conn
*
sftp
.
Client
,
path
string
)
error
{
var
err
error
var
files
[]
os
.
FileInfo
files
,
err
=
conn
.
ReadDir
(
path
)
if
err
!=
nil
{
return
err
}
for
_
,
item
:=
range
files
{
realPath
:=
filepath
.
Join
(
path
,
item
.
Name
())
if
item
.
IsDir
()
{
err
=
ad
.
removeDirectoryAll
(
conn
,
realPath
)
if
err
!=
nil
{
return
err
}
continue
}
err
=
conn
.
Remove
(
realPath
)
if
err
!=
nil
{
return
err
}
}
return
conn
.
RemoveDirectory
(
path
)
}
func
(
ad
*
AssetDir
)
GetSFTPAndRealPath
(
su
*
model
.
SystemUser
,
path
string
)
(
conn
*
SftpConn
,
realPath
string
)
{
var
ok
bool
conn
,
ok
=
ad
.
sftpClients
[
su
.
ID
]
if
!
ok
{
var
err
error
conn
,
err
=
ad
.
GetSftpClient
(
su
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get Sftp Client err: %s"
,
err
.
Error
())
return
nil
,
""
}
ad
.
sftpClients
[
su
.
ID
]
=
conn
}
switch
strings
.
ToLower
(
ad
.
RootPath
)
{
case
"home"
,
"~"
,
""
:
realPath
=
filepath
.
Join
(
conn
.
HomeDirPath
,
strings
.
TrimPrefix
(
path
,
"/"
))
default
:
realPath
=
filepath
.
Join
(
ad
.
RootPath
,
strings
.
TrimPrefix
(
path
,
"/"
))
}
return
}
func
(
ad
*
AssetDir
)
IsUniqueSu
()
(
folderName
string
,
ok
bool
)
{
sus
:=
ad
.
getSubFolderNames
()
if
len
(
sus
)
==
1
{
return
sus
[
0
],
true
}
return
}
func
(
ad
*
AssetDir
)
getSubFolderNames
()
[]
string
{
sus
:=
make
([]
string
,
0
,
len
(
ad
.
suMaps
))
for
folderName
:=
range
ad
.
suMaps
{
sus
=
append
(
sus
,
folderName
)
}
return
sus
}
func
(
ad
*
AssetDir
)
validatePermission
(
su
*
model
.
SystemUser
,
action
string
)
bool
{
for
_
,
pemAction
:=
range
su
.
Actions
{
if
pemAction
==
action
||
pemAction
==
model
.
AllAction
{
return
true
}
}
return
false
}
func
(
ad
*
AssetDir
)
GetSftpClient
(
su
*
model
.
SystemUser
)
(
conn
*
SftpConn
,
err
error
)
{
var
(
sshClient
*
SSHClient
ok
bool
)
if
ad
.
reuse
{
key
:=
MakeReuseSSHClientKey
(
ad
.
user
,
ad
.
asset
,
su
)
switch
su
.
Username
{
case
""
:
sshClient
,
ok
=
searchSSHClientFromCache
(
key
)
if
ok
{
su
.
Username
=
sshClient
.
username
}
default
:
sshClient
,
ok
=
GetClientFromCache
(
key
)
}
if
!
ok
{
sshClient
,
err
=
NewClient
(
ad
.
user
,
ad
.
asset
,
su
,
ad
.
Overtime
,
ad
.
reuse
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
}
}
else
{
logger
.
Infof
(
"Reuse connection for SFTP: %s->%s@%s. SSH client %p current ref: %d"
,
ad
.
user
.
Username
,
sshClient
.
username
,
ad
.
asset
.
IP
,
sshClient
,
sshClient
.
RefCount
())
}
}
else
{
sshClient
,
err
=
NewClient
(
ad
.
user
,
ad
.
asset
,
su
,
ad
.
Overtime
,
ad
.
reuse
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
}
}
sftpClient
,
err
:=
sftp
.
NewClient
(
sshClient
.
client
)
if
err
!=
nil
{
logger
.
Errorf
(
"SSH client %p start sftp client session err %s"
,
sshClient
,
err
)
RecycleClient
(
sshClient
)
return
nil
,
err
}
HomeDirPath
,
err
:=
sftpClient
.
Getwd
()
if
err
!=
nil
{
logger
.
Errorf
(
"SSH client %p get home dir err %s"
,
sshClient
,
err
)
_
=
sftpClient
.
Close
()
RecycleClient
(
sshClient
)
return
nil
,
err
}
logger
.
Infof
(
"SSH client %p start sftp client session success"
,
sshClient
)
conn
=
&
SftpConn
{
client
:
sftpClient
,
conn
:
sshClient
,
HomeDirPath
:
HomeDirPath
}
return
conn
,
err
}
func
(
ad
*
AssetDir
)
parsePath
(
path
string
)
[]
string
{
path
=
strings
.
TrimPrefix
(
path
,
"/"
)
return
strings
.
Split
(
path
,
"/"
)
}
func
(
ad
*
AssetDir
)
close
()
{
for
_
,
conn
:=
range
ad
.
sftpClients
{
if
conn
!=
nil
{
conn
.
Close
()
}
}
}
func
(
ad
*
AssetDir
)
CreateFTPLog
(
su
*
model
.
SystemUser
,
operate
,
filename
string
,
isSuccess
bool
)
{
data
:=
model
.
FTPLog
{
User
:
fmt
.
Sprintf
(
"%s (%s)"
,
ad
.
user
.
Name
,
ad
.
user
.
Username
),
Hostname
:
ad
.
asset
.
Hostname
,
OrgID
:
ad
.
asset
.
OrgID
,
SystemUser
:
su
.
Name
,
RemoteAddr
:
ad
.
addr
,
Operate
:
operate
,
Path
:
filename
,
DataStart
:
common
.
CurrentUTCTime
(),
IsSuccess
:
isSuccess
,
}
ad
.
logChan
<-
&
data
}
type
SftpConn
struct
{
HomeDirPath
string
client
*
sftp
.
Client
conn
*
SSHClient
}
func
(
s
*
SftpConn
)
Close
()
{
if
s
.
client
==
nil
{
return
}
_
=
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
()
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment