From 5b246ec86bc3eee768da2347f031b349d1e1553d Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Fri, 15 Sep 2023 18:25:37 +0200 Subject: [PATCH] basic logic for switching alias from local/global --- api.go | 153 ++++++++++++++++++++++++++++++++++++++++++++++++------ garage.go | 46 +++++++++++++++- main.go | 2 +- 3 files changed, 184 insertions(+), 17 deletions(-) diff --git a/api.go b/api.go index bce9993..1ddb4ea 100644 --- a/api.go +++ b/api.go @@ -2,11 +2,12 @@ package main import ( //"context" + "encoding/json" "errors" "fmt" garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang" "github.com/go-ldap/ldap/v3" - //"github.com/gorilla/mux" + "github.com/gorilla/mux" "log" "net/http" "strings" @@ -111,21 +112,132 @@ func checkLoginAndS3API(w http.ResponseWriter, r *http.Request) (*LoginStatus, * return login, keyPair, err } +type ApiQuotaView struct { + files *uint64 + size *uint64 +} + +type ApiBucketView struct { + global *bool + max *ApiQuotaView + used *ApiQuotaView +} + +type BucketRequest struct { + s3key *garage.KeyInfo + bucketName string + bucketId string + global bool + http *http.Request +} + func handleAPIGarageBucket(w http.ResponseWriter, r *http.Request) { - login, s3key, err := checkLoginAndS3API(w, r) + br, err := buildBucketRequest(w, r) if err != nil { - log.Println(err) return } - // CHECK PATCH REQUEST + if r.Method == http.MethodPatch { + patchGarageBucket(w, br) + return + } - // READ BODY JSON + if r.Method == http.MethodGet { + getGarageBucket(w, br) + return + } - // VALIDATE OBJECT - // --- bucket query parameter --- - // 1. bucket must be owned by the key with owner permission, otherwise throw "unauthorized" (401) - // 2. must not end with deuxfleurs.fr or deuxfleurs.org, otherwise throw "forbidden" (403) + http.Error(w, "This method is not implemented for this endpoint", http.StatusNotImplemented) + return +} + +func buildBucketRequest(w http.ResponseWriter, r *http.Request) (*BucketRequest, error) { + _, s3key, err := checkLoginAndS3API(w, r) + if err != nil { + http.Error(w, "Unable to connect on LDAP", http.StatusUnauthorized) + return nil, err + } + + // FETCH BUCKET ID by iterating over buckets owned by this key + bucketName := mux.Vars(r)["bucket"] + var bucketId *string + var global *bool + +findBucketIdLoop: + for _, bucket := range s3key.Buckets { + for _, localAlias := range bucket.LocalAliases { + if localAlias == bucketName { + bucketId = bucket.Id + *global = false + break findBucketIdLoop + } + } + for _, globalAlias := range bucket.GlobalAliases { + if globalAlias == bucketName { + bucketId = bucket.Id + *global = true + break findBucketIdLoop + } + } + } + + if bucketId == nil || global == nil { + http.Error(w, "Bucket not found in this account", http.StatusNotFound) + return nil, errors.New("Unable to fetch bucket ID") + } + + return &BucketRequest{ + s3key: s3key, + bucketName: bucketName, + bucketId: *bucketId, + global: *global, + http: r, + }, nil +} + +func patchGarageBucket(w http.ResponseWriter, br *BucketRequest) { + var err error + + // DECODE BODY + var queuedChange ApiBucketView + decoder := json.NewDecoder(br.http.Body) + err = decoder.Decode(&queuedChange) + if err != nil { + log.Println(err) + http.Error(w, "Unable to decode the body", http.StatusBadRequest) + return + } + + // SET THE GLOBAL FLAG + if queuedChange.global != nil { + if *queuedChange.global && !br.global { + _, err = grgAddGlobalAlias(br.bucketId, br.bucketName) + if err != nil { + http.Error(w, "Unable to add the requested name as global alias for this bucket", http.StatusInternalServerError) + return + } + _, err = grgDelLocalAlias(br.bucketId, *br.s3key.AccessKeyId, br.bucketName) + if err != nil { + http.Error(w, "Unable to remove the local alias for this bucket", http.StatusInternalServerError) + return + } + } else if !*queuedChange.global && br.global { + grgAddLocalAlias(br.bucketId, *br.s3key.AccessKeyId, br.bucketName) + if err != nil { + http.Error(w, "Unable to add the requested name as local alias for this bucket", http.StatusInternalServerError) + return + } + grgDelGlobalAlias(br.bucketId, br.bucketName) + if err != nil { + http.Error(w, "Unable to remove the global alias for this bucket", http.StatusInternalServerError) + return + } + } + } + + // CHECK IF QUOTA MUST BE ADDED TO THIS BUCKET + + // VALIDATE IT // --- global --- // 1. can be true, false, or nil (use pointers) // 2. if nil do nothing @@ -137,17 +249,28 @@ func handleAPIGarageBucket(w http.ResponseWriter, r *http.Request) { // --- quota.files --- // 1. if no quota on the bucket + this field is none, set to 10k // 2. if lower than 10k, set to 10k. If higher than 40k, set to 40k + // READ BODY JSON // IF BODY.GLOBAL is not NONE - // Add an alias + // DO: Add an alias // IF BODY.QUOTA.SIZE is not NONE - // Change quota + // DO: Change quota // IF BODY.QUOTA.FILE is not NONE - // Change quota + // DO: Change quota - log.Println(login, s3key) - - return + getGarageBucket(w, br) +} + +func getGarageBucket(w http.ResponseWriter, br *BucketRequest) { + // FETCH AN UPDATED BUCKET VIEW + bucket, err := grgGetBucket(br.bucketId) + if err != nil { + http.Error(w, "Unable to fetch bucket details", http.StatusInternalServerError) + return + } + + // BUILD A VIEW + log.Println(bucket) } diff --git a/garage.go b/garage.go index 61d1b93..db35366 100644 --- a/garage.go +++ b/garage.go @@ -97,12 +97,56 @@ func grgCreateWebsite(gkey, bucket string) (*garage.BucketInfo, error) { return binfo, nil } +func grgAddGlobalAlias(bid, alias string) (*garage.BucketInfo, error) { + client, ctx := gadmin() + + resp, _, err := client.BucketApi.PutBucketGlobalAlias(ctx).Id(bid).Alias(alias).Execute() + if err != nil { + log.Println(err) + return nil, err + } + return resp, nil +} + +func grgAddLocalAlias(bid, key, alias string) (*garage.BucketInfo, error) { + client, ctx := gadmin() + + resp, _, err := client.BucketApi.PutBucketLocalAlias(ctx).Id(bid).AccessKeyId(key).Alias(alias).Execute() + if err != nil { + log.Println(err) + return nil, err + } + return resp, nil +} + +func grgDelGlobalAlias(bid, alias string) (*garage.BucketInfo, error) { + client, ctx := gadmin() + + resp, _, err := client.BucketApi.DeleteBucketGlobalAlias(ctx).Id(bid).Alias(alias).Execute() + if err != nil { + log.Println(err) + return nil, err + } + return resp, nil +} + +func grgDelLocalAlias(bid, key, alias string) (*garage.BucketInfo, error) { + client, ctx := gadmin() + + resp, _, err := client.BucketApi.DeleteBucketLocalAlias(ctx).Id(bid).AccessKeyId(key).Alias(alias).Execute() + if err != nil { + log.Println(err) + return nil, err + } + return resp, nil +} + func grgGetBucket(bid string) (*garage.BucketInfo, error) { client, ctx := gadmin() resp, _, err := client.BucketApi.GetBucketInfo(ctx, bid).Execute() if err != nil { - fmt.Printf("%+v\n", err) + log.Println(err) return nil, err } return resp, nil diff --git a/main.go b/main.go index 1402ff2..8bfb2f8 100644 --- a/main.go +++ b/main.go @@ -130,7 +130,7 @@ func main() { r.HandleFunc("/", handleHome) r.HandleFunc("/logout", handleLogout) - r.HandleFunc("/api/unstable/garage/bucket/{b}", handleAPIGarageBucket) + r.HandleFunc("/api/unstable/garage/bucket/{bucket}", handleAPIGarageBucket) r.HandleFunc("/profile", handleProfile) r.HandleFunc("/passwd", handlePasswd)