Refactor config settings encrypt + metadata (#6387)

Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
stonezdj(Daojun Zhang) 2018-12-12 12:14:33 +08:00 committed by Yan
parent f7a28ee2a2
commit 13511d74ed
11 changed files with 899 additions and 0 deletions

View 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)
}

View 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)
}

View 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
}

View 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
}
}

View 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
}

View 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))
}
}

View 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},
}
)

View 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
}

View 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"})
}

View 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
}

View 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))
}