mirror of
https://github.com/project-zot/zot.git
synced 2025-01-06 22:40:28 -05:00
bcdd9988f5
1. Only scan CVEs for images returned by graphql calls Since pagination was refactored to account for image indexes, we had started to run the CVE scanner before pagination was applied, resulting in decreased ZOT performance if CVE information was requested 2. Increase in medory-cache of cve results to 1m, from 10k digests. 3. Update CVE model to use CVSS severity values in our code. Previously we relied upon the strings returned by trivy directly, and the sorting they implemented. Since CVE severities are standardized, we don't need to pass around an adapter object just for pagination and sorting purposes anymore. This also improves our testing since we don't mock the sorting functions anymore. 4. Fix a flaky CLI test not waiting for the zot service to start. 5. Add the search build label on search/cve tests which were missing it. 6. The boltdb update method was used in a few places where view was supposed to be called. 7. Add logs for start and finish of parsing MetaDB. 8. Avoid unmarshalling twice to obtain annotations for multiarch images. Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
2006 lines
49 KiB
Go
2006 lines
49 KiB
Go
package boltdb
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"go.etcd.io/bbolt"
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
"zotregistry.io/zot/pkg/api/constants"
|
|
zcommon "zotregistry.io/zot/pkg/common"
|
|
"zotregistry.io/zot/pkg/log"
|
|
"zotregistry.io/zot/pkg/meta/common"
|
|
mTypes "zotregistry.io/zot/pkg/meta/types"
|
|
"zotregistry.io/zot/pkg/meta/version"
|
|
reqCtx "zotregistry.io/zot/pkg/requestcontext"
|
|
)
|
|
|
|
type BoltDB struct {
|
|
DB *bbolt.DB
|
|
Patches []func(DB *bbolt.DB) error
|
|
imgTrustStore mTypes.ImageTrustStore
|
|
Log log.Logger
|
|
}
|
|
|
|
func New(boltDB *bbolt.DB, log log.Logger) (*BoltDB, error) {
|
|
err := boltDB.Update(func(transaction *bbolt.Tx) error {
|
|
versionBuck, err := transaction.CreateBucketIfNotExists([]byte(VersionBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = versionBuck.Put([]byte(version.DBVersionKey), []byte(version.CurrentVersion))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = transaction.CreateBucketIfNotExists([]byte(ManifestDataBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = transaction.CreateBucketIfNotExists([]byte(IndexDataBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = transaction.CreateBucketIfNotExists([]byte(RepoMetadataBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = transaction.CreateBucketIfNotExists([]byte(UserDataBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = transaction.CreateBucketIfNotExists([]byte(UserAPIKeysBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &BoltDB{
|
|
DB: boltDB,
|
|
Patches: version.GetBoltDBPatches(),
|
|
imgTrustStore: nil,
|
|
Log: log,
|
|
}, nil
|
|
}
|
|
|
|
func (bdw *BoltDB) ImageTrustStore() mTypes.ImageTrustStore {
|
|
return bdw.imgTrustStore
|
|
}
|
|
|
|
func (bdw *BoltDB) SetImageTrustStore(imgTrustStore mTypes.ImageTrustStore) {
|
|
bdw.imgTrustStore = imgTrustStore
|
|
}
|
|
|
|
func (bdw *BoltDB) SetManifestData(manifestDigest godigest.Digest, manifestData mTypes.ManifestData) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(ManifestDataBucket))
|
|
|
|
mdBlob, err := json.Marshal(manifestData)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while calculating blob for manifest with digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
err = buck.Put([]byte(manifestDigest), mdBlob)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while setting manifest data with for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetManifestData(manifestDigest godigest.Digest) (mTypes.ManifestData, error) {
|
|
var manifestData mTypes.ManifestData
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(ManifestDataBucket))
|
|
|
|
mdBlob := buck.Get([]byte(manifestDigest))
|
|
|
|
if len(mdBlob) == 0 {
|
|
return zerr.ErrManifestDataNotFound
|
|
}
|
|
|
|
err := json.Unmarshal(mdBlob, &manifestData)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling manifest meta for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return manifestData, err
|
|
}
|
|
|
|
func (bdw *BoltDB) SetManifestMeta(repo string, manifestDigest godigest.Digest, manifestMeta mTypes.ManifestMetadata,
|
|
) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
dataBuck := tx.Bucket([]byte(ManifestDataBucket))
|
|
repoBuck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMeta := mTypes.RepoMetadata{
|
|
Name: repo,
|
|
Tags: map[string]mTypes.Descriptor{},
|
|
Statistics: map[string]mTypes.DescriptorStatistics{},
|
|
Signatures: map[string]mTypes.ManifestSignatures{},
|
|
Referrers: map[string][]mTypes.ReferrerInfo{},
|
|
}
|
|
|
|
repoMetaBlob := repoBuck.Get([]byte(repo))
|
|
if len(repoMetaBlob) > 0 {
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
mdBlob, err := json.Marshal(mTypes.ManifestData{
|
|
ManifestBlob: manifestMeta.ManifestBlob,
|
|
ConfigBlob: manifestMeta.ConfigBlob,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while calculating blob for manifest with digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
err = dataBuck.Put([]byte(manifestDigest), mdBlob)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while setting manifest meta with for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
updatedRepoMeta := common.UpdateManifestMeta(repoMeta, manifestDigest, manifestMeta)
|
|
|
|
updatedRepoMetaBlob, err := json.Marshal(updatedRepoMeta)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while calculating blob for updated repo meta '%s' %w", repo, err)
|
|
}
|
|
|
|
return repoBuck.Put([]byte(repo), updatedRepoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetManifestMeta(repo string, manifestDigest godigest.Digest) (mTypes.ManifestMetadata, error) {
|
|
var manifestMetadata mTypes.ManifestMetadata
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
dataBuck := tx.Bucket([]byte(ManifestDataBucket))
|
|
repoBuck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
mdBlob := dataBuck.Get([]byte(manifestDigest))
|
|
|
|
if len(mdBlob) == 0 {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
var manifestData mTypes.ManifestData
|
|
|
|
err := json.Unmarshal(mdBlob, &manifestData)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling manifest meta for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
repoMetaBlob := repoBuck.Get([]byte(repo))
|
|
if len(repoMetaBlob) > 0 {
|
|
err = json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling manifest meta for digest %s %w", manifestDigest, err)
|
|
}
|
|
}
|
|
|
|
manifestMetadata.ManifestBlob = manifestData.ManifestBlob
|
|
manifestMetadata.ConfigBlob = manifestData.ConfigBlob
|
|
manifestMetadata.DownloadCount = repoMeta.Statistics[manifestDigest.String()].DownloadCount
|
|
|
|
manifestMetadata.Signatures = mTypes.ManifestSignatures{}
|
|
if repoMeta.Signatures[manifestDigest.String()] != nil {
|
|
manifestMetadata.Signatures = repoMeta.Signatures[manifestDigest.String()]
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return manifestMetadata, err
|
|
}
|
|
|
|
func (bdw *BoltDB) SetIndexData(indexDigest godigest.Digest, indexMetadata mTypes.IndexData) error {
|
|
// we make the assumption that the oci layout is consistent and all manifests refferenced inside the
|
|
// index are present
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(IndexDataBucket))
|
|
|
|
imBlob, err := json.Marshal(indexMetadata)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while calculating blob for manifest with digest %s %w", indexDigest, err)
|
|
}
|
|
|
|
err = buck.Put([]byte(indexDigest), imBlob)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while setting manifest meta with for digest %s %w", indexDigest, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetIndexData(indexDigest godigest.Digest) (mTypes.IndexData, error) {
|
|
var indexMetadata mTypes.IndexData
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(IndexDataBucket))
|
|
|
|
mmBlob := buck.Get([]byte(indexDigest))
|
|
|
|
if len(mmBlob) == 0 {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
err := json.Unmarshal(mmBlob, &indexMetadata)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling manifest meta for digest %s %w", indexDigest, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return indexMetadata, err
|
|
}
|
|
|
|
func (bdw BoltDB) SetReferrer(repo string, referredDigest godigest.Digest, referrer mTypes.ReferrerInfo) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
// object not found
|
|
if len(repoMetaBlob) == 0 {
|
|
var err error
|
|
|
|
// create a new object
|
|
repoMeta := mTypes.RepoMetadata{
|
|
Name: repo,
|
|
Tags: map[string]mTypes.Descriptor{},
|
|
Statistics: map[string]mTypes.DescriptorStatistics{},
|
|
Signatures: map[string]mTypes.ManifestSignatures{},
|
|
Referrers: map[string][]mTypes.ReferrerInfo{
|
|
referredDigest.String(): {
|
|
referrer,
|
|
},
|
|
},
|
|
}
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
}
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
referrers := repoMeta.Referrers[referredDigest.String()]
|
|
|
|
for i := range referrers {
|
|
if referrers[i].Digest == referrer.Digest {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
referrers = append(referrers, referrer)
|
|
|
|
repoMeta.Referrers[referredDigest.String()] = referrers
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw BoltDB) DeleteReferrer(repo string, referredDigest godigest.Digest,
|
|
referrerDigest godigest.Digest,
|
|
) error {
|
|
return bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
if len(repoMetaBlob) == 0 {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
referrers := repoMeta.Referrers[referredDigest.String()]
|
|
|
|
for i := range referrers {
|
|
if referrers[i].Digest == referrerDigest.String() {
|
|
referrers = append(referrers[:i], referrers[i+1:]...)
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
repoMeta.Referrers[referredDigest.String()] = referrers
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
}
|
|
|
|
func (bdw BoltDB) GetReferrersInfo(repo string, referredDigest godigest.Digest, artifactTypes []string,
|
|
) ([]mTypes.ReferrerInfo, error) {
|
|
referrersInfoResult := []mTypes.ReferrerInfo{}
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if len(repoMetaBlob) == 0 {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
referrersInfo := repoMeta.Referrers[referredDigest.String()]
|
|
|
|
for i := range referrersInfo {
|
|
if !common.MatchesArtifactTypes(referrersInfo[i].ArtifactType, artifactTypes) {
|
|
continue
|
|
}
|
|
|
|
referrersInfoResult = append(referrersInfoResult, referrersInfo[i])
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return referrersInfoResult, err
|
|
}
|
|
|
|
func (bdw *BoltDB) SetRepoReference(repo string, reference string, manifestDigest godigest.Digest,
|
|
mediaType string,
|
|
) error {
|
|
if err := common.ValidateRepoReferenceInput(repo, reference, manifestDigest); err != nil {
|
|
return err
|
|
}
|
|
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
repoMeta := mTypes.RepoMetadata{
|
|
Name: repo,
|
|
Tags: map[string]mTypes.Descriptor{},
|
|
Statistics: map[string]mTypes.DescriptorStatistics{},
|
|
Signatures: map[string]mTypes.ManifestSignatures{},
|
|
Referrers: map[string][]mTypes.ReferrerInfo{},
|
|
}
|
|
|
|
// object not found
|
|
if len(repoMetaBlob) > 0 {
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if !common.ReferenceIsDigest(reference) {
|
|
repoMeta.Tags[reference] = mTypes.Descriptor{
|
|
Digest: manifestDigest.String(),
|
|
MediaType: mediaType,
|
|
}
|
|
}
|
|
|
|
if _, ok := repoMeta.Statistics[manifestDigest.String()]; !ok {
|
|
repoMeta.Statistics[manifestDigest.String()] = mTypes.DescriptorStatistics{DownloadCount: 0}
|
|
}
|
|
|
|
if _, ok := repoMeta.Signatures[manifestDigest.String()]; !ok {
|
|
repoMeta.Signatures[manifestDigest.String()] = mTypes.ManifestSignatures{}
|
|
}
|
|
|
|
if _, ok := repoMeta.Referrers[manifestDigest.String()]; !ok {
|
|
repoMeta.Referrers[manifestDigest.String()] = []mTypes.ReferrerInfo{}
|
|
}
|
|
|
|
repoMetaBlob, err := json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetRepoMeta(repo string) (mTypes.RepoMetadata, error) {
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
// object not found
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
// object found
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return repoMeta, err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetUserRepoMeta(ctx context.Context, repo string) (mTypes.RepoMetadata, error) {
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
userBookmarks := getUserBookmarks(ctx, tx)
|
|
userStars := getUserStars(ctx, tx)
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
// object not found
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
// object found
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta.IsBookmarked = zcommon.Contains(userBookmarks, repo)
|
|
repoMeta.IsStarred = zcommon.Contains(userStars, repo)
|
|
|
|
return nil
|
|
})
|
|
|
|
return repoMeta, err
|
|
}
|
|
|
|
func (bdw *BoltDB) SetRepoMeta(repo string, repoMeta mTypes.RepoMetadata) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMeta.Name = repo
|
|
|
|
repoMetaBlob, err := json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) DeleteRepoTag(repo string, tag string) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
// object not found
|
|
if repoMetaBlob == nil {
|
|
return nil
|
|
}
|
|
|
|
// object found
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delete(repoMeta.Tags, tag)
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) IncrementRepoStars(repo string) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta.Stars++
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) DecrementRepoStars(repo string) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if repoMeta.Stars > 0 {
|
|
repoMeta.Stars--
|
|
}
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetRepoStars(repo string) (int, error) {
|
|
stars := 0
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
buck.Get([]byte(repo))
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
stars = repoMeta.Stars
|
|
|
|
return nil
|
|
})
|
|
|
|
return stars, err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetMultipleRepoMeta(ctx context.Context, filter func(repoMeta mTypes.RepoMetadata) bool,
|
|
) ([]mTypes.RepoMetadata, error) {
|
|
foundRepos := []mTypes.RepoMetadata{}
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
cursor := buck.Cursor()
|
|
|
|
for repoName, repoMetaBlob := cursor.First(); repoName != nil; repoName, repoMetaBlob = cursor.Next() {
|
|
if ok, err := reqCtx.RepoIsUserAvailable(ctx, string(repoName)); !ok || err != nil {
|
|
continue
|
|
}
|
|
|
|
repoMeta := mTypes.RepoMetadata{}
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if filter(repoMeta) {
|
|
foundRepos = append(foundRepos, repoMeta)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return foundRepos, err
|
|
}
|
|
|
|
func (bdw *BoltDB) IncrementImageDownloads(repo string, reference string) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
manifestDigest := reference
|
|
|
|
if !common.ReferenceIsDigest(reference) {
|
|
// search digest for tag
|
|
descriptor, found := repoMeta.Tags[reference]
|
|
|
|
if !found {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
manifestDigest = descriptor.Digest
|
|
}
|
|
|
|
manifestStatistics := repoMeta.Statistics[manifestDigest]
|
|
manifestStatistics.DownloadCount++
|
|
repoMeta.Statistics[manifestDigest] = manifestStatistics
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) UpdateSignaturesValidity(repo string, manifestDigest godigest.Digest) error {
|
|
err := bdw.DB.Update(func(transaction *bbolt.Tx) error {
|
|
imgTrustStore := bdw.ImageTrustStore()
|
|
|
|
if imgTrustStore == nil {
|
|
return nil
|
|
}
|
|
|
|
// get ManifestData of signed manifest
|
|
manifestBuck := transaction.Bucket([]byte(ManifestDataBucket))
|
|
mdBlob := manifestBuck.Get([]byte(manifestDigest))
|
|
|
|
var blob []byte
|
|
|
|
if len(mdBlob) != 0 {
|
|
var manifestData mTypes.ManifestData
|
|
|
|
err := json.Unmarshal(mdBlob, &manifestData)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: %w error while unmashaling manifest meta for digest %s", err, manifestDigest)
|
|
}
|
|
|
|
blob = manifestData.ManifestBlob
|
|
} else {
|
|
var indexData mTypes.IndexData
|
|
|
|
indexBuck := transaction.Bucket([]byte(IndexDataBucket))
|
|
idBlob := indexBuck.Get([]byte(manifestDigest))
|
|
|
|
if len(idBlob) == 0 {
|
|
// manifest meta not found, updating signatures with details about validity and author will not be performed
|
|
return nil
|
|
}
|
|
|
|
err := json.Unmarshal(idBlob, &indexData)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: %w error while unmashaling index meta for digest %s", err, manifestDigest)
|
|
}
|
|
|
|
blob = indexData.IndexBlob
|
|
}
|
|
|
|
// update signatures with details about validity and author
|
|
repoBuck := transaction.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := repoBuck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
manifestSignatures := mTypes.ManifestSignatures{}
|
|
for sigType, sigs := range repoMeta.Signatures[manifestDigest.String()] {
|
|
signaturesInfo := []mTypes.SignatureInfo{}
|
|
|
|
for _, sigInfo := range sigs {
|
|
layersInfo := []mTypes.LayerInfo{}
|
|
|
|
for _, layerInfo := range sigInfo.LayersInfo {
|
|
author, date, isTrusted, _ := imgTrustStore.VerifySignature(sigType, layerInfo.LayerContent,
|
|
layerInfo.SignatureKey, manifestDigest, blob, repo)
|
|
|
|
if isTrusted {
|
|
layerInfo.Signer = author
|
|
}
|
|
|
|
if !date.IsZero() {
|
|
layerInfo.Signer = author
|
|
layerInfo.Date = date
|
|
}
|
|
|
|
layersInfo = append(layersInfo, layerInfo)
|
|
}
|
|
|
|
signaturesInfo = append(signaturesInfo, mTypes.SignatureInfo{
|
|
SignatureManifestDigest: sigInfo.SignatureManifestDigest,
|
|
LayersInfo: layersInfo,
|
|
})
|
|
}
|
|
|
|
manifestSignatures[sigType] = signaturesInfo
|
|
}
|
|
|
|
repoMeta.Signatures[manifestDigest.String()] = manifestSignatures
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return repoBuck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) AddManifestSignature(repo string, signedManifestDigest godigest.Digest,
|
|
sygMeta mTypes.SignatureMetadata,
|
|
) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
|
|
if len(repoMetaBlob) == 0 {
|
|
var err error
|
|
// create a new object
|
|
repoMeta := mTypes.RepoMetadata{
|
|
Name: repo,
|
|
Tags: map[string]mTypes.Descriptor{},
|
|
Signatures: map[string]mTypes.ManifestSignatures{
|
|
signedManifestDigest.String(): {
|
|
sygMeta.SignatureType: []mTypes.SignatureInfo{
|
|
{
|
|
SignatureManifestDigest: sygMeta.SignatureDigest,
|
|
LayersInfo: sygMeta.LayersInfo,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Statistics: map[string]mTypes.DescriptorStatistics{},
|
|
Referrers: map[string][]mTypes.ReferrerInfo{},
|
|
}
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
manifestSignatures mTypes.ManifestSignatures
|
|
found bool
|
|
)
|
|
|
|
if manifestSignatures, found = repoMeta.Signatures[signedManifestDigest.String()]; !found {
|
|
manifestSignatures = mTypes.ManifestSignatures{}
|
|
}
|
|
|
|
signatureSlice := manifestSignatures[sygMeta.SignatureType]
|
|
if !common.SignatureAlreadyExists(signatureSlice, sygMeta) {
|
|
if sygMeta.SignatureType == zcommon.NotationSignature {
|
|
signatureSlice = append(signatureSlice, mTypes.SignatureInfo{
|
|
SignatureManifestDigest: sygMeta.SignatureDigest,
|
|
LayersInfo: sygMeta.LayersInfo,
|
|
})
|
|
} else if sygMeta.SignatureType == zcommon.CosignSignature {
|
|
signatureSlice = []mTypes.SignatureInfo{{
|
|
SignatureManifestDigest: sygMeta.SignatureDigest,
|
|
LayersInfo: sygMeta.LayersInfo,
|
|
}}
|
|
}
|
|
}
|
|
|
|
manifestSignatures[sygMeta.SignatureType] = signatureSlice
|
|
|
|
repoMeta.Signatures[signedManifestDigest.String()] = manifestSignatures
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) DeleteSignature(repo string, signedManifestDigest godigest.Digest,
|
|
sigMeta mTypes.SignatureMetadata,
|
|
) error {
|
|
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := buck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sigType := sigMeta.SignatureType
|
|
|
|
var (
|
|
manifestSignatures mTypes.ManifestSignatures
|
|
found bool
|
|
)
|
|
|
|
if manifestSignatures, found = repoMeta.Signatures[signedManifestDigest.String()]; !found {
|
|
return zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
signatureSlice := manifestSignatures[sigType]
|
|
|
|
newSignatureSlice := make([]mTypes.SignatureInfo, 0, len(signatureSlice)-1)
|
|
|
|
for _, sigDigest := range signatureSlice {
|
|
if sigDigest.SignatureManifestDigest != sigMeta.SignatureDigest {
|
|
newSignatureSlice = append(newSignatureSlice, sigDigest)
|
|
}
|
|
}
|
|
|
|
manifestSignatures[sigType] = newSignatureSlice
|
|
|
|
repoMeta.Signatures[signedManifestDigest.String()] = manifestSignatures
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return buck.Put([]byte(repo), repoMetaBlob)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) SearchRepos(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMetadata, map[string]mTypes.ManifestMetadata, map[string]mTypes.IndexData, error) {
|
|
var (
|
|
foundRepos = make([]mTypes.RepoMetadata, 0)
|
|
manifestMetadataMap = make(map[string]mTypes.ManifestMetadata)
|
|
indexDataMap = make(map[string]mTypes.IndexData)
|
|
)
|
|
|
|
err := bdw.DB.View(func(transaction *bbolt.Tx) error {
|
|
var (
|
|
repoBuck = transaction.Bucket([]byte(RepoMetadataBucket))
|
|
indexBuck = transaction.Bucket([]byte(IndexDataBucket))
|
|
manifestBuck = transaction.Bucket([]byte(ManifestDataBucket))
|
|
userBookmarks = getUserBookmarks(ctx, transaction)
|
|
userStars = getUserStars(ctx, transaction)
|
|
)
|
|
|
|
cursor := repoBuck.Cursor()
|
|
|
|
for repoName, repoMetaBlob := cursor.First(); repoName != nil; repoName, repoMetaBlob = cursor.Next() {
|
|
if ok, err := reqCtx.RepoIsUserAvailable(ctx, string(repoName)); !ok || err != nil {
|
|
continue
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rank := common.RankRepoName(searchText, repoMeta.Name)
|
|
if rank == -1 {
|
|
continue
|
|
}
|
|
|
|
repoMeta.IsBookmarked = zcommon.Contains(userBookmarks, repoMeta.Name)
|
|
repoMeta.IsStarred = zcommon.Contains(userStars, repoMeta.Name)
|
|
repoMeta.Rank = rank
|
|
|
|
for tag, descriptor := range repoMeta.Tags {
|
|
switch descriptor.MediaType {
|
|
case ispec.MediaTypeImageManifest:
|
|
manifestDigest := descriptor.Digest
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest,
|
|
manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error fetching manifest meta for manifest with digest %s %w",
|
|
manifestDigest, err)
|
|
}
|
|
|
|
manifestMetadataMap[descriptor.Digest] = manifestMeta
|
|
case ispec.MediaTypeImageIndex:
|
|
indexDigest := descriptor.Digest
|
|
|
|
indexData, err := fetchIndexDataWithCheck(indexDigest, indexDataMap, indexBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error fetching index data for index with digest %s %w",
|
|
indexDigest, err)
|
|
}
|
|
|
|
var indexContent ispec.Index
|
|
|
|
err = json.Unmarshal(indexData.IndexBlob, &indexContent)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling index content for %s:%s %w",
|
|
repoName, tag, err)
|
|
}
|
|
|
|
for _, manifest := range indexContent.Manifests {
|
|
manifestDigest := manifest.Digest
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest.String(),
|
|
manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
manifestMetadataMap[manifest.Digest.String()] = manifestMeta
|
|
}
|
|
|
|
indexDataMap[indexDigest] = indexData
|
|
default:
|
|
bdw.Log.Error().Str("mediaType", descriptor.MediaType).Msg("Unsupported media type")
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
foundRepos = append(foundRepos, repoMeta)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return foundRepos, manifestMetadataMap, indexDataMap, err
|
|
}
|
|
|
|
func fetchManifestMetaWithCheck(repoMeta mTypes.RepoMetadata, manifestDigest string,
|
|
manifestMetadataMap map[string]mTypes.ManifestMetadata, manifestBuck *bbolt.Bucket,
|
|
) (mTypes.ManifestMetadata, error) {
|
|
manifestMeta, manifestDownloaded := manifestMetadataMap[manifestDigest]
|
|
|
|
if !manifestDownloaded {
|
|
var manifestData mTypes.ManifestData
|
|
|
|
manifestDataBlob := manifestBuck.Get([]byte(manifestDigest))
|
|
if manifestDataBlob == nil {
|
|
return mTypes.ManifestMetadata{}, zerr.ErrManifestMetaNotFound
|
|
}
|
|
|
|
err := json.Unmarshal(manifestDataBlob, &manifestData)
|
|
if err != nil {
|
|
return mTypes.ManifestMetadata{}, fmt.Errorf("metadb: error while unmarshaling manifest metadata for digest %s %w",
|
|
manifestDigest, err)
|
|
}
|
|
|
|
manifestMeta = NewManifestMetadata(manifestDigest, repoMeta, manifestData)
|
|
}
|
|
|
|
return manifestMeta, nil
|
|
}
|
|
|
|
func fetchIndexDataWithCheck(indexDigest string, indexDataMap map[string]mTypes.IndexData,
|
|
indexBuck *bbolt.Bucket,
|
|
) (mTypes.IndexData, error) {
|
|
var (
|
|
indexData mTypes.IndexData
|
|
err error
|
|
)
|
|
|
|
indexData, indexExists := indexDataMap[indexDigest]
|
|
|
|
if !indexExists {
|
|
indexDataBlob := indexBuck.Get([]byte(indexDigest))
|
|
if indexDataBlob == nil {
|
|
return mTypes.IndexData{}, zerr.ErrIndexDataNotFount
|
|
}
|
|
|
|
err := json.Unmarshal(indexDataBlob, &indexData)
|
|
if err != nil {
|
|
return mTypes.IndexData{},
|
|
fmt.Errorf("metadb: error while unmashaling index data for digest %s %w", indexDigest, err)
|
|
}
|
|
}
|
|
|
|
return indexData, err
|
|
}
|
|
|
|
func NewManifestMetadata(manifestDigest string, repoMeta mTypes.RepoMetadata,
|
|
manifestData mTypes.ManifestData,
|
|
) mTypes.ManifestMetadata {
|
|
manifestMeta := mTypes.ManifestMetadata{
|
|
ManifestBlob: manifestData.ManifestBlob,
|
|
ConfigBlob: manifestData.ConfigBlob,
|
|
}
|
|
|
|
manifestMeta.DownloadCount = repoMeta.Statistics[manifestDigest].DownloadCount
|
|
|
|
manifestMeta.Signatures = mTypes.ManifestSignatures{}
|
|
if repoMeta.Signatures[manifestDigest] != nil {
|
|
manifestMeta.Signatures = repoMeta.Signatures[manifestDigest]
|
|
}
|
|
|
|
return manifestMeta
|
|
}
|
|
|
|
func (bdw *BoltDB) FilterTags(ctx context.Context, filterFunc mTypes.FilterFunc,
|
|
) ([]mTypes.RepoMetadata, map[string]mTypes.ManifestMetadata, map[string]mTypes.IndexData, error,
|
|
) {
|
|
var (
|
|
foundRepos = make([]mTypes.RepoMetadata, 0)
|
|
manifestMetadataMap = make(map[string]mTypes.ManifestMetadata)
|
|
indexDataMap = make(map[string]mTypes.IndexData)
|
|
)
|
|
|
|
err := bdw.DB.View(func(transaction *bbolt.Tx) error {
|
|
var (
|
|
repoBuck = transaction.Bucket([]byte(RepoMetadataBucket))
|
|
indexBuck = transaction.Bucket([]byte(IndexDataBucket))
|
|
manifestBuck = transaction.Bucket([]byte(ManifestDataBucket))
|
|
cursor = repoBuck.Cursor()
|
|
userBookmarks = getUserBookmarks(ctx, transaction)
|
|
userStars = getUserStars(ctx, transaction)
|
|
)
|
|
|
|
repoName, repoMetaBlob := cursor.First()
|
|
|
|
for ; repoName != nil; repoName, repoMetaBlob = cursor.Next() {
|
|
if ok, err := reqCtx.RepoIsUserAvailable(ctx, string(repoName)); !ok || err != nil {
|
|
continue
|
|
}
|
|
|
|
repoMeta := mTypes.RepoMetadata{}
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta.IsBookmarked = zcommon.Contains(userBookmarks, repoMeta.Name)
|
|
repoMeta.IsStarred = zcommon.Contains(userStars, repoMeta.Name)
|
|
|
|
matchedTags := make(map[string]mTypes.Descriptor)
|
|
// take all manifestsMeta
|
|
for tag, descriptor := range repoMeta.Tags {
|
|
switch descriptor.MediaType {
|
|
case ispec.MediaTypeImageManifest:
|
|
manifestDigest := descriptor.Digest
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest, manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling manifest metadata for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
if filterFunc(repoMeta, manifestMeta) {
|
|
matchedTags[tag] = descriptor
|
|
manifestMetadataMap[manifestDigest] = manifestMeta
|
|
}
|
|
case ispec.MediaTypeImageIndex:
|
|
indexDigest := descriptor.Digest
|
|
|
|
indexData, err := fetchIndexDataWithCheck(indexDigest, indexDataMap, indexBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while getting index data for digest %s %w", indexDigest, err)
|
|
}
|
|
|
|
var indexContent ispec.Index
|
|
|
|
err = json.Unmarshal(indexData.IndexBlob, &indexContent)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while unmashaling index content for digest %s %w", indexDigest, err)
|
|
}
|
|
|
|
matchedManifests := []ispec.Descriptor{}
|
|
|
|
for _, manifest := range indexContent.Manifests {
|
|
manifestDigest := manifest.Digest.String()
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest, manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error while getting manifest data for digest %s %w", manifestDigest, err)
|
|
}
|
|
|
|
if filterFunc(repoMeta, manifestMeta) {
|
|
matchedManifests = append(matchedManifests, manifest)
|
|
manifestMetadataMap[manifestDigest] = manifestMeta
|
|
}
|
|
}
|
|
|
|
if len(matchedManifests) > 0 {
|
|
indexContent.Manifests = matchedManifests
|
|
|
|
indexBlob, err := json.Marshal(indexContent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
indexData.IndexBlob = indexBlob
|
|
|
|
indexDataMap[indexDigest] = indexData
|
|
matchedTags[tag] = descriptor
|
|
}
|
|
default:
|
|
bdw.Log.Error().Str("mediaType", descriptor.MediaType).Msg("Unsupported media type")
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(matchedTags) == 0 {
|
|
continue
|
|
}
|
|
|
|
repoMeta.Tags = matchedTags
|
|
|
|
foundRepos = append(foundRepos, repoMeta)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return foundRepos, manifestMetadataMap, indexDataMap, err
|
|
}
|
|
|
|
func (bdw *BoltDB) FilterRepos(ctx context.Context, filter mTypes.FilterRepoFunc) (
|
|
[]mTypes.RepoMetadata, map[string]mTypes.ManifestMetadata, map[string]mTypes.IndexData, error,
|
|
) {
|
|
foundRepos := make([]mTypes.RepoMetadata, 0)
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
var (
|
|
buck = tx.Bucket([]byte(RepoMetadataBucket))
|
|
cursor = buck.Cursor()
|
|
userBookmarks = getUserBookmarks(ctx, tx)
|
|
userStars = getUserStars(ctx, tx)
|
|
)
|
|
|
|
for repoName, repoMetaBlob := cursor.First(); repoName != nil; repoName, repoMetaBlob = cursor.Next() {
|
|
if ok, err := reqCtx.RepoIsUserAvailable(ctx, string(repoName)); !ok || err != nil {
|
|
continue
|
|
}
|
|
|
|
repoMeta := mTypes.RepoMetadata{}
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta.IsBookmarked = zcommon.Contains(userBookmarks, repoMeta.Name)
|
|
repoMeta.IsStarred = zcommon.Contains(userStars, repoMeta.Name)
|
|
|
|
if filter(repoMeta) {
|
|
foundRepos = append(foundRepos, repoMeta)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return []mTypes.RepoMetadata{}, map[string]mTypes.ManifestMetadata{}, map[string]mTypes.IndexData{}, err
|
|
}
|
|
|
|
foundManifestMetadataMap, foundIndexDataMap, err := common.FetchDataForRepos(bdw, foundRepos)
|
|
|
|
return foundRepos, foundManifestMetadataMap, foundIndexDataMap, err
|
|
}
|
|
|
|
func (bdw *BoltDB) SearchTags(ctx context.Context, searchText string,
|
|
) ([]mTypes.RepoMetadata, map[string]mTypes.ManifestMetadata, map[string]mTypes.IndexData, error) {
|
|
var (
|
|
foundRepos = make([]mTypes.RepoMetadata, 0)
|
|
manifestMetadataMap = make(map[string]mTypes.ManifestMetadata)
|
|
indexDataMap = make(map[string]mTypes.IndexData)
|
|
)
|
|
|
|
searchedRepo, searchedTag, err := common.GetRepoTag(searchText)
|
|
if err != nil {
|
|
return []mTypes.RepoMetadata{}, map[string]mTypes.ManifestMetadata{}, map[string]mTypes.IndexData{},
|
|
fmt.Errorf("metadb: error while parsing search text, invalid format %w", err)
|
|
}
|
|
|
|
err = bdw.DB.View(func(transaction *bbolt.Tx) error {
|
|
var (
|
|
repoBuck = transaction.Bucket([]byte(RepoMetadataBucket))
|
|
indexBuck = transaction.Bucket([]byte(IndexDataBucket))
|
|
manifestBuck = transaction.Bucket([]byte(ManifestDataBucket))
|
|
userBookmarks = getUserBookmarks(ctx, transaction)
|
|
userStars = getUserStars(ctx, transaction)
|
|
)
|
|
|
|
repoName, repoMetaBlob := repoBuck.Cursor().Seek([]byte(searchedRepo))
|
|
|
|
if string(repoName) != searchedRepo {
|
|
return nil
|
|
}
|
|
|
|
if ok, err := reqCtx.RepoIsUserAvailable(ctx, string(repoName)); !ok || err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta := mTypes.RepoMetadata{}
|
|
|
|
err := json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoMeta.IsBookmarked = zcommon.Contains(userBookmarks, repoMeta.Name)
|
|
repoMeta.IsStarred = zcommon.Contains(userStars, repoMeta.Name)
|
|
|
|
matchedTags := make(map[string]mTypes.Descriptor)
|
|
|
|
for tag, descriptor := range repoMeta.Tags {
|
|
if !strings.HasPrefix(tag, searchedTag) {
|
|
continue
|
|
}
|
|
|
|
matchedTags[tag] = descriptor
|
|
|
|
switch descriptor.MediaType {
|
|
case ispec.MediaTypeImageManifest:
|
|
manifestDigest := descriptor.Digest
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest, manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error fetching manifest meta for manifest with digest %s %w",
|
|
manifestDigest, err)
|
|
}
|
|
|
|
manifestMetadataMap[descriptor.Digest] = manifestMeta
|
|
case ispec.MediaTypeImageIndex:
|
|
indexDigest := descriptor.Digest
|
|
|
|
indexData, err := fetchIndexDataWithCheck(indexDigest, indexDataMap, indexBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error fetching index data for index with digest %s %w",
|
|
indexDigest, err)
|
|
}
|
|
|
|
var indexContent ispec.Index
|
|
|
|
err = json.Unmarshal(indexData.IndexBlob, &indexContent)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error collecting filter data for index with digest %s %w",
|
|
indexDigest, err)
|
|
}
|
|
|
|
for _, manifest := range indexContent.Manifests {
|
|
manifestDigest := manifest.Digest.String()
|
|
|
|
manifestMeta, err := fetchManifestMetaWithCheck(repoMeta, manifestDigest, manifestMetadataMap, manifestBuck)
|
|
if err != nil {
|
|
return fmt.Errorf("metadb: error fetching from db manifest meta for manifest with digest %s %w",
|
|
manifestDigest, err)
|
|
}
|
|
|
|
manifestMetadataMap[manifestDigest] = manifestMeta
|
|
}
|
|
|
|
indexDataMap[indexDigest] = indexData
|
|
default:
|
|
bdw.Log.Error().Str("mediaType", descriptor.MediaType).Msg("Unsupported media type")
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(matchedTags) == 0 {
|
|
return nil
|
|
}
|
|
|
|
repoMeta.Tags = matchedTags
|
|
|
|
foundRepos = append(foundRepos, repoMeta)
|
|
|
|
return nil
|
|
})
|
|
|
|
return foundRepos, manifestMetadataMap, indexDataMap, err
|
|
}
|
|
|
|
func (bdw *BoltDB) ToggleStarRepo(ctx context.Context, repo string) (mTypes.ToggleState, error) {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return mTypes.NotChanged, err
|
|
}
|
|
|
|
if userAc.IsAnonymous() || !userAc.Can(constants.ReadPermission, repo) {
|
|
return mTypes.NotChanged, zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
var res mTypes.ToggleState
|
|
|
|
if err := bdw.DB.Update(func(tx *bbolt.Tx) error { //nolint:varnamelen
|
|
var userData mTypes.UserData
|
|
|
|
err := bdw.getUserData(userid, tx, &userData)
|
|
if err != nil && !errors.Is(err, zerr.ErrUserDataNotFound) {
|
|
return err
|
|
}
|
|
|
|
isRepoStarred := zcommon.Contains(userData.StarredRepos, repo)
|
|
|
|
if isRepoStarred {
|
|
res = mTypes.Removed
|
|
userData.StarredRepos = zcommon.RemoveFrom(userData.StarredRepos, repo)
|
|
} else {
|
|
res = mTypes.Added
|
|
userData.StarredRepos = append(userData.StarredRepos, repo)
|
|
}
|
|
|
|
err = bdw.setUserData(userid, tx, userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repoBuck := tx.Bucket([]byte(RepoMetadataBucket))
|
|
|
|
repoMetaBlob := repoBuck.Get([]byte(repo))
|
|
if repoMetaBlob == nil {
|
|
return zerr.ErrRepoMetaNotFound
|
|
}
|
|
|
|
var repoMeta mTypes.RepoMetadata
|
|
|
|
err = json.Unmarshal(repoMetaBlob, &repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch res {
|
|
case mTypes.Added:
|
|
repoMeta.Stars++
|
|
case mTypes.Removed:
|
|
repoMeta.Stars--
|
|
}
|
|
|
|
repoMetaBlob, err = json.Marshal(repoMeta)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = repoBuck.Put([]byte(repo), repoMetaBlob)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return mTypes.NotChanged, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (bdw *BoltDB) GetStarredRepos(ctx context.Context) ([]string, error) {
|
|
userData, err := bdw.GetUserData(ctx)
|
|
if errors.Is(err, zerr.ErrUserDataNotFound) || errors.Is(err, zerr.ErrUserDataNotAllowed) {
|
|
return []string{}, nil
|
|
}
|
|
|
|
return userData.StarredRepos, err
|
|
}
|
|
|
|
func (bdw *BoltDB) ToggleBookmarkRepo(ctx context.Context, repo string) (mTypes.ToggleState, error) {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return mTypes.NotChanged, err
|
|
}
|
|
|
|
if userAc.IsAnonymous() || !userAc.Can(constants.ReadPermission, repo) {
|
|
return mTypes.NotChanged, zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
var res mTypes.ToggleState
|
|
|
|
if err := bdw.DB.Update(func(transaction *bbolt.Tx) error { //nolint:dupl
|
|
var userData mTypes.UserData
|
|
|
|
err := bdw.getUserData(userid, transaction, &userData)
|
|
if err != nil && !errors.Is(err, zerr.ErrUserDataNotFound) {
|
|
return err
|
|
}
|
|
|
|
isRepoBookmarked := zcommon.Contains(userData.BookmarkedRepos, repo)
|
|
|
|
if isRepoBookmarked {
|
|
res = mTypes.Removed
|
|
userData.BookmarkedRepos = zcommon.RemoveFrom(userData.BookmarkedRepos, repo)
|
|
} else {
|
|
res = mTypes.Added
|
|
userData.BookmarkedRepos = append(userData.BookmarkedRepos, repo)
|
|
}
|
|
|
|
return bdw.setUserData(userid, transaction, userData)
|
|
}); err != nil {
|
|
return mTypes.NotChanged, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (bdw *BoltDB) GetBookmarkedRepos(ctx context.Context) ([]string, error) {
|
|
userData, err := bdw.GetUserData(ctx)
|
|
if errors.Is(err, zerr.ErrUserDataNotFound) || errors.Is(err, zerr.ErrUserDataNotAllowed) {
|
|
return []string{}, nil
|
|
}
|
|
|
|
return userData.BookmarkedRepos, err
|
|
}
|
|
|
|
func (bdw *BoltDB) PatchDB() error {
|
|
var DBVersion string
|
|
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
versionBuck := tx.Bucket([]byte(VersionBucket))
|
|
DBVersion = string(versionBuck.Get([]byte(version.DBVersionKey)))
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("patching the database failed, can't read db version %w", err)
|
|
}
|
|
|
|
if version.GetVersionIndex(DBVersion) == -1 {
|
|
return fmt.Errorf("DB has broken format, no version found %w", err)
|
|
}
|
|
|
|
for patchIndex, patch := range bdw.Patches {
|
|
if patchIndex < version.GetVersionIndex(DBVersion) {
|
|
continue
|
|
}
|
|
|
|
err := patch(bdw.DB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getUserStars(ctx context.Context, transaction *bbolt.Tx) []string {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return []string{}
|
|
}
|
|
|
|
var (
|
|
userData mTypes.UserData
|
|
userid = userAc.GetUsername()
|
|
userdb = transaction.Bucket([]byte(UserDataBucket))
|
|
)
|
|
|
|
if userid == "" || userdb == nil {
|
|
return []string{}
|
|
}
|
|
|
|
mdata := userdb.Get([]byte(userid))
|
|
if mdata == nil {
|
|
return []string{}
|
|
}
|
|
|
|
if err := json.Unmarshal(mdata, &userData); err != nil {
|
|
return []string{}
|
|
}
|
|
|
|
return userData.StarredRepos
|
|
}
|
|
|
|
func getUserBookmarks(ctx context.Context, transaction *bbolt.Tx) []string {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return []string{}
|
|
}
|
|
|
|
var (
|
|
userData mTypes.UserData
|
|
userid = userAc.GetUsername()
|
|
userdb = transaction.Bucket([]byte(UserDataBucket))
|
|
)
|
|
|
|
if userid == "" || userdb == nil {
|
|
return []string{}
|
|
}
|
|
|
|
mdata := userdb.Get([]byte(userid))
|
|
if mdata == nil {
|
|
return []string{}
|
|
}
|
|
|
|
if err := json.Unmarshal(mdata, &userData); err != nil {
|
|
return []string{}
|
|
}
|
|
|
|
return userData.BookmarkedRepos
|
|
}
|
|
|
|
func (bdw *BoltDB) SetUserGroups(ctx context.Context, groups []string) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(tx *bbolt.Tx) error { //nolint:varnamelen
|
|
var userData mTypes.UserData
|
|
|
|
err := bdw.getUserData(userid, tx, &userData)
|
|
if err != nil && !errors.Is(err, zerr.ErrUserDataNotFound) {
|
|
return err
|
|
}
|
|
|
|
userData.Groups = append(userData.Groups, groups...)
|
|
|
|
err = bdw.setUserData(userid, tx, userData)
|
|
|
|
return err
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetUserGroups(ctx context.Context) ([]string, error) {
|
|
userData, err := bdw.GetUserData(ctx)
|
|
|
|
return userData.Groups, err
|
|
}
|
|
|
|
func (bdw *BoltDB) UpdateUserAPIKeyLastUsed(ctx context.Context, hashedKey string) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(tx *bbolt.Tx) error { //nolint:varnamelen
|
|
var userData mTypes.UserData
|
|
|
|
err := bdw.getUserData(userid, tx, &userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
apiKeyDetails := userData.APIKeys[hashedKey]
|
|
apiKeyDetails.LastUsed = time.Now()
|
|
|
|
userData.APIKeys[hashedKey] = apiKeyDetails
|
|
|
|
err = bdw.setUserData(userid, tx, userData)
|
|
|
|
return err
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) IsAPIKeyExpired(ctx context.Context, hashedKey string) (bool, error) {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return false, zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
var isExpired bool
|
|
|
|
err = bdw.DB.Update(func(tx *bbolt.Tx) error { //nolint:varnamelen
|
|
var userData mTypes.UserData
|
|
|
|
err := bdw.getUserData(userid, tx, &userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
apiKeyDetails := userData.APIKeys[hashedKey]
|
|
if apiKeyDetails.IsExpired {
|
|
isExpired = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// if expiresAt is not nil value
|
|
if !apiKeyDetails.ExpirationDate.Equal(time.Time{}) && time.Now().After(apiKeyDetails.ExpirationDate) {
|
|
isExpired = true
|
|
apiKeyDetails.IsExpired = true
|
|
}
|
|
|
|
userData.APIKeys[hashedKey] = apiKeyDetails
|
|
|
|
err = bdw.setUserData(userid, tx, userData)
|
|
|
|
return err
|
|
})
|
|
|
|
return isExpired, err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetUserAPIKeys(ctx context.Context) ([]mTypes.APIKeyDetails, error) {
|
|
apiKeys := make([]mTypes.APIKeyDetails, 0)
|
|
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return nil, zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(transaction *bbolt.Tx) error {
|
|
var userData mTypes.UserData
|
|
|
|
err = bdw.getUserData(userid, transaction, &userData)
|
|
if err != nil && !errors.Is(err, zerr.ErrUserDataNotFound) {
|
|
return err
|
|
}
|
|
|
|
for hashedKey, apiKeyDetails := range userData.APIKeys {
|
|
// if expiresAt is not nil value
|
|
if !apiKeyDetails.ExpirationDate.Equal(time.Time{}) && time.Now().After(apiKeyDetails.ExpirationDate) {
|
|
apiKeyDetails.IsExpired = true
|
|
}
|
|
|
|
userData.APIKeys[hashedKey] = apiKeyDetails
|
|
|
|
err = bdw.setUserData(userid, transaction, userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
apiKeys = append(apiKeys, apiKeyDetails)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return apiKeys, err
|
|
}
|
|
|
|
func (bdw *BoltDB) AddUserAPIKey(ctx context.Context, hashedKey string, apiKeyDetails *mTypes.APIKeyDetails) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(transaction *bbolt.Tx) error {
|
|
var userData mTypes.UserData
|
|
|
|
apiKeysbuck := transaction.Bucket([]byte(UserAPIKeysBucket))
|
|
if apiKeysbuck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
err := apiKeysbuck.Put([]byte(hashedKey), []byte(userid))
|
|
if err != nil {
|
|
return fmt.Errorf("metaDB: error while setting userData for identity %s %w", userid, err)
|
|
}
|
|
|
|
err = bdw.getUserData(userid, transaction, &userData)
|
|
if err != nil && !errors.Is(err, zerr.ErrUserDataNotFound) {
|
|
return err
|
|
}
|
|
|
|
if userData.APIKeys == nil {
|
|
userData.APIKeys = make(map[string]mTypes.APIKeyDetails)
|
|
}
|
|
|
|
userData.APIKeys[hashedKey] = *apiKeyDetails
|
|
|
|
err = bdw.setUserData(userid, transaction, userData)
|
|
|
|
return err
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) DeleteUserAPIKey(ctx context.Context, keyID string) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(transaction *bbolt.Tx) error {
|
|
var userData mTypes.UserData
|
|
|
|
apiKeysbuck := transaction.Bucket([]byte(UserAPIKeysBucket))
|
|
if apiKeysbuck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
err := bdw.getUserData(userid, transaction, &userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for hash, apiKeyDetails := range userData.APIKeys {
|
|
if apiKeyDetails.UUID == keyID {
|
|
delete(userData.APIKeys, hash)
|
|
|
|
err := apiKeysbuck.Delete([]byte(hash))
|
|
if err != nil {
|
|
return fmt.Errorf("userDB: error while deleting userAPIKey entry for hash %s %w", hash, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return bdw.setUserData(userid, transaction, userData)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetUserAPIKeyInfo(hashedKey string) (string, error) {
|
|
var userid string
|
|
err := bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(UserAPIKeysBucket))
|
|
if buck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
uiBlob := buck.Get([]byte(hashedKey))
|
|
if len(uiBlob) == 0 {
|
|
return zerr.ErrUserAPIKeyNotFound
|
|
}
|
|
|
|
userid = string(uiBlob)
|
|
|
|
return nil
|
|
})
|
|
|
|
return userid, err
|
|
}
|
|
|
|
func (bdw *BoltDB) GetUserData(ctx context.Context) (mTypes.UserData, error) {
|
|
var userData mTypes.UserData
|
|
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return userData, err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return userData, zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.View(func(tx *bbolt.Tx) error {
|
|
return bdw.getUserData(userid, tx, &userData)
|
|
})
|
|
|
|
return userData, err
|
|
}
|
|
|
|
func (bdw *BoltDB) getUserData(userid string, transaction *bbolt.Tx, res *mTypes.UserData) error {
|
|
buck := transaction.Bucket([]byte(UserDataBucket))
|
|
if buck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
upBlob := buck.Get([]byte(userid))
|
|
|
|
if len(upBlob) == 0 {
|
|
return zerr.ErrUserDataNotFound
|
|
}
|
|
|
|
err := json.Unmarshal(upBlob, res)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (bdw *BoltDB) SetUserData(ctx context.Context, userData mTypes.UserData) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
return bdw.setUserData(userid, tx, userData)
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (bdw *BoltDB) setUserData(userid string, transaction *bbolt.Tx, userData mTypes.UserData) error {
|
|
buck := transaction.Bucket([]byte(UserDataBucket))
|
|
if buck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
upBlob, err := json.Marshal(userData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = buck.Put([]byte(userid), upBlob)
|
|
if err != nil {
|
|
return fmt.Errorf("metaDB: error while setting userData for identity %s %w", userid, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (bdw *BoltDB) DeleteUserData(ctx context.Context) error {
|
|
userAc, err := reqCtx.UserAcFromContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if userAc.IsAnonymous() {
|
|
return zerr.ErrUserDataNotAllowed
|
|
}
|
|
|
|
userid := userAc.GetUsername()
|
|
|
|
err = bdw.DB.Update(func(tx *bbolt.Tx) error {
|
|
buck := tx.Bucket([]byte(UserDataBucket))
|
|
if buck == nil {
|
|
return zerr.ErrBucketDoesNotExist
|
|
}
|
|
|
|
err := buck.Delete([]byte(userid))
|
|
if err != nil {
|
|
return fmt.Errorf("metaDB: error while deleting userData for identity %s %w", userid, err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|