Skip to content

Commit

Permalink
feat: Webdav server
Browse files Browse the repository at this point in the history
  • Loading branch information
pluveto committed Feb 4, 2023
1 parent 6b60521 commit 0416383
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 84 deletions.
9 changes: 5 additions & 4 deletions cmd/flydav/app/args.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package app

type Args struct {
Names []string `arg:"positional,required" help:"names to flydav"`
Seperately bool `arg:"-s,--seperately" help:"flydav each name seperately" default:"false"`
Verbose bool `arg:"-v,--verbose" help:"verbose output" default:"false"`
Config string `arg:"-c,--config" help:"config file" default:"config.toml"`
Host string `arg:"-H,--host" help:"host address"`
Port int `arg:"-p,--port" help:"port"`
Username string `arg:"-u,--user" help:"username"`
Verbose bool `arg:"-v,--verbose" help:"verbose output"`
Config string `arg:"-c,--config" help:"config file"`
}
22 changes: 22 additions & 0 deletions cmd/flydav/app/entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app

import (
"fmt"

"github.com/pluveto/flydav/cmd/flydav/conf"
"github.com/pluveto/flydav/cmd/flydav/service"
)

func Run(conf conf.Conf) {

fmt.Println("Serving on: ", fmt.Sprintf("%s:%d", conf.Server.Host, conf.Server.Port))
fmt.Println("Username: ", conf.Auth.User[0].Username)
// fmt.Println("Password(Encrypted): ", conf.Auth.User[0].PasswordHash)

server := &WebdavServer{
AuthService: &service.BasicAuthService{Users: conf.Auth.User},
Host: conf.Server.Host,
Port: conf.Server.Port,
}
server.Listen()
}
25 changes: 0 additions & 25 deletions cmd/flydav/app/handler.go

This file was deleted.

45 changes: 34 additions & 11 deletions cmd/flydav/app/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import (
"os"

"github.com/natefinch/lumberjack"
"github.com/pluveto/flydav/cmd/flydav/conf"
"github.com/pluveto/flydav/pkg/logger"
"github.com/sirupsen/logrus"
)

