mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
Refactor config settings encrypt + metadata (#6387)
Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
parent
f7a28ee2a2
commit
13511d74ed
83
src/common/config/encrypt/encrypt.go
Normal file
83
src/common/config/encrypt/encrypt.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 encrypt
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultKeyPath = "/etc/core/key"
|
||||
)
|
||||
|
||||
// Encryptor encrypts or decrypts a strings
|
||||
type Encryptor interface {
|
||||
// Encrypt encrypts plaintext
|
||||
Encrypt(string) (string, error)
|
||||
// Decrypt decrypts ciphertext
|
||||
Decrypt(string) (string, error)
|
||||
}
|
||||
|
||||
// AESEncryptor uses AES to encrypt or decrypt string
|
||||
type AESEncryptor struct {
|
||||
keyProvider KeyProvider
|
||||
keyParams map[string]interface{}
|
||||
}
|
||||
|
||||
// NewAESEncryptor returns an instance of an AESEncryptor
|
||||
func NewAESEncryptor(keyProvider KeyProvider) Encryptor {
|
||||
return &AESEncryptor{
|
||||
keyProvider: keyProvider,
|
||||
}
|
||||
}
|
||||
|
||||
var encryptInstance Encryptor
|
||||
var encryptOnce sync.Once
|
||||
|
||||
// Instance ... Get instance of encryptor
|
||||
func Instance() Encryptor {
|
||||
encryptOnce.Do(func() {
|
||||
kp := os.Getenv("KEY_PATH")
|
||||
if len(kp) == 0 {
|
||||
kp = defaultKeyPath
|
||||
}
|
||||
log.Infof("the path of key used by key provider: %s", kp)
|
||||
encryptInstance = NewAESEncryptor(NewFileKeyProvider(kp))
|
||||
|
||||
})
|
||||
return encryptInstance
|
||||
}
|
||||
|
||||
// Encrypt ...
|
||||
func (a *AESEncryptor) Encrypt(plaintext string) (string, error) {
|
||||
key, err := a.keyProvider.Get(a.keyParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return utils.ReversibleEncrypt(plaintext, key)
|
||||
}
|
||||
|
||||
// Decrypt ...
|
||||
func (a *AESEncryptor) Decrypt(ciphertext string) (string, error) {
|
||||
key, err := a.keyProvider.Get(a.keyParams)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return utils.ReversibleDecrypt(ciphertext, key)
|
||||
}
|
39
src/common/config/encrypt/encrypt_test.go
Normal file
39
src/common/config/encrypt/encrypt_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
secret := []byte("9TXCcHgNAAp1aSHh")
|
||||
filename, err := ioutil.TempFile(os.TempDir(), "keyfile")
|
||||
err = ioutil.WriteFile(filename.Name(), secret, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to create temp key file\n")
|
||||
}
|
||||
|
||||
defer os.Remove(filename.Name())
|
||||
|
||||
os.Setenv("KEY_PATH", filename.Name())
|
||||
|
||||
ret := m.Run()
|
||||
os.Exit(ret)
|
||||
}
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
password := "zhu888jie"
|
||||
encrypted, err := Instance().Encrypt(password)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decrypt password, error %v", err)
|
||||
}
|
||||
decrypted, err := Instance().Decrypt(encrypted)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to decrypt password, error %v", err)
|
||||
}
|
||||
assert.NotEqual(t, password, encrypted)
|
||||
assert.Equal(t, password, decrypted)
|
||||
}
|
48
src/common/config/encrypt/keyprovider.go
Normal file
48
src/common/config/encrypt/keyprovider.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 encrypt
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// KeyProvider provides the key used to encrypt and decrypt attrs
|
||||
type KeyProvider interface {
|
||||
// Get returns the key
|
||||
// params can be used to pass parameters in different implements
|
||||
Get(params map[string]interface{}) (string, error)
|
||||
}
|
||||
|
||||
// FileKeyProvider reads key from file
|
||||
type FileKeyProvider struct {
|
||||
path string
|
||||
}
|
||||
|
||||
// NewFileKeyProvider returns an instance of FileKeyProvider
|
||||
// path: where the key should be read from
|
||||
func NewFileKeyProvider(path string) KeyProvider {
|
||||
return &FileKeyProvider{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the key read from file
|
||||
func (f *FileKeyProvider) Get(params map[string]interface{}) (string, error) {
|
||||
b, err := ioutil.ReadFile(f.path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
44
src/common/config/encrypt/keyprovider_test.go
Normal file
44
src/common/config/encrypt/keyprovider_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 encrypt
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetOfFileKeyProvider(t *testing.T) {
|
||||
path := "/tmp/key"
|
||||
key := "key_content"
|
||||
|
||||
if err := ioutil.WriteFile(path, []byte(key), 0777); err != nil {
|
||||
t.Errorf("failed to write to file %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
provider := NewFileKeyProvider(path)
|
||||
k, err := provider.Get(nil)
|
||||
if err != nil {
|
||||
t.Errorf("failed to get key from the file provider: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if k != key {
|
||||
t.Errorf("unexpected key: %s != %s", k, key)
|
||||
return
|
||||
}
|
||||
}
|
70
src/common/config/metadata/metadata.go
Normal file
70
src/common/config/metadata/metadata.go
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var metaDataOnce sync.Once
|
||||
var metaDataInstance *CfgMetaData
|
||||
|
||||
// Instance - Get Instance, make it singleton because there is only one copy of metadata in an env
|
||||
func Instance() *CfgMetaData {
|
||||
metaDataOnce.Do(func() {
|
||||
metaDataInstance = newCfgMetaData()
|
||||
metaDataInstance.init()
|
||||
})
|
||||
return metaDataInstance
|
||||
}
|
||||
|
||||
func newCfgMetaData() *CfgMetaData {
|
||||
return &CfgMetaData{metaMap: make(map[string]Item)}
|
||||
}
|
||||
|
||||
// CfgMetaData ...
|
||||
type CfgMetaData struct {
|
||||
metaMap map[string]Item
|
||||
}
|
||||
|
||||
// init ...
|
||||
func (c *CfgMetaData) init() {
|
||||
c.initFromArray(ConfigList)
|
||||
}
|
||||
|
||||
// initFromArray - Initial metadata from an array
|
||||
func (c *CfgMetaData) initFromArray(items []Item) {
|
||||
c.metaMap = make(map[string]Item)
|
||||
for _, item := range items {
|
||||
c.metaMap[item.Name] = item
|
||||
}
|
||||
}
|
||||
|
||||
// GetByName - Get current metadata of current name, if not defined, return false in second params
|
||||
func (c *CfgMetaData) GetByName(name string) (*Item, bool) {
|
||||
if item, ok := c.metaMap[name]; ok {
|
||||
return &item, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetAll - Get all metadata in current env
|
||||
func (c *CfgMetaData) GetAll() []Item {
|
||||
metaDataList := make([]Item, 0)
|
||||
for _, value := range c.metaMap {
|
||||
metaDataList = append(metaDataList, value)
|
||||
}
|
||||
return metaDataList
|
||||
}
|
51
src/common/config/metadata/metadata_test.go
Normal file
51
src/common/config/metadata/metadata_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCfgMetaData_InitFromArray(t *testing.T) {
|
||||
testArray := []Item{
|
||||
{Scope: SystemScope, Group: BasicGroup, EnvKey: "HARBOR_ADMIN_PASSWORD", DefaultValue: "", Name: "admin_initial_password", ItemType: &PasswordType{}, Editable: true},
|
||||
{Scope: SystemScope, Group: BasicGroup, EnvKey: "ADMIRAL_URL", DefaultValue: "NA", Name: "admiral_url", ItemType: &StringType{}, Editable: false},
|
||||
{Scope: UserScope, Group: BasicGroup, EnvKey: "AUTH_MODE", DefaultValue: "db_auth", Name: "auth_mode", ItemType: &StringType{}, Editable: false},
|
||||
{Scope: SystemScope, Group: BasicGroup, EnvKey: "CFG_EXPIRATION", DefaultValue: "5", Name: "cfg_expiration", ItemType: &StringType{}, Editable: false},
|
||||
{Scope: SystemScope, Group: BasicGroup, EnvKey: "CHART_REPOSITORY_URL", DefaultValue: "http://chartmuseum:9999", Name: "chart_repository_url", ItemType: &StringType{}, Editable: false},
|
||||
}
|
||||
curInst := Instance()
|
||||
curInst.initFromArray(testArray)
|
||||
|
||||
if len(metaDataInstance.metaMap) != 5 {
|
||||
t.Errorf("Can not initial metadata, size %v", len(metaDataInstance.metaMap))
|
||||
}
|
||||
item, ok := curInst.GetByName("admin_initial_password")
|
||||
if ok == false {
|
||||
t.Errorf("Can not get admin_initial_password metadata")
|
||||
}
|
||||
if item.Name != "admin_initial_password" {
|
||||
t.Errorf("Can not get admin_initial_password metadata")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCfgMetaData_Init(t *testing.T) {
|
||||
curInst := Instance()
|
||||
curInst.init()
|
||||
if len(metaDataInstance.metaMap) < 60 {
|
||||
t.Errorf("Can not initial metadata, size %v", len(metaDataInstance.metaMap))
|
||||
}
|
||||
}
|
134
src/common/config/metadata/metadatalist.go
Normal file
134
src/common/config/metadata/metadatalist.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import "github.com/goharbor/harbor/src/common"
|
||||
|
||||
// Item - Configure item include default value, type, env name
|
||||
type Item struct {
|
||||
// The Scope of this configuration item: eg: SystemScope, UserScope
|
||||
Scope string `json:"scope,omitempty"`
|
||||
// email, ldapbasic, ldapgroup, uaa settings, used to retieve configure items by group
|
||||
Group string `json:"group,omitempty"`
|
||||
// environment key to retrieves this value when initialize, for example: POSTGRESQL_HOST, only used for system settings, for user settings no EnvKey
|
||||
EnvKey string `json:"environment_key,omitempty"`
|
||||
// The default string value for this key
|
||||
DefaultValue string `json:"default_value,omitempty"`
|
||||
// The key for current configure settings in database or rest api
|
||||
Name string `json:"name,omitempty"`
|
||||
// It can be &IntType{}, &StringType{}, &BoolType{}, &PasswordType{}, &MapType{} etc, any type interface implementation
|
||||
ItemType Type
|
||||
// Is this settign can be modified after configure
|
||||
Editable bool `json:"editable,omitempty"`
|
||||
}
|
||||
|
||||
// Constant for configure item
|
||||
const (
|
||||
// Scope
|
||||
UserScope = "user"
|
||||
SystemScope = "system"
|
||||
// Group
|
||||
LdapBasicGroup = "ldapbasic"
|
||||
LdapGroupGroup = "ldapgroup"
|
||||
EmailGroup = "email"
|
||||
UAAGroup = "uaa"
|
||||
DatabaseGroup = "database"
|
||||
// Put all config items do not belong a existing group into basic
|
||||
BasicGroup = "basic"
|
||||
ClairGroup = "clair"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ConfigList - All configure items used in harbor
|
||||
// Steps to onboard a new setting
|
||||
// 1. Add configure item in metadatalist.go
|
||||
// 2. Get/Set config settings by CfgManager
|
||||
// 3. CfgManager.Load()/CfgManager.Save() to load/save from configure storage.
|
||||
ConfigList = []Item{
|
||||
{Name: "admin_initial_password", Scope: SystemScope, Group: BasicGroup, EnvKey: "HARBOR_ADMIN_PASSWORD", DefaultValue: "", ItemType: &PasswordType{}, Editable: true},
|
||||
{Name: "admiral_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "ADMIRAL_URL", DefaultValue: "NA", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "auth_mode", Scope: UserScope, Group: BasicGroup, EnvKey: "AUTH_MODE", DefaultValue: "db_auth", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "cfg_expiration", Scope: SystemScope, Group: BasicGroup, EnvKey: "CFG_EXPIRATION", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "chart_repository_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "CHART_REPOSITORY_URL", DefaultValue: "http://chartmuseum:9999", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "clair_db", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "clair_db_host", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "clair_db_password", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
||||
{Name: "clair_db_port", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_PORT", DefaultValue: "5432", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "clair_db_sslmode", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "clair_db_username", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_DB_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "clair_url", Scope: SystemScope, Group: ClairGroup, EnvKey: "CLAIR_URL", DefaultValue: "http://clair:6060", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "core_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "CORE_URL", DefaultValue: "http://core:8080", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "database_type", Scope: SystemScope, Group: BasicGroup, EnvKey: "DATABASE_TYPE", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "email_from", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_FROM", DefaultValue: "admin <sample_admin@mydomain.com>", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "email_host", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_HOST", DefaultValue: "smtp.mydomain.com", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "email_identity", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_IDENTITY", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "email_insecure", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_INSECURE", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||
{Name: "email_password", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
||||
{Name: "email_port", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_PORT", DefaultValue: "25", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "email_ssl", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_SSL", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||
{Name: "email_username", Scope: UserScope, Group: EmailGroup, EnvKey: "EMAIL_USR", DefaultValue: "sample_admin@mydomain.com", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "ext_endpoint", Scope: SystemScope, Group: BasicGroup, EnvKey: "EXT_ENDPOINT", DefaultValue: "https://host01.com", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "jobservice_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "JOBSERVICE_URL", DefaultValue: "http://jobservice:8080", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "ldap_base_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_BASE_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_filter", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_group_base_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_BASE_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_group_admin_dn", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_ADMIN_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_group_attribute_name", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_GID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_group_search_filter", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_FILTER", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_group_search_scope", Scope: UserScope, Group: LdapGroupGroup, EnvKey: "LDAP_GROUP_SCOPE", DefaultValue: "2", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "ldap_scope", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SCOPE", DefaultValue: "2", ItemType: &IntType{}, Editable: true},
|
||||
{Name: "ldap_search_dn", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_DN", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "ldap_search_password", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_SEARCH_PWD", DefaultValue: "", ItemType: &PasswordType{}, Editable: false},
|
||||
{Name: "ldap_timeout", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_TIMEOUT", DefaultValue: "5", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "ldap_uid", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_UID", DefaultValue: "cn", ItemType: &StringType{}, Editable: true},
|
||||
{Name: "ldap_url", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &StringType{}, Editable: true},
|
||||
{Name: "ldap_verify_cert", Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
||||
|
||||
{Name: "max_job_workers", Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "notary_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "NOTARY_URL", DefaultValue: "http://notary-server:4443", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "postgresql_database", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_DATABASE", DefaultValue: "registry", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "postgresql_host", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_HOST", DefaultValue: "postgresql", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "postgresql_password", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PASSWORD", DefaultValue: "root123", ItemType: &PasswordType{}, Editable: false},
|
||||
{Name: "postgresql_port", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_PORT", DefaultValue: "5432", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "postgresql_sslmode", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_SSLMODE", DefaultValue: "disable", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "postgresql_username", Scope: SystemScope, Group: DatabaseGroup, EnvKey: "POSTGRESQL_USERNAME", DefaultValue: "postgres", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "project_creation_restriction", Scope: UserScope, Group: BasicGroup, EnvKey: "PROJECT_CREATION_RESTRICTION", DefaultValue: common.ProCrtRestrEveryone, ItemType: &StringType{}, Editable: false},
|
||||
{Name: "read_only", Scope: UserScope, Group: BasicGroup, EnvKey: "READ_ONLY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||
|
||||
{Name: "registry_storage_provider_name", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_STORAGE_PROVIDER_NAME", DefaultValue: "filesystem", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "registry_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_URL", DefaultValue: "http://registry:5000", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "registry_controller_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "REGISTRY_CONTROLLER_URL", DefaultValue: "http://registryctl:8080", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "self_registration", Scope: UserScope, Group: BasicGroup, EnvKey: "SELF_REGISTRATION", DefaultValue: "true", ItemType: &BoolType{}, Editable: false},
|
||||
{Name: "token_expiration", Scope: UserScope, Group: BasicGroup, EnvKey: "TOKEN_EXPIRATION", DefaultValue: "30", ItemType: &IntType{}, Editable: false},
|
||||
{Name: "token_service_url", Scope: SystemScope, Group: BasicGroup, EnvKey: "TOKEN_SERVICE_URL", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
|
||||
{Name: "uaa_client_id", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTID", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "uaa_client_secret", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_CLIENTSECRET", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "uaa_endpoint", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false},
|
||||
{Name: "uaa_verify_cert", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_VERIFY_CERT", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
|
||||
|
||||
{Name: "with_chartmuseum", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||
{Name: "with_clair", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "true", ItemType: &BoolType{}, Editable: true},
|
||||
{Name: "with_notary", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_NOTARY", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
|
||||
}
|
||||
)
|
122
src/common/config/metadata/type.go
Normal file
122
src/common/config/metadata/type.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Type - Use this interface to define and encapsulate the behavior of validation and transformation
|
||||
type Type interface {
|
||||
// validate the configure value
|
||||
validate(str string) error
|
||||
// get the real type of current value, if it is int, return int, if it is string return string etc.
|
||||
get(str string) (interface{}, error)
|
||||
}
|
||||
|
||||
// StringType ...
|
||||
type StringType struct {
|
||||
}
|
||||
|
||||
func (t *StringType) validate(str string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StringType) get(str string) (interface{}, error) {
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// IntType ..
|
||||
type IntType struct {
|
||||
}
|
||||
|
||||
func (t *IntType) validate(str string) error {
|
||||
_, err := strconv.Atoi(str)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInt ...
|
||||
func (t *IntType) get(str string) (interface{}, error) {
|
||||
return strconv.Atoi(str)
|
||||
}
|
||||
|
||||
// LdapScopeType - The LDAP scope is a int type, but its is limit to 0, 1, 2
|
||||
type LdapScopeType struct {
|
||||
IntType
|
||||
}
|
||||
|
||||
// Validate - Verify the range is limited
|
||||
func (t *LdapScopeType) validate(str string) error {
|
||||
if str == "0" || str == "1" || str == "2" {
|
||||
return nil
|
||||
}
|
||||
return ErrInvalidData
|
||||
}
|
||||
|
||||
// Int64Type ...
|
||||
type Int64Type struct {
|
||||
}
|
||||
|
||||
func (t *Int64Type) validate(str string) error {
|
||||
_, err := strconv.ParseInt(str, 10, 64)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInt64 ...
|
||||
func (t *Int64Type) get(str string) (interface{}, error) {
|
||||
return strconv.ParseInt(str, 10, 64)
|
||||
}
|
||||
|
||||
// BoolType ...
|
||||
type BoolType struct {
|
||||
}
|
||||
|
||||
func (t *BoolType) validate(str string) error {
|
||||
_, err := strconv.ParseBool(str)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *BoolType) get(str string) (interface{}, error) {
|
||||
return strconv.ParseBool(str)
|
||||
}
|
||||
|
||||
// PasswordType ...
|
||||
type PasswordType struct {
|
||||
}
|
||||
|
||||
func (t *PasswordType) validate(str string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PasswordType) get(str string) (interface{}, error) {
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// MapType ...
|
||||
type MapType struct {
|
||||
}
|
||||
|
||||
func (t *MapType) validate(str string) error {
|
||||
result := map[string]interface{}{}
|
||||
err := json.Unmarshal([]byte(str), &result)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *MapType) get(str string) (interface{}, error) {
|
||||
result := map[string]string{}
|
||||
err := json.Unmarshal([]byte(str), &result)
|
||||
return result, err
|
||||
}
|
98
src/common/config/metadata/type_test.go
Normal file
98
src/common/config/metadata/type_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntType_validate(t *testing.T) {
|
||||
test := &IntType{}
|
||||
assert.NotNil(t, test.validate("sample"))
|
||||
assert.Nil(t, test.validate("1000"))
|
||||
|
||||
}
|
||||
|
||||
func TestIntType_get(t *testing.T) {
|
||||
test := &IntType{}
|
||||
result, _ := test.get("1000")
|
||||
assert.IsType(t, result, 1000)
|
||||
}
|
||||
|
||||
func TestStringType_get(t *testing.T) {
|
||||
test := &StringType{}
|
||||
result, _ := test.get("1000")
|
||||
assert.IsType(t, result, "sample")
|
||||
}
|
||||
|
||||
func TestStringType_validate(t *testing.T) {
|
||||
test := &StringType{}
|
||||
assert.Nil(t, test.validate("sample"))
|
||||
}
|
||||
|
||||
func TestLdapScopeType_validate(t *testing.T) {
|
||||
test := &LdapScopeType{}
|
||||
assert.NotNil(t, test.validate("3"))
|
||||
assert.Nil(t, test.validate("2"))
|
||||
}
|
||||
|
||||
func TestInt64Type_validate(t *testing.T) {
|
||||
test := &Int64Type{}
|
||||
assert.NotNil(t, test.validate("sample"))
|
||||
assert.Nil(t, test.validate("1000"))
|
||||
}
|
||||
|
||||
func TestInt64Type_get(t *testing.T) {
|
||||
test := &Int64Type{}
|
||||
result, _ := test.get("32")
|
||||
assert.Equal(t, result, int64(32))
|
||||
}
|
||||
|
||||
func TestBoolType_validate(t *testing.T) {
|
||||
test := &BoolType{}
|
||||
assert.NotNil(t, test.validate("sample"))
|
||||
assert.Nil(t, test.validate("True"))
|
||||
}
|
||||
|
||||
func TestBoolType_get(t *testing.T) {
|
||||
test := &BoolType{}
|
||||
result, _ := test.get("true")
|
||||
assert.Equal(t, result, true)
|
||||
result, _ = test.get("false")
|
||||
assert.Equal(t, result, false)
|
||||
}
|
||||
|
||||
func TestPasswordType_validate(t *testing.T) {
|
||||
test := &PasswordType{}
|
||||
assert.Nil(t, test.validate("zhu88jie"))
|
||||
}
|
||||
|
||||
func TestPasswordType_get(t *testing.T) {
|
||||
test := &PasswordType{}
|
||||
assert.Nil(t, test.validate("zhu88jie"))
|
||||
}
|
||||
|
||||
func TestMapType_validate(t *testing.T) {
|
||||
test := &MapType{}
|
||||
assert.Nil(t, test.validate(`{"sample":"abc", "another":"welcome"}`))
|
||||
assert.NotNil(t, test.validate(`{"sample":"abc", "another":"welcome"`))
|
||||
}
|
||||
|
||||
func TestMapType_get(t *testing.T) {
|
||||
test := &MapType{}
|
||||
result, _ := test.get(`{"sample":"abc", "another":"welcome"}`)
|
||||
assert.Equal(t, result, map[string]string{"sample": "abc", "another": "welcome"})
|
||||
}
|
159
src/common/config/metadata/value.go
Normal file
159
src/common/config/metadata/value.go
Normal file
@ -0,0 +1,159 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotDefined ...
|
||||
ErrNotDefined = errors.New("configure item is not defined in metadata")
|
||||
// ErrTypeNotMatch ...
|
||||
ErrTypeNotMatch = errors.New("the required value doesn't matched with metadata defined")
|
||||
// ErrInvalidData ...
|
||||
ErrInvalidData = errors.New("the data provided is invalid")
|
||||
// ErrValueNotSet ...
|
||||
ErrValueNotSet = errors.New("the configure value is not set")
|
||||
)
|
||||
|
||||
// ConfigureValue - struct to hold a actual value, also include the name of config metadata.
|
||||
type ConfigureValue struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// NewConfigureValue ...
|
||||
func NewConfigureValue(name, value string) *ConfigureValue {
|
||||
result := &ConfigureValue{}
|
||||
err := result.Set(name, value)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set name:%v, value:%v, error %v", name, value, err)
|
||||
result.Name = name // Keep name to trace error
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetString - Get the string value of current configure
|
||||
func (c *ConfigureValue) GetString() string {
|
||||
// Any type has the string value
|
||||
if _, ok := Instance().GetByName(c.Name); ok {
|
||||
return c.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetName ...
|
||||
func (c *ConfigureValue) GetName() string {
|
||||
return c.Name
|
||||
}
|
||||
|
||||
// GetInt - return the int value of current value
|
||||
func (c *ConfigureValue) GetInt() int {
|
||||
if item, ok := Instance().GetByName(c.Name); ok {
|
||||
val, err := item.ItemType.get(c.Value)
|
||||
if err != nil {
|
||||
log.Errorf("GetInt failed, error: %+v", err)
|
||||
return 0
|
||||
}
|
||||
if intValue, suc := val.(int); suc {
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
log.Errorf("The current value's metadata is not defined, %+v", c)
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetInt64 - return the int64 value of current value
|
||||
func (c *ConfigureValue) GetInt64() int64 {
|
||||
if item, ok := Instance().GetByName(c.Name); ok {
|
||||
val, err := item.ItemType.get(c.Value)
|
||||
if err != nil {
|
||||
log.Errorf("GetInt64 failed, error: %+v", err)
|
||||
return 0
|
||||
}
|
||||
if int64Value, suc := val.(int64); suc {
|
||||
return int64Value
|
||||
}
|
||||
}
|
||||
log.Errorf("The current value's metadata is not defined, %+v", c)
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetBool - return the bool value of current setting
|
||||
func (c *ConfigureValue) GetBool() bool {
|
||||
if item, ok := Instance().GetByName(c.Name); ok {
|
||||
val, err := item.ItemType.get(c.Value)
|
||||
if err != nil {
|
||||
log.Errorf("GetBool failed, error: %+v", err)
|
||||
return false
|
||||
}
|
||||
if boolValue, suc := val.(bool); suc {
|
||||
return boolValue
|
||||
}
|
||||
}
|
||||
log.Errorf("The current value's metadata is not defined, %+v", c)
|
||||
return false
|
||||
}
|
||||
|
||||
// GetStringToStringMap - return the string to string map of current value
|
||||
func (c *ConfigureValue) GetStringToStringMap() map[string]string {
|
||||
result := map[string]string{}
|
||||
if item, ok := Instance().GetByName(c.Name); ok {
|
||||
val, err := item.ItemType.get(c.Value)
|
||||
if err != nil {
|
||||
log.Errorf("The GetBool failed, error: %+v", err)
|
||||
return result
|
||||
}
|
||||
if mapValue, suc := val.(map[string]string); suc {
|
||||
return mapValue
|
||||
}
|
||||
}
|
||||
log.Errorf("GetStringToStringMap failed, current value's metadata is not defined, %+v", c)
|
||||
return result
|
||||
}
|
||||
|
||||
// Validate - to validate configure items, if passed, return nil, else return error
|
||||
func (c *ConfigureValue) Validate() error {
|
||||
if item, ok := Instance().GetByName(c.Name); ok {
|
||||
return item.ItemType.validate(c.Value)
|
||||
}
|
||||
return ErrNotDefined
|
||||
}
|
||||
|
||||
// GetPassword ...
|
||||
func (c *ConfigureValue) GetPassword() string {
|
||||
if _, ok := Instance().GetByName(c.Name); ok {
|
||||
return c.Value
|
||||
}
|
||||
log.Errorf("GetPassword failed, metadata not defined: %v", c.Name)
|
||||
return ""
|
||||
}
|
||||
|
||||
// Set - set this configure item to configure store
|
||||
func (c *ConfigureValue) Set(name, value string) error {
|
||||
if item, ok := Instance().GetByName(name); ok {
|
||||
err := item.ItemType.validate(value)
|
||||
if err == nil {
|
||||
c.Name = name
|
||||
c.Value = value
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return ErrNotDefined
|
||||
}
|
51
src/common/config/metadata/value_test.go
Normal file
51
src/common/config/metadata/value_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testingMetaDataArray = []Item{
|
||||
{Name: "ldap_search_scope", ItemType: &LdapScopeType{}, Scope: "system", Group: "ldapbasic"},
|
||||
{Name: "ldap_search_dn", ItemType: &StringType{}, Scope: "user", Group: "ldapbasic"},
|
||||
{Name: "ulimit", ItemType: &Int64Type{}, Scope: "user", Group: "ldapbasic"},
|
||||
{Name: "ldap_verify_cert", ItemType: &BoolType{}, Scope: "user", Group: "ldapbasic"},
|
||||
{Name: "sample_map_setting", ItemType: &MapType{}, Scope: "user", Group: "ldapbasic"},
|
||||
}
|
||||
|
||||
func TestConfigureValue_GetBool(t *testing.T) {
|
||||
assert.Equal(t, NewConfigureValue("ldap_verify_cert", "true").GetBool(), true)
|
||||
assert.Equal(t, NewConfigureValue("unknown", "false").GetBool(), false)
|
||||
}
|
||||
|
||||
func TestConfigureValue_GetString(t *testing.T) {
|
||||
assert.Equal(t, NewConfigureValue("ldap_url", "ldaps://ldap.vmware.com").GetString(), "ldaps://ldap.vmware.com")
|
||||
}
|
||||
|
||||
func TestConfigureValue_GetStringToStringMap(t *testing.T) {
|
||||
Instance().initFromArray(testingMetaDataArray)
|
||||
assert.Equal(t, NewConfigureValue("sample_map_setting", `{"sample":"abc"}`).GetStringToStringMap(), map[string]string{"sample": "abc"})
|
||||
Instance().init()
|
||||
}
|
||||
func TestConfigureValue_GetInt(t *testing.T) {
|
||||
assert.Equal(t, NewConfigureValue("ldap_timeout", "5").GetInt(), 5)
|
||||
}
|
||||
|
||||
func TestConfigureValue_GetInt64(t *testing.T) {
|
||||
Instance().initFromArray(testingMetaDataArray)
|
||||
assert.Equal(t, NewConfigureValue("ulimit", "99999").GetInt64(), int64(99999))
|
||||
}
|
Loading…
Reference in New Issue
Block a user