// Copyright 2021 Woodpecker Authors // Copyright 2018 Drone.IO Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package model import ( "errors" "fmt" "path/filepath" "regexp" "sort" ) var ( errSecretNameInvalid = errors.New("Invalid Secret Name") errSecretValueInvalid = errors.New("Invalid Secret Value") errSecretEventInvalid = errors.New("Invalid Secret Event") ) // SecretService defines a service for managing secrets. type SecretService interface { SecretListPipeline(*Repo, *Pipeline) ([]*Secret, error) // Repository secrets SecretFind(*Repo, string) (*Secret, error) SecretList(*Repo) ([]*Secret, error) SecretCreate(*Repo, *Secret) error SecretUpdate(*Repo, *Secret) error SecretDelete(*Repo, string) error // Organization secrets OrgSecretFind(string, string) (*Secret, error) OrgSecretList(string) ([]*Secret, error) OrgSecretCreate(string, *Secret) error OrgSecretUpdate(string, *Secret) error OrgSecretDelete(string, string) error // Global secrets GlobalSecretFind(string) (*Secret, error) GlobalSecretList() ([]*Secret, error) GlobalSecretCreate(*Secret) error GlobalSecretUpdate(*Secret) error GlobalSecretDelete(string) error } // SecretStore persists secret information to storage. type SecretStore interface { SecretFind(*Repo, string) (*Secret, error) SecretList(*Repo, bool) ([]*Secret, error) SecretCreate(*Secret) error SecretUpdate(*Secret) error SecretDelete(*Secret) error OrgSecretFind(string, string) (*Secret, error) OrgSecretList(string) ([]*Secret, error) GlobalSecretFind(string) (*Secret, error) GlobalSecretList() ([]*Secret, error) SecretListAll() ([]*Secret, error) } // Secret represents a secret variable, such as a password or token. // swagger:model registry type Secret struct { ID int64 `json:"id" xorm:"pk autoincr 'secret_id'"` Owner string `json:"-" xorm:"NOT NULL DEFAULT '' UNIQUE(s) INDEX 'secret_owner'"` RepoID int64 `json:"-" xorm:"NOT NULL DEFAULT 0 UNIQUE(s) INDEX 'secret_repo_id'"` Name string `json:"name" xorm:"NOT NULL UNIQUE(s) INDEX 'secret_name'"` Value string `json:"value,omitempty" xorm:"TEXT 'secret_value'"` Images []string `json:"image" xorm:"json 'secret_images'"` PluginsOnly bool `json:"plugins_only" xorm:"secret_plugins_only"` Events []WebhookEvent `json:"event" xorm:"json 'secret_events'"` SkipVerify bool `json:"-" xorm:"secret_skip_verify"` Conceal bool `json:"-" xorm:"secret_conceal"` } // TableName return database table name for xorm func (Secret) TableName() string { return "secrets" } // BeforeInsert will sort events before inserted into database func (s *Secret) BeforeInsert() { s.Events = sortEvents(s.Events) } // Global secret. func (s Secret) Global() bool { return s.RepoID == 0 && s.Owner == "" } // Organization secret. func (s Secret) Organization() bool { return s.RepoID == 0 && s.Owner != "" } // Match returns true if an image and event match the restricted list. func (s *Secret) Match(event WebhookEvent) bool { if len(s.Events) == 0 { return true } for _, pattern := range s.Events { if match, _ := filepath.Match(string(pattern), string(event)); match { return true } } return false } var validDockerImageString = regexp.MustCompile( `^([\w\d\-_\.\/]*` + // optional url prefix `[\w\d\-_]+` + // image name `)+` + `(:[\w\d\-_]+)?$`, // optional image tag ) // Validate validates the required fields and formats. func (s *Secret) Validate() error { for _, event := range s.Events { if !ValidateWebhookEvent(event) { return fmt.Errorf("%s: '%s'", errSecretEventInvalid, event) } } if len(s.Events) == 0 { return fmt.Errorf("%s: no event specified", errSecretEventInvalid) } for _, image := range s.Images { if len(image) == 0 { return fmt.Errorf("empty image in images") } if !validDockerImageString.MatchString(image) { return fmt.Errorf("image '%s' do not match regexp '%s'", image, validDockerImageString.String()) } } switch { case len(s.Name) == 0: return errSecretNameInvalid case len(s.Value) == 0: return errSecretValueInvalid default: return nil } } // Copy makes a copy of the secret without the value. func (s *Secret) Copy() *Secret { return &Secret{ ID: s.ID, Owner: s.Owner, RepoID: s.RepoID, Name: s.Name, Images: s.Images, PluginsOnly: s.PluginsOnly, Events: sortEvents(s.Events), } } func sortEvents(wel WebhookEventList) WebhookEventList { sort.Sort(wel) return wel }