2020-03-04 11:07:12 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2021-11-18 11:12:28 +00:00
|
|
|
"time"
|
2020-03-04 11:07:12 +00:00
|
|
|
|
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
)
|
|
|
|
|
|
|
|
var router = httprouter.New()
|
|
|
|
|
|
|
|
func Router() *httprouter.Router {
|
|
|
|
return router
|
|
|
|
}
|
|
|
|
|
|
|
|
type HTTPResponse interface {
|
|
|
|
WriteResponse(http.ResponseWriter)
|
|
|
|
}
|
|
|
|
|
|
|
|
type APIResponse struct {
|
|
|
|
response interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r APIResponse) WriteResponse(w http.ResponseWriter) {
|
|
|
|
if str, found := r.response.(string); found {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
io.WriteString(w, str)
|
|
|
|
} else if bts, found := r.response.([]byte); found {
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
w.Header().Set("Content-Disposition", "attachment")
|
|
|
|
w.Header().Set("Content-Transfer-Encoding", "binary")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write(bts)
|
|
|
|
} else if j, err := json.Marshal(r.response); err != nil {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-03-08 00:06:44 +00:00
|
|
|
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", err.Error()), http.StatusInternalServerError)
|
2020-03-04 11:07:12 +00:00
|
|
|
} else {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write(j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type APIErrorResponse struct {
|
|
|
|
status int
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r APIErrorResponse) WriteResponse(w http.ResponseWriter) {
|
|
|
|
if r.status == 0 {
|
|
|
|
r.status = http.StatusBadRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}", r.err.Error()), r.status)
|
|
|
|
}
|
|
|
|
|
|
|
|
type DispatchFunction func(httprouter.Params, []byte) HTTPResponse
|
|
|
|
|
2021-11-18 11:12:28 +00:00
|
|
|
func eraseCookie(w http.ResponseWriter) {
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
|
|
Name: "auth",
|
|
|
|
Value: "",
|
|
|
|
Path: baseURL + "/",
|
|
|
|
Expires: time.Unix(0, 0),
|
|
|
|
HttpOnly: true,
|
|
|
|
SameSite: http.SameSiteStrictMode,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-04 11:07:12 +00:00
|
|
|
func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
2022-02-28 18:00:30 +00:00
|
|
|
return rawAuthHandler(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, _ *User, body []byte) {
|
|
|
|
f(w, r, ps, body)
|
|
|
|
}, access...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func rawAuthHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, *User, []byte), access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
2020-03-04 11:07:12 +00:00
|
|
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|
|
|
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
|
|
|
r.RemoteAddr = addr
|
|
|
|
}
|
|
|
|
log.Printf("%s \"%s %s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
|
|
|
|
|
|
|
|
// Read Authorization header
|
|
|
|
var user *User = nil
|
|
|
|
if cookie, err := r.Cookie("auth"); err == nil {
|
|
|
|
if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
2021-11-18 11:12:28 +00:00
|
|
|
eraseCookie(w)
|
2020-03-04 11:07:12 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-03-08 00:06:44 +00:00
|
|
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusNotAcceptable)
|
2020-03-04 11:07:12 +00:00
|
|
|
return
|
|
|
|
} else if session, err := getSession(sessionid); err != nil {
|
2021-11-18 11:12:28 +00:00
|
|
|
eraseCookie(w)
|
2020-03-04 11:07:12 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-03-08 00:06:44 +00:00
|
|
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusUnauthorized)
|
2020-03-04 11:07:12 +00:00
|
|
|
return
|
|
|
|
} else if session.IdUser == nil {
|
|
|
|
user = nil
|
|
|
|
} else if std, err := getUser(int(*session.IdUser)); err != nil {
|
2021-11-18 11:12:28 +00:00
|
|
|
eraseCookie(w)
|
2020-03-04 11:07:12 +00:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-03-08 00:06:44 +00:00
|
|
|
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err.Error()), http.StatusUnauthorized)
|
2020-03-04 11:07:12 +00:00
|
|
|
return
|
|
|
|
} else {
|
|
|
|
user = &std
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check access limitation
|
|
|
|
for _, a := range access {
|
|
|
|
if err := a(user, r); err != nil {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-03-08 00:06:44 +00:00
|
|
|
http.Error(w, fmt.Sprintf(`{"errmsg":%q}`, err.err.Error()), http.StatusForbidden)
|
2020-03-04 11:07:12 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the body
|
|
|
|
if r.ContentLength < 0 || r.ContentLength > 6553600 {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
http.Error(w, "{errmsg:\"Request too large or request size unknown\"}", http.StatusRequestEntityTooLarge)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var body []byte
|
|
|
|
if r.ContentLength > 0 {
|
|
|
|
tmp := make([]byte, 1024)
|
|
|
|
for {
|
|
|
|
n, err := r.Body.Read(tmp)
|
|
|
|
for j := 0; j < n; j++ {
|
|
|
|
body = append(body, tmp[j])
|
|
|
|
}
|
|
|
|
if err != nil || n <= 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 18:00:30 +00:00
|
|
|
f(w, r, ps, user, body)
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatResponseHandler(f func(*http.Request, httprouter.Params, []byte) HTTPResponse) func(http.ResponseWriter, *http.Request, httprouter.Params, []byte) {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, body []byte) {
|
|
|
|
f(r, ps, body).WriteResponse(w)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) HTTPResponse, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
2021-11-18 11:12:28 +00:00
|
|
|
return rawHandler(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) {
|
|
|
|
formatResponseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) HTTPResponse {
|
2020-03-04 11:07:12 +00:00
|
|
|
return f(w, ps, b)
|
|
|
|
})(w, r, ps, b)
|
|
|
|
}, access...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func apiHandler(f DispatchFunction, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
2021-11-18 11:12:28 +00:00
|
|
|
return rawHandler(formatResponseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) HTTPResponse { return f(ps, b) }), access...)
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func formatApiResponse(i interface{}, err error) HTTPResponse {
|
|
|
|
if err != nil {
|
|
|
|
return APIErrorResponse{
|
|
|
|
status: http.StatusBadRequest,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: err,
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return APIResponse{i}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func apiAuthHandler(f func(*User, httprouter.Params, []byte) HTTPResponse, access ...func(*User, *http.Request) *APIErrorResponse) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
2021-11-18 11:12:28 +00:00
|
|
|
return rawHandler(formatResponseHandler(func(r *http.Request, ps httprouter.Params, b []byte) HTTPResponse {
|
2020-03-04 11:07:12 +00:00
|
|
|
if cookie, err := r.Cookie("auth"); err != nil {
|
2020-03-08 00:06:44 +00:00
|
|
|
return f(nil, ps, b)
|
2020-03-04 11:07:12 +00:00
|
|
|
} else if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
|
|
|
return APIErrorResponse{
|
|
|
|
status: http.StatusBadRequest,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: err,
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
} else if session, err := getSession(sessionid); err != nil {
|
|
|
|
return APIErrorResponse{
|
|
|
|
status: http.StatusBadRequest,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: err,
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
} else if session.IdUser == nil {
|
2020-03-08 00:06:44 +00:00
|
|
|
return f(nil, ps, b)
|
2020-03-04 11:07:12 +00:00
|
|
|
} else if std, err := getUser(int(*session.IdUser)); err != nil {
|
|
|
|
return APIErrorResponse{
|
|
|
|
status: http.StatusInternalServerError,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: err,
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return f(&std, ps, b)
|
|
|
|
}
|
|
|
|
}), access...)
|
|
|
|
}
|
|
|
|
|
2020-03-08 00:06:44 +00:00
|
|
|
func loggedUser(u *User, r *http.Request) *APIErrorResponse {
|
|
|
|
if u != nil {
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
ret := &APIErrorResponse{
|
|
|
|
status: http.StatusForbidden,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: errors.New("Permission Denied"),
|
2020-03-08 00:06:44 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 11:07:12 +00:00
|
|
|
func adminRestricted(u *User, r *http.Request) *APIErrorResponse {
|
|
|
|
if u != nil && u.IsAdmin {
|
|
|
|
return nil
|
|
|
|
} else {
|
|
|
|
ret := &APIErrorResponse{
|
|
|
|
status: http.StatusForbidden,
|
2021-11-18 11:12:28 +00:00
|
|
|
err: errors.New("Permission Denied"),
|
2020-03-04 11:07:12 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
}
|