wings/api/sftp_endpoints.go

87 lines
2.2 KiB
Go

package api
import (
"encoding/json"
"github.com/apex/log"
"github.com/pkg/errors"
"regexp"
)
type SftpAuthRequest struct {
User string `json:"username"`
Pass string `json:"password"`
IP string `json:"ip"`
SessionID []byte `json:"session_id"`
ClientVersion []byte `json:"client_version"`
}
type SftpAuthResponse struct {
Server string `json:"server"`
Token string `json:"token"`
Permissions []string `json:"permissions"`
}
type sftpInvalidCredentialsError struct {
}
func (ice sftpInvalidCredentialsError) Error() string {
return "the credentials provided were invalid"
}
func IsInvalidCredentialsError(err error) bool {
_, ok := err.(*sftpInvalidCredentialsError)
return ok
}
// Usernames all follow the same format, so don't even bother hitting the API if the username is not
// at least in the expected format. This is very basic protection against random bots finding the SFTP
// server and sending a flood of usernames.
var validUsernameRegexp = regexp.MustCompile(`^(?i)(.+)\.([a-z0-9]{8})$`)
func (r *PanelRequest) ValidateSftpCredentials(request SftpAuthRequest) (*SftpAuthResponse, error) {
// If the username doesn't meet the expected format that the Panel would even recognize just go ahead
// and bail out of the process here to avoid accidentially brute forcing the panel if a bot decides
// to connect to spam username attempts.
if !validUsernameRegexp.MatchString(request.User) {
log.WithFields(log.Fields{
"subsystem": "sftp",
"username": request.User,
"ip": request.IP,
}).Warn("failed to validate user credentials (invalid format)")
return nil, new(sftpInvalidCredentialsError)
}
b, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := r.Post("/sftp/auth", b)
if err != nil {
return nil, err
}
defer resp.Body.Close()
r.Response = resp
if r.HasError() {
if r.HttpResponseCode() >= 400 && r.HttpResponseCode() < 500 {
return nil, new(sftpInvalidCredentialsError)
}
rerr := errors.New(r.Error().String())
return nil, rerr
}
response := new(SftpAuthResponse)
body, _ := r.ReadBody()
if err := json.Unmarshal(body, response); err != nil {
return nil, err
}
return response, nil
}