gvguichet/garage.go

252 lines
6.1 KiB
Go
Raw Normal View History

2023-04-18 19:37:51 +02:00
package main
import (
2023-04-19 15:07:46 +02:00
"context"
"errors"
"fmt"
2023-07-17 11:32:41 +02:00
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
2023-04-19 11:36:13 +02:00
"github.com/go-ldap/ldap/v3"
2023-04-19 14:58:39 +02:00
"github.com/gorilla/mux"
2023-04-19 15:07:46 +02:00
"log"
"net/http"
"strings"
2023-04-18 19:37:51 +02:00
)
2023-04-18 22:45:04 +02:00
func gadmin() (*garage.APIClient, context.Context) {
2023-04-19 15:07:46 +02:00
// Set Host and other parameters
configuration := garage.NewConfiguration()
configuration.Host = config.S3AdminEndpoint
2023-04-18 22:45:04 +02:00
2023-04-19 15:07:46 +02:00
// We can now generate a client
client := garage.NewAPIClient(configuration)
2023-04-18 22:45:04 +02:00
2023-04-19 15:07:46 +02:00
// Authentication is handled through the context pattern
ctx := context.WithValue(context.Background(), garage.ContextAccessToken, config.S3AdminToken)
return client, ctx
2023-04-18 22:45:04 +02:00
}
2023-04-19 11:36:13 +02:00
func grgCreateKey(name string) (*garage.KeyInfo, error) {
2023-04-19 15:07:46 +02:00
client, ctx := gadmin()
kr := garage.AddKeyRequest{Name: &name}
resp, _, err := client.KeyApi.AddKey(ctx).AddKeyRequest(kr).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
return resp, nil
2023-04-19 11:36:13 +02:00
}
func grgGetKey(accessKey string) (*garage.KeyInfo, error) {
2023-04-19 15:07:46 +02:00
client, ctx := gadmin()
resp, _, err := client.KeyApi.GetKey(ctx, accessKey).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
return resp, nil
2023-04-19 11:36:13 +02:00
}
2023-04-19 14:58:39 +02:00
func grgCreateWebsite(gkey, bucket string) (*garage.BucketInfo, error) {
2023-04-19 15:07:46 +02:00
client, ctx := gadmin()
br := garage.NewCreateBucketRequest()
br.SetGlobalAlias(bucket)
// Create Bucket
binfo, _, err := client.BucketApi.CreateBucket(ctx).CreateBucketRequest(*br).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
// Allow user's key
ar := garage.AllowBucketKeyRequest{
BucketId: *binfo.Id,
AccessKeyId: gkey,
Permissions: *garage.NewAllowBucketKeyRequestPermissions(true, true, true),
}
binfo, _, err = client.BucketApi.AllowBucketKey(ctx).AllowBucketKeyRequest(ar).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
// Expose website and set quota
wr := garage.NewUpdateBucketRequestWebsiteAccess()
wr.SetEnabled(true)
wr.SetIndexDocument("index.html")
wr.SetErrorDocument("error.html")
qr := garage.NewUpdateBucketRequestQuotas()
qr.SetMaxSize(1024 * 1024 * 50) // 50MB
qr.SetMaxObjects(10000) //10k objects
ur := garage.NewUpdateBucketRequest()
ur.SetWebsiteAccess(*wr)
ur.SetQuotas(*qr)
binfo, _, err = client.BucketApi.UpdateBucket(ctx, *binfo.Id).UpdateBucketRequest(*ur).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
// Return updated binfo
return binfo, nil
2023-04-19 14:58:39 +02:00
}
func grgGetBucket(bid string) (*garage.BucketInfo, error) {
2023-04-19 15:07:46 +02:00
client, ctx := gadmin()
2023-04-19 14:58:39 +02:00
2023-04-19 15:07:46 +02:00
resp, _, err := client.BucketApi.GetBucketInfo(ctx, bid).Execute()
if err != nil {
fmt.Printf("%+v\n", err)
return nil, err
}
return resp, nil
2023-04-19 14:58:39 +02:00
}
2023-04-19 11:36:13 +02:00
func checkLoginAndS3(w http.ResponseWriter, r *http.Request) (*LoginStatus, *garage.KeyInfo, error) {
login := checkLogin(w, r)
if login == nil {
return nil, nil, errors.New("LDAP login failed")
}
keyID := login.UserEntry.GetAttributeValue("garage_s3_access_key")
2023-04-19 15:07:46 +02:00
if keyID == "" {
keyPair, err := grgCreateKey(login.Info.Username)
if err != nil {
return login, nil, err
}
2023-04-19 11:36:13 +02:00
modify_request := ldap.NewModifyRequest(login.Info.DN, nil)
modify_request.Replace("garage_s3_access_key", []string{*keyPair.AccessKeyId})
2023-04-19 15:07:46 +02:00
// @FIXME compatibility feature for bagage (SFTP+webdav)
// you can remove it once bagage will be updated to fetch the key from garage directly
// or when bottin will be able to dynamically fetch it.
2023-04-19 11:36:13 +02:00
modify_request.Replace("garage_s3_secret_key", []string{*keyPair.SecretAccessKey})
2023-04-19 15:07:46 +02:00
err = login.conn.Modify(modify_request)
return login, keyPair, err
}
// Note: we could simply return the login info, but LX asked we do not
// store the secrets in LDAP in the future.
keyPair, err := grgGetKey(keyID)
return login, keyPair, err
2023-04-19 11:36:13 +02:00
}
type keyView struct {
2023-04-19 15:07:46 +02:00
Status *LoginStatus
Key *garage.KeyInfo
2023-04-18 22:45:04 +02:00
}
2023-04-18 19:37:51 +02:00
func handleGarageKey(w http.ResponseWriter, r *http.Request) {
2023-04-19 15:07:46 +02:00
login, s3key, err := checkLoginAndS3(w, r)
if err != nil {
log.Println(err)
return
}
view := keyView{Status: login, Key: s3key}
tKey := getTemplate("garage_key.html")
2023-04-19 11:36:13 +02:00
tKey.Execute(w, &view)
2023-04-18 19:37:51 +02:00
}
2023-04-19 12:17:27 +02:00
type webListView struct {
2023-04-19 15:07:46 +02:00
Status *LoginStatus
Key *garage.KeyInfo
2023-04-19 12:17:27 +02:00
}
2023-04-19 15:07:46 +02:00
2023-04-18 19:37:51 +02:00
func handleGarageWebsiteList(w http.ResponseWriter, r *http.Request) {
2023-04-19 15:07:46 +02:00
login, s3key, err := checkLoginAndS3(w, r)
if err != nil {
log.Println(err)
return
}
view := webListView{Status: login, Key: s3key}
tWebsiteList := getTemplate("garage_website_list.html")
2023-04-19 12:17:27 +02:00
tWebsiteList.Execute(w, &view)
2023-04-18 19:37:51 +02:00
}
func handleGarageWebsiteNew(w http.ResponseWriter, r *http.Request) {
2023-04-19 15:07:46 +02:00
_, s3key, err := checkLoginAndS3(w, r)
if err != nil {
log.Println(err)
return
}
2023-04-19 14:58:39 +02:00
2023-04-19 15:07:46 +02:00
tWebsiteNew := getTemplate("garage_website_new.html")
2023-04-19 14:58:39 +02:00
if r.Method == "POST" {
2023-04-19 15:07:46 +02:00
r.ParseForm()
log.Println(r.Form)
2023-04-19 14:58:39 +02:00
bucket := strings.Join(r.Form["bucket"], "")
2023-04-19 15:07:46 +02:00
if bucket == "" {
bucket = strings.Join(r.Form["bucket2"], "")
}
if bucket == "" {
log.Println("Form empty")
// @FIXME we need to return the error to the user
tWebsiteNew.Execute(w, nil)
return
}
binfo, err := grgCreateWebsite(*s3key.AccessKeyId, bucket)
if err != nil {
log.Println(err)
// @FIXME we need to return the error to the user
tWebsiteNew.Execute(w, nil)
return
}
http.Redirect(w, r, "/garage/website/b/"+*binfo.Id, http.StatusFound)
return
}
2023-04-19 14:58:39 +02:00
2023-04-18 19:37:51 +02:00
tWebsiteNew.Execute(w, nil)
}
2023-04-19 14:58:39 +02:00
type webInspectView struct {
2023-04-19 15:07:46 +02:00
Status *LoginStatus
Key *garage.KeyInfo
Bucket *garage.BucketInfo
IndexDoc string
ErrorDoc string
MaxObjects int64
MaxSize int64
UsedSizePct float64
2023-04-19 14:58:39 +02:00
}
2023-04-19 15:07:46 +02:00
2023-04-18 19:37:51 +02:00
func handleGarageWebsiteInspect(w http.ResponseWriter, r *http.Request) {
2023-04-19 15:07:46 +02:00
login, s3key, err := checkLoginAndS3(w, r)
if err != nil {
log.Println(err)
return
}
2023-04-19 14:58:39 +02:00
bucketId := mux.Vars(r)["bucket"]
2023-04-19 15:07:46 +02:00
binfo, err := grgGetBucket(bucketId)
if err != nil {
log.Println(err)
return
}
wc := binfo.GetWebsiteConfig()
q := binfo.GetQuotas()
view := webInspectView{
Status: login,
Key: s3key,
Bucket: binfo,
IndexDoc: (&wc).GetIndexDocument(),
ErrorDoc: (&wc).GetErrorDocument(),
MaxObjects: (&q).GetMaxObjects(),
MaxSize: (&q).GetMaxSize(),
}
tWebsiteInspect := getTemplate("garage_website_inspect.html")
2023-04-19 14:58:39 +02:00
tWebsiteInspect.Execute(w, &view)
2023-04-18 19:37:51 +02:00
}