2021-12-19 17:30:51 +01:00

124 lines
2.7 KiB
Go

package api
import (
"bytes"
"docker4ssh/config"
"docker4ssh/ssh"
"encoding/json"
"fmt"
"go.uber.org/zap"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
)
type EndpointHandler struct {
http.Handler
auth bool
get func(http.ResponseWriter, *http.Request, *ssh.User) (interface{}, int)
post func(http.ResponseWriter, *http.Request, *ssh.User) (interface{}, int)
}
func (h *EndpointHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.RemoteAddr, ":")[0]
zap.S().Infof("User connected to api with remote address %s", ip)
w.Header().Add("Content-Type", "application/json")
user := ssh.GetUser(ip)
// checks if auth should be checked and if so and no user could be found, response an error
if h.auth && user == nil {
zap.S().Errorf("Could not find api user with ip %s", ip)
json.NewEncoder(w).Encode(APIError{Message: "unauthorized"})
return
} else if user != nil {
zap.S().Debugf("API ip %s is %s", ip, user.ID)
}
raw := bytes.Buffer{}
if r.ContentLength > 0 {
io.Copy(&raw, r.Body)
defer r.Body.Close()
if !json.Valid(raw.Bytes()) {
zap.S().Errorf("API user %s sent invalid body", ip)
w.WriteHeader(http.StatusNotAcceptable)
json.NewEncoder(w).Encode(APIError{Message: "invalid body"})
return
}
r.Body = ioutil.NopCloser(&raw)
}
zap.S().Debugf("API user %s request - \"%s %s %s\" \"%s\" \"%s\"", ip, r.Method, r.URL.Path, r.Proto, r.UserAgent(), raw.String())
var response interface{}
var code int
switch r.Method {
case http.MethodGet:
if h.get != nil {
response, code = h.get(w, r, user)
}
case http.MethodPost:
if h.post != nil {
response, code = h.post(w, r, user)
}
}
if response == nil && code == 0 {
zap.S().Infof("API user %s sent invalid method: %s", ip, r.Method)
response = APIError{Message: fmt.Sprintf("invalid method '%s'", r.Method)}
code = http.StatusConflict
} else {
zap.S().Infof("API user %s issued %s successfully", ip, r.URL.Path)
}
w.WriteHeader(code)
if response != nil {
json.NewEncoder(w).Encode(response)
}
}
func ServeAPI(config *config.Config) (errChan chan error, closer func() error) {
errChan = make(chan error, 1)
mux := http.NewServeMux()
mux.Handle("/ping", &EndpointHandler{
get: PingGet,
})
mux.Handle("/error", &EndpointHandler{
get: ErrorGet,
})
mux.Handle("/info", &EndpointHandler{
get: InfoGet,
auth: true,
})
mux.Handle("/config", &EndpointHandler{
get: ConfigGet,
post: ConfigPost,
auth: true,
})
mux.Handle("/auth", &EndpointHandler{
get: AuthGet,
post: AuthPost,
auth: true,
})
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", config.Api.Port))
if err != nil {
errChan <- err
return
}
go func() {
errChan <- http.Serve(listener, mux)
}()
return errChan, listener.Close
}