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