mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-15 21:04:46 +01:00
maintenance (server): backend refactoring
This commit is contained in:
parent
d3a5153920
commit
346cec3130
8 changed files with 110 additions and 103 deletions
6
server/common/constants.go
Normal file
6
server/common/constants.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package common
|
||||
|
||||
const (
|
||||
COOKIE_NAME = "auth"
|
||||
COOKIE_PATH = "/api/"
|
||||
)
|
||||
51
server/common/crypto.go
Normal file
51
server/common/crypto.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Encrypt(keystr string, text map[string]string) (string, error) {
|
||||
key := []byte(keystr)
|
||||
plaintext, err := json.Marshal(text)
|
||||
if err != nil {
|
||||
return "", NewError("json marshalling: "+err.Error(), 500)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", NewError("encryption issue (cipher): "+err.Error(), 500)
|
||||
}
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return "", NewError("encryption issue: "+err.Error(), 500)
|
||||
}
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
|
||||
return base64.URLEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func Decrypt(keystr string, cryptoText string) (map[string]string, error) {
|
||||
var raw map[string]string
|
||||
|
||||
key := []byte(keystr)
|
||||
ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
if err != nil || len(ciphertext) < aes.BlockSize {
|
||||
return raw, NewError("Cipher is too short", 500)
|
||||
}
|
||||
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
|
||||
json.Unmarshal(ciphertext, &raw)
|
||||
return raw, nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package router
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -27,25 +27,25 @@ type APIErrorMessage struct {
|
|||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func sendSuccessResult(res http.ResponseWriter, data interface{}) {
|
||||
func SendSuccessResult(res http.ResponseWriter, data interface{}) {
|
||||
encoder := json.NewEncoder(res)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.Encode(APISuccessResult{"ok", data})
|
||||
}
|
||||
|
||||
func sendSuccessResults(res http.ResponseWriter, data interface{}) {
|
||||
func SendSuccessResults(res http.ResponseWriter, data interface{}) {
|
||||
encoder := json.NewEncoder(res)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.Encode(APISuccessResults{"ok", data})
|
||||
}
|
||||
|
||||
func sendSuccessResultsWithMetadata(res http.ResponseWriter, data interface{}, p interface{}) {
|
||||
func SendSuccessResultsWithMetadata(res http.ResponseWriter, data interface{}, p interface{}) {
|
||||
encoder := json.NewEncoder(res)
|
||||
encoder.SetEscapeHTML(false)
|
||||
encoder.Encode(APISuccessResultsWithMetadata{"ok", data, p})
|
||||
}
|
||||
|
||||
func sendErrorResult(res http.ResponseWriter, err error) {
|
||||
func SendErrorResult(res http.ResponseWriter, err error) {
|
||||
encoder := json.NewEncoder(res)
|
||||
encoder.SetEscapeHTML(false)
|
||||
obj, ok := err.(interface{ Status() int })
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package router
|
||||
package ctrl
|
||||
|
||||
import (
|
||||
. "github.com/mickael-kerjean/nuage/server/common"
|
||||
|
|
@ -20,13 +20,13 @@ type FileInfo struct {
|
|||
func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
entries, err := ctx.Backend.Ls(path)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -52,13 +52,13 @@ func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
if obj, ok := ctx.Backend.(interface{ Meta(path string) *Metadata }); ok {
|
||||
perms = obj.Meta(path)
|
||||
}
|
||||
sendSuccessResultsWithMetadata(res, files, perms)
|
||||
SendSuccessResultsWithMetadata(res, files, perms)
|
||||
}
|
||||
|
||||
func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
defer obj.Close()
|
||||
}
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
|
||||
file, err = services.ProcessFileBeforeSend(file, &ctx, req, &res)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
io.Copy(res, file)
|
||||
|
|
@ -89,13 +89,13 @@ func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
func FileSave(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
file, _, err := req.FormFile("file")
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
|
@ -105,78 +105,78 @@ func FileSave(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
obj.Close()
|
||||
}
|
||||
if err != nil {
|
||||
sendErrorResult(res, NewError(err.Error(), 403))
|
||||
SendErrorResult(res, NewError(err.Error(), 403))
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func FileMv(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
from, err := pathBuilder(ctx, req.URL.Query().Get("from"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
to, err := pathBuilder(ctx, req.URL.Query().Get("to"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
if from == "" || to == "" {
|
||||
sendErrorResult(res, NewError("missing path parameter", 400))
|
||||
SendErrorResult(res, NewError("missing path parameter", 400))
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.Backend.Mv(from, to)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func FileRm(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
err = ctx.Backend.Rm(path)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func FileMkdir(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.Backend.Mkdir(path)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func FileTouch(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
path, err := pathBuilder(ctx, req.URL.Query().Get("path"))
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.Backend.Touch(path)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func pathBuilder(ctx App, path string) (string, error) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package router
|
||||
package ctrl
|
||||
|
||||
import (
|
||||
"github.com/mickael-kerjean/mux"
|
||||
|
|
@ -8,33 +8,28 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
COOKIE_NAME = "auth"
|
||||
COOKIE_PATH = "/api/"
|
||||
)
|
||||
|
||||
func SessionIsValid(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
if ctx.Backend == nil {
|
||||
sendSuccessResult(res, false)
|
||||
SendSuccessResult(res, false)
|
||||
return
|
||||
}
|
||||
if _, err := ctx.Backend.Ls("/"); err != nil {
|
||||
sendSuccessResult(res, false)
|
||||
SendSuccessResult(res, false)
|
||||
return
|
||||
}
|
||||
home, _ := model.GetHome(ctx.Backend)
|
||||
if home == "" {
|
||||
sendSuccessResult(res, true)
|
||||
SendSuccessResult(res, true)
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, true)
|
||||
SendSuccessResult(res, true)
|
||||
}
|
||||
|
||||
func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
ctx.Body["timestamp"] = time.Now().String()
|
||||
backend, err := model.NewBackend(&ctx, ctx.Body)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -43,25 +38,25 @@ func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
}); ok {
|
||||
err := obj.OAuthToken(&ctx.Body)
|
||||
if err != nil {
|
||||
sendErrorResult(res, NewError("Can't authenticate (OAuth error)", 401))
|
||||
SendErrorResult(res, NewError("Can't authenticate (OAuth error)", 401))
|
||||
return
|
||||
}
|
||||
backend, err = model.NewBackend(&ctx, ctx.Body)
|
||||
if err != nil {
|
||||
sendErrorResult(res, NewError("Can't authenticate", 401))
|
||||
SendErrorResult(res, NewError("Can't authenticate", 401))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
home, err := model.GetHome(backend)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
|
||||
obfuscate, err := encrypt(ctx.Config.General.SecretKey, ctx.Body)
|
||||
obfuscate, err := Encrypt(ctx.Config.General.SecretKey, ctx.Body)
|
||||
if err != nil {
|
||||
sendErrorResult(res, NewError(err.Error(), 500))
|
||||
SendErrorResult(res, NewError(err.Error(), 500))
|
||||
return
|
||||
}
|
||||
cookie := http.Cookie{
|
||||
|
|
@ -74,9 +69,9 @@ func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
http.SetCookie(res, &cookie)
|
||||
|
||||
if home == "" {
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
} else {
|
||||
sendSuccessResult(res, home)
|
||||
SendSuccessResult(res, home)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +89,7 @@ func SessionLogout(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
http.SetCookie(res, &cookie)
|
||||
sendSuccessResult(res, nil)
|
||||
SendSuccessResult(res, nil)
|
||||
}
|
||||
|
||||
func SessionOAuthBackend(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
|
|
@ -104,13 +99,13 @@ func SessionOAuthBackend(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
b, err := model.NewBackend(&ctx, a)
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
SendErrorResult(res, err)
|
||||
return
|
||||
}
|
||||
obj, ok := b.(interface{ OAuthURL() string })
|
||||
if ok == false {
|
||||
sendErrorResult(res, NewError("No backend authentication ("+b.Info()+")", 500))
|
||||
SendErrorResult(res, NewError("No backend authentication ("+b.Info()+")", 500))
|
||||
return
|
||||
}
|
||||
sendSuccessResult(res, obj.OAuthURL())
|
||||
SendSuccessResult(res, obj.OAuthURL())
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import (
|
|||
func ConfigHandler(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
c, err := ctx.Config.Export()
|
||||
if err != nil {
|
||||
sendErrorResult(res, err)
|
||||
res.Write([]byte("window.CONFIG = {}"))
|
||||
return
|
||||
}
|
||||
res.Write([]byte("window.CONFIG = "))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package router
|
|||
import (
|
||||
"github.com/mickael-kerjean/mux"
|
||||
. "github.com/mickael-kerjean/nuage/server/common"
|
||||
. "github.com/mickael-kerjean/nuage/server/ctrl"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
|
@ -28,8 +29,8 @@ func Init(a *App) *http.Server {
|
|||
|
||||
share := r.PathPrefix("/api/share").Subrouter()
|
||||
share.HandleFunc("", APIHandler(ShareList, *a)).Methods("GET")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("POST")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareInsert, *a)).Methods("DELETE")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareUpsert, *a)).Methods("POST")
|
||||
share.HandleFunc("/{id}", APIHandler(ShareDelete, *a)).Methods("DELETE")
|
||||
|
||||
r.HandleFunc("/api/config", CtxInjector(ConfigHandler, *a))
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,9 @@ package router
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
. "github.com/mickael-kerjean/nuage/server/common"
|
||||
"github.com/mickael-kerjean/nuage/server/model"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
|
@ -42,7 +37,7 @@ func APIHandler(fn func(App, http.ResponseWriter, *http.Request), ctx App) http.
|
|||
func LoggedInOnly(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
return func(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
if ctx.Backend == nil || ctx.Session == nil {
|
||||
sendErrorResult(res, NewError("Forbidden", 403))
|
||||
SendErrorResult(res, NewError("Forbidden", 403))
|
||||
return
|
||||
}
|
||||
fn(ctx, res, req)
|
||||
|
|
@ -75,7 +70,7 @@ func extractSession(req *http.Request, ctx *App) (map[string]string, error) {
|
|||
if err != nil {
|
||||
return make(map[string]string), err
|
||||
}
|
||||
return decrypt(ctx.Config.General.SecretKey, cookie.Value)
|
||||
return Decrypt(ctx.Config.General.SecretKey, cookie.Value)
|
||||
}
|
||||
|
||||
func extractBackend(req *http.Request, ctx *App) (IBackend, error) {
|
||||
|
|
@ -120,47 +115,6 @@ func logPoint(req *http.Request, res *ResponseWriter, start time.Time, backendTy
|
|||
}
|
||||
}
|
||||
|
||||
func encrypt(keystr string, text map[string]string) (string, error) {
|
||||
key := []byte(keystr)
|
||||
plaintext, err := json.Marshal(text)
|
||||
if err != nil {
|
||||
return "", NewError("json marshalling: "+err.Error(), 500)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", NewError("encryption issue (cipher): "+err.Error(), 500)
|
||||
}
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return "", NewError("encryption issue: "+err.Error(), 500)
|
||||
}
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
|
||||
return base64.URLEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func decrypt(keystr string, cryptoText string) (map[string]string, error) {
|
||||
var raw map[string]string
|
||||
|
||||
key := []byte(keystr)
|
||||
ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
if err != nil || len(ciphertext) < aes.BlockSize {
|
||||
return raw, NewError("Cipher is too short", 500)
|
||||
}
|
||||
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
|
||||
json.Unmarshal(ciphertext, &raw)
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
|
|
|
|||
Loading…
Reference in a new issue