maintenance (server): backend refactoring

This commit is contained in:
Mickael Kerjean 2018-09-01 02:45:51 +10:00 committed by Mickael KERJEAN
parent d3a5153920
commit 346cec3130
8 changed files with 110 additions and 103 deletions

View file

@ -0,0 +1,6 @@
package common
const (
COOKIE_NAME = "auth"
COOKIE_PATH = "/api/"
)

51
server/common/crypto.go Normal file
View 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
}

View file

@ -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 })

View file

@ -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) {

View file

@ -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())
}

View file

@ -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 = "))

View file

@ -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))

View file

@ -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