Format the source file

This commit is contained in:
Quentin 2021-08-19 22:14:53 +02:00
parent d69640894d
commit 8f7e8d2c73

395
main.go
View file

@ -1,42 +1,43 @@
package main package main
import ( import (
"context" "context"
"errors"
"fmt"
"io/fs"
"log" "log"
"fmt"
"os"
"io/fs"
"time"
"errors"
"net/http" "net/http"
"strings" "os"
"path" "path"
"strings"
"time"
"golang.org/x/net/webdav" "golang.org/x/net/webdav"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
"github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/credentials"
) )
type bagageCtxKey string type bagageCtxKey string
const garageEntry = bagageCtxKey("garage") const garageEntry = bagageCtxKey("garage")
type garageCtx struct { type garageCtx struct {
MC *minio.Client MC *minio.Client
StatCache map[string]*GarageStat StatCache map[string]*GarageStat
} }
func main() { func main() {
pathPrefix := "/webdav" pathPrefix := "/webdav"
UserBaseDN := "ou=users,dc=deuxfleurs,dc=fr" UserBaseDN := "ou=users,dc=deuxfleurs,dc=fr"
UserNameAttr := "cn" UserNameAttr := "cn"
Endpoint := "garage.deuxfleurs.fr" Endpoint := "garage.deuxfleurs.fr"
UseSSL := true UseSSL := true
srv := &webdav.Handler{ srv := &webdav.Handler{
Prefix: pathPrefix, Prefix: pathPrefix,
FileSystem: NewGarageFS(), FileSystem: NewGarageFS(),
LockSystem: webdav.NewMemLS(), LockSystem: webdav.NewMemLS(),
Logger: func(r *http.Request, err error) { Logger: func(r *http.Request, err error) {
@ -45,79 +46,79 @@ func main() {
} }
//http.Handle("/", srv) //http.Handle("/", srv)
http.HandleFunc(pathPrefix + "/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc(pathPrefix+"/", func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth() username, password, ok := r.BasicAuth()
if !ok { if !ok {
NotAuthorized(w,r) NotAuthorized(w, r)
return return
} }
ldapSock, err := ldap.Dial("tcp", "127.0.0.1:1389") ldapSock, err := ldap.Dial("tcp", "127.0.0.1:1389")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
InternalError(w,r) InternalError(w, r)
return return
} }
defer ldapSock.Close() defer ldapSock.Close()
// Check credential // Check credential
userDn := fmt.Sprintf("%s=%s,%s", UserNameAttr, username, UserBaseDN) userDn := fmt.Sprintf("%s=%s,%s", UserNameAttr, username, UserBaseDN)
err = ldapSock.Bind(userDn, password) err = ldapSock.Bind(userDn, password)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
NotAuthorized(w,r) NotAuthorized(w, r)
return return
} }
// Get S3 creds garage_s3_access_key garage_s3_secret_key // Get S3 creds garage_s3_access_key garage_s3_secret_key
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
userDn, userDn,
ldap.ScopeBaseObject, ldap.ScopeBaseObject,
ldap.NeverDerefAliases, ldap.NeverDerefAliases,
0, 0,
0, 0,
false, false,
"(objectClass=*)", "(objectClass=*)",
[]string{"garage_s3_access_key", "garage_s3_secret_key"}, []string{"garage_s3_access_key", "garage_s3_secret_key"},
nil) nil)
sr, err := ldapSock.Search(searchRequest) sr, err := ldapSock.Search(searchRequest)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
InternalError(w,r) InternalError(w, r)
return return
} }
if len(sr.Entries) != 1 { if len(sr.Entries) != 1 {
log.Println("Wrong number of LDAP entries, expected 1, got", len(sr.Entries)) log.Println("Wrong number of LDAP entries, expected 1, got", len(sr.Entries))
InternalError(w,r) InternalError(w, r)
return return
} }
access_key := sr.Entries[0].GetAttributeValue("garage_s3_access_key") access_key := sr.Entries[0].GetAttributeValue("garage_s3_access_key")
secret_key := sr.Entries[0].GetAttributeValue("garage_s3_secret_key") secret_key := sr.Entries[0].GetAttributeValue("garage_s3_secret_key")
if access_key == "" || secret_key == "" { if access_key == "" || secret_key == "" {
log.Println("Either access key or secret key is missing in LDAP for ", userDn) log.Println("Either access key or secret key is missing in LDAP for ", userDn)
InternalError(w,r) InternalError(w, r)
return return
} }
mc, err := minio.New(Endpoint, &minio.Options{ mc, err := minio.New(Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(access_key, secret_key, ""), Creds: credentials.NewStaticV4(access_key, secret_key, ""),
Secure: UseSSL, Secure: UseSSL,
}) })
if err != nil { if err != nil {
log.Println(err) log.Println(err)
InternalError(w,r) InternalError(w, r)
return return
} }
nctx := context.WithValue(r.Context(), garageEntry, garageCtx{MC: mc, StatCache: make(map[string]*GarageStat)}) nctx := context.WithValue(r.Context(), garageEntry, garageCtx{MC: mc, StatCache: make(map[string]*GarageStat)})
srv.ServeHTTP(w, r.WithContext(nctx)) srv.ServeHTTP(w, r.WithContext(nctx))
return return
}) })
if err := http.ListenAndServe(":8080", nil); err != nil { if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Error with WebDAV server: %v", err) log.Fatalf("Error with WebDAV server: %v", err)
@ -132,7 +133,7 @@ func NotAuthorized(w http.ResponseWriter, r *http.Request) {
func InternalError(w http.ResponseWriter, r *http.Request) { func InternalError(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte("500 Internal Server Error\n")) w.Write([]byte("500 Internal Server Error\n"))
} }
/* /*
@ -146,7 +147,7 @@ func InternalError(w http.ResponseWriter, r *http.Request) {
Else Else
return obj return obj
*/ */
type GarageFS struct {} type GarageFS struct{}
func NewGarageFS() *GarageFS { func NewGarageFS() *GarageFS {
grg := new(GarageFS) grg := new(GarageFS)
@ -154,53 +155,53 @@ func NewGarageFS() *GarageFS {
} }
func (s *GarageFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error { func (s *GarageFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
return errors.New("Not implemented Mkdir") return errors.New("Not implemented Mkdir")
} }
func (s *GarageFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { func (s *GarageFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
return NewGarageFile(ctx, name) return NewGarageFile(ctx, name)
} }
func (s *GarageFS) RemoveAll(ctx context.Context, name string) error { func (s *GarageFS) RemoveAll(ctx context.Context, name string) error {
return errors.New("Not implemented RemoveAll") return errors.New("Not implemented RemoveAll")
} }
func (s *GarageFS) Rename(ctx context.Context, oldName, newName string) error { func (s *GarageFS) Rename(ctx context.Context, oldName, newName string) error {
return errors.New("Not implemented Rename") return errors.New("Not implemented Rename")
} }
func (s *GarageFS) Stat(ctx context.Context, name string) (os.FileInfo, error) { func (s *GarageFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
return NewGarageStat(ctx, name) return NewGarageStat(ctx, name)
} }
type GarageFile struct { type GarageFile struct {
ctx context.Context ctx context.Context
mc *minio.Client mc *minio.Client
path string path string
} }
func NewGarageFile(ctx context.Context, path string) (webdav.File, error) { func NewGarageFile(ctx context.Context, path string) (webdav.File, error) {
gf := new(GarageFile) gf := new(GarageFile)
gf.ctx = ctx gf.ctx = ctx
gf.mc = ctx.Value(garageEntry).(garageCtx).MC gf.mc = ctx.Value(garageEntry).(garageCtx).MC
gf.path = path gf.path = path
return gf, nil return gf, nil
} }
func (gf *GarageFile) Close() error { func (gf *GarageFile) Close() error {
return errors.New("not implemented Close") return errors.New("not implemented Close")
} }
func (gf *GarageFile) Read(p []byte) (n int, err error) { func (gf *GarageFile) Read(p []byte) (n int, err error) {
return 0, errors.New("not implemented Read") return 0, errors.New("not implemented Read")
} }
func (gf *GarageFile) Write(p []byte) (n int, err error) { func (gf *GarageFile) Write(p []byte) (n int, err error) {
return 0, errors.New("not implemented Write") return 0, errors.New("not implemented Write")
} }
func (gf *GarageFile) Seek(offset int64, whence int) (int64, error) { func (gf *GarageFile) Seek(offset int64, whence int) (int64, error) {
return 0, errors.New("not implemented Seek") return 0, errors.New("not implemented Seek")
} }
/* /*
@ -211,155 +212,155 @@ If n > 0, ReadDir returns at most n DirEntry records. In this case, if ReadDir r
If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. When it succeeds, it returns a nil error (not io.EOF). If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. When it succeeds, it returns a nil error (not io.EOF).
*/ */
func (gf *GarageFile) Readdir(count int) ([]fs.FileInfo, error) { func (gf *GarageFile) Readdir(count int) ([]fs.FileInfo, error) {
log.Println("Call Readdir with count", count) log.Println("Call Readdir with count", count)
if gf.path == "/" { if gf.path == "/" {
return gf.readDirRoot(count) return gf.readDirRoot(count)
} else { } else {
exploded_path := strings.SplitN(gf.path, "/", 3) exploded_path := strings.SplitN(gf.path, "/", 3)
return gf.readDirChild(count, exploded_path[1], exploded_path[2]) return gf.readDirChild(count, exploded_path[1], exploded_path[2])
} }
} }
func (gf *GarageFile) readDirRoot(count int) ([]fs.FileInfo, error) { func (gf *GarageFile) readDirRoot(count int) ([]fs.FileInfo, error) {
buckets, err := gf.mc.ListBuckets(gf.ctx) buckets, err := gf.mc.ListBuckets(gf.ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
entries := make([]fs.FileInfo, 0, len(buckets)) entries := make([]fs.FileInfo, 0, len(buckets))
for _, bucket := range buckets { for _, bucket := range buckets {
ngf, err := NewGarageStat(gf.ctx, "/"+bucket.Name) ngf, err := NewGarageStat(gf.ctx, "/"+bucket.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
entries = append(entries, ngf) entries = append(entries, ngf)
} }
return entries, nil return entries, nil
} }
func (gf *GarageFile) readDirChild(count int, bucket, prefix string) ([]fs.FileInfo, error) { func (gf *GarageFile) readDirChild(count int, bucket, prefix string) ([]fs.FileInfo, error) {
log.Println("call ListObjects with", bucket, prefix) log.Println("call ListObjects with", bucket, prefix)
objs_info := gf.mc.ListObjects(gf.ctx, bucket, minio.ListObjectsOptions{ objs_info := gf.mc.ListObjects(gf.ctx, bucket, minio.ListObjectsOptions{
Prefix: prefix, Prefix: prefix,
Recursive: false, Recursive: false,
}) })
entries := make([]fs.FileInfo,0) entries := make([]fs.FileInfo, 0)
for object := range objs_info { for object := range objs_info {
if object.Err != nil { if object.Err != nil {
return nil, object.Err return nil, object.Err
} }
ngf, err := NewGarageStatFromObjectInfo(gf.ctx, bucket, object) ngf, err := NewGarageStatFromObjectInfo(gf.ctx, bucket, object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
entries = append(entries, ngf) entries = append(entries, ngf)
} }
return entries, nil return entries, nil
} }
func (gf *GarageFile) Stat() (fs.FileInfo, error) { func (gf *GarageFile) Stat() (fs.FileInfo, error) {
return NewGarageStat(gf.ctx, gf.path) return NewGarageStat(gf.ctx, gf.path)
} }
/* Implements */ /* Implements */
// StatObject??? // StatObject???
type GarageStat struct { type GarageStat struct {
obj minio.ObjectInfo obj minio.ObjectInfo
bucket string bucket string
} }
func NewGarageStat(ctx context.Context, path string) (*GarageStat, error) { func NewGarageStat(ctx context.Context, path string) (*GarageStat, error) {
cache := ctx.Value(garageEntry).(garageCtx).StatCache cache := ctx.Value(garageEntry).(garageCtx).StatCache
if entry, ok := cache[path]; ok { if entry, ok := cache[path]; ok {
return entry, nil return entry, nil
} }
gs, err := newGarageStatFresh(ctx, path) gs, err := newGarageStatFresh(ctx, path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cache[path] = gs cache[path] = gs
return gs, nil return gs, nil
} }
func newGarageStatFresh(ctx context.Context, path string) (*GarageStat, error) { func newGarageStatFresh(ctx context.Context, path string) (*GarageStat, error) {
mc := ctx.Value(garageEntry).(garageCtx).MC mc := ctx.Value(garageEntry).(garageCtx).MC
gs := new(GarageStat) gs := new(GarageStat)
gs.bucket = "/" gs.bucket = "/"
gs.obj = minio.ObjectInfo{} gs.obj = minio.ObjectInfo{}
exploded_path := strings.SplitN(path, "/", 3) exploded_path := strings.SplitN(path, "/", 3)
// Check if we can extract the bucket name // Check if we can extract the bucket name
if len(exploded_path) < 2 { if len(exploded_path) < 2 {
return gs, nil return gs, nil
} }
gs.bucket = exploded_path[1] gs.bucket = exploded_path[1]
// Check if we can extract the prefix // Check if we can extract the prefix
if len(exploded_path) < 3 || exploded_path[2] == "" { if len(exploded_path) < 3 || exploded_path[2] == "" {
return gs, nil return gs, nil
} }
gs.obj.Key = exploded_path[2] gs.obj.Key = exploded_path[2]
// Check if this is a file or a folder // Check if this is a file or a folder
log.Println("call StatObject with", gs.bucket, gs.obj.Key) log.Println("call StatObject with", gs.bucket, gs.obj.Key)
obj, err := mc.StatObject(ctx, gs.bucket, gs.obj.Key, minio.StatObjectOptions{}) obj, err := mc.StatObject(ctx, gs.bucket, gs.obj.Key, minio.StatObjectOptions{})
if e, ok := err.(minio.ErrorResponse); ok && e.Code == "NoSuchKey" { if e, ok := err.(minio.ErrorResponse); ok && e.Code == "NoSuchKey" {
return gs, nil return gs, nil
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
// If it is a file, assign its data // If it is a file, assign its data
gs.obj = obj gs.obj = obj
return gs, nil return gs, nil
} }
func NewGarageStatFromObjectInfo(ctx context.Context, bucket string, obj minio.ObjectInfo) (*GarageStat, error) { func NewGarageStatFromObjectInfo(ctx context.Context, bucket string, obj minio.ObjectInfo) (*GarageStat, error) {
gs := new(GarageStat) gs := new(GarageStat)
gs.bucket = bucket gs.bucket = bucket
gs.obj = obj gs.obj = obj
cache := ctx.Value(garageEntry).(garageCtx).StatCache cache := ctx.Value(garageEntry).(garageCtx).StatCache
cache[path.Join("/", bucket, obj.Key)] = gs cache[path.Join("/", bucket, obj.Key)] = gs
return gs, nil return gs, nil
} }
func (gs *GarageStat) Name() string { func (gs *GarageStat) Name() string {
if gs.obj.Key != "" { if gs.obj.Key != "" {
return path.Base(gs.obj.Key) return path.Base(gs.obj.Key)
} else { } else {
return gs.bucket return gs.bucket
} }
} }
func (gs *GarageStat) Size() int64 { func (gs *GarageStat) Size() int64 {
return gs.obj.Size return gs.obj.Size
} }
func (gs *GarageStat) Mode() fs.FileMode { func (gs *GarageStat) Mode() fs.FileMode {
if gs.obj.ETag == "" { if gs.obj.ETag == "" {
return fs.ModeDir | fs.ModePerm return fs.ModeDir | fs.ModePerm
} else { } else {
return fs.ModePerm return fs.ModePerm
} }
} }
func (gs *GarageStat) ModTime() time.Time { func (gs *GarageStat) ModTime() time.Time {
return gs.obj.LastModified return gs.obj.LastModified
} }
func (gs *GarageStat) IsDir() bool { func (gs *GarageStat) IsDir() bool {
return gs.Mode().IsDir() return gs.Mode().IsDir()
} }
func (gs *GarageStat) Sys() interface{} { func (gs *GarageStat) Sys() interface{} {
return nil return nil
} }