This commit is contained in:
33
api/http.go
33
api/http.go
@@ -18,6 +18,8 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const multipartMaxMemory = 1024 * 1024 * 100
|
||||
|
||||
type HTTPServer struct {
|
||||
Files files.FileStore
|
||||
Users users.UserStore
|
||||
@@ -41,8 +43,8 @@ func NewHTTPServer(cfg *gpaste.ServerConfig) *HTTPServer {
|
||||
// Create initial user
|
||||
// TODO: Do properly
|
||||
user := &users.User{Username: "admin", Role: users.RoleAdmin}
|
||||
user.SetPassword("admin")
|
||||
srv.Users.Store(user)
|
||||
_ = user.SetPassword("admin")
|
||||
_ = srv.Users.Store(user)
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Use(middleware.RealIP)
|
||||
@@ -82,9 +84,12 @@ func (s *HTTPServer) HandlerAPIFilePost(w http.ResponseWriter, r *http.Request)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
s.Logger.Warnw("Error storing file.", "req_id", reqID, "error", err, "id", f.ID, "remote_addr", r.RemoteAddr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s.Logger.Infow("Stored file.", "req_id", reqID, "id", f.ID, "remote_addr", r.RemoteAddr)
|
||||
|
||||
fileURL := path.Join(s.config.URL, "/api/file", f.ID)
|
||||
resp := &ResponseAPIFilePost{
|
||||
Message: "OK",
|
||||
@@ -95,7 +100,9 @@ func (s *HTTPServer) HandlerAPIFilePost(w http.ResponseWriter, r *http.Request)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(&resp); err != nil {
|
||||
s.Logger.Warnw("Error encoding response to client.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
@@ -117,6 +124,7 @@ func (s *HTTPServer) HandlerAPIFileGet(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if _, err := io.Copy(w, f.Body); err != nil {
|
||||
reqID := middleware.GetReqID(r.Context())
|
||||
s.Logger.Warnw("Error writing file to client.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
@@ -136,6 +144,7 @@ func (s *HTTPServer) HandlerAPIFileDelete(w http.ResponseWriter, r *http.Request
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
reqID := middleware.GetReqID(r.Context())
|
||||
s.Logger.Infow("Deleted file", "id", id, "req_id", reqID)
|
||||
}
|
||||
@@ -145,15 +154,17 @@ func (s *HTTPServer) processMultiPartFormUpload(w http.ResponseWriter, r *http.R
|
||||
|
||||
var resp ResponseAPIFilePost
|
||||
|
||||
if err := r.ParseMultipartForm(1024 * 1024 * 10); err != nil {
|
||||
if err := r.ParseMultipartForm(multipartMaxMemory); err != nil {
|
||||
s.Logger.Warnw("Error parsing multipart form.", "req_id", reqID, "err", err)
|
||||
}
|
||||
|
||||
for k := range r.MultipartForm.File {
|
||||
ff, fh, err := r.FormFile(k)
|
||||
if err != nil {
|
||||
s.Logger.Warnw("Error reading file from multipart form.", "req_id", reqID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
f := fileFromParams(r)
|
||||
f.ID = uuid.NewString()
|
||||
f.OriginalFilename = fh.Filename
|
||||
@@ -162,18 +173,20 @@ func (s *HTTPServer) processMultiPartFormUpload(w http.ResponseWriter, r *http.R
|
||||
if err := s.Files.Store(f); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
s.Logger.Warnw("Error storing file.", "req_id", reqID, "error", err, "id", f.ID, "remote_addr", r.RemoteAddr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s.Logger.Infow("Stored file.", "req_id", reqID, "id", f.ID, "filename", f.OriginalFilename, "remote_addr", r.RemoteAddr)
|
||||
|
||||
fileURL := path.Join(s.config.URL, "/api/file", f.ID)
|
||||
fileResponse := ResponseAPIFilePostFiles{ID: f.ID, URL: fileURL}
|
||||
resp.Files = append(resp.Files, fileResponse)
|
||||
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
encoder := json.NewEncoder(w)
|
||||
|
||||
if err := encoder.Encode(&resp); err != nil {
|
||||
s.Logger.Warnw("Error encoding response to client.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
}
|
||||
@@ -181,9 +194,12 @@ func (s *HTTPServer) processMultiPartFormUpload(w http.ResponseWriter, r *http.R
|
||||
|
||||
func (s *HTTPServer) HandlerAPILogin(w http.ResponseWriter, r *http.Request) {
|
||||
reqID := middleware.GetReqID(r.Context())
|
||||
|
||||
var expectedRequest RequestAPILogin
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
defer r.Body.Close()
|
||||
|
||||
if err := decoder.Decode(&expectedRequest); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
@@ -218,10 +234,12 @@ func (s *HTTPServer) HandlerAPIUserCreate(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
var req RequestAPIUserCreate
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
s.Logger.Debugw("Error parsing request.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -230,14 +248,17 @@ func (s *HTTPServer) HandlerAPIUserCreate(w http.ResponseWriter, r *http.Request
|
||||
if err := user.SetPassword(req.Password); err != nil {
|
||||
s.Logger.Warnw("Error setting user password.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.Users.Store(user); err != nil {
|
||||
s.Logger.Warnw("Error setting user password.", "req_id", reqID, "error", err, "remote_addr", r.RemoteAddr)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
s.Logger.Infow("Created user.", "req_id", reqID, "remote_addr", r.RemoteAddr, "username", req.Username)
|
||||
}
|
||||
@@ -249,6 +270,7 @@ func (s *HTTPServer) HandlerAPIUserList(w http.ResponseWriter, r *http.Request)
|
||||
if err != nil {
|
||||
s.Logger.Warnw("Error listing users.", "req_id", reqID, "error", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -263,12 +285,13 @@ func fileFromParams(r *http.Request) *files.File {
|
||||
keyMaxViews = "max_views"
|
||||
keyExpiresOn = "exp"
|
||||
)
|
||||
|
||||
var f files.File
|
||||
|
||||
q := r.URL.Query()
|
||||
|
||||
if q.Has(keyMaxViews) {
|
||||
views, err := strconv.ParseUint(q.Get(keyMaxViews), 10, 64)
|
||||
views, err := strconv.ParseUint(q.Get(keyMaxViews), 10, 64) // nolint: gomnd
|
||||
if err == nil {
|
||||
f.MaxViews = uint(views)
|
||||
}
|
||||
|
@@ -39,31 +39,37 @@ func (s *HTTPServer) MiddlewareAccessLogger(next http.Handler) http.Handler {
|
||||
|
||||
next.ServeHTTP(ww, r)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
func (s *HTTPServer) MiddlewareAuthentication(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
reqID := middleware.GetReqID(r.Context())
|
||||
|
||||
header := r.Header.Get("Authorization")
|
||||
if header == "" {
|
||||
s.Logger.Debugw("Request has no auth header.", "req_id", reqID)
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
splitHeader := strings.Split(header, "Bearer ")
|
||||
if len(splitHeader) != 2 {
|
||||
if len(splitHeader) != 2 { // nolint: gomnd
|
||||
s.Logger.Debugw("Request has invalid token.", "req_id", reqID)
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
token := splitHeader[1]
|
||||
|
||||
claims, err := s.Auth.ValidateToken(token)
|
||||
if err != nil {
|
||||
s.Logger.Debugw("Request has invalid token.", "req_id", reqID)
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,6 +77,7 @@ func (s *HTTPServer) MiddlewareAuthentication(next http.Handler) http.Handler {
|
||||
ctx = context.WithValue(ctx, authCtxAuthLevel, claims.Role)
|
||||
ctx = context.WithValue(ctx, authCtxClaims, claims)
|
||||
withCtx := r.WithContext(ctx)
|
||||
|
||||
s.Logger.Debugw("Request is authenticated.", "req_id", reqID, "username", claims.Subject, "role", claims.Role)
|
||||
|
||||
next.ServeHTTP(w, withCtx)
|
||||
@@ -84,10 +91,12 @@ func UsernameFromRequest(r *http.Request) (string, error) {
|
||||
if rawUsername == nil {
|
||||
return "", fmt.Errorf("no username")
|
||||
}
|
||||
|
||||
username, ok := rawUsername.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no username")
|
||||
}
|
||||
|
||||
return username, nil
|
||||
}
|
||||
|
||||
@@ -96,10 +105,12 @@ func RoleFromRequest(r *http.Request) (users.Role, error) {
|
||||
if rawLevel == nil {
|
||||
return users.RoleUnset, fmt.Errorf("no username")
|
||||
}
|
||||
|
||||
level, ok := rawLevel.(users.Role)
|
||||
if !ok {
|
||||
return users.RoleUnset, fmt.Errorf("no username")
|
||||
}
|
||||
|
||||
return level, nil
|
||||
}
|
||||
|
||||
@@ -108,9 +119,11 @@ func ClaimsFromRequest(r *http.Request) *gpaste.Claims {
|
||||
if rawClaims == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
claims, ok := rawClaims.(*gpaste.Claims)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return claims
|
||||
}
|
||||
|
Reference in New Issue
Block a user