diff options
Diffstat (limited to 'pim_ctrl.go')
-rw-r--r-- | pim_ctrl.go | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/pim_ctrl.go b/pim_ctrl.go new file mode 100644 index 0000000..8589536 --- /dev/null +++ b/pim_ctrl.go @@ -0,0 +1,211 @@ +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 |