aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.go439
1 files changed, 220 insertions, 219 deletions
diff --git a/main.go b/main.go
index 89fc88a..0ad5668 100644
--- a/main.go
+++ b/main.go
@@ -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()
-
- 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()
-
- // 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)
-
- 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
- }
-
- 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
- }
-
- 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
- })
+ http.HandleFunc(pathPrefix+"/", func(w http.ResponseWriter, r *http.Request) {
+ username, password, ok := r.BasicAuth()
+
+ 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()
+
+ // 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)
+
+ 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
+ }
+
+ 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
+ }
+
+ 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
+ })
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)
-
- 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])
- }
+ 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])
+ }
}
func (gf *GarageFile) readDirRoot(count int) ([]fs.FileInfo, error) {
- 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)
- }
-
- return entries, nil
+ 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)
+ }
+
+ 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,
- })
-
- 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
+ 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)
+ }
+
+ 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
- }
-
- gs, err := newGarageStatFresh(ctx, path)
- if err != nil {
- return nil, err
- }
-
- cache[path] = gs
- return gs, 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
+ }
+
+ 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{}
-
- 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 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
- }
-
- // If it is a file, assign its data
- gs.obj = obj
- return gs, nil
+ mc := ctx.Value(garageEntry).(garageCtx).MC
+ gs := new(GarageStat)
+ gs.bucket = "/"
+ gs.obj = minio.ObjectInfo{}
+
+ 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 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
+ }
+
+ // 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
}