diff --git a/manager/controller/registry.go b/manager/controller/registry.go index 56b0bd1..be21c60 100644 --- a/manager/controller/registry.go +++ b/manager/controller/registry.go @@ -17,7 +17,8 @@ type RegistryRequest struct { } type RegistryUpdateAllowImagesRequest struct { - Items []string `json:"items"` + Items []string `json:"items"` + BlockMessage string `json:"block_message"` } type RegistryUpdateIPDataRequest struct { @@ -247,7 +248,7 @@ func (rc *RegistryController) UpdateAllowImages(req *restful.Request, resp *rest return } - err = rc.registryService.UpdateAllowImages(req.Request.Context(), session.UserID, registryRequest.Items) + err = rc.registryService.UpdateAllowImages(req.Request.Context(), session.UserID, registryRequest.Items, registryRequest.BlockMessage) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, Error{Code: "RegistryUpdateError", Message: "Failed to update registry: " + err.Error()}) return diff --git a/manager/manager.go b/manager/manager.go index a74ae34..964d4ba 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -144,7 +144,7 @@ func (m *Manager) getRegistry(ctx context.Context, t *token.Token) (registryCach return rc, nil } -func (m *Manager) getToken(ctx context.Context, userinfo *url.Userinfo, t *token.Token, registry registryCache, image string) (model.Token, error) { +func (m *Manager) getToken(ctx context.Context, userinfo *url.Userinfo, t *token.Token, registry registryCache) (model.Token, error) { if userinfo == nil { if len(registry.Registry.Data.SpecialIPs) != 0 { tt, ok := registry.Registry.Data.SpecialIPs[t.IP] @@ -160,12 +160,6 @@ func (m *Manager) getToken(ctx context.Context, userinfo *url.Userinfo, t *token return model.Token{}, fmt.Errorf("anonymous access is not allowed") } - if !registry.Registry.Data.Anonymous.NoAllowlist && registry.ImagesMatcher != nil { - if !registry.ImagesMatcher.Match(image) { - return model.Token{}, fmt.Errorf("image %q is not allowed", image) - } - } - return model.Token{ UserID: registry.Registry.UserID, Data: registry.Registry.Data.Anonymous, @@ -188,7 +182,7 @@ func (m *Manager) getToken(ctx context.Context, userinfo *url.Userinfo, t *token return cached.attr, cached.err } - tt, err := m.TokenService.GetByAccount(ctx, up.UserID, up.TokenUser, up.TokenPassword) + tok, err := m.TokenService.GetByAccount(ctx, up.UserID, up.TokenUser, up.TokenPassword) if err != nil { m.tokenCache.Set(up, responseItem[model.Token]{err: err}, m.cacheTTL) return model.Token{}, err @@ -199,14 +193,8 @@ func (m *Manager) getToken(ctx context.Context, userinfo *url.Userinfo, t *token ttl = time.Duration(registry.Registry.Data.TTLSecond) * time.Second } - if !tt.Data.NoAllowlist && registry.ImagesMatcher != nil { - if !registry.ImagesMatcher.Match(image) { - return model.Token{}, fmt.Errorf("image %q is not allowed", image) - } - } - - m.tokenCache.Set(up, responseItem[model.Token]{attr: tt}, ttl) - return tt, nil + m.tokenCache.Set(up, responseItem[model.Token]{attr: tok}, ttl) + return tok, nil } func getHostAndImage(repo string, allowPrefix bool, source string) (host string, image string, err error) { @@ -219,7 +207,7 @@ func getHostAndImage(repo string, allowPrefix bool, source string) (host string, return source, repo, nil } - return "", "", fmt.Errorf("invalid repository format: %q, source: %q", repo, source) + return "", "", fmt.Errorf("invalid repository: %q, source: %q", repo, source) } func (m *Manager) GetTokenWithUser(ctx context.Context, userinfo *url.Userinfo, t *token.Token) (token.Attribute, error) { @@ -228,33 +216,48 @@ func (m *Manager) GetTokenWithUser(ctx context.Context, userinfo *url.Userinfo, return token.Attribute{}, err } - host, image, err := getHostAndImage(t.Image, registry.Registry.Data.AllowPrefix, registry.Registry.Data.Source) - if err != nil { - return token.Attribute{}, err - } - - tt, err := m.getToken(ctx, userinfo, t, registry, host+"/"+image) + tok, err := m.getToken(ctx, userinfo, t, registry) if err != nil { return token.Attribute{}, err } attr := token.Attribute{ - UserID: tt.UserID, - TokenID: tt.TokenID, + UserID: tok.UserID, + TokenID: tok.TokenID, RegistryID: registry.Registry.RegistryID, - NoRateLimit: tt.Data.NoRateLimit, - RateLimitPerSecond: tt.Data.RateLimitPerSecond, + NoRateLimit: tok.Data.NoRateLimit, + RateLimitPerSecond: tok.Data.RateLimitPerSecond, - NoAllowlist: tt.Data.NoAllowlist, - NoBlock: tt.Data.NoBlock, - AllowTagsList: tt.Data.AllowTagsList, + NoAllowlist: tok.Data.NoAllowlist, + NoBlock: tok.Data.NoBlock, + AllowTagsList: tok.Data.AllowTagsList, - Block: tt.Data.Block, - BlockMessage: tt.Data.BlockMessage, + BlobsURL: tok.Data.BlobsURL, - Host: host, - Image: image, + Block: tok.Data.Block, + BlockMessage: tok.Data.BlockMessage, + } + + if !attr.Block { + if t.Image != "" && !attr.NoAllowlist && registry.ImagesMatcher != nil { + host, image, err := getHostAndImage(t.Image, registry.Registry.Data.AllowPrefix, registry.Registry.Data.Source) + if err != nil { + attr.Block = true + attr.BlockMessage = err.Error() + } else { + attr.Host = host + attr.Image = image + if !registry.ImagesMatcher.Match(host + "/" + image) { + attr.Block = true + if registry.Registry.Data.AllowlisBlockMessage != "" { + attr.BlockMessage = registry.Registry.Data.AllowlisBlockMessage + } else { + attr.BlockMessage = fmt.Sprintf("image %q is not allowed", image) + } + } + } + } } return attr, nil } diff --git a/manager/model/registry.go b/manager/model/registry.go index cce3eb3..00d705b 100644 --- a/manager/model/registry.go +++ b/manager/model/registry.go @@ -20,8 +20,9 @@ type RegistryAttr struct { AllowPrefix bool `json:"allow_prefix"` Source string `json:"source"` - Allowlist []string `json:"allowlist"` - EnableAllowlist bool `json:"enable_allowlist"` + EnableAllowlist bool `json:"enable_allowlist"` + Allowlist []string `json:"allowlist"` + AllowlisBlockMessage string `json:"allowlist_block_message"` SpecialIPs map[string]TokenAttr `json:"special_ips"` } diff --git a/manager/model/token.go b/manager/model/token.go index afea71c..7f8a7cc 100644 --- a/manager/model/token.go +++ b/manager/model/token.go @@ -14,11 +14,13 @@ type Token struct { } type TokenAttr struct { - NoRateLimit bool `json:"no_rate_limit"` - RateLimitPerSecond uint64 `json:"rate_limit_per_second"` - AllowTagsList bool `json:"allow_tags_list"` - NoAllowlist bool `json:"no_allowlist"` - NoBlock bool `json:"no_block"` + NoRateLimit bool `json:"no_rate_limit,omitempty"` + RateLimitPerSecond uint64 `json:"rate_limit_per_second,omitempty"` + AllowTagsList bool `json:"allow_tags_list,omitempty"` + NoAllowlist bool `json:"no_allowlist,omitempty"` + NoBlock bool `json:"no_block,omitempty"` + + BlobsURL string `json:"blobs_url,omitempty"` Block bool `json:"block,omitempty"` BlockMessage string `json:"block_message,omitempty"` diff --git a/manager/service/registry.go b/manager/service/registry.go index d38fdc0..72c08cc 100644 --- a/manager/service/registry.go +++ b/manager/service/registry.go @@ -53,7 +53,7 @@ func (s *RegistryService) DeleteByID(ctx context.Context, registryID, userID int return s.registryDao.DeleteByID(ctx, registryID, userID) } -func (s *RegistryService) UpdateAllowImages(ctx context.Context, userID int64, allows []string) (retErr error) { +func (s *RegistryService) UpdateAllowImages(ctx context.Context, userID int64, allows []string, blockMessage string) (retErr error) { tx, err := s.db.BeginTx(ctx, nil) if err != nil { return err @@ -71,11 +71,9 @@ func (s *RegistryService) UpdateAllowImages(ctx context.Context, userID int64, a for _, registry := range registrys { if registry.Data.AllowPrefix { - registry.Data.EnableAllowlist = true registry.Data.Allowlist = allows } else if registry.Data.Source != "" { s := registry.Data.Source + "/" - registry.Data.EnableAllowlist = true registry.Data.Allowlist = slices.Filter(allows, func(image string) bool { return strings.HasPrefix(image, s) }) @@ -86,6 +84,11 @@ func (s *RegistryService) UpdateAllowImages(ctx context.Context, userID int64, a continue } + registry.Data.EnableAllowlist = true + if blockMessage != "" { + registry.Data.AllowlisBlockMessage = blockMessage + } + err = s.registryDao.UpdateByID(ctx, registry.RegistryID, userID, registry) if err != nil { tx.Rollback()