aboutsummaryrefslogtreecommitdiff
path: root/pim_ctrl.go
diff options
context:
space:
mode:
Diffstat (limited to 'pim_ctrl.go')
-rw-r--r--pim_ctrl.go211
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