func InitLogger(conf Log, verbose bool) {
newLoggerCount := len(conf.Stdout) + len(conf.File)
func InitLogger(cnf conf.Log, verbose bool) {
newLoggerCount := len(cnf.Stdout) + len(cnf.File)
if newLoggerCount != 0 {
for i := 0; i < newLoggerCount; i++ {
logger.AddLogger(logrus.New())
Expand All @@ -22,33 +23,33 @@ func InitLogger(conf Log, verbose bool) {
// enable source code line numbers
logger.SetReportCaller(true)
} else {
logger.SetLevel(levelToLogrusLevel(conf.Level))
logger.SetLevel(levelToLogrusLevel(cnf.Level))
}

for _, stdout := range conf.Stdout {
for _, stdout := range cnf.Stdout {
currentLogger := logger.DefaultCombinedLogger.GetLogger(nextLoggerIndex)
switch stdout.Format {
case LogFormatJSON:
case conf.LogFormatJSON:
currentLogger.SetFormatter(&logrus.JSONFormatter{})
case LogFormatText:
case conf.LogFormatText:
currentLogger.SetFormatter(&logrus.TextFormatter{})
}
switch stdout.Output {
case LogOutputStdout:
case conf.LogOutputStdout:
currentLogger.SetOutput(os.Stdout)
case LogOutputStderr:
case conf.LogOutputStderr:
currentLogger.SetOutput(os.Stderr)
}
nextLoggerIndex++
}

for _, file := range conf.File {
for _, file := range cnf.File {
currentLogger := logger.DefaultCombinedLogger.GetLogger(nextLoggerIndex)

switch file.Format {
case LogFormatJSON:
case conf.LogFormatJSON:
currentLogger.SetFormatter(&logrus.JSONFormatter{})
case LogFormatText:
case conf.LogFormatText:
currentLogger.SetFormatter(&logrus.TextFormatter{})
}
currentLogger.SetOutput(&lumberjack.Logger{
Expand All @@ -62,3 +63,25 @@ func InitLogger(conf Log, verbose bool) {
}

}

// levelToLogrusLevel converts a string to a logrus.Level
func levelToLogrusLevel(level string) logrus.Level {
switch level {
case "debug":
return logrus.DebugLevel
case "info":
return logrus.InfoLevel
case "warn":
return logrus.WarnLevel
case "warning":
return logrus.WarnLevel
case "error":
return logrus.ErrorLevel
case "fatal":
return logrus.FatalLevel
case "panic":
return logrus.PanicLevel
default:
return logrus.InfoLevel
}
}
74 changes: 74 additions & 0 deletions cmd/flydav/app/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package app

import (
"fmt"
"net/http"
"path"
"strings"

"github.com/pluveto/flydav/pkg/logger"
"github.com/sirupsen/logrus"
"golang.org/x/net/webdav"
)

type AuthService interface {
Authenticate(username, password string) error
}

type WebdavServer struct {
AuthService AuthService
Host string
Port int
MappedDir string
}

func (s *WebdavServer) Listen() {
if nil == s.AuthService {
logger.Fatal("AuthService is nil")
}
if s.MappedDir == "" {
logger.Fatal("MappedDir is empty")
}
if !path.IsAbs(s.MappedDir) {
logger.Fatal("MappedDir is not an absolute path")
}
if !strings.HasPrefix(s.MappedDir, "/home/") {
logger.Warn("You're using a path which isn't under /home/ as mapped directory. This may cause security issues.")
}

davfs := &webdav.Handler{
FileSystem: webdav.Dir(s.MappedDir),
LockSystem: webdav.NewMemLS(),
Logger: func(r *http.Request, err error) {
ent := logger.WithFields(logrus.Fields{
"method": r.Method,
"path": r.URL.Path,
})
if err != nil {
ent.Error(err)
} else {
ent.Info()
}
},
}

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
http.Error(w, "Unauthorized.", http.StatusUnauthorized)
return
}
err := s.AuthService.Authenticate(username, password)
if err != nil {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
http.Error(w, "Unauthorized.", http.StatusUnauthorized)
logger.Error("Unauthorized: ", err)
return
}
davfs.ServeHTTP(w, r)
})
addr := fmt.Sprintf("%s:%d", s.Host, s.Port)
err := http.ListenAndServe(addr, nil)
logger.Fatal("failed to listen and serve on", addr, ":", err)
}
82 changes: 47 additions & 35 deletions cmd/flydav/app/conf.go → cmd/flydav/conf/conf.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,54 @@
package app
package conf

import "github.com/sirupsen/logrus"
import (
"golang.org/x/crypto/bcrypt"
)

func GetDefaultConf() Conf {
return Conf{
Log: Log{
Level: "warn",
Stdout: []Stdout{},
File: []File{},
},
Server: Server{
Host: "127.0.0.1",
Port: 7086,
},
Auth: Auth{
User: []User{
{
Username: "flydav",
PasswordHash: (func() string {
b, _ := bcrypt.GenerateFromPassword([]byte("flydav"), bcrypt.DefaultCost)
return string(b)
})(),
PasswordCrypt: "bcrypt",
},
},
},
}
}

type Conf struct {
Log Log `toml:"log"`
Log Log `toml:"log"`
Server Server `toml:"server"`
Auth Auth `toml:"auth"`
}

type Server struct {
Host string `toml:"host"`
Port int `toml:"port"`
}
type User struct {
Username string `toml:"username"`
PasswordHash string `toml:"password_hash"`
PasswordCrypt string `toml:"password_crypt"`
}
type Auth struct {
User []User `toml:"user"`
}

type File struct {
Format LogFormat `toml:"format"`
Path string `toml:"path"`
Expand Down Expand Up @@ -36,35 +80,3 @@ type Log struct {
File []File `toml:"file"`
Stdout []Stdout `toml:"stdout"`
}

func GetDefaultConf() Conf {
return Conf{
Log: Log{
Level: "warn",
Stdout: []Stdout{},
File: []File{},
},
}
}

// levelToLogrusLevel converts a string to a logrus.Level
func levelToLogrusLevel(level string) logrus.Level {
switch level {
case "debug":
return logrus.DebugLevel
case "info":
return logrus.InfoLevel
case "warn":
return logrus.WarnLevel
case "warning":
return logrus.WarnLevel
case "error":
return logrus.ErrorLevel
case "fatal":
return logrus.FatalLevel
case "panic":
return logrus.PanicLevel
default:
return logrus.InfoLevel
}
}
Loading

0 comments on commit 0416383

Please sign in to comment.