212 lines
5.1 KiB
Go
212 lines
5.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"os/exec"
|
||
|
"strings"
|
||
|
"slices"
|
||
|
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
|
||
|
"github.com/go-ldap/ldap/v3"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
FIELD_AEROGRAMME_CRYPTOROOT = "aero_cryptoroot"
|
||
|
FIELD_AEROGRAMME_BUCKET_ID = "aero_bucket_id"
|
||
|
FIELD_AEROGRAMME_BUCKET_NAME = "aero_bucket"
|
||
|
LOCAL_ALIAS_NAME = "aerogramme"
|
||
|
)
|
||
|
|
||
|
|
||
|
var (
|
||
|
ErrPimBuilderDirty = fmt.Errorf("builder is dirty.")
|
||
|
ErrPimBucketLocalAliasNotFound = fmt.Errorf("local alias does not exist in garage or points to the wrong bucket.")
|
||
|
ErrPimBucketIdEmpty = fmt.Errorf("missing bucket ID in LDAP.")
|
||
|
ErrPimBucketNameEmpty = fmt.Errorf("missing bucket local garage alias in LDAP.")
|
||
|
ErrPimBucketInfoNotFetched = fmt.Errorf("bucket info has not been fetched.")
|
||
|
ErrPimCryptoRootEmpty = fmt.Errorf("missing cryptoroot in LDAP.")
|
||
|
ErrPimCantCreateBucket = fmt.Errorf("unable to create PIM bucket.")
|
||
|
)
|
||
|
|
||
|
type PimBuilder struct {
|
||
|
user *LoggedUser
|
||
|
cryptoroot string
|
||
|
bucketId string
|
||
|
bucketName string
|
||
|
bucketInfo *garage.BucketInfo
|
||
|
dirty bool
|
||
|
errors []error
|
||
|
}
|
||
|
|
||
|
func NewPimBuilder(user *LoggedUser) *PimBuilder {
|
||
|
return &PimBuilder {
|
||
|
user: user,
|
||
|
cryptoroot: user.Entry.GetAttributeValue(FIELD_AEROGRAMME_CRYPTOROOT),
|
||
|
bucketId: user.Entry.GetAttributeValue(FIELD_AEROGRAMME_BUCKET_ID),
|
||
|
bucketName: user.Entry.GetAttributeValue(FIELD_AEROGRAMME_BUCKET_NAME),
|
||
|
bucketInfo: nil,
|
||
|
dirty: false,
|
||
|
errors: make([]error, 0),
|
||
|
}
|
||
|
}
|
||
|
func (pm *PimBuilder) CheckCryptoRoot() *PimBuilder {
|
||
|
if pm.cryptoroot == "" {
|
||
|
cmd := exec.Command("./aerogramme", "tools", "crypto-root", "new-clear-text")
|
||
|
var out strings.Builder
|
||
|
cmd.Stdout = &out
|
||
|
err := cmd.Run()
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, err)
|
||
|
return pm
|
||
|
}
|
||
|
pm.cryptoroot = out.String()
|
||
|
pm.dirty = true
|
||
|
}
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
func (pm *PimBuilder) CheckBucket() *PimBuilder {
|
||
|
keyInfo, err := pm.user.S3KeyInfo()
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, err)
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
if pm.bucketId == "" {
|
||
|
candidateName := LOCAL_ALIAS_NAME
|
||
|
var bInfo *garage.BucketInfo
|
||
|
var err error
|
||
|
|
||
|
err = nil
|
||
|
for _, ext := range []string{"", "-1", "-2", "-3", "-4", "-5"} {
|
||
|
candidateName = LOCAL_ALIAS_NAME + ext
|
||
|
bInfo, err = grgCreateLocalBucket(candidateName, *keyInfo.AccessKeyId)
|
||
|
if err == nil {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, ErrPimCantCreateBucket)
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
qr := pm.user.Quota.DefaultPimQuota()
|
||
|
ur := garage.NewUpdateBucketRequest()
|
||
|
ur.SetQuotas(*qr)
|
||
|
bInfo, err = grgUpdateBucket(*bInfo.Id, ur)
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, err)
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
pm.bucketId = *bInfo.Id
|
||
|
pm.bucketName = candidateName
|
||
|
pm.bucketInfo = bInfo
|
||
|
pm.dirty = true
|
||
|
} else {
|
||
|
binfo, err := grgGetBucket(pm.bucketId)
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, err)
|
||
|
return pm
|
||
|
}
|
||
|
pm.bucketInfo = binfo
|
||
|
|
||
|
//@TODO find my key, check that pm.bucketName exists in bucketLocalAliases
|
||
|
nameFound := false
|
||
|
for _, k := range binfo.Keys {
|
||
|
if *k.AccessKeyId != *keyInfo.AccessKeyId {
|
||
|
// not my key
|
||
|
continue
|
||
|
}
|
||
|
if slices.Contains(k.BucketLocalAliases, pm.bucketName) {
|
||
|
nameFound = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !nameFound {
|
||
|
pm.errors = append(pm.errors, ErrPimBucketLocalAliasNotFound)
|
||
|
return pm
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
func (pm *PimBuilder) LdapUpdate() *PimBuilder {
|
||
|
if len(pm.errors) > 0 {
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
modify_request := ldap.NewModifyRequest(pm.user.Login.Info.DN(), nil)
|
||
|
modify_request.Replace(FIELD_AEROGRAMME_CRYPTOROOT, []string{pm.cryptoroot})
|
||
|
modify_request.Replace(FIELD_AEROGRAMME_BUCKET_NAME, []string{pm.bucketName})
|
||
|
modify_request.Replace(FIELD_AEROGRAMME_BUCKET_ID, []string{pm.bucketId})
|
||
|
err := pm.user.Login.conn.Modify(modify_request)
|
||
|
if err != nil {
|
||
|
pm.errors = append(pm.errors, err)
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
pm.dirty = false
|
||
|
return pm
|
||
|
}
|
||
|
|
||
|
func (pm *PimBuilder) Build() (*PimController, error) {
|
||
|
// checks
|
||
|
if pm.dirty {
|
||
|
pm.errors = append(pm.errors, ErrPimBuilderDirty)
|
||
|
}
|
||
|
if pm.bucketId == "" {
|
||
|
pm.errors = append(pm.errors, ErrPimBucketIdEmpty)
|
||
|
}
|
||
|
if pm.bucketName == "" {
|
||
|
pm.errors = append(pm.errors, ErrPimBucketNameEmpty)
|
||
|
}
|
||
|
if pm.bucketInfo == nil {
|
||
|
pm.errors = append(pm.errors, ErrPimBucketInfoNotFetched)
|
||
|
}
|
||
|
if pm.cryptoroot == "" {
|
||
|
pm.errors = append(pm.errors, ErrPimCryptoRootEmpty)
|
||
|
}
|
||
|
if len(pm.errors) > 0 {
|
||
|
err := errors.New("PIM Builder failed")
|
||
|
for _, iterErr := range pm.errors {
|
||
|
err = errors.Join(err, iterErr)
|
||
|
}
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// quotas
|
||
|
q := pm.bucketInfo.GetQuotas()
|
||
|
size := NewQuotaStat(*pm.bucketInfo.Bytes, (&q).GetMaxSize(), true)
|
||
|
objects := NewQuotaStat(*pm.bucketInfo.Objects, (&q).GetMaxObjects(), false)
|
||
|
|
||
|
// final object
|
||
|
pim_ctl := &PimController {
|
||
|
BucketId: pm.bucketId,
|
||
|
BucketName: pm.bucketName,
|
||
|
Size: size,
|
||
|
Files: objects,
|
||
|
user: pm.user,
|
||
|
bucketInfo: pm.bucketInfo,
|
||
|
cryptoroot: pm.cryptoroot,
|
||
|
|
||
|
}
|
||
|
|
||
|
return pim_ctl, nil
|
||
|
}
|
||
|
|
||
|
// --- Controller ---
|
||
|
type PimController struct {
|
||
|
BucketId string `json:"bucket_id"`
|
||
|
BucketName string `json:"bucket_name"`
|
||
|
Size QuotaStat `json:"quota_size"`
|
||
|
Files QuotaStat `json:"quota_files"`
|
||
|
user *LoggedUser
|
||
|
bucketInfo *garage.BucketInfo
|
||
|
cryptoroot string
|
||
|
}
|
||
|
|
||
|
//@FIXME Implement quota bursting
|