package utils import ( "crypto/sha256" "encoding/json" "errors" "slices" "time" "cloud.o-forge.io/core/oc-lib/dbs" "cloud.o-forge.io/core/oc-lib/tools" "github.com/go-playground/validator/v10" "github.com/google/uuid" "github.com/rs/zerolog" ) // single instance of the validator used in every model Struct to validate the fields var validate = validator.New(validator.WithRequiredStructEnabled()) type AccessMode int const ( Private AccessMode = iota Public ) /* * AbstractObject is a struct that represents the basic fields of an object * it defines the object id and name * every data in base root model should inherit from this struct (only exception is the ResourceModel) */ type AbstractObject struct { UUID string `json:"id,omitempty" bson:"id,omitempty" validate:"required"` Name string `json:"name,omitempty" bson:"name,omitempty" validate:"required"` IsDraft bool `json:"is_draft" bson:"is_draft" default:"false"` CreatorID string `json:"creator_id,omitempty" bson:"creator_id,omitempty"` UserCreatorID string `json:"user_creator_id,omitempty" bson:"user_creator_id,omitempty"` CreationDate time.Time `json:"creation_date,omitempty" bson:"creation_date,omitempty"` UpdateDate time.Time `json:"update_date,omitempty" bson:"update_date,omitempty"` UpdaterID string `json:"updater_id,omitempty" bson:"updater_id,omitempty"` UserUpdaterID string `json:"user_updater_id,omitempty" bson:"user_updater_id,omitempty"` AccessMode AccessMode `json:"access_mode" bson:"access_mode" default:"0"` Signature []byte `bson:"signature,omitempty" json:"signature,omitempty"` } func (ri *AbstractObject) GetAccessor(request *tools.APIRequest) Accessor { return nil } func (r *AbstractObject) Unsign() { r.Signature = nil } func (r *AbstractObject) Sign() { priv, err := tools.LoadKeyFromFilePrivate() // your node private key if err != nil { return } b, _ := json.Marshal(r.DeepCopy()) hash := sha256.Sum256(b) r.Signature, err = priv.Sign(hash[:]) } func (r *AbstractObject) SetID(id string) { r.UUID = id } func (r *AbstractObject) DeepCopy() *AbstractObject { var obj AbstractObject b, err := json.Marshal(r) if err != nil { return nil } if err := json.Unmarshal(b, &obj); err != nil { return nil } return &obj } func (r *AbstractObject) SetName(name string) { r.Name = name } func (r *AbstractObject) GenerateID() { if r.UUID == "" { r.UUID = uuid.New().String() } } func (r *AbstractObject) StoreDraftDefault() { r.IsDraft = false } func (r *AbstractObject) CanUpdate(set DBObject) (bool, DBObject) { return true, set } func (r *AbstractObject) CanDelete() bool { return true } func (r *AbstractObject) IsDrafted() bool { return r.IsDraft } // GetID implements ShallowDBObject. func (ao AbstractObject) GetID() string { return ao.UUID } func (ao AbstractObject) GetSignature() []byte { return ao.Signature } // GetName implements ShallowDBObject. func (ao AbstractObject) GetName() string { return ao.Name } func (ao *AbstractObject) GetCreatorID() string { return ao.CreatorID } func (ao *AbstractObject) UpToDate(user string, peer string, create bool) { ao.UpdateDate = time.Now() ao.UpdaterID = peer ao.UserUpdaterID = user if create && ao.CreatorID != "" { ao.CreationDate = time.Now() ao.CreatorID = peer ao.UserCreatorID = user } } func (ao *AbstractObject) VerifyAuth(callName string, request *tools.APIRequest) bool { return (ao.AccessMode == Public && callName == "get") || (request != nil && (request.Admin || (ao.CreatorID == request.PeerID && request.PeerID != ""))) } // TODO : check write per auth func (ao *AbstractObject) GetObjectFilters(search string) *dbs.Filters { if search == "*" { search = "" } return &dbs.Filters{ Or: map[string][]dbs.Filter{ // filter by name if no filters are provided "abstractobject.name": {{Operator: dbs.LIKE.String(), Value: search}}, }} } func (dma *AbstractObject) Deserialize(j map[string]interface{}, obj DBObject) DBObject { b, err := json.Marshal(j) if err != nil { return nil } json.Unmarshal(b, obj) return obj } func (dma *AbstractObject) Serialize(obj DBObject) map[string]interface{} { var m map[string]interface{} b, err := json.Marshal(obj) if err != nil { return nil } json.Unmarshal(b, &m) return m } type AbstractAccessor[T DBObject] struct { Logger zerolog.Logger // Logger is the logger of the accessor, it's a specilized logger for the accessor Type tools.DataType // Type is the data type of the accessor Request *tools.APIRequest // Caller is the http caller of the accessor (optionnal) only need in a peer connection ResourceModelAccessor Accessor New func() T NotImplemented []string } func (r *AbstractAccessor[T]) ShouldVerifyAuth() bool { return true } func (r *AbstractAccessor[T]) GetRequest() *tools.APIRequest { return r.Request } func (dma *AbstractAccessor[T]) GetUser() string { if dma.Request == nil { return "" } return dma.Request.Username } func (dma *AbstractAccessor[T]) GetPeerID() string { if dma.Request == nil { return "" } return dma.Request.PeerID } func (dma *AbstractAccessor[T]) GetGroups() []string { if dma.Request == nil { return []string{} } return dma.Request.Groups } func (dma *AbstractAccessor[T]) GetLogger() *zerolog.Logger { return &dma.Logger } func (dma *AbstractAccessor[T]) GetType() tools.DataType { return dma.Type } func (dma *AbstractAccessor[T]) GetCaller() *tools.HTTPCaller { if dma.Request == nil { return nil } return dma.Request.Caller } /* * Nothing special here, just the basic CRUD operations */ func (a *AbstractAccessor[T]) DeleteOne(id string) (DBObject, int, error) { if len(a.NotImplemented) > 0 && slices.Contains(a.NotImplemented, "DeleteOne") { return nil, 404, errors.New("not implemented") } return GenericDeleteOne(id, a) } func (a *AbstractAccessor[T]) UpdateOne(set DBObject, id string) (DBObject, int, error) { if len(a.NotImplemented) > 0 && slices.Contains(a.NotImplemented, "UpdateOne") { return nil, 404, errors.New("not implemented") } // should verify if a source is existing... return GenericUpdateOne(set, id, a, a.New()) } func (a *AbstractAccessor[T]) StoreOne(data DBObject) (DBObject, int, error) { if len(a.NotImplemented) > 0 && slices.Contains(a.NotImplemented, "StoreOne") { return nil, 404, errors.New("not implemented") } return GenericStoreOne(data.(T), a) } func (a *AbstractAccessor[T]) CopyOne(data DBObject) (DBObject, int, error) { if len(a.NotImplemented) > 0 && slices.Contains(a.NotImplemented, "CopyOne") { return nil, 404, errors.New("not implemented") } return GenericStoreOne(data.(T), a) } func (a *AbstractAccessor[T]) LoadOne(id string) (DBObject, int, error) { return GenericLoadOne[T](id, func(d DBObject) (DBObject, int, error) { return d, 200, nil }, a) } func (a *AbstractAccessor[T]) LoadAll(isDraft bool) ([]ShallowDBObject, int, error) { return GenericLoadAll[T](a.GetExec(isDraft), isDraft, a) } func (a *AbstractAccessor[T]) Search(filters *dbs.Filters, search string, isDraft bool) ([]ShallowDBObject, int, error) { return GenericSearch[T](filters, search, a.New().GetObjectFilters(search), a.GetExec(isDraft), isDraft, a) } func (a *AbstractAccessor[T]) GetExec(isDraft bool) func(DBObject) ShallowDBObject { return func(d DBObject) ShallowDBObject { return d } }