Refactor configure api to new programming model

Changes include:
1. Move core/config to controller/config
2. Change the job_service and gcreadonly to depends on lib/config instead of core/config
3. Move the config related dao, manager and driver to pkg/config
4. Adjust the invocation of the config API, most of then should provide a context parameter, when accessing system config, you can call it with background context, when accessing user config, the context should provide orm.Context

Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
stonezdj 2021-03-11 20:25:51 +08:00
parent d9052c8241
commit 107e468b60
186 changed files with 3528 additions and 2683 deletions

View File

@ -1008,46 +1008,6 @@ paths:
description: Only admin has this authority.
'500':
description: Unexpected internal errors.
/configurations:
get:
summary: Get system configurations.
description: |
This endpoint is for retrieving system configurations that only provides for admin user.
tags:
- Products
responses:
'200':
description: Get system configurations successfully. The response body is a map.
schema:
$ref: '#/definitions/ConfigurationsResponse'
'401':
description: User need to log in first.ß
'403':
description: User does not have permission of admin role.
'500':
description: Unexpected internal errors.
put:
summary: Modify system configurations.
description: |
This endpoint is for modifying system configurations that only provides for admin user.
tags:
- Products
parameters:
- name: configurations
in: body
required: true
schema:
$ref: '#/definitions/Configurations'
description: 'The configuration map can contain a subset of the attributes of the schema, which are to be updated.'
responses:
'200':
description: Modify system configurations successfully.
'401':
description: User need to log in first.
'403':
description: User does not have permission of admin role.
'500':
description: Unexpected internal errors.
/email/ping:
post:
summary: Test connection and authentication with email server.

View File

@ -163,6 +163,69 @@ paths:
$ref: '#/responses/403'
'500':
$ref: '#/responses/500'
/internalconfig:
get:
summary: Get internal configurations.
description: |
This endpoint is for retrieving system configurations that only provides for internal api call.
tags:
- configure
parameters:
- $ref: '#/parameters/requestId'
responses:
'200':
description: Get system configurations successfully. The response body is a map.
schema:
$ref: '#/definitions/InternalConfigurationsResponse'
'401':
description: User need to log in first.
'403':
description: User does not have permission of admin role.
'500':
description: Unexpected internal errors.
/configurations:
get:
summary: Get system configurations.
description: |
This endpoint is for retrieving system configurations that only provides for admin user.
tags:
- configure
parameters:
- $ref: '#/parameters/requestId'
responses:
'200':
description: Get system configurations successfully. The response body is a map.
schema:
$ref: '#/definitions/ConfigurationsResponse'
'401':
description: User need to log in first.ß
'403':
description: User does not have permission of admin role.
'500':
description: Unexpected internal errors.
put:
summary: Modify system configurations.
description: |
This endpoint is for modifying system configurations that only provides for admin user.
tags:
- configure
parameters:
- $ref: '#/parameters/requestId'
- name: configurations
in: body
required: true
schema:
$ref: '#/definitions/Configurations'
description: 'The configuration map can contain a subset of the attributes of the schema, which are to be updated.'
responses:
'200':
description: Modify system configurations successfully.
'401':
description: User need to log in first.
'403':
description: User does not have permission of admin role.
'500':
description: Unexpected internal errors.
/projects:
get:
summary: List projects
@ -6635,3 +6698,212 @@ definitions:
type: string
description: The webhook job update time.
format: date-time
InternalConfigurationsResponse:
type: object
x-go-type:
type: InternalCfg
import:
package: "github.com/goharbor/harbor/src/lib/config"
ConfigurationsResponse:
type: object
properties:
auth_mode:
$ref: '#/definitions/StringConfigItem'
description: The auth mode of current system, such as "db_auth", "ldap_auth", "oidc_auth"
email_from:
$ref: '#/definitions/StringConfigItem'
description: The sender name for Email notification.
email_host:
$ref: '#/definitions/StringConfigItem'
description: The hostname of SMTP server that sends Email notification.
email_identity:
$ref: '#/definitions/StringConfigItem'
description: By default it's empty so the email_username is picked
email_insecure:
$ref: '#/definitions/BoolConfigItem'
description: Whether or not the certificate will be verified when Harbor tries to access the email server.
email_port:
$ref: '#/definitions/IntegerConfigItem'
description: The port of SMTP server
email_ssl:
$ref: '#/definitions/BoolConfigItem'
description: When it''s set to true the system will access Email server via TLS by default. If it''s set to false, it still will handle "STARTTLS" from server side.
email_username:
$ref: '#/definitions/StringConfigItem'
description: The username for authenticate against SMTP server
ldap_base_dn:
$ref: '#/definitions/StringConfigItem'
description: The Base DN for LDAP binding.
ldap_filter:
$ref: '#/definitions/StringConfigItem'
description: The filter for LDAP search
ldap_group_base_dn:
$ref: '#/definitions/StringConfigItem'
description: The base DN to search LDAP group.
ldap_group_admin_dn:
$ref: '#/definitions/StringConfigItem'
description: Specify the ldap group which have the same privilege with Harbor admin
ldap_group_attribute_name:
$ref: '#/definitions/StringConfigItem'
description: The attribute which is used as identity of the LDAP group, default is cn.'
ldap_group_search_filter:
$ref: '#/definitions/StringConfigItem'
description: The filter to search the ldap group
ldap_group_search_scope:
$ref: '#/definitions/IntegerConfigItem'
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
ldap_scope:
$ref: '#/definitions/IntegerConfigItem'
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
ldap_search_dn:
$ref: '#/definitions/StringConfigItem'
description: The DN of the user to do the search.
ldap_timeout:
$ref: '#/definitions/IntegerConfigItem'
description: Timeout in seconds for connection to LDAP server
ldap_uid:
$ref: '#/definitions/StringConfigItem'
description: The attribute which is used as identity for the LDAP binding, such as "CN" or "SAMAccountname"
ldap_url:
$ref: '#/definitions/StringConfigItem'
description: The URL of LDAP server
ldap_verify_cert:
$ref: '#/definitions/BoolConfigItem'
description: Whether verify your OIDC server certificate, disable it if your OIDC server is hosted via self-hosted certificate.
ldap_group_membership_attribute:
$ref: '#/definitions/StringConfigItem'
description: The user attribute to identify the group membership
project_creation_restriction:
$ref: '#/definitions/StringConfigItem'
description: Indicate who can create projects, it could be ''adminonly'' or ''everyone''.
read_only:
$ref: '#/definitions/BoolConfigItem'
description: The flag to indicate whether Harbor is in readonly mode.
self_registration:
$ref: '#/definitions/BoolConfigItem'
description: Whether the Harbor instance supports self-registration. If it''s set to false, admin need to add user to the instance.
token_expiration:
$ref: '#/definitions/IntegerConfigItem'
description: The expiration time of the token for internal Registry, in minutes.
uaa_client_id:
$ref: '#/definitions/StringConfigItem'
description: The client id of UAA
uaa_client_secret:
$ref: '#/definitions/StringConfigItem'
description: The client secret of the UAA
uaa_endpoint:
$ref: '#/definitions/StringConfigItem'
description: The endpoint of the UAA
uaa_verify_cert:
$ref: '#/definitions/BoolConfigItem'
description: Verify the certificate in UAA server
http_authproxy_endpoint:
$ref: '#/definitions/StringConfigItem'
description: The endpoint of the HTTP auth
http_authproxy_tokenreview_endpoint:
$ref: '#/definitions/StringConfigItem'
description: The token review endpoint
http_authproxy_admin_groups:
$ref: '#/definitions/StringConfigItem'
description: The group which has the harbor admin privileges
http_authproxy_admin_usernames:
$ref: '#/definitions/StringConfigItem'
description: The usernames which has the harbor admin privileges
http_authproxy_verify_cert:
$ref: '#/definitions/BoolConfigItem'
description: Verify the HTTP auth provider's certificate
http_authproxy_skip_search:
$ref: '#/definitions/BoolConfigItem'
description: Search user before onboard
http_authproxy_server_certificate:
$ref: '#/definitions/StringConfigItem'
description: The certificate of the HTTP auth provider
oidc_name:
$ref: '#/definitions/StringConfigItem'
description: The OIDC provider name
oidc_endpoint:
$ref: '#/definitions/StringConfigItem'
description: The endpoint of the OIDC provider
oidc_client_id:
$ref: '#/definitions/StringConfigItem'
description: The client ID of the OIDC provider
oidc_groups_claim:
$ref: '#/definitions/StringConfigItem'
description: The attribute claims the group name
oidc_admin_group:
$ref: '#/definitions/StringConfigItem'
description: The OIDC group which has the harbor admin privileges
oidc_scope:
$ref: '#/definitions/StringConfigItem'
description: The scope of the OIDC provider
oidc_user_claim:
$ref: '#/definitions/StringConfigItem'
description: The attribute claims the username
oidc_verify_cert:
$ref: '#/definitions/BoolConfigItem'
description: Verify the OIDC provider's certificate'
oidc_auto_onboard:
$ref: '#/definitions/BoolConfigItem'
description: Auto onboard the OIDC user
oidc_extra_redirect_parms:
$ref: '#/definitions/StringConfigItem'
description: Extra parameters to add when redirect request to OIDC provider
robot_token_duration:
$ref: '#/definitions/IntegerConfigItem'
description: The robot account token duration in days
robot_name_prefix:
$ref: '#/definitions/StringConfigItem'
description: The rebot account name prefix
notification_enable:
$ref: '#/definitions/BoolConfigItem'
description: Enable notification
quota_per_project_enable:
$ref: '#/definitions/BoolConfigItem'
description: Enable quota per project
storage_per_project:
$ref: '#/definitions/IntegerConfigItem'
description: The storage quota per project
scan_all_policy:
type: object
properties:
type:
type: string
description: 'The type of scan all policy, currently the valid values are "none" and "daily"'
parameter:
type: object
properties:
daily_time:
type: integer
description: 'The offset in seconds of UTC 0 o''clock, only valid when the policy type is "daily"'
description: 'The parameters of the policy, the values are dependant on the type of the policy.'
Configurations:
type: object
additionalProperties:
type: object
StringConfigItem:
type: object
properties:
value:
type: string
description: The string value of current config item
editable:
type: boolean
description: The configure item can be updated or not
BoolConfigItem:
type: object
properties:
value:
type: boolean
description: The boolean value of current config item
editable:
type: boolean
description: The configure item can be updated or not
IntegerConfigItem:
type: object
properties:
value:
type: integer
description: The integer value of current config item
editable:
type: boolean
description: The configure item can be updated or not

View File

@ -2,6 +2,7 @@ package chartserver
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"path"
"strings"
"sync"
@ -9,7 +10,6 @@ import (
"github.com/goharbor/harbor/src/lib/errors"
"helm.sh/helm/v3/cmd/helm/search"
"github.com/goharbor/harbor/src/core/config"
hlog "github.com/goharbor/harbor/src/lib/log"
)

View File

@ -1,6 +1,7 @@
package main
import (
"github.com/goharbor/harbor/src/common/models"
"net/http"
"os"
"strings"
@ -11,7 +12,6 @@ import (
"github.com/goharbor/harbor/src/common/dao"
commonthttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/exporter"
)

View File

@ -1,11 +1,11 @@
package main
import (
"github.com/goharbor/harbor/src/common/models"
"os"
"strconv"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/migration"
)

View File

@ -1,48 +0,0 @@
// 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

@ -1,44 +0,0 @@
// 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

@ -1,54 +0,0 @@
// 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 config
import (
"github.com/stretchr/testify/assert"
"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
}
}
func TestPresetKeyProvider(t *testing.T) {
kp := &PresetKeyProvider{
Key: "mykey",
}
k, err := kp.Get(nil)
assert.Nil(t, err)
assert.Equal(t, "mykey", k)
}

View File

@ -1,256 +0,0 @@
// 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 config
import (
"fmt"
"os"
"sync"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/config/store"
"github.com/goharbor/harbor/src/common/config/store/driver"
"github.com/goharbor/harbor/src/common/http/modifier/auth"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/cache"
"github.com/goharbor/harbor/src/lib/log"
)
// CfgManager ... Configure Manager
type CfgManager struct {
store *store.ConfigStore
}
// NewDBCfgManager - create DB config manager
func NewDBCfgManager() *CfgManager {
cfgDriver := (driver.Driver)(&driver.Database{})
if cache.Default() != nil {
log.Debug("create DB config manager with cache enabled")
cfgDriver = driver.NewCacheDriver(cache.Default(), cfgDriver)
}
manager := &CfgManager{store: store.NewConfigStore(cfgDriver)}
// load default value
manager.loadDefault()
// load system config from env
manager.loadSystemConfigFromEnv()
return manager
}
// NewRESTCfgManager - create REST config manager
func NewRESTCfgManager(configURL, secret string) *CfgManager {
secAuth := auth.NewSecretAuthorizer(secret)
manager := &CfgManager{store: store.NewConfigStore(driver.NewRESTDriver(configURL, secAuth))}
return manager
}
// InMemoryDriver driver for unit testing
type InMemoryDriver struct {
sync.Mutex
cfgMap map[string]interface{}
}
// Load load data from driver, for example load from database,
// it should be invoked before get any user scope config
// for system scope config, because it is immutable, no need to call this method
func (d *InMemoryDriver) Load() (map[string]interface{}, error) {
d.Lock()
defer d.Unlock()
res := make(map[string]interface{})
for k, v := range d.cfgMap {
res[k] = v
}
return res, nil
}
// Save only save user config setting to driver, for example: database, REST
func (d *InMemoryDriver) Save(cfg map[string]interface{}) error {
d.Lock()
defer d.Unlock()
for k, v := range cfg {
d.cfgMap[k] = v
}
return nil
}
// NewInMemoryManager create a manager for unit testing, doesn't involve database or REST
func NewInMemoryManager() *CfgManager {
manager := &CfgManager{store: store.NewConfigStore(&InMemoryDriver{cfgMap: map[string]interface{}{}})}
// load default value
manager.loadDefault()
// load system config from env
manager.loadSystemConfigFromEnv()
return manager
}
// loadDefault ...
func (c *CfgManager) loadDefault() {
// Init Default Value
itemArray := metadata.Instance().GetAll()
for _, item := range itemArray {
if len(item.DefaultValue) > 0 {
cfgValue, err := metadata.NewCfgValue(item.Name, item.DefaultValue)
if err != nil {
log.Errorf("loadDefault failed, config item, key: %v, err: %v", item.Name, err)
continue
}
c.store.Set(item.Name, *cfgValue)
}
}
}
// loadSystemConfigFromEnv ...
func (c *CfgManager) loadSystemConfigFromEnv() {
itemArray := metadata.Instance().GetAll()
// Init System Value
for _, item := range itemArray {
if item.Scope == metadata.SystemScope && len(item.EnvKey) > 0 {
if envValue, ok := os.LookupEnv(item.EnvKey); ok {
configValue, err := metadata.NewCfgValue(item.Name, envValue)
if err != nil {
log.Errorf("loadSystemConfigFromEnv failed, config item, key: %v, err: %v", item.Name, err)
continue
}
c.store.Set(item.Name, *configValue)
}
}
}
}
// GetAll get all settings.
func (c *CfgManager) GetAll() map[string]interface{} {
resultMap := map[string]interface{}{}
if err := c.store.Load(); err != nil {
log.Errorf("GetAll failed, error %v", err)
return resultMap
}
metaDataList := metadata.Instance().GetAll()
for _, item := range metaDataList {
cfgValue, err := c.store.GetAnyType(item.Name)
if err != nil {
if err != metadata.ErrValueNotSet {
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
}
continue
}
resultMap[item.Name] = cfgValue
}
return resultMap
}
// GetUserCfgs retrieve all user configs
func (c *CfgManager) GetUserCfgs() map[string]interface{} {
resultMap := map[string]interface{}{}
if err := c.store.Load(); err != nil {
log.Errorf("GetUserCfgs failed, error %v", err)
return resultMap
}
metaDataList := metadata.Instance().GetAll()
for _, item := range metaDataList {
if item.Scope == metadata.UserScope {
cfgValue, err := c.store.GetAnyType(item.Name)
if err != nil {
if err == metadata.ErrValueNotSet {
if _, ok := item.ItemType.(*metadata.StringType); ok {
cfgValue = ""
}
if _, ok := item.ItemType.(*metadata.NonEmptyStringType); ok {
cfgValue = ""
}
} else {
log.Errorf("Failed to get value of key %v, error %v", item.Name, err)
continue
}
}
resultMap[item.Name] = cfgValue
}
}
return resultMap
}
// Load load configuration from storage, like database or redis
func (c *CfgManager) Load() error {
return c.store.Load()
}
// Save - Save all current configuration to storage
func (c *CfgManager) Save() error {
return c.store.Save()
}
// Get ...
func (c *CfgManager) Get(key string) *metadata.ConfigureValue {
configValue, err := c.store.Get(key)
if err != nil {
log.Debugf("failed to get key %v, error: %v", key, err)
configValue = &metadata.ConfigureValue{}
}
return configValue
}
// Set ...
func (c *CfgManager) Set(key string, value interface{}) {
configValue, err := metadata.NewCfgValue(key, utils.GetStrValueOfAnyType(value))
if err != nil {
log.Errorf("error when setting key: %v, error %v", key, err)
return
}
c.store.Set(key, *configValue)
}
// GetDatabaseCfg - Get database configurations
func (c *CfgManager) GetDatabaseCfg() *models.Database {
return &models.Database{
Type: c.Get(common.DatabaseType).GetString(),
PostGreSQL: &models.PostGreSQL{
Host: c.Get(common.PostGreSQLHOST).GetString(),
Port: c.Get(common.PostGreSQLPort).GetInt(),
Username: c.Get(common.PostGreSQLUsername).GetString(),
Password: c.Get(common.PostGreSQLPassword).GetString(),
Database: c.Get(common.PostGreSQLDatabase).GetString(),
SSLMode: c.Get(common.PostGreSQLSSLMode).GetString(),
MaxIdleConns: c.Get(common.PostGreSQLMaxIdleConns).GetInt(),
MaxOpenConns: c.Get(common.PostGreSQLMaxOpenConns).GetInt(),
},
}
}
// UpdateConfig - Update config store with a specified configuration and also save updated configure.
func (c *CfgManager) UpdateConfig(cfgs map[string]interface{}) error {
return c.store.Update(cfgs)
}
// ValidateCfg validate config by metadata. return the first error if exist.
func (c *CfgManager) ValidateCfg(cfgs map[string]interface{}) error {
for key, value := range cfgs {
strVal := utils.GetStrValueOfAnyType(value)
_, err := metadata.NewCfgValue(key, strVal)
if err != nil {
return fmt.Errorf("%v, item name: %v", err, key)
}
}
return nil
}
// DumpTrace dump all configurations
func (c *CfgManager) DumpTrace() {
cfgs := c.GetAll()
for k, v := range cfgs {
log.Info(k, ":=", v)
}
}

View File

@ -1,141 +0,0 @@
// 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 config
import (
"fmt"
"os"
"testing"
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/stretchr/testify/assert"
)
var TestDBConfig = map[string]interface{}{
"postgresql_host": "localhost",
"postgresql_database": "registry",
"postgresql_password": "root123",
"postgresql_username": "postgres",
"postgresql_sslmode": "disable",
"email_host": "127.0.0.1",
"scan_all_policy": `{"parameter":{"daily_time":0},"type":"daily"}`,
}
var configManager *CfgManager
func TestMain(m *testing.M) {
configManager = NewDBCfgManager()
test.InitDatabaseFromEnv()
configManager.UpdateConfig(TestDBConfig)
os.Exit(m.Run())
}
func TestLoadFromDatabase(t *testing.T) {
configManager.UpdateConfig(TestDBConfig)
configManager.Load()
assert.Equal(t, "127.0.0.1", configManager.Get("email_host").GetString())
assert.Equal(t, `{"parameter":{"daily_time":0},"type":"daily"}`, configManager.Get("scan_all_policy").GetString())
}
func TestLoadUserCfg(t *testing.T) {
configMap := configManager.GetUserCfgs()
assert.NotNil(t, configMap["ldap_url"])
assert.NotNil(t, configMap["ldap_base_dn"])
}
func TestSaveToDatabase(t *testing.T) {
fmt.Printf("database config %#v\n", configManager.GetDatabaseCfg())
configManager.Load()
configManager.Set("read_only", "true")
configManager.Save()
configManager.Load()
assert.Equal(t, true, configManager.Get("read_only").GetBool())
}
func TestUpdateCfg(t *testing.T) {
testConfig := map[string]interface{}{
"ldap_url": "ldaps://ldap.vmware.com",
"ldap_search_dn": "cn=admin,dc=example,dc=com",
"ldap_timeout": 10,
"ldap_search_password": "admin",
"ldap_base_dn": "dc=example,dc=com",
}
configManager.Load()
configManager.UpdateConfig(testConfig)
assert.Equal(t, "ldaps://ldap.vmware.com", configManager.Get("ldap_url").GetString())
assert.Equal(t, 10, configManager.Get("ldap_timeout").GetInt())
assert.Equal(t, "admin", configManager.Get("ldap_search_password").GetPassword())
assert.Equal(t, "cn=admin,dc=example,dc=com", configManager.Get("ldap_search_dn").GetString())
assert.Equal(t, "dc=example,dc=com", configManager.Get("ldap_base_dn").GetString())
}
func TestCfgManager_loadDefaultValues(t *testing.T) {
configManager.loadDefault()
if configManager.Get("ldap_timeout").GetInt() != 5 {
t.Errorf("Failed to load ldap_timeout")
}
}
func TestCfgManger_loadSystemValues(t *testing.T) {
configManager.loadDefault()
configManager.loadSystemConfigFromEnv()
configManager.UpdateConfig(map[string]interface{}{
"postgresql_host": "127.0.0.1",
})
if configManager.Get("postgresql_host").GetString() != "127.0.0.1" {
t.Errorf("Failed to set system value postgresql_host, expected %v, actual %v", "127.0.0.1", configManager.Get("postgresql_host").GetString())
}
}
func TestCfgManager_GetDatabaseCfg(t *testing.T) {
configManager.UpdateConfig(map[string]interface{}{
"postgresql_host": "localhost",
"postgresql_database": "registry",
"postgresql_password": "root123",
"postgresql_username": "postgres",
"postgresql_sslmode": "disable",
})
dbCfg := configManager.GetDatabaseCfg()
assert.Equal(t, "localhost", dbCfg.PostGreSQL.Host)
assert.Equal(t, "registry", dbCfg.PostGreSQL.Database)
assert.Equal(t, "root123", dbCfg.PostGreSQL.Password)
assert.Equal(t, "postgres", dbCfg.PostGreSQL.Username)
assert.Equal(t, "disable", dbCfg.PostGreSQL.SSLMode)
}
func TestNewInMemoryManager(t *testing.T) {
inMemoryManager := NewInMemoryManager()
inMemoryManager.UpdateConfig(map[string]interface{}{
"ldap_url": "ldaps://ldap.vmware.com",
"ldap_timeout": 5,
"ldap_verify_cert": true,
})
assert.Equal(t, "ldaps://ldap.vmware.com", inMemoryManager.Get("ldap_url").GetString())
assert.Equal(t, 5, inMemoryManager.Get("ldap_timeout").GetInt())
assert.Equal(t, true, inMemoryManager.Get("ldap_verify_cert").GetBool())
}
/*
func TestNewRESTCfgManager(t *testing.T) {
restMgr := NewRESTCfgManager("http://10.161.47.13:8080"+common.CoreConfigPath, "0XtgSGFx1amMDTaH")
err := restMgr.Load()
if err != nil {
t.Errorf("Failed with error %v", err)
}
fmt.Printf("db:%v", restMgr.GetDatabaseCfg().Type)
fmt.Printf("host:%#v\n", restMgr.GetDatabaseCfg().PostGreSQL.Host)
fmt.Printf("port:%#v\n", restMgr.GetDatabaseCfg().PostGreSQL.Port)
}*/

View File

@ -1,85 +0,0 @@
// 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 driver
import (
"fmt"
"os"
"testing"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/dao"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
os.Exit(m.Run())
}
func TestDatabase_Load(t *testing.T) {
cfgs := map[string]interface{}{
common.AUTHMode: "db_auth",
common.LDAPURL: "ldap://ldap.vmware.com",
}
driver := Database{}
driver.Save(cfgs)
cfgMap, err := driver.Load()
if err != nil {
t.Errorf("failed to load, error %v", err)
}
assert.True(t, len(cfgMap) >= 1)
if _, ok := cfgMap["ldap_url"]; !ok {
t.Error("Can not find ldap_url")
}
}
func TestDatabase_Save(t *testing.T) {
ldapURL := "ldap://ldap.vmware.com"
driver := Database{}
prevCfg, err := driver.Load()
if err != nil {
t.Errorf("failed to load config %v", err)
}
cfgMap := map[string]interface{}{"ldap_url": ldapURL}
driver.Save(cfgMap)
updatedMap, err := driver.Load()
if err != nil {
t.Errorf("failed to load config %v", err)
}
assert.Equal(t, updatedMap["ldap_url"], ldapURL)
driver.Save(prevCfg)
}
func BenchmarkDatabaseLoad(b *testing.B) {
cfgs := map[string]interface{}{}
for _, item := range metadata.Instance().GetAll() {
cfgs[item.Name] = item.DefaultValue
}
driver := Database{}
driver.Save(cfgs)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if _, err := driver.Load(); err != nil {
fmt.Printf("load failed %v", err)
}
}
})
}

View File

@ -1,10 +0,0 @@
// Package driver provide the implementation of config driver used in CfgManager
package driver
// Driver the interface to save/load config
type Driver interface {
// Load - load config item from config driver
Load() (map[string]interface{}, error)
// Save - save config item into config driver
Save(cfg map[string]interface{}) error
}

View File

@ -1,45 +0,0 @@
package driver
import (
"errors"
"net/http"
commonhttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/http/modifier"
"github.com/goharbor/harbor/src/lib/log"
)
// RESTDriver - config store driver based on REST API
type RESTDriver struct {
configRESTURL string
client *commonhttp.Client
}
// NewRESTDriver - Create RESTDriver
func NewRESTDriver(configRESTURL string, modifiers ...modifier.Modifier) *RESTDriver {
if commonhttp.InternalTLSEnabled() {
tr := commonhttp.GetHTTPTransport(commonhttp.SecureTransport)
return &RESTDriver{configRESTURL: configRESTURL, client: commonhttp.NewClient(&http.Client{Transport: tr}, modifiers...)}
}
return &RESTDriver{configRESTURL: configRESTURL, client: commonhttp.NewClient(nil, modifiers...)}
}
// Load - load config data from REST server
func (h *RESTDriver) Load() (map[string]interface{}, error) {
cfgMap := map[string]interface{}{}
log.Infof("get configuration from url: %+v", h.configRESTURL)
err := h.client.Get(h.configRESTURL, &cfgMap)
if err != nil {
log.Errorf("Failed on load rest config err:%v, url:%v", err, h.configRESTURL)
}
if len(cfgMap) < 1 {
return cfgMap, errors.New("failed to load rest config")
}
return cfgMap, err
}
// Save - Save config data to REST server by PUT method
func (h *RESTDriver) Save(cfgMap map[string]interface{}) error {
return h.client.Put(h.configRESTURL, cfgMap)
}

View File

@ -1,93 +0,0 @@
package store
import (
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/config/store/driver"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/log"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestMain(m *testing.M) {
dao.PrepareTestForPostgresSQL()
cfgStore := NewConfigStore(&driver.Database{})
cfgStore.Set("ldap_url", metadata.ConfigureValue{Name: "ldap_url", Value: "ldap://ldap.vmware.com"})
err := cfgStore.Save()
if err != nil {
log.Fatal(err)
}
os.Exit(m.Run())
}
func TestConfigStore_Save(t *testing.T) {
cfgStore := NewConfigStore(&driver.Database{})
err := cfgStore.Save()
cfgStore.Set("ldap_verify_cert", metadata.ConfigureValue{Name: "ldap_verify_cert", Value: "true"})
if err != nil {
t.Fatal(err)
}
cfgValue, err := cfgStore.Get("ldap_verify_cert")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, true, cfgValue.GetBool())
}
func TestConfigStore_Load(t *testing.T) {
cfgStore := NewConfigStore(&driver.Database{})
err := cfgStore.Load()
if err != nil {
t.Fatal(err)
}
cfgValue, err := cfgStore.Get("ldap_url")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "ldap://ldap.vmware.com", cfgValue.GetString())
}
func TestToString(t *testing.T) {
cases := []struct {
name string
value interface{}
expect string
}{
{
name: "transform int",
value: 999,
expect: "999",
},
{
name: "transform slice",
value: []int{0, 1, 2},
expect: "[0,1,2]",
},
{
name: "transform map",
value: map[string]string{"k": "v"},
expect: "{\"k\":\"v\"}",
},
{
name: "transform bool",
value: false,
expect: "false",
},
{
name: "transform nil",
value: nil,
expect: "nil",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
s, err := toString(c.value)
assert.Nil(t, err)
assert.Equal(t, c.expect, s)
})
}
}

View File

@ -23,6 +23,9 @@ const (
UAAAuth = "uaa_auth"
HTTPAuth = "http_auth"
OIDCAuth = "oidc_auth"
DBCfgManager = "db_cfg_manager"
InMemoryCfgManager = "in_memory_manager"
RestCfgManager = "rest_config_manager"
ProCrtRestrEveryone = "everyone"
ProCrtRestrAdmOnly = "adminonly"
LDAPScopeBase = 0
@ -141,7 +144,7 @@ const (
RobotNamePrefix = "robot_name_prefix"
// Use this prefix to index user who tries to login with web hook token.
AuthProxyUserNamePrefix = "tokenreview$"
CoreConfigPath = "/api/internal/configurations"
CoreConfigPath = "/api/v2.0/internalconfig"
RobotTokenDuration = "robot_token_duration"
OIDCCallbackPath = "/c/oidc/callback"

View File

@ -146,14 +146,6 @@ func PaginateForQuerySetter(qs orm.QuerySeter, page, size int64) orm.QuerySeter
return qs
}
// Escape ..
func Escape(str string) string {
str = strings.Replace(str, `\`, `\\`, -1)
str = strings.Replace(str, `%`, `\%`, -1)
str = strings.Replace(str, `_`, `\_`, -1)
return str
}
// implements github.com/golang-migrate/migrate/v4.Logger
type mLogger struct {
logger *log.Logger

View File

@ -1,77 +0,0 @@
// 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 dao
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
)
// AuthModeCanBeModified determines whether auth mode can be
// modified or not. Auth mode can modified when there is only admin
// user in database.
func AuthModeCanBeModified() (bool, error) {
c, err := GetOrmer().QueryTable(&models.User{}).Count()
if err != nil {
return false, err
}
// admin and anonymous
return c == 2, nil
}
// GetConfigEntries Get configuration from database
func GetConfigEntries() ([]*models.ConfigEntry, error) {
o := GetOrmer()
var p []*models.ConfigEntry
sql := "select * from properties"
n, err := o.Raw(sql, []interface{}{}).QueryRows(&p)
if err != nil {
return nil, err
}
if n == 0 {
return nil, nil
}
return p, nil
}
// SaveConfigEntries Save configuration to database.
func SaveConfigEntries(entries []models.ConfigEntry) error {
o := GetOrmer()
for _, entry := range entries {
if entry.Key == common.LDAPGroupAdminDn {
entry.Value = utils.TrimLower(entry.Value)
}
tempEntry := models.ConfigEntry{}
tempEntry.Key = entry.Key
tempEntry.Value = entry.Value
created, _, err := o.ReadOrCreate(&tempEntry, "k")
if err != nil && !IsDupRecErr(err) {
log.Errorf("Error create configuration entry: %v", err)
return err
}
if !created {
entry.ID = tempEntry.ID
_, err := o.Update(&entry, "v")
if err != nil {
return err
}
}
}
return nil
}

View File

@ -15,19 +15,21 @@
package dao
import (
"github.com/goharbor/harbor/src/lib/orm"
"testing"
"github.com/goharbor/harbor/src/common/models"
)
func TestAuthModeCanBeModified(t *testing.T) {
ctx := orm.Context()
c, err := GetOrmer().QueryTable(&models.User{}).Count()
if err != nil {
t.Fatalf("failed to count users: %v", err)
}
if c == 2 {
flag, err := AuthModeCanBeModified()
flag, err := AuthModeCanBeModified(ctx)
if err != nil {
t.Fatalf("failed to determine whether auth mode can be modified: %v", err)
}
@ -51,7 +53,7 @@ func TestAuthModeCanBeModified(t *testing.T) {
}
}(id)
flag, err = AuthModeCanBeModified()
flag, err = AuthModeCanBeModified(ctx)
if err != nil {
t.Fatalf("failed to determine whether auth mode can be modified: %v", err)
}
@ -60,7 +62,7 @@ func TestAuthModeCanBeModified(t *testing.T) {
}
} else {
flag, err := AuthModeCanBeModified()
flag, err := AuthModeCanBeModified(ctx)
if err != nil {
t.Fatalf("failed to determine whether auth mode can be modified: %v", err)
}

View File

@ -15,20 +15,23 @@
package dao
import (
"context"
"fmt"
"os"
"testing"
"time"
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
libOrm "github.com/goharbor/harbor/src/lib/orm"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var testCtx context.Context
func execUpdate(o orm.Ormer, sql string, params ...interface{}) error {
p, err := o.Raw(sql).Prepare()
if err != nil {
@ -120,6 +123,7 @@ func TestMain(m *testing.M) {
default:
log.Fatalf("invalid database: %s", database)
}
testCtx = libOrm.Context()
result = testForAll(m)
if result != 0 {
@ -566,104 +570,6 @@ func TestIsSuperUser(t *testing.T) {
assert.False(IsSuperUser("none"))
}
func TestSaveConfigEntries(t *testing.T) {
configEntries := []models.ConfigEntry{
{
Key: "teststringkey",
Value: "192.168.111.211",
},
{
Key: "testboolkey",
Value: "true",
},
{
Key: "testnumberkey",
Value: "5",
},
{
Key: common.CfgDriverDB,
Value: "db",
},
}
err := SaveConfigEntries(configEntries)
if err != nil {
t.Fatalf("failed to save configuration to database %v", err)
}
readEntries, err := GetConfigEntries()
if err != nil {
t.Fatalf("Failed to get configuration from database %v", err)
}
findItem := 0
for _, entry := range readEntries {
switch entry.Key {
case "teststringkey":
if "192.168.111.211" == entry.Value {
findItem++
}
case "testnumberkey":
if "5" == entry.Value {
findItem++
}
case "testboolkey":
if "true" == entry.Value {
findItem++
}
default:
}
}
if findItem != 3 {
t.Fatalf("Should update 3 configuration but only update %d", findItem)
}
configEntries = []models.ConfigEntry{
{
Key: "teststringkey",
Value: "192.168.111.215",
},
{
Key: "testboolkey",
Value: "false",
},
{
Key: "testnumberkey",
Value: "7",
},
{
Key: common.CfgDriverDB,
Value: "db",
},
}
err = SaveConfigEntries(configEntries)
if err != nil {
t.Fatalf("failed to save configuration to database %v", err)
}
readEntries, err = GetConfigEntries()
if err != nil {
t.Fatalf("Failed to get configuration from database %v", err)
}
findItem = 0
for _, entry := range readEntries {
switch entry.Key {
case "teststringkey":
if "192.168.111.215" == entry.Value {
findItem++
}
case "testnumberkey":
if "7" == entry.Value {
findItem++
}
case "testboolkey":
if "false" == entry.Value {
findItem++
}
default:
}
}
if findItem != 3 {
t.Fatalf("Should update 3 configuration but only update %d", findItem)
}
}
func TestIsDupRecError(t *testing.T) {
assert.True(t, IsDupRecErr(fmt.Errorf("pq: duplicate key value violates unique constraint \"properties_k_key\"")))
assert.False(t, IsDupRecErr(fmt.Errorf("other error")))

View File

@ -20,6 +20,7 @@ import (
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common/models"
libOrm "github.com/goharbor/harbor/src/lib/orm"
)
// AddLabel creates a label
@ -71,7 +72,7 @@ func getLabelQuerySetter(query *models.LabelQuery) orm.QuerySeter {
qs := GetOrmer().QueryTable(&models.Label{})
if len(query.Name) > 0 {
if query.FuzzyMatchName {
qs = qs.Filter("Name__icontains", Escape(query.Name))
qs = qs.Filter("Name__icontains", libOrm.Escape(query.Name))
} else {
qs = qs.Filter("Name", query.Name)
}

View File

@ -16,12 +16,12 @@ package dao
import (
"fmt"
"github.com/goharbor/harbor/src/common/models"
"net/url"
"os"
"strconv"
"github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
migrate "github.com/golang-migrate/migrate/v4"

View File

@ -16,7 +16,7 @@ package dao
import (
"fmt"
"strings"
"github.com/goharbor/harbor/src/lib/orm"
"time"
"github.com/goharbor/harbor/src/common/models"
@ -43,7 +43,7 @@ func DeleteProjectMetadata(projectID int64, name ...string) error {
params = append(params, projectID)
if len(name) > 0 {
sql += fmt.Sprintf(` and name in ( %s )`, ParamPlaceholderForIn(len(name)))
sql += fmt.Sprintf(` and name in ( %s )`, orm.ParamPlaceholderForIn(len(name)))
params = append(params, name)
}
@ -73,7 +73,7 @@ func GetProjectMetadata(projectID int64, name ...string) ([]*models.ProjectMetad
params = append(params, projectID)
if len(name) > 0 {
sql += fmt.Sprintf(` and name in ( %s )`, ParamPlaceholderForIn(len(name)))
sql += fmt.Sprintf(` and name in ( %s )`, orm.ParamPlaceholderForIn(len(name)))
params = append(params, name)
}
@ -81,16 +81,6 @@ func GetProjectMetadata(projectID int64, name ...string) ([]*models.ProjectMetad
return proMetas, err
}
// ParamPlaceholderForIn returns a string that contains placeholders for sql keyword "in"
// e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string {
placeholders := []string{}
for i := 0; i < n; i++ {
placeholders = append(placeholders, "?")
}
return strings.Join(placeholders, ",")
}
// ListProjectMetadata ...
func ListProjectMetadata(name, value string) ([]*models.ProjectMetadata, error) {
sql := `select * from project_metadata

View File

@ -17,7 +17,9 @@ package dao
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
libOrm "github.com/goharbor/harbor/src/lib/orm"
"fmt"
"time"
@ -234,7 +236,7 @@ func projectQueryConditions(query *models.ProjectQueryParam) (string, []interfac
if len(query.Name) != 0 {
sql += ` and p.name like ?`
params = append(params, "%"+Escape(query.Name)+"%")
params = append(params, "%"+libOrm.Escape(query.Name)+"%")
}
if query.RegistryID > 0 {
@ -266,7 +268,7 @@ func projectQueryConditions(query *models.ProjectQueryParam) (string, []interfac
}
if len(query.ProjectIDs) > 0 {
sql += fmt.Sprintf(` and p.project_id in ( %s )`,
ParamPlaceholderForIn(len(query.ProjectIDs)))
utils.ParamPlaceholderForIn(len(query.ProjectIDs)))
params = append(params, query.ProjectIDs)
}
return sql, params

View File

@ -18,7 +18,9 @@ import (
"fmt"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
)
// GetProjectMember gets all members of the project.
@ -160,9 +162,9 @@ func SearchMemberByName(projectID int64, entityName string) ([]*models.Member, e
order by entity_name `
queryParam := make([]interface{}, 4)
queryParam = append(queryParam, projectID)
queryParam = append(queryParam, "%"+dao.Escape(entityName)+"%")
queryParam = append(queryParam, "%"+orm.Escape(entityName)+"%")
queryParam = append(queryParam, projectID)
queryParam = append(queryParam, "%"+dao.Escape(entityName)+"%")
queryParam = append(queryParam, "%"+orm.Escape(entityName)+"%")
members := []*models.Member{}
log.Debugf("Query sql: %v", sql)
_, err := o.Raw(sql, queryParam).QueryRows(&members)
@ -184,7 +186,7 @@ func ListRoles(user *models.User, projectID int64) ([]int, error) {
sql += fmt.Sprintf(`union
select role
from project_member
where entity_type = 'g' and entity_id in ( %s ) and project_id = ? `, dao.ParamPlaceholderForIn(len(user.GroupIDs)))
where entity_type = 'g' and entity_id in ( %s ) and project_id = ? `, utils.ParamPlaceholderForIn(len(user.GroupIDs)))
params = append(params, user.GroupIDs)
params = append(params, projectID)
}

View File

@ -16,6 +16,7 @@ package project
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"os"
"testing"
@ -28,7 +29,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
_ "github.com/goharbor/harbor/src/core/auth/db"
_ "github.com/goharbor/harbor/src/core/auth/ldap"
cfg "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -71,7 +71,7 @@ func TestMain(m *testing.M) {
"delete from project_member where id > 1",
}
dao.PrepareTestData(clearSqls, initSqls)
cfg.Init()
config.Init()
result = m.Run()
if result != 0 {

View File

@ -16,6 +16,8 @@ package dao
import (
"fmt"
"github.com/goharbor/harbor/src/common/utils"
libOrm "github.com/goharbor/harbor/src/lib/orm"
"time"
"github.com/astaxie/beego/orm"
@ -102,12 +104,12 @@ func repositoryQueryConditions(query ...*models.RepositoryQuery) (string, []inte
if len(q.Name) > 0 {
sql += `and r.name like ? `
params = append(params, "%"+Escape(q.Name)+"%")
params = append(params, "%"+libOrm.Escape(q.Name)+"%")
}
if len(q.ProjectIDs) > 0 {
sql += fmt.Sprintf(`and r.project_id in ( %s ) `,
ParamPlaceholderForIn(len(q.ProjectIDs)))
utils.ParamPlaceholderForIn(len(q.ProjectIDs)))
params = append(params, q.ProjectIDs)
}

View File

@ -16,10 +16,10 @@ package dao
import (
"fmt"
"github.com/goharbor/harbor/src/common/models"
"os"
"strconv"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/log"
)

View File

@ -15,8 +15,10 @@
package dao
import (
"context"
"errors"
"fmt"
libOrm "github.com/goharbor/harbor/src/lib/orm"
"time"
"github.com/astaxie/beego/orm"
@ -129,11 +131,11 @@ func userQueryConditions(query *models.UserQuery) orm.QuerySeter {
}
if len(query.Username) > 0 {
qs = qs.Filter("username__contains", Escape(query.Username))
qs = qs.Filter("username__contains", libOrm.Escape(query.Username))
}
if len(query.Email) > 0 {
qs = qs.Filter("email__contains", Escape(query.Email))
qs = qs.Filter("email__contains", libOrm.Escape(query.Email))
}
return qs
@ -292,3 +294,19 @@ func CleanUser(id int64) error {
func matchPassword(u *models.User, password string) bool {
return utils.Encrypt(password, u.Salt, u.PasswordVersion) == u.Password
}
// AuthModeCanBeModified determines whether auth mode can be
// modified or not. Auth mode can modified when there is only admin
// user in database.
func AuthModeCanBeModified(ctx context.Context) (bool, error) {
o, err := libOrm.FromContext(ctx)
if err != nil {
return false, err
}
c, err := o.QueryTable(&models.User{}).Count()
if err != nil {
return false, err
}
// admin and anonymous
return c == 2, nil
}

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"io/ioutil"
"net/http"
"regexp"
@ -13,7 +14,6 @@ import (
commonhttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/http/modifier/auth"
"github.com/goharbor/harbor/src/common/job/models"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/jobservice/job"
)

View File

@ -25,7 +25,6 @@ func init() {
new(Role),
new(RepoRecord),
new(ProjectMetadata),
new(ConfigEntry),
new(Label),
new(ResourceLabel),
new(UserGroup),

View File

@ -0,0 +1,47 @@
// 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 models
// Database ...
type Database struct {
Type string `json:"type"`
PostGreSQL *PostGreSQL `json:"postgresql,omitempty"`
}
// MySQL ...
type MySQL struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
Database string `json:"database"`
}
// SQLite ...
type SQLite struct {
File string `json:"file"`
}
// PostGreSQL ...
type PostGreSQL struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
Database string `json:"database"`
SSLMode string `json:"sslmode"`
MaxIdleConns int `json:"max_idle_conns"`
MaxOpenConns int `json:"max_open_conns"`
}

View File

@ -295,3 +295,13 @@ func FindNamedMatches(regex *regexp.Regexp, str string) map[string]string {
}
return results
}
// ParamPlaceholderForIn returns a string that contains placeholders for sql keyword "in"
// e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string {
placeholders := []string{}
for i := 0; i < n; i++ {
placeholders = append(placeholders, "?")
}
return strings.Join(placeholders, ",")
}

View File

@ -0,0 +1,217 @@
// 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 config
import (
"context"
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/lib/config"
"github.com/goharbor/harbor/src/lib/config/metadata"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/config/db"
"github.com/goharbor/harbor/src/pkg/config/inmemory"
)
var (
// Ctl Global instance of the config controller
Ctl = NewController()
)
// Controller define operations related to configures
type Controller interface {
// UserConfigs get the user scope configurations
UserConfigs(ctx context.Context) (map[string]*config.Value, error)
// UpdateUserConfigs update the user scope configurations
UpdateUserConfigs(ctx context.Context, conf map[string]interface{}) error
// GetAll get all configurations, used by internal, should include the system config items
AllConfigs(ctx context.Context) (map[string]interface{}, error)
// Load ...
Load(ctx context.Context) error
// GetString ...
GetString(ctx context.Context, item string) string
// GetBool ...
GetBool(ctx context.Context, item string) bool
// GetInt ...
GetInt(ctx context.Context, item string) int
// Get ...
Get(ctx context.Context, item string) *metadata.ConfigureValue
// GetCfgManager ...
GetManager() config.Manager
}
type controller struct {
mgr config.Manager
}
// NewController ...
func NewController() Controller {
return &controller{mgr: db.NewDBCfgManager()}
}
// NewInMemoryController ...
func NewInMemoryController() Controller {
return &controller{mgr: inmemory.NewInMemoryManager()}
}
func (c *controller) GetManager() config.Manager {
return c.mgr
}
func (c *controller) Get(ctx context.Context, item string) *metadata.ConfigureValue {
return c.mgr.Get(ctx, item)
}
func (c *controller) Load(ctx context.Context) error {
return c.mgr.Load(ctx)
}
func (c *controller) GetString(ctx context.Context, item string) string {
return c.mgr.Get(ctx, item).GetString()
}
func (c *controller) GetBool(ctx context.Context, item string) bool {
return c.mgr.Get(ctx, item).GetBool()
}
func (c *controller) GetInt(ctx context.Context, item string) int {
return c.mgr.Get(ctx, item).GetInt()
}
func (c *controller) UserConfigs(ctx context.Context) (map[string]*config.Value, error) {
configs := c.mgr.GetUserCfgs(ctx)
return ConvertForGet(ctx, configs, false)
}
func (c *controller) AllConfigs(ctx context.Context) (map[string]interface{}, error) {
configs := c.mgr.GetAll(ctx)
return configs, nil
}
func (c *controller) UpdateUserConfigs(ctx context.Context, conf map[string]interface{}) error {
err := c.mgr.Load(ctx)
if err != nil {
return err
}
isSysErr, err := c.validateCfg(ctx, conf)
if err != nil {
if isSysErr {
log.Errorf("failed to validate configurations: %v", err)
return fmt.Errorf("failed to validate configuration")
}
return err
}
if err := c.mgr.UpdateConfig(ctx, conf); err != nil {
log.Errorf("failed to upload configurations: %v", err)
return fmt.Errorf("failed to validate configuration")
}
return nil
}
func (c *controller) validateCfg(ctx context.Context, cfgs map[string]interface{}) (bool, error) {
flag, err := authModeCanBeModified(ctx)
if err != nil {
return true, err
}
if !flag {
if failedKeys := c.checkUnmodifiable(ctx, cfgs, common.AUTHMode); len(failedKeys) > 0 {
return false, errors.BadRequestError(nil).
WithMessage(fmt.Sprintf("the keys %v can not be modified as new users have been inserted into database", failedKeys))
}
}
err = c.mgr.ValidateCfg(ctx, cfgs)
if err != nil {
return false, errors.BadRequestError(err)
}
return false, nil
}
func (c *controller) checkUnmodifiable(ctx context.Context, cfgs map[string]interface{}, keys ...string) (failed []string) {
if c.mgr == nil || cfgs == nil || keys == nil {
return
}
for _, k := range keys {
v := c.mgr.Get(ctx, k).GetString()
if nv, ok := cfgs[k]; ok {
if v != fmt.Sprintf("%v", nv) {
failed = append(failed, k)
}
}
}
return
}
// ScanAllPolicy is represent the json request and object for scan all policy
// Only for migrating from the legacy schedule.
type ScanAllPolicy struct {
Type string `json:"type"`
Param map[string]interface{} `json:"parameter,omitempty"`
}
// ConvertForGet - delete sensitive attrs and add editable field to every attr
func ConvertForGet(ctx context.Context, cfg map[string]interface{}, internal bool) (map[string]*config.Value, error) {
result := map[string]*config.Value{}
mList := metadata.Instance().GetAll()
for _, item := range mList {
val, exist := cfg[item.Name]
// skip undefined items
if !exist {
continue
}
switch item.ItemType.(type) {
case *metadata.PasswordType:
// remove password for external api call
if !internal {
delete(cfg, item.Name)
continue
}
case *metadata.MapType, *metadata.StringToStringMapType:
// convert to string for map type
valByte, err := json.Marshal(val)
if err != nil {
return nil, err
}
val = string(valByte)
}
result[item.Name] = &config.Value{
Val: val,
Editable: true,
}
}
// default value for ScanAllPolicy
if _, ok := result[common.ScanAllPolicy]; !ok {
cfg[common.ScanAllPolicy] = `{"type":"none"}`
}
// set value for auth_mode
flag, err := authModeCanBeModified(ctx)
if err != nil {
return nil, err
}
result[common.AUTHMode].Editable = flag
return result, nil
}
func authModeCanBeModified(ctx context.Context) (bool, error) {
return dao.AuthModeCanBeModified(ctx)
}

View File

@ -0,0 +1,250 @@
// 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 config
import (
"context"
"errors"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/secret"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/encrypt"
"os"
"strconv"
"strings"
)
var (
// Use backgroundCtx to access system scope config
backgroundCtx context.Context = context.Background()
)
// It contains all system settings
// If the config is set in env, just get it from env
// If the config might not be set in env, and may have a default value, get it in this way:
// Ctl.GetString(backgroundCtx, "xxxx")
// TokenPrivateKeyPath returns the path to the key for signing token for registry
func TokenPrivateKeyPath() string {
path := os.Getenv("TOKEN_PRIVATE_KEY_PATH")
if len(path) == 0 {
path = defaultRegistryTokenPrivateKeyPath
}
return path
}
// RegistryURL ...
func RegistryURL() (string, error) {
url := os.Getenv("REGISTRY_URL")
if len(url) == 0 {
url = "http://registry:5000"
}
return url, nil
}
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
func InternalJobServiceURL() string {
return os.Getenv("JOBSERVICE_URL")
}
// GetCoreURL returns the url of core from env
func GetCoreURL() string {
return os.Getenv("CORE_URL")
}
// CoreSecret returns a secret to mark harbor-core when communicate with
// other component
func CoreSecret() string {
return os.Getenv("CORE_SECRET")
}
// RegistryCredential returns the username and password the core uses to access registry
func RegistryCredential() (string, string) {
return os.Getenv("REGISTRY_CREDENTIAL_USERNAME"), os.Getenv("REGISTRY_CREDENTIAL_PASSWORD")
}
// JobserviceSecret returns a secret to mark Jobservice when communicate with
// other component
// TODO replace it with method of SecretStore
func JobserviceSecret() string {
return os.Getenv("JOBSERVICE_SECRET")
}
// GetRedisOfRegURL returns the URL of Redis used by registry
func GetRedisOfRegURL() string {
return os.Getenv("_REDIS_URL_REG")
}
// GetPortalURL returns the URL of portal
func GetPortalURL() string {
url := os.Getenv("PORTAL_URL")
if len(url) == 0 {
return common.DefaultPortalURL
}
return url
}
// GetRegistryCtlURL returns the URL of registryctl
func GetRegistryCtlURL() string {
url := os.Getenv("REGISTRY_CONTROLLER_URL")
if len(url) == 0 {
return common.DefaultRegistryCtlURL
}
return url
}
// GetPermittedRegistryTypesForProxyCache returns the permitted registry types for proxy cache
func GetPermittedRegistryTypesForProxyCache() []string {
types := os.Getenv("PERMITTED_REGISTRY_TYPES_FOR_PROXY_CACHE")
if len(types) == 0 {
return []string{}
}
return strings.Split(types, ",")
}
// GetGCTimeWindow returns the reserve time window of blob.
func GetGCTimeWindow() int64 {
// the env is for testing/debugging. For production, Do NOT set it.
if env, exist := os.LookupEnv("GC_TIME_WINDOW_HOURS"); exist {
timeWindow, err := strconv.ParseInt(env, 10, 64)
if err == nil {
return timeWindow
}
}
return common.DefaultGCTimeWindowHours
}
// WithNotary returns a bool value to indicate if Harbor's deployed with Notary
func WithNotary() bool {
return Ctl.GetBool(backgroundCtx, common.WithNotary)
}
// WithTrivy returns a bool value to indicate if Harbor's deployed with Trivy.
func WithTrivy() bool {
return Ctl.GetBool(backgroundCtx, common.WithTrivy)
}
// WithChartMuseum returns a bool to indicate if chartmuseum is deployed with Harbor.
func WithChartMuseum() bool {
return Ctl.GetBool(backgroundCtx, common.WithChartMuseum)
}
// GetChartMuseumEndpoint returns the endpoint of the chartmuseum service
// otherwise an non nil error is returned
func GetChartMuseumEndpoint() (string, error) {
chartEndpoint := strings.TrimSpace(Ctl.GetString(backgroundCtx, common.ChartRepoURL))
if len(chartEndpoint) == 0 {
return "", errors.New("empty chartmuseum endpoint")
}
return chartEndpoint, nil
}
// ExtEndpoint returns the external URL of Harbor: protocol://host:port
func ExtEndpoint() (string, error) {
return Ctl.GetString(backgroundCtx, common.ExtEndpoint), nil
}
// ExtURL returns the external URL: host:port
func ExtURL() (string, error) {
endpoint, err := ExtEndpoint()
if err != nil {
log.Errorf("failed to load config, error %v", err)
}
l := strings.Split(endpoint, "://")
if len(l) > 1 {
return l[1], nil
}
return endpoint, nil
}
// SecretKey returns the secret key to encrypt the password of target
func SecretKey() (string, error) {
return keyProvider.Get(nil)
}
func initKeyProvider() {
path := os.Getenv("KEY_PATH")
if len(path) == 0 {
path = defaultKeyPath
}
log.Infof("key path: %s", path)
keyProvider = encrypt.NewFileKeyProvider(path)
}
func initSecretStore() {
m := map[string]string{}
m[JobserviceSecret()] = secret.JobserviceUser
SecretStore = secret.NewStore(m)
}
// InternalCoreURL returns the local harbor core url
func InternalCoreURL() string {
return strings.TrimSuffix(Ctl.GetString(backgroundCtx, common.CoreURL), "/")
}
// LocalCoreURL returns the local harbor core url
func LocalCoreURL() string {
return Ctl.GetString(backgroundCtx, common.CoreLocalURL)
}
// InternalTokenServiceEndpoint returns token service endpoint for internal communication between Harbor containers
func InternalTokenServiceEndpoint() string {
return InternalCoreURL() + "/service/token"
}
// InternalNotaryEndpoint returns notary server endpoint for internal communication between Harbor containers
// This is currently a conventional value and can be unaccessible when Harbor is not deployed with Notary.
func InternalNotaryEndpoint() string {
return Ctl.GetString(backgroundCtx, common.NotaryURL)
}
// TrivyAdapterURL returns the endpoint URL of a Trivy adapter instance, by default it's the one deployed within Harbor.
func TrivyAdapterURL() string {
return Ctl.GetString(backgroundCtx, common.TrivyAdapterURL)
}
// Metric returns the overall metric settings
func Metric() *models.Metric {
return &models.Metric{
Enabled: Ctl.GetBool(backgroundCtx, common.MetricEnable),
Port: Ctl.GetInt(backgroundCtx, common.MetricPort),
Path: Ctl.GetString(backgroundCtx, common.MetricPath),
}
}
// InitialAdminPassword returns the initial password for administrator
func InitialAdminPassword() (string, error) {
return Ctl.GetString(backgroundCtx, common.AdminInitialPassword), nil
}
// Database returns database settings
func Database() (*models.Database, error) {
database := &models.Database{}
database.Type = Ctl.GetString(backgroundCtx, common.DatabaseType)
postgresql := &models.PostGreSQL{
Host: Ctl.GetString(backgroundCtx, common.PostGreSQLHOST),
Port: Ctl.GetInt(backgroundCtx, common.PostGreSQLPort),
Username: Ctl.GetString(backgroundCtx, common.PostGreSQLUsername),
Password: Ctl.GetString(backgroundCtx, common.PostGreSQLPassword),
Database: Ctl.GetString(backgroundCtx, common.PostGreSQLDatabase),
SSLMode: Ctl.GetString(backgroundCtx, common.PostGreSQLSSLMode),
MaxIdleConns: Ctl.GetInt(backgroundCtx, common.PostGreSQLMaxIdleConns),
MaxOpenConns: Ctl.GetInt(backgroundCtx, common.PostGreSQLMaxOpenConns),
}
database.PostGreSQL = postgresql
return database, nil
}

View File

@ -0,0 +1,145 @@
// 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 test
import (
"context"
"github.com/goharbor/harbor/src/common"
. "github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/errors"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
var TestDBConfig = map[string]interface{}{
common.LDAPBaseDN: "dc=example,dc=com",
common.LDAPURL: "ldap.example.com",
common.EmailHost: "127.0.0.1",
}
var TestConfigWithScanAll = map[string]interface{}{
"postgresql_host": "localhost",
"postgresql_database": "registry",
"postgresql_password": "root123",
"postgresql_username": "postgres",
"postgresql_sslmode": "disable",
"ldap_base_dn": "dc=example,dc=com",
"ldap_url": "ldap.example.com",
"email_host": "127.0.0.1",
"scan_all_policy": `{"parameter":{"daily_time":0},"type":"daily"}`,
}
var ctx context.Context
type controllerTestSuite struct {
htesting.Suite
controller Controller
}
func (c *controllerTestSuite) SetupTest() {
c.controller = Ctl
ctx = c.Context()
c.controller.UpdateUserConfigs(ctx, TestDBConfig)
}
func (c *controllerTestSuite) TestGetUserCfg() {
resp, err := c.controller.UserConfigs(ctx)
if err != nil {
c.Error(err, "failed to get user config")
}
c.Equal("dc=example,dc=com", resp["ldap_base_dn"].Val)
c.Equal("127.0.0.1", resp["email_host"].Val)
c.Equal("ldap.example.com", resp["ldap_url"].Val)
}
func (c *controllerTestSuite) TestConvertForGet() {
conf := map[string]interface{}{
"ldap_url": "ldaps.myexample,com",
"ldap_base_dn": "dc=myexample,dc=com",
"auth_mode": "ldap_auth",
"ldap_search_password": "admin",
}
// password type should not sent to external api call
resp, err := ConvertForGet(ctx, conf, false)
c.Nil(err)
c.Equal("ldaps.myexample,com", resp["ldap_url"].Val)
c.Equal("ldap_auth", resp["auth_mode"].Val)
_, exist := resp["ldap_search_password"]
c.False(exist)
// password type should be sent to internal api call
conf2 := map[string]interface{}{
"ldap_url": "ldaps.myexample,com",
"ldap_base_dn": "dc=myexample,dc=com",
"auth_mode": "ldap_auth",
"ldap_search_password": "admin",
}
resp2, err2 := ConvertForGet(ctx, conf2, true)
c.Nil(err2)
c.Equal("ldaps.myexample,com", resp2["ldap_url"].Val)
c.Equal("ldap_auth", resp2["auth_mode"].Val)
_, exist2 := resp2["ldap_search_password"]
c.True(exist2)
}
func (c *controllerTestSuite) TestGetAll() {
resp, err := c.controller.AllConfigs(ctx)
if err != nil {
c.Error(err, "failed to get user config")
}
c.Equal("dc=example,dc=com", resp["ldap_base_dn"])
c.Equal("127.0.0.1", resp["email_host"])
c.Equal("ldap.example.com", resp["ldap_url"])
}
func (c *controllerTestSuite) TestUpdateUserCfg() {
userConf := map[string]interface{}{
common.LDAPURL: "ldaps.myexample,com",
common.LDAPBaseDN: "dc=myexample,dc=com",
}
err := c.controller.UpdateUserConfigs(ctx, userConf)
c.Nil(err)
cfgResp, err := c.controller.UserConfigs(ctx)
if err != nil {
c.Error(err, "failed to get user config")
}
c.Equal("dc=myexample,dc=com", cfgResp["ldap_base_dn"].Val)
c.Equal("ldaps.myexample,com", cfgResp["ldap_url"].Val)
badCfg := map[string]interface{}{
common.LDAPScope: 5,
}
err2 := c.controller.UpdateUserConfigs(ctx, badCfg)
c.NotNil(err2)
c.True(errors.IsErr(err2, errors.BadRequestCode))
}
/*func (c *controllerTestSuite) TestCheckUnmodifiable() {
conf := map[string]interface{}{
"ldap_url": "ldaps.myexample,com",
"ldap_base_dn": "dc=myexample,dc=com",
"auth_mode": "ldap_auth",
}
failed := c.controller.checkUnmodifiable(ctx, conf, "auth_mode")
c.True(len(failed) > 0)
c.Equal(failed[0], "auth_mode")
}
*/
func TestControllerTestSuite(t *testing.T) {
suite.Run(t, &controllerTestSuite{})
}

View File

@ -0,0 +1,282 @@
// 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 config provide config for core api and other modules
// Before accessing user settings, need to call Load()
// For system settings, no need to call Load()
package config
import (
"context"
"errors"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/config"
"github.com/goharbor/harbor/src/pkg/encrypt"
"strings"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/secret"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/ldap/model"
)
const (
defaultKeyPath = "/etc/core/key"
defaultRegistryTokenPrivateKeyPath = "/etc/core/private_key.pem"
// SessionCookieName is the name of the cookie for session ID
SessionCookieName = "sid"
)
var (
// SecretStore manages secrets
SecretStore *secret.Store
keyProvider encrypt.KeyProvider
// defined as a var for testing.
defaultCACertPath = "/etc/core/ca/ca.crt"
)
// Init configurations
func Init() {
// init key provider
initKeyProvider()
log.Info("init secret store")
// init secret store
initSecretStore()
}
// InitWithSettings init config with predefined configs, and optionally overwrite the keyprovider
func InitWithSettings(cfgs map[string]interface{}, kp ...encrypt.KeyProvider) {
Init()
Ctl = NewInMemoryController()
mgr := Ctl.GetManager()
mgr.UpdateConfig(backgroundCtx, cfgs)
if len(kp) > 0 {
keyProvider = kp[0]
}
}
// GetCfgManager return the current config manager
func GetCfgManager(ctx context.Context) config.Manager {
return Ctl.GetManager()
}
// Load configurations
func Load(ctx context.Context) error {
return Ctl.Load(ctx)
}
// Upload save all configurations, used by testing
func Upload(cfg map[string]interface{}) error {
mgr := Ctl.GetManager()
return mgr.UpdateConfig(orm.Context(), cfg)
}
// GetSystemCfg returns the system configurations
func GetSystemCfg(ctx context.Context) (map[string]interface{}, error) {
sysCfg, err := Ctl.AllConfigs(ctx)
if err != nil {
return nil, err
}
if len(sysCfg) == 0 {
return nil, errors.New("can not load system config, the database might be down")
}
return sysCfg, nil
}
// AuthMode ...
func AuthMode(ctx context.Context) (string, error) {
err := Ctl.Load(ctx)
if err != nil {
log.Errorf("failed to load config, error %v", err)
return "db_auth", err
}
return Ctl.GetString(ctx, common.AUTHMode), nil
}
// LDAPConf returns the setting of ldap server
func LDAPConf(ctx context.Context) (*model.LdapConf, error) {
err := Ctl.Load(ctx)
if err != nil {
return nil, err
}
return &model.LdapConf{
URL: Ctl.GetString(ctx, common.LDAPURL),
SearchDn: Ctl.GetString(ctx, common.LDAPSearchDN),
SearchPassword: Ctl.GetString(ctx, common.LDAPSearchPwd),
BaseDn: Ctl.GetString(ctx, common.LDAPBaseDN),
UID: Ctl.GetString(ctx, common.LDAPUID),
Filter: Ctl.GetString(ctx, common.LDAPFilter),
Scope: Ctl.GetInt(ctx, common.LDAPScope),
ConnectionTimeout: Ctl.GetInt(ctx, common.LDAPTimeout),
VerifyCert: Ctl.GetBool(ctx, common.LDAPVerifyCert),
}, nil
}
// LDAPGroupConf returns the setting of ldap group search
func LDAPGroupConf(ctx context.Context) (*model.GroupConf, error) {
err := Ctl.Load(ctx)
if err != nil {
return nil, err
}
return &model.GroupConf{
BaseDN: Ctl.GetString(ctx, common.LDAPGroupBaseDN),
Filter: Ctl.GetString(ctx, common.LDAPGroupSearchFilter),
NameAttribute: Ctl.GetString(ctx, common.LDAPGroupAttributeName),
SearchScope: Ctl.GetInt(ctx, common.LDAPGroupSearchScope),
AdminDN: Ctl.GetString(ctx, common.LDAPGroupAdminDn),
MembershipAttribute: Ctl.GetString(ctx, common.LDAPGroupMembershipAttribute),
}, nil
}
// TokenExpiration returns the token expiration time (in minute)
func TokenExpiration(ctx context.Context) (int, error) {
return Ctl.GetInt(ctx, common.TokenExpiration), nil
}
// RobotTokenDuration returns the token expiration time of robot account (in minute)
func RobotTokenDuration(ctx context.Context) int {
return Ctl.GetInt(ctx, common.RobotTokenDuration)
}
// SelfRegistration returns the enablement of self registration
func SelfRegistration(ctx context.Context) (bool, error) {
return Ctl.GetBool(ctx, common.SelfRegistration), nil
}
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
func OnlyAdminCreateProject(ctx context.Context) (bool, error) {
err := Ctl.Load(ctx)
if err != nil {
return true, err
}
return Ctl.GetString(ctx, common.ProjectCreationRestriction) == common.ProCrtRestrAdmOnly, nil
}
// Email returns email server settings
func Email(ctx context.Context) (*cfgModels.Email, error) {
err := Ctl.Load(ctx)
if err != nil {
return nil, err
}
return &cfgModels.Email{
Host: Ctl.GetString(ctx, common.EmailHost),
Port: Ctl.GetInt(ctx, common.EmailPort),
Username: Ctl.GetString(ctx, common.EmailUsername),
Password: Ctl.GetString(ctx, common.EmailPassword),
SSL: Ctl.GetBool(ctx, common.EmailSSL),
From: Ctl.GetString(ctx, common.EmailFrom),
Identity: Ctl.GetString(ctx, common.EmailIdentity),
Insecure: Ctl.GetBool(ctx, common.EmailInsecure),
}, nil
}
// UAASettings returns the UAASettings to access UAA service.
func UAASettings(ctx context.Context) (*models.UAASettings, error) {
err := Ctl.Load(ctx)
if err != nil {
return nil, err
}
us := &models.UAASettings{
Endpoint: Ctl.GetString(ctx, common.UAAEndpoint),
ClientID: Ctl.GetString(ctx, common.UAAClientID),
ClientSecret: Ctl.GetString(ctx, common.UAAClientSecret),
VerifyCert: Ctl.GetBool(ctx, common.UAAVerifyCert),
}
return us, nil
}
// ReadOnly returns a bool to indicates if Harbor is in read only mode.
func ReadOnly(ctx context.Context) bool {
return Ctl.GetBool(ctx, common.ReadOnly)
}
// HTTPAuthProxySetting returns the setting of HTTP Auth proxy. the settings are only meaningful when the auth_mode is
// set to http_auth
func HTTPAuthProxySetting(ctx context.Context) (*cfgModels.HTTPAuthProxy, error) {
if err := Ctl.Load(ctx); err != nil {
return nil, err
}
return &cfgModels.HTTPAuthProxy{
Endpoint: Ctl.GetString(ctx, common.HTTPAuthProxyEndpoint),
TokenReviewEndpoint: Ctl.GetString(ctx, common.HTTPAuthProxyTokenReviewEndpoint),
AdminGroups: splitAndTrim(Ctl.GetString(ctx, common.HTTPAuthProxyAdminGroups), ","),
AdminUsernames: splitAndTrim(Ctl.GetString(ctx, common.HTTPAuthProxyAdminUsernames), ","),
VerifyCert: Ctl.GetBool(ctx, common.HTTPAuthProxyVerifyCert),
SkipSearch: Ctl.GetBool(ctx, common.HTTPAuthProxySkipSearch),
ServerCertificate: Ctl.GetString(ctx, common.HTTPAuthProxyServerCertificate),
}, nil
}
// OIDCSetting returns the setting of OIDC provider, currently there's only one OIDC provider allowed for Harbor and it's
// only effective when auth_mode is set to oidc_auth
func OIDCSetting(ctx context.Context) (*cfgModels.OIDCSetting, error) {
if err := Ctl.Load(ctx); err != nil {
return nil, err
}
scopeStr := Ctl.GetString(ctx, common.OIDCScope)
extEndpoint := strings.TrimSuffix(Ctl.GetString(nil, common.ExtEndpoint), "/")
scope := splitAndTrim(scopeStr, ",")
return &cfgModels.OIDCSetting{
Name: Ctl.GetString(ctx, common.OIDCName),
Endpoint: Ctl.GetString(ctx, common.OIDCEndpoint),
VerifyCert: Ctl.GetBool(ctx, common.OIDCVerifyCert),
AutoOnboard: Ctl.GetBool(ctx, common.OIDCAutoOnboard),
ClientID: Ctl.GetString(ctx, common.OIDCCLientID),
ClientSecret: Ctl.GetString(ctx, common.OIDCClientSecret),
GroupsClaim: Ctl.GetString(ctx, common.OIDCGroupsClaim),
AdminGroup: Ctl.GetString(ctx, common.OIDCAdminGroup),
RedirectURL: extEndpoint + common.OIDCCallbackPath,
Scope: scope,
UserClaim: Ctl.GetString(ctx, common.OIDCUserClaim),
ExtraRedirectParms: Ctl.Get(ctx, common.OIDCExtraRedirectParms).GetStringToStringMap(),
}, nil
}
// NotificationEnable returns a bool to indicates if notification enabled in harbor
func NotificationEnable(ctx context.Context) bool {
return Ctl.GetBool(ctx, common.NotificationEnable)
}
// QuotaPerProjectEnable returns a bool to indicates if quota per project enabled in harbor
func QuotaPerProjectEnable(ctx context.Context) bool {
return Ctl.GetBool(ctx, common.QuotaPerProjectEnable)
}
// QuotaSetting returns the setting of quota.
func QuotaSetting(ctx context.Context) (*cfgModels.QuotaSetting, error) {
if err := Ctl.Load(ctx); err != nil {
return nil, err
}
return &cfgModels.QuotaSetting{
StoragePerProject: Ctl.Get(ctx, common.StoragePerProject).GetInt64(),
}, nil
}
// RobotPrefix user defined robot name prefix.
func RobotPrefix(ctx context.Context) string {
return Ctl.GetString(ctx, common.RobotNamePrefix)
}
func splitAndTrim(s, sep string) []string {
res := make([]string, 0)
for _, s := range strings.Split(s, sep) {
if e := strings.TrimSpace(s); len(e) > 0 {
res = append(res, e)
}
}
return res
}

View File

@ -1,19 +1,21 @@
// Copyright 2018 Project Harbor Authors
// 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
// 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
// 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.
// 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 config
import (
"github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"os"
"path"
"runtime"
@ -21,8 +23,8 @@ import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/test"
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
"github.com/stretchr/testify/assert"
)
@ -62,8 +64,8 @@ func TestConfig(t *testing.T) {
os.Setenv("GC_TIME_WINDOW_HOURS", "0")
Init()
if err := Load(); err != nil {
ctx := orm.Context()
if err := Load(ctx); err != nil {
t.Fatalf("failed to load configurations: %v", err)
}
@ -71,11 +73,10 @@ func TestConfig(t *testing.T) {
t.Fatalf("failed to upload configurations: %v", err)
}
if _, err := GetSystemCfg(); err != nil {
if _, err := GetSystemCfg(ctx); err != nil {
t.Fatalf("failed to get system configurations: %v", err)
}
mode, err := AuthMode()
mode, err := AuthMode(ctx)
if err != nil {
t.Fatalf("failed to get auth mode: %v", err)
}
@ -83,19 +84,19 @@ func TestConfig(t *testing.T) {
t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
}
if _, err := LDAPConf(); err != nil {
if _, err := LDAPConf(ctx); err != nil {
t.Fatalf("failed to get ldap settings: %v", err)
}
if _, err := LDAPGroupConf(); err != nil {
if _, err := LDAPGroupConf(ctx); err != nil {
t.Fatalf("failed to get ldap group settings: %v", err)
}
if _, err := TokenExpiration(); err != nil {
if _, err := TokenExpiration(ctx); err != nil {
t.Fatalf("failed to get token expiration: %v", err)
}
tkExp := RobotTokenDuration()
tkExp := RobotTokenDuration(ctx)
assert.Equal(tkExp, 30)
if _, err := ExtEndpoint(); err != nil {
@ -106,7 +107,7 @@ func TestConfig(t *testing.T) {
t.Fatalf("failed to get secret key: %v", err)
}
if _, err := SelfRegistration(); err != nil {
if _, err := SelfRegistration(ctx); err != nil {
t.Fatalf("failed to get self registration: %v", err)
}
@ -126,11 +127,11 @@ func TestConfig(t *testing.T) {
t.Fatalf("failed to get initial admin password: %v", err)
}
if _, err := OnlyAdminCreateProject(); err != nil {
if _, err := OnlyAdminCreateProject(ctx); err != nil {
t.Fatalf("failed to get onldy admin create project: %v", err)
}
if _, err := Email(); err != nil {
if _, err := Email(ctx); err != nil {
t.Fatalf("failed to get email settings: %v", err)
}
@ -150,7 +151,7 @@ func TestConfig(t *testing.T) {
if WithTrivy() {
t.Errorf("WithTrivy should be false")
}
if ReadOnly() {
if ReadOnly(ctx) {
t.Errorf("ReadOnly should be false")
}
@ -162,7 +163,7 @@ func TestConfig(t *testing.T) {
t.Errorf(`extURL should be "host01.com".`)
}
mode, err = AuthMode()
mode, err = AuthMode(ctx)
if err != nil {
t.Fatalf("failed to get auth mode: %v", err)
}
@ -174,7 +175,7 @@ func TestConfig(t *testing.T) {
t.Errorf("Unexpected token private key path: %s, expected: %s", tokenKeyPath, "/etc/core/private_key.pem")
}
us, err := UAASettings()
us, err := UAASettings(ctx)
if err != nil {
t.Fatalf("failed to get UAA setting, error: %v", err)
}
@ -188,9 +189,9 @@ func TestConfig(t *testing.T) {
localCoreURL := LocalCoreURL()
assert.Equal("http://127.0.0.1:8080", localCoreURL)
assert.True(NotificationEnable())
assert.True(NotificationEnable(ctx))
assert.Equal(int64(0), GetGCTimeWindow())
assert.Equal("robot$", RobotPrefix())
assert.Equal("robot$", RobotPrefix(ctx))
}
@ -243,7 +244,7 @@ y1bQusZMygQezfCuEzsewF+OpANFovCTUEs6s5vyoVNP8lk=
common.HTTPAuthProxyServerCertificate: certificate,
}
InitWithSettings(m)
v, e := HTTPAuthProxySetting()
v, e := HTTPAuthProxySetting(orm.Context())
assert.Nil(t, e)
assert.Equal(t, *v, models.HTTPAuthProxy{
Endpoint: "https://auth.proxy/suffix",
@ -269,7 +270,7 @@ func TestOIDCSetting(t *testing.T) {
common.ExtEndpoint: "https://harbor.test",
}
InitWithSettings(m)
v, e := OIDCSetting()
v, e := OIDCSetting(orm.Context())
assert.Nil(t, e)
assert.Equal(t, "test", v.Name)
assert.Equal(t, "https://oidc.test", v.Endpoint)

View File

@ -3,19 +3,22 @@ package util
import (
"errors"
"fmt"
"github.com/goharbor/harbor/src/core/config"
"strings"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/distribution"
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
"github.com/goharbor/harbor/src/pkg/notifier/event"
notifyModel "github.com/goharbor/harbor/src/pkg/notifier/model"
"strings"
)
// SendHookWithPolicies send hook by publishing topic of specified target type(notify type)
func SendHookWithPolicies(policies []*policy_model.Policy, payload *notifyModel.Payload, eventType string) error {
// if global notification configured disabled, return directly
if !config.NotificationEnable() {
if !config.NotificationEnable(orm.Context()) {
log.Debug("notification feature is not enabled")
return nil
}

View File

@ -1,12 +1,20 @@
package util
import (
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
"os"
"testing"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
)
func TestMain(m *testing.M) {
// do some initialization
test.InitDatabaseFromEnv()
os.Exit(m.Run())
}
func TestBuildImageResourceURL(t *testing.T) {
cfg := map[string]interface{}{
common.ExtEndpoint: "https://demo.goharbor.io",

View File

@ -6,13 +6,14 @@ import (
"fmt"
"strings"
"github.com/goharbor/harbor/src/controller/config"
commonModels "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/event/handler/util"
ctlModel "github.com/goharbor/harbor/src/controller/event/model"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/controller/replication"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
@ -33,7 +34,7 @@ func (r *ReplicationHandler) Name() string {
// Handle ...
func (r *ReplicationHandler) Handle(ctx context.Context, value interface{}) error {
if !config.NotificationEnable() {
if !config.NotificationEnable(ctx) {
log.Debug("notification feature is not enabled")
return nil
}

View File

@ -19,13 +19,14 @@ import (
"testing"
"time"
"github.com/goharbor/harbor/src/controller/config"
common_dao "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/project"
repctl "github.com/goharbor/harbor/src/controller/replication"
repctlmodel "github.com/goharbor/harbor/src/controller/replication/model"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/notification"
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
projecttesting "github.com/goharbor/harbor/src/testing/controller/project"

View File

@ -3,14 +3,15 @@ package artifact
import (
"context"
"fmt"
"strings"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/retention"
"github.com/goharbor/harbor/src/lib/orm"
"strings"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/event/handler/util"
evtModel "github.com/goharbor/harbor/src/controller/event/model"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/notification"
@ -28,7 +29,7 @@ func (r *RetentionHandler) Name() string {
// Handle ...
func (r *RetentionHandler) Handle(ctx context.Context, value interface{}) error {
if !config.NotificationEnable() {
if !config.NotificationEnable(ctx) {
log.Debug("notification feature is not enabled")
return nil
}

View File

@ -2,10 +2,16 @@ package artifact
import (
"context"
"os"
"testing"
"time"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/retention"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/selector"
"github.com/goharbor/harbor/src/pkg/notification"
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
@ -15,9 +21,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"os"
"testing"
"time"
)
func TestRetentionHandler_Handle(t *testing.T) {

View File

@ -18,12 +18,12 @@ import (
"context"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/event/handler/util"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notifier/model"

View File

@ -16,12 +16,14 @@ package chart
import (
"context"
testutils "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
"os"
"testing"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notification/policy/model"
projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
@ -31,6 +33,12 @@ import (
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
// do some initialization
testutils.InitDatabaseFromEnv()
os.Exit(m.Run())
}
func TestChartPreprocessHandler_Handle(t *testing.T) {
PolicyMgr := notification.PolicyMgr
defer func() {

View File

@ -17,6 +17,7 @@ package quota
import (
"context"
common_dao "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/event"
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
"github.com/goharbor/harbor/src/testing/mock"
@ -25,7 +26,6 @@ import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notification/policy"
"github.com/goharbor/harbor/src/pkg/notifier"

View File

@ -17,6 +17,7 @@ package scan
import (
"context"
common_dao "github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/event"
policy_model "github.com/goharbor/harbor/src/pkg/notification/policy/model"
"testing"
@ -25,7 +26,6 @@ import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/controller/artifact"
sc "github.com/goharbor/harbor/src/controller/scan"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/pkg/notification"
"github.com/goharbor/harbor/src/pkg/notification/policy"
"github.com/goharbor/harbor/src/pkg/notifier"

View File

@ -4,9 +4,9 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/quota"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
@ -39,7 +39,7 @@ func gcCallback(ctx context.Context, p string) error {
}
func gcTaskStatusChange(ctx context.Context, taskID int64, status string) error {
if status == job.SuccessStatus.String() && config.QuotaPerProjectEnable() {
if status == job.SuccessStatus.String() && config.QuotaPerProjectEnable(ctx) {
go func() {
quota.RefreshForProjects(orm.Context())
}()

View File

@ -17,7 +17,7 @@ package ldap
import (
"context"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/ldap"
"github.com/goharbor/harbor/src/pkg/ldap/model"
@ -52,7 +52,7 @@ func NewController() Controller {
}
func (c *controller) Session(ctx context.Context) (*ldap.Session, error) {
cfg, groupCfg, err := c.ldapConfigs()
cfg, groupCfg, err := c.ldapConfigs(ctx)
if err != nil {
return nil, err
}
@ -61,7 +61,7 @@ func (c *controller) Session(ctx context.Context) (*ldap.Session, error) {
func (c *controller) Ping(ctx context.Context, cfg model.LdapConf) (bool, error) {
if len(cfg.SearchPassword) == 0 {
pwd, err := defaultPassword()
pwd, err := defaultPassword(ctx)
if err != nil {
return false, err
}
@ -73,12 +73,12 @@ func (c *controller) Ping(ctx context.Context, cfg model.LdapConf) (bool, error)
return c.mgr.Ping(ctx, cfg)
}
func (c *controller) ldapConfigs() (*model.LdapConf, *model.GroupConf, error) {
cfg, err := config.LDAPConf()
func (c *controller) ldapConfigs(ctx context.Context) (*model.LdapConf, *model.GroupConf, error) {
cfg, err := config.LDAPConf(ctx)
if err != nil {
return nil, nil, err
}
groupCfg, err := config.LDAPGroupConf()
groupCfg, err := config.LDAPGroupConf(ctx)
if err != nil {
log.Warningf("failed to get the ldap group config, error %v", err)
groupCfg = &model.GroupConf{}
@ -87,20 +87,20 @@ func (c *controller) ldapConfigs() (*model.LdapConf, *model.GroupConf, error) {
}
func (c *controller) SearchUser(ctx context.Context, username string) ([]model.User, error) {
cfg, groupCfg, err := c.ldapConfigs()
cfg, groupCfg, err := c.ldapConfigs(ctx)
if err != nil {
return nil, err
}
return c.mgr.SearchUser(ctx, ldap.NewSession(*cfg, *groupCfg), username)
}
func defaultPassword() (string, error) {
mod, err := config.AuthMode()
func defaultPassword(ctx context.Context) (string, error) {
mod, err := config.AuthMode(ctx)
if err != nil {
return "", err
}
if mod == common.LDAPAuth {
conf, err := config.LDAPConf()
conf, err := config.LDAPConf(ctx)
if err != nil {
return "", err
}
@ -113,7 +113,7 @@ func defaultPassword() (string, error) {
}
func (c *controller) ImportUser(ctx context.Context, ldapImportUsers []string) ([]model.FailedImportUser, error) {
cfg, groupCfg, err := c.ldapConfigs()
cfg, groupCfg, err := c.ldapConfigs(ctx)
if err != nil {
return nil, err
}
@ -121,7 +121,7 @@ func (c *controller) ImportUser(ctx context.Context, ldapImportUsers []string) (
}
func (c *controller) SearchGroup(ctx context.Context, groupName, groupDN string) ([]model.Group, error) {
cfg, groupCfg, err := c.ldapConfigs()
cfg, groupCfg, err := c.ldapConfigs(ctx)
if err != nil {
return nil, err
}

View File

@ -16,7 +16,7 @@ package ldap
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/pkg/ldap/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/mock"

View File

@ -4,11 +4,11 @@ import (
"context"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"net/http"
"net/http/httptest"
"testing"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy"

View File

@ -17,6 +17,8 @@ package preheat
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"strings"
tk "github.com/docker/distribution/registry/auth/token"
@ -25,7 +27,6 @@ import (
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/controller/scan"
"github.com/goharbor/harbor/src/controller/tag"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/service/token"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/lib/errors"
@ -170,7 +171,7 @@ func NewEnforcer() Enforcer {
Actions: []string{resourcePullAction},
},
}
t, err := token.MakeToken("distributor", token.Registry, ac)
t, err := token.MakeToken(orm.Context(), "distributor", token.Registry, ac)
if err != nil {
return "", err
}

View File

@ -18,8 +18,8 @@ import (
"context"
"github.com/docker/distribution"
"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/controller/event/metadata"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/cache"
"github.com/goharbor/harbor/src/lib/errors"

View File

@ -17,10 +17,11 @@ package project
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/lib/config"
"github.com/goharbor/harbor/src/pkg/config/db"
"strconv"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/blob"
"github.com/goharbor/harbor/src/lib/log"
@ -34,7 +35,7 @@ func init() {
}
type driver struct {
cfg *config.CfgManager
cfg config.Manager
loader *dataloader.Loader
blobCtl blob.Controller
@ -42,20 +43,20 @@ type driver struct {
func (d *driver) Enabled(ctx context.Context, key string) (bool, error) {
// NOTE: every time load the new configurations from the db to get the latest configurations may have performance problem.
if err := d.cfg.Load(); err != nil {
if err := d.cfg.Load(ctx); err != nil {
return false, err
}
return d.cfg.Get(common.QuotaPerProjectEnable).GetBool(), nil
return d.cfg.Get(ctx, common.QuotaPerProjectEnable).GetBool(), nil
}
func (d *driver) HardLimits(ctx context.Context) types.ResourceList {
// NOTE: every time load the new configurations from the db to get the latest configurations may have performance problem.
if err := d.cfg.Load(); err != nil {
if err := d.cfg.Load(ctx); err != nil {
log.Warningf("load configurations failed, error: %v", err)
}
return types.ResourceList{
types.ResourceStorage: d.cfg.Get(common.StoragePerProject).GetInt64(),
types.ResourceStorage: d.cfg.Get(ctx, common.StoragePerProject).GetInt64(),
}
}
@ -118,7 +119,7 @@ func (d *driver) CalculateUsage(ctx context.Context, key string) (types.Resource
}
func newDriver() dr.Driver {
cfg := config.NewDBCfgManager()
cfg := db.NewDBCfgManager()
loader := dataloader.NewBatchedLoader(getProjectsBatchFn, dataloader.WithClearCacheOnBatch())

View File

@ -5,7 +5,7 @@ import (
"fmt"
rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/q"
@ -85,8 +85,8 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
expiresAt = -1
} else if r.Duration == 0 {
// system default robot duration
r.Duration = int64(config.RobotTokenDuration())
expiresAt = time.Now().AddDate(0, 0, config.RobotTokenDuration()).Unix()
r.Duration = int64(config.RobotTokenDuration(ctx))
expiresAt = time.Now().AddDate(0, 0, config.RobotTokenDuration(ctx)).Unix()
} else {
expiresAt = time.Now().AddDate(0, 0, int(r.Duration)).Unix()
}
@ -216,7 +216,7 @@ func (d *controller) populate(ctx context.Context, r *model.Robot, option *Optio
// for the v2 robots, add prefix to the robot name
// for the v1 legacy robots, keep the robot name
if robot.Editable {
robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(), r.Name)
robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(ctx), r.Name)
} else {
robot.Name = r.Name
}

View File

@ -6,12 +6,12 @@ import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/core/config"
core_cfg "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/permission/types"
rbac_model "github.com/goharbor/harbor/src/pkg/rbac/model"
"github.com/goharbor/harbor/src/pkg/robot/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/goharbor/harbor/src/testing/mock"
"github.com/goharbor/harbor/src/testing/pkg/project"
"github.com/goharbor/harbor/src/testing/pkg/rbac"
@ -22,7 +22,7 @@ import (
)
type ControllerTestSuite struct {
suite.Suite
htesting.Suite
}
func (suite *ControllerTestSuite) TestGet() {
@ -92,7 +92,7 @@ func (suite *ControllerTestSuite) TestCreate() {
conf := map[string]interface{}{
common.RobotTokenDuration: "30",
}
core_cfg.InitWithSettings(conf)
config.InitWithSettings(conf)
projectMgr := &project.Manager{}
rbacMgr := &rbac.Manager{}

View File

@ -3,12 +3,13 @@ package robot
import (
"github.com/goharbor/harbor/src/pkg/permission/types"
"github.com/goharbor/harbor/src/pkg/robot/model"
htesting "github.com/goharbor/harbor/src/testing"
"github.com/stretchr/testify/suite"
"testing"
)
type ModelTestSuite struct {
suite.Suite
htesting.Suite
}
func (suite *ModelTestSuite) TestSetLevel() {

View File

@ -18,6 +18,7 @@ import (
"bytes"
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"strings"
"sync"
@ -25,7 +26,6 @@ import (
ar "github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/robot"
sc "github.com/goharbor/harbor/src/controller/scanner"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/errors"

View File

@ -19,6 +19,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"testing"
"time"
@ -26,7 +27,6 @@ import (
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/robot"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/permission/types"

View File

@ -17,14 +17,14 @@ package systeminfo
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/config/models"
"io"
"os"
"strings"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/systeminfo"
@ -84,7 +84,7 @@ type controller struct{}
func (c *controller) GetInfo(ctx context.Context, opt Options) (*Data, error) {
logger := log.GetLogger(ctx)
cfg, err := config.GetSystemCfg()
cfg, err := config.GetSystemCfg(ctx)
if err != nil {
logger.Errorf("Error occurred getting config: %v", err)
return nil, err
@ -95,7 +95,7 @@ func (c *controller) GetInfo(ctx context.Context, opt Options) (*Data, error) {
HarborVersion: fmt.Sprintf("%s-%s", version.ReleaseVersion, version.GitCommit),
}
if res.AuthMode == common.HTTPAuth {
if s, err := config.HTTPAuthProxySetting(); err == nil {
if s, err := config.HTTPAuthProxySetting(ctx); err == nil {
res.AuthProxySettings = s
} else {
logger.Warningf("Failed to get auth proxy setting, error: %v", err)
@ -117,7 +117,7 @@ func (c *controller) GetInfo(ctx context.Context, opt Options) (*Data, error) {
res.Protected = &protectedData{
WithNotary: config.WithNotary(),
WithChartMuseum: config.WithChartMuseum(),
ReadOnly: config.ReadOnly(),
ReadOnly: config.ReadOnly(ctx),
ExtURL: extURL,
RegistryURL: registryURL,
HasCARoot: enableCADownload,

View File

@ -2,17 +2,18 @@ package systeminfo
import (
"context"
"github.com/goharbor/harbor/src/controller/config"
htesting "github.com/goharbor/harbor/src/testing"
"testing"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/pkg/version"
"github.com/stretchr/testify/suite"
)
type sysInfoCtlTestSuite struct {
suite.Suite
htesting.Suite
ctl Controller
}

View File

@ -16,7 +16,7 @@ package tag
import (
"github.com/goharbor/harbor/src/common"
coreConfig "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
pkg_artifact "github.com/goharbor/harbor/src/pkg/artifact"
@ -54,7 +54,7 @@ func (c *controllerTestSuite) SetupTest() {
var tagCtlTestConfig = map[string]interface{}{
common.WithNotary: false,
}
coreConfig.InitWithSettings(tagCtlTestConfig)
config.InitWithSettings(tagCtlTestConfig)
}
func (c *controllerTestSuite) TestEnsureTag() {

View File

@ -18,6 +18,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"net/http"
"github.com/ghodss/yaml"
@ -29,7 +30,6 @@ import (
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/controller/p2p/preheat"
projectcontroller "github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/scheduler"

View File

@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"io"
"io/ioutil"
"mime/multipart"
@ -21,7 +22,6 @@ import (
rep_event "github.com/goharbor/harbor/src/controller/event/handler/replication/event"
"github.com/goharbor/harbor/src/controller/event/metadata"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/label"
hlog "github.com/goharbor/harbor/src/lib/log"
n_event "github.com/goharbor/harbor/src/pkg/notifier/event"

View File

@ -1,191 +0,0 @@
// Copyright 2018 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 api
import (
"errors"
"fmt"
"strings"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/config/metadata"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/security"
corecfg "github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
// ConfigAPI ...
type ConfigAPI struct {
BaseController
cfgManager *config.CfgManager
}
// Prepare validates the user
func (c *ConfigAPI) Prepare() {
c.BaseController.Prepare()
c.cfgManager = corecfg.GetCfgManager()
if !c.SecurityCtx.IsAuthenticated() {
c.SendUnAuthorizedError(errors.New("UnAuthorized"))
return
}
// Only internal container can access /api/internal/configurations
if strings.EqualFold(c.Ctx.Request.RequestURI, "/api/internal/configurations") {
if s, ok := security.FromContext(c.Ctx.Request.Context()); !ok || s.Name() != "secret" {
c.SendUnAuthorizedError(errors.New("UnAuthorized"))
return
}
}
if !c.SecurityCtx.IsSysAdmin() && !c.SecurityCtx.IsSolutionUser() {
c.SendForbiddenError(errors.New(c.SecurityCtx.GetUsername()))
return
}
}
type value struct {
Value interface{} `json:"value"`
Editable bool `json:"editable"`
}
// Get returns configurations
func (c *ConfigAPI) Get() {
configs := c.cfgManager.GetUserCfgs()
m, err := convertForGet(configs)
if err != nil {
log.Errorf("failed to convert configurations: %v", err)
c.SendInternalServerError(errors.New(""))
return
}
c.Data["json"] = m
c.ServeJSON()
}
// GetInternalConfig returns internal configurations
func (c *ConfigAPI) GetInternalConfig() {
configs := c.cfgManager.GetAll()
c.Data["json"] = configs
c.ServeJSON()
}
// Put updates configurations
func (c *ConfigAPI) Put() {
m := map[string]interface{}{}
if err := c.DecodeJSONReq(&m); err != nil {
c.SendBadRequestError(err)
return
}
err := c.cfgManager.Load()
if err != nil {
log.Errorf("failed to get configurations: %v", err)
c.SendInternalServerError(errors.New(""))
return
}
isSysErr, err := c.validateCfg(m)
if err != nil {
if isSysErr {
log.Errorf("failed to validate configurations: %v", err)
c.SendInternalServerError(errors.New(""))
return
}
c.SendBadRequestError(err)
return
}
if err := c.cfgManager.UpdateConfig(m); err != nil {
log.Errorf("failed to upload configurations: %v", err)
c.SendInternalServerError(errors.New(""))
return
}
}
func (c *ConfigAPI) validateCfg(cfgs map[string]interface{}) (bool, error) {
flag, err := authModeCanBeModified()
if err != nil {
return true, err
}
if !flag {
if failedKeys := checkUnmodifiable(c.cfgManager, cfgs, common.AUTHMode); len(failedKeys) > 0 {
return false, fmt.Errorf("the keys %v can not be modified as new users have been inserted into database", failedKeys)
}
}
err = c.cfgManager.ValidateCfg(cfgs)
return false, err
}
func checkUnmodifiable(mgr *config.CfgManager, cfgs map[string]interface{}, keys ...string) (failed []string) {
if mgr == nil || cfgs == nil || keys == nil {
return
}
for _, k := range keys {
v := mgr.Get(k).GetString()
if nv, ok := cfgs[k]; ok {
if v != fmt.Sprintf("%v", nv) {
failed = append(failed, k)
}
}
}
return
}
// ScanAllPolicy is represent the json request and object for scan all policy
// Only for migrating from the legacy schedule.
type ScanAllPolicy struct {
Type string `json:"type"`
Param map[string]interface{} `json:"parameter,omitempty"`
}
// delete sensitive attrs and add editable field to every attr
func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
result := map[string]*value{}
mList := metadata.Instance().GetAll()
for _, item := range mList {
if _, ok := item.ItemType.(*metadata.PasswordType); ok {
delete(cfg, item.Name)
}
}
if _, ok := cfg[common.ScanAllPolicy]; !ok {
cfg[common.ScanAllPolicy] = ScanAllPolicy{
Type: "none", // For legacy compatible
}
}
for k, v := range cfg {
result[k] = &value{
Value: v,
Editable: true,
}
}
flag, err := authModeCanBeModified()
if err != nil {
return nil, err
}
result[common.AUTHMode].Editable = flag
return result, nil
}
func authModeCanBeModified() (bool, error) {
return dao.AuthModeCanBeModified()
}

View File

@ -1,127 +0,0 @@
// Copyright 2018 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 api
import (
"fmt"
"testing"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
"github.com/stretchr/testify/assert"
)
func TestGetConfig(t *testing.T) {
fmt.Println("Testing getting configurations")
assert := assert.New(t)
apiTest := newHarborAPI()
// case 1: get configurations without admin role
code, _, err := apiTest.GetConfig(*testUser)
if err != nil {
t.Fatalf("failed to get configurations: %v", err)
}
assert.Equal(401, code, "the status code of getting configurations with non-admin user should be 401")
// case 2: get configurations with admin role
code, cfg, err := apiTest.GetConfig(*admin)
if err != nil {
t.Fatalf("failed to get configurations: %v", err)
}
if !assert.Equal(200, code, "the status code of getting configurations with admin user should be 200") {
return
}
t.Logf("cfg: %+v", cfg)
mode := cfg[common.AUTHMode].Value.(string)
assert.Equal(common.DBAuth, mode, fmt.Sprintf("the auth mode should be %s", common.DBAuth))
ccc, err := config.GetSystemCfg()
if err != nil {
t.Logf("failed to get system configurations: %v", err)
}
t.Logf("%v", ccc)
}
func TestInternalConfig(t *testing.T) {
fmt.Println("Testing internal configurations")
assert := assert.New(t)
apiTest := newHarborAPI()
// case 1: get configurations without admin role
code, _, err := apiTest.GetInternalConfig(*testUser)
if err != nil {
t.Fatalf("failed to get configurations: %v", err)
}
assert.Equal(401, code, "the status code of getting configurations with non-admin user should be 401")
// case 2: get configurations with admin role
code, _, err = apiTest.GetInternalConfig(*admin)
if err != nil {
t.Fatalf("failed to get configurations: %v", err)
}
if !assert.Equal(200, code, "the status code of getting configurations with admin user should be 200") {
return
}
}
func TestPutConfig(t *testing.T) {
fmt.Println("Testing modifying configurations")
assert := assert.New(t)
apiTest := newHarborAPI()
cfg := map[string]interface{}{
common.TokenExpiration: 60,
}
code, err := apiTest.PutConfig(*admin, cfg)
if err != nil {
t.Fatalf("failed to get configurations: %v", err)
}
if !assert.Equal(200, code, "the status code of modifying configurations with admin user should be 200") {
return
}
ccc, err := config.GetSystemCfg()
if err != nil {
t.Logf("failed to get system configurations: %v", err)
}
t.Logf("%v", ccc)
}
func TestPutConfigMaxLength(t *testing.T) {
fmt.Println("Testing modifying configurations with max length.")
assert := assert.New(t)
apiTest := newHarborAPI()
// length is 1059expected code: 200
cfg := map[string]interface{}{
common.LDAPGroupSearchFilter: "YU2YcM13JtSx5jtBiftTjfaEM9KZFQ0XA5fKQHU02E9Xe0aLYaSy7YBokrTA8oHFjSkWFSgWZJ6FEmTS" +
"Vy5Ovsy5to2kWnFtbVNX3pzbeQpZeAqK3mEGnXdMkMSQu9WTq74s99GpwjEdA628pcZqLx6wCR0IvwryqIcNoRtqPlUcuRGODWA8ZXaC0d" +
"Qs7cRUYSe8onHsM2c9JWuUS8Jv4E7KggfytrxeKAT0WGP5DBZsB7rHZKxoAppE3C0NueEeC4yV791PUOODJt9rc0RrcD6ORUIO5RriCwym" +
"IinJZa03MtTk3vGFTmL9wM0wEYZP3fEBmoiB0iF8o4wkHGyMpNJoDyPuo7huuCbipAXClEcX1R7xD4aijTF9iOMKymvsObMZ4qqI7flco5" +
"yLFf7W8cpSisk3YJSvxDWfrl91WT4IFE5KHK976DgLQJhTZ8msGOImnFiUGtuIUNQpOgFFtlXJV41OltSsjW5jwAzxcko0MFkOIc7XuPjB" +
"XMrdjC9poYldrxNFrGOPFSyh19iS2UWKayKrtnhvDYAWrNCqOmRs01awEXBlwHp17VcLuze6XGCx7ZoPQX1Nu4uF1InAGpSm1B3pKtteeR" +
"WNNeLZjmNGNuiorHyxLTx1bQTfkG2UzZTTR0e2XatiXt5nCDxSqP2OkOxH7dew36fm9LpkFbmgtlxWxjHX8buYzSJCAjTqqwW3rHCEfQjv" +
"B4T7CTJrAgehCG9zL82P59DQbGXXWqRHbw5g9QszREQys1m56SHLosNptVPUwy7vD70rRf5s8knohW5npEZS9f3RGel64mj5g7bQBBkopx" +
"f6uac3MlJAe9d6C0B7fexZJABln2kCtXXYzITflICISwxuZ0YXHJmT2sMSIpn9VwMnMidV4JsM2BD8ykExZ5QyeVyOCXHDxvRvFwQwjQfR" +
"kkqQmtFREitKWl5njO8wLJw0XyeIVAej75NsGKKZWVjyaupaM9Bqn6NFrWjELFacLox6OCcRIDSDl3ntNN8tIzGOF7aXVCIqljJl0IL9Pz" +
"NenmmubaNm48YjfkBk8MqOUSYJYaFkO1qCKbVdMg7yTqKEHgSUqEkoFPoJMH6GAozC",
}
sc, _ := apiTest.PutConfig(*admin, cfg)
assert.Equal(200, sc, "the status code of modifying configurations with admin user should be 200")
}

View File

@ -16,11 +16,12 @@ package api
import (
"errors"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net"
"strconv"
"github.com/goharbor/harbor/src/common/utils/email"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -52,9 +53,10 @@ func (e *EmailAPI) Ping() {
var host, username, password, identity string
var port int
var ssl, insecure bool
ctx := orm.Context()
body := e.Ctx.Input.CopyBody(1 << 32)
if body == nil || len(body) == 0 {
cfg, err := config.Email()
cfg, err := config.Email(ctx)
if err != nil {
log.Errorf("failed to get email configurations: %v", err)
e.SendInternalServerError(err)
@ -88,7 +90,7 @@ func (e *EmailAPI) Ping() {
}
if settings.Password == nil {
cfg, err := config.Email()
cfg, err := config.Email(ctx)
if err != nil {
log.Errorf("failed to get email configurations: %v", err)
e.SendInternalServerError(err)

View File

@ -35,10 +35,11 @@ import (
"github.com/goharbor/harbor/src/common/job/test"
"github.com/goharbor/harbor/src/common/models"
testutils "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
apimodels "github.com/goharbor/harbor/src/core/api/models"
_ "github.com/goharbor/harbor/src/core/auth/db"
_ "github.com/goharbor/harbor/src/core/auth/ldap"
"github.com/goharbor/harbor/src/core/config"
libOrm "github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/server/middleware"
"github.com/goharbor/harbor/src/server/middleware/orm"
"github.com/goharbor/harbor/src/server/middleware/security"
@ -77,12 +78,12 @@ type usrInfo struct {
}
func init() {
config.Init()
testutils.InitDatabaseFromEnv()
config.Init()
dao.PrepareTestData([]string{"delete from harbor_user where user_id >2", "delete from project where owner_id >2"}, []string{})
config.Upload(testutils.GetUnitTestConfig())
allCfgs, _ := config.GetSystemCfg()
allCfgs, _ := config.GetSystemCfg(libOrm.Context())
testutils.TraceCfgMap(allCfgs)
_, file, _, _ := runtime.Caller(0)
@ -106,8 +107,6 @@ func init() {
beego.Router("/api/statistics", &StatisticAPI{})
beego.Router("/api/users/?:id", &UserAPI{})
beego.Router("/api/usergroups/?:ugid([0-9]+)", &UserGroupAPI{})
beego.Router("/api/configurations", &ConfigAPI{})
beego.Router("/api/configs", &ConfigAPI{}, "get:GetInternalConfig")
beego.Router("/api/email/ping", &EmailAPI{}, "post:Ping")
beego.Router("/api/labels", &LabelAPI{}, "post:Post;get:List")
beego.Router("/api/labels/:id([0-9]+", &LabelAPI{}, "get:Get;put:Put;delete:Delete")
@ -677,82 +676,6 @@ func (a testapi) UsersDelete(userID int, authInfo usrInfo) (int, error) {
return httpStatusCode, err
}
// Post ldap test
func (a testapi) LdapPost(authInfo usrInfo, ldapConf apilib.LdapConf) (int, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/api/ldap/ping"
_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(ldapConf)
httpStatusCode, _, err := request(_sling, jsonAcceptHeader, authInfo)
return httpStatusCode, err
}
// Search Ldap Groups
func (a testapi) LdapGroupsSearch(groupName, groupDN string, authInfo ...usrInfo) (int, []apilib.LdapGroupsSearch, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/api/ldap/groups/search"
_sling = _sling.Path(path)
// body params
type QueryParams struct {
GroupName string `url:"groupname, omitempty"`
GroupDN string `url:"groupdn, omitempty"`
}
_sling = _sling.QueryStruct(&QueryParams{GroupName: groupName, GroupDN: groupDN})
httpStatusCode, body, err := request(_sling, jsonAcceptHeader, authInfo...)
var successPayLoad []apilib.LdapGroupsSearch
if 200 == httpStatusCode && nil == err {
err = json.Unmarshal(body, &successPayLoad)
}
return httpStatusCode, successPayLoad, err
}
func (a testapi) GetConfig(authInfo usrInfo) (int, map[string]*value, error) {
_sling := sling.New().Base(a.basePath).Get("/api/configurations")
cfg := map[string]*value{}
code, body, err := request(_sling, jsonAcceptHeader, authInfo)
if err == nil && code == 200 {
err = json.Unmarshal(body, &cfg)
}
return code, cfg, err
}
func (a testapi) GetInternalConfig(authInfo usrInfo) (int, map[string]interface{}, error) {
_sling := sling.New().Base(a.basePath).Get("/api/configs")
cfg := map[string]interface{}{}
code, body, err := request(_sling, jsonAcceptHeader, authInfo)
if err == nil && code == 200 {
err = json.Unmarshal(body, &cfg)
}
return code, cfg, err
}
func (a testapi) PutConfig(authInfo usrInfo, cfg map[string]interface{}) (int, error) {
_sling := sling.New().Base(a.basePath).Put("/api/configurations").BodyJSON(cfg)
code, _, err := request(_sling, jsonAcceptHeader, authInfo)
return code, err
}
func (a testapi) ResetConfig(authInfo usrInfo) (int, error) {
_sling := sling.New().Base(a.basePath).Post("/api/configurations/reset")
code, _, err := request(_sling, jsonAcceptHeader, authInfo)
return code, err
}
func (a testapi) PingEmail(authInfo usrInfo, settings []byte) (int, string, error) {
_sling := sling.New().Base(a.basePath).Post("/api/email/ping").Body(bytes.NewReader(settings))

View File

@ -17,6 +17,7 @@ package api
import (
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"io/ioutil"
"net/http"
"sort"
@ -28,7 +29,6 @@ import (
"github.com/goharbor/harbor/src/common/dao"
httputil "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/redis"
)

View File

@ -16,13 +16,13 @@ package api
import (
"context"
"github.com/goharbor/harbor/src/controller/config"
o "github.com/astaxie/beego/orm"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/controller/quota"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
@ -78,46 +78,47 @@ func (ia *InternalAPI) SwitchQuota() {
ia.SendBadRequestError(err)
return
}
cur := config.ReadOnly()
ctx := orm.NewContext(ia.Ctx.Request.Context(), o.NewOrm())
cur := config.ReadOnly(ctx)
// quota per project from disable to enable, it needs to update the quota usage bases on the DB records.
if !config.QuotaPerProjectEnable() && req.Enabled {
if !config.QuotaPerProjectEnable(ctx) && req.Enabled {
if !cur {
config.GetCfgManager().Set(common.ReadOnly, true)
config.GetCfgManager().Save()
config.GetCfgManager(ctx).Set(ctx, common.ReadOnly, true)
config.GetCfgManager(ctx).Save(ctx)
}
ctx := orm.NewContext(ia.Ctx.Request.Context(), o.NewOrm())
if err := quota.RefreshForProjects(ctx); err != nil {
ia.SendInternalServerError(err)
return
}
}
defer func() {
config.GetCfgManager().Set(common.ReadOnly, cur)
config.GetCfgManager().Set(common.QuotaPerProjectEnable, req.Enabled)
config.GetCfgManager().Save()
ctx := orm.Context()
config.GetCfgManager(ctx).Set(ctx, common.ReadOnly, cur)
config.GetCfgManager(ctx).Set(ctx, common.QuotaPerProjectEnable, req.Enabled)
config.GetCfgManager(ctx).Save(ctx)
}()
return
}
// SyncQuota ...
func (ia *InternalAPI) SyncQuota() {
if !config.QuotaPerProjectEnable() {
if !config.QuotaPerProjectEnable(orm.Context()) {
ia.SendError(errors.ForbiddenError(nil).WithMessage("quota per project is disabled"))
return
}
cur := config.ReadOnly()
cfgMgr := config.GetCfgManager()
ctx := orm.Context()
cur := config.ReadOnly(ctx)
cfgMgr := config.GetCfgManager(ctx)
if !cur {
cfgMgr.Set(common.ReadOnly, true)
cfgMgr.Save()
cfgMgr.Set(ctx, common.ReadOnly, true)
cfgMgr.Save(ctx)
}
// For api call, to avoid the timeout, it should be asynchronous
go func() {
defer func() {
cfgMgr.Set(common.ReadOnly, cur)
cfgMgr.Save()
ctx := orm.Context()
cfgMgr.Set(ctx, common.ReadOnly, cur)
cfgMgr.Save(ctx)
}()
log.Info("start to sync quota(API), the system will be set to ReadOnly and back it normal once it done.")
ctx := orm.NewContext(context.TODO(), o.NewOrm())

View File

@ -16,6 +16,8 @@ package api
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"strconv"
"strings"
@ -27,7 +29,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
)
@ -98,7 +99,7 @@ func (pma *ProjectMemberAPI) Prepare() {
pma.member = members[0]
}
authMode, err := config.AuthMode()
authMode, err := config.AuthMode(orm.Context())
if err != nil {
pma.SendInternalServerError(fmt.Errorf("failed to get authentication mode"))
}

View File

@ -19,6 +19,8 @@ import (
"errors"
"fmt"
"github.com/goharbor/harbor/src/common/rbac/system"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"regexp"
"strconv"
@ -29,7 +31,6 @@ import (
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security/local"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/permission/types"
@ -63,7 +64,7 @@ type secretReq struct {
// Prepare validates the URL and parms
func (ua *UserAPI) Prepare() {
ua.BaseController.Prepare()
mode, err := config.AuthMode()
mode, err := config.AuthMode(orm.Context())
if err != nil {
log.Errorf("failed to get auth mode: %v", err)
ua.SendInternalServerError(errors.New(""))
@ -81,7 +82,7 @@ func (ua *UserAPI) Prepare() {
ua.secretKey = key
}
self, err := config.SelfRegistration()
self, err := config.SelfRegistration(orm.Context())
if err != nil {
log.Errorf("failed to get self registration: %v", err)
ua.SendInternalServerError(errors.New(""))

View File

@ -16,6 +16,7 @@ package api
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
securitytesting "github.com/goharbor/harbor/src/testing/common/security"
"github.com/goharbor/harbor/src/testing/mock"
"net/http"
@ -31,7 +32,6 @@ import (
"github.com/astaxie/beego"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/config"
)
var testUser0002ID, testUser0003ID int

View File

@ -19,6 +19,8 @@ import (
"fmt"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/rbac/system"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"strconv"
"strings"
@ -27,7 +29,6 @@ import (
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/ldap"
)
@ -60,7 +61,7 @@ func (uga *UserGroupAPI) Prepare() {
return
}
uga.id = int(ugid)
authMode, err := config.AuthMode()
authMode, err := config.AuthMode(orm.Context())
if err != nil {
uga.SendInternalServerError(errors.New("failed to get authentication mode"))
}

View File

@ -17,12 +17,13 @@ package auth
import (
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"time"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -129,7 +130,7 @@ func Register(name string, h AuthenticateHelper) {
// Login authenticates user credentials based on setting.
func Login(m models.AuthModel) (*models.User, error) {
authMode, err := config.AuthMode()
authMode, err := config.AuthMode(orm.Context())
if err != nil {
return nil, err
}
@ -160,7 +161,7 @@ func Login(m models.AuthModel) (*models.User, error) {
}
func getHelper() (AuthenticateHelper, error) {
authMode, err := config.AuthMode()
authMode, err := config.AuthMode(orm.Context())
if err != nil {
return nil, err
}

View File

@ -20,7 +20,10 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/jobservice/logger"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/goharbor/harbor/src/lib/orm"
"io/ioutil"
"net/http"
"strings"
@ -33,7 +36,6 @@ import (
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/authproxy"
)
@ -108,7 +110,7 @@ func (a *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
}
func (a *Auth) tokenReview(sessionID string) (*models.User, error) {
httpAuthProxySetting, err := config.HTTPAuthProxySetting()
httpAuthProxySetting, err := config.HTTPAuthProxySetting(orm.Context())
if err != nil {
return nil, err
}
@ -216,7 +218,7 @@ func (a *Auth) ensure() error {
a.client = &http.Client{}
}
if time.Now().Sub(a.settingTimeStamp) >= refreshDuration {
setting, err := config.HTTPAuthProxySetting()
setting, err := config.HTTPAuthProxySetting(orm.Context())
if err != nil {
return err
}
@ -233,7 +235,7 @@ func (a *Auth) ensure() error {
return nil
}
func getTLSConfig(setting *models.HTTPAuthProxy) (*tls.Config, error) {
func getTLSConfig(setting *cfgModels.HTTPAuthProxy) (*tls.Config, error) {
c := setting.ServerCertificate
if setting.VerifyCert && len(c) > 0 {
certs := x509.NewCertPool()

View File

@ -20,9 +20,10 @@ import (
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/common/models"
cut "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/auth/authproxy/test"
"github.com/goharbor/harbor/src/core/config"
cfgModels "github.com/goharbor/harbor/src/lib/config/models"
"github.com/stretchr/testify/assert"
"net/http/httptest"
"os"
@ -214,11 +215,11 @@ func TestGetTLSConfig(t *testing.T) {
nilRootCA bool
}
cases := []struct {
input *models.HTTPAuthProxy
input *cfgModels.HTTPAuthProxy
expect result
}{
{
input: &models.HTTPAuthProxy{
input: &cfgModels.HTTPAuthProxy{
Endpoint: "https://127.0.0.1/login",
TokenReviewEndpoint: "https://127.0.0.1/tokenreview",
VerifyCert: false,
@ -232,7 +233,7 @@ func TestGetTLSConfig(t *testing.T) {
},
},
{
input: &models.HTTPAuthProxy{
input: &cfgModels.HTTPAuthProxy{
Endpoint: "https://127.0.0.1/login",
TokenReviewEndpoint: "https://127.0.0.1/tokenreview",
VerifyCert: false,
@ -246,7 +247,7 @@ func TestGetTLSConfig(t *testing.T) {
},
},
{
input: &models.HTTPAuthProxy{
input: &cfgModels.HTTPAuthProxy{
Endpoint: "https://127.0.0.1/login",
TokenReviewEndpoint: "https://127.0.0.1/tokenreview",
VerifyCert: true,
@ -258,7 +259,7 @@ func TestGetTLSConfig(t *testing.T) {
},
},
{
input: &models.HTTPAuthProxy{
input: &cfgModels.HTTPAuthProxy{
Endpoint: "https://127.0.0.1/login",
TokenReviewEndpoint: "https://127.0.0.1/tokenreview",
VerifyCert: true,
@ -272,7 +273,7 @@ func TestGetTLSConfig(t *testing.T) {
},
},
{
input: &models.HTTPAuthProxy{
input: &cfgModels.HTTPAuthProxy{
Endpoint: "https://127.0.0.1/login",
TokenReviewEndpoint: "https://127.0.0.1/tokenreview",
VerifyCert: true,

View File

@ -14,6 +14,7 @@
package db
import (
"github.com/goharbor/harbor/src/controller/config"
"log"
"os"
"testing"
@ -24,8 +25,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
coreConfig "github.com/goharbor/harbor/src/core/config"
)
var testConfig = map[string]interface{}{
@ -62,7 +61,7 @@ func TestMain(m *testing.M) {
log.Fatalf("failed to set env %s: %v", "KEY_PATH", err)
}
coreConfig.Init()
config.Init()
config.Upload(testConfig)
retCode := m.Run()

View File

@ -17,6 +17,8 @@ package ldap
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/pkg/ldap/model"
"regexp"
"strings"
@ -32,7 +34,6 @@ import (
"github.com/goharbor/harbor/src/pkg/ldap"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -45,13 +46,13 @@ type Auth struct {
// if the check is successful a dummy record will be inserted into DB, such that this user can
// be associated to other entities in the system.
func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
ctx := orm.Context()
p := m.Principal
if len(strings.TrimSpace(p)) == 0 {
log.Debugf("LDAP authentication failed for empty user id.")
return nil, auth.NewErrAuth("Empty user id")
}
ldapSession, err := ldapCtl.Ctl.Session(context.Background())
ldapSession, err := ldapCtl.Ctl.Session(ctx)
if err != nil {
return nil, fmt.Errorf("can not load system ldap config: %v", err)
}
@ -88,15 +89,15 @@ func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) {
u.Email = strings.TrimSpace(ldapUsers[0].Email)
l.syncUserInfoFromDB(&u)
l.attachLDAPGroup(ldapUsers, &u, ldapSession)
l.attachLDAPGroup(ctx, ldapUsers, &u, ldapSession)
return &u, nil
}
func (l *Auth) attachLDAPGroup(ldapUsers []model.User, u *models.User, sess *ldap.Session) {
func (l *Auth) attachLDAPGroup(ctx context.Context, ldapUsers []model.User, u *models.User, sess *ldap.Session) {
// Retrieve ldap related info in login to avoid too many traffic with LDAP server.
// Get group admin dn
groupCfg, err := config.LDAPGroupConf()
groupCfg, err := config.LDAPGroupConf(ctx)
if err != nil {
log.Warningf("Failed to fetch ldap group configuration:%v", err)
// most likely user doesn't configure user group info, it should not block user login
@ -161,7 +162,7 @@ func (l *Auth) OnBoardUser(u *models.User) error {
// SearchUser -- Search user in ldap
func (l *Auth) SearchUser(username string) (*models.User, error) {
var user models.User
s, err := ldapCtl.Ctl.Session(context.Background())
s, err := ldapCtl.Ctl.Session(orm.Context())
if err != nil {
return nil, err
}
@ -196,7 +197,7 @@ func (l *Auth) SearchGroup(groupKey string) (*models.UserGroup, error) {
if _, err := goldap.ParseDN(groupKey); err != nil {
return nil, auth.ErrInvalidLDAPGroupDN
}
s, err := ldapCtl.Ctl.Session(context.Background())
s, err := ldapCtl.Ctl.Session(orm.Context())
if err != nil {
return nil, fmt.Errorf("can not load system ldap config: %v", err)

View File

@ -14,6 +14,7 @@
package ldap
import (
"github.com/goharbor/harbor/src/controller/config"
"github.com/stretchr/testify/assert"
// "fmt"
// "strings"
@ -30,7 +31,6 @@ import (
"github.com/goharbor/harbor/src/common/dao/group"
"github.com/goharbor/harbor/src/core/auth"
coreConfig "github.com/goharbor/harbor/src/core/config"
)
var ldapTestConfig = map[string]interface{}{
@ -61,7 +61,7 @@ var ldapTestConfig = map[string]interface{}{
func TestMain(m *testing.M) {
test.InitDatabaseFromEnv()
coreConfig.InitWithSettings(ldapTestConfig)
config.InitWithSettings(ldapTestConfig)
secretKeyPath := "/tmp/secretkey"
_, err := test.GenerateKey(secretKeyPath)

View File

@ -16,6 +16,8 @@ package uaa
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"os"
"strings"
"sync"
@ -25,7 +27,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/uaa"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -129,7 +130,7 @@ func (u *Auth) SearchUser(username string) (*models.User, error) {
func (u *Auth) ensureClient() error {
var cfg *uaa.ClientConfig
UAASettings, err := config.UAASettings()
UAASettings, err := config.UAASettings(orm.Context())
// log.Debugf("Uaa settings: %+v", UAASettings)
if err != nil {
log.Warningf("Failed to get UAA setting from Admin Server, error: %v", err)

View File

@ -15,6 +15,7 @@
package uaa
import (
"github.com/goharbor/harbor/src/controller/config"
"os"
"testing"
@ -22,7 +23,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/common/utils/uaa"
"github.com/goharbor/harbor/src/core/config"
"github.com/stretchr/testify/assert"
)

View File

@ -1,491 +0,0 @@
// Copyright 2018 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 config provide config for core api and other modules
// Before accessing user settings, need to call Load()
// For system settings, no need to call Load()
package config
import (
"errors"
"os"
"strconv"
"strings"
"github.com/goharbor/harbor/src/common"
comcfg "github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/secret"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/ldap/model"
)
const (
defaultKeyPath = "/etc/core/key"
defaultRegistryTokenPrivateKeyPath = "/etc/core/private_key.pem"
// SessionCookieName is the name of the cookie for session ID
SessionCookieName = "sid"
)
var (
// SecretStore manages secrets
SecretStore *secret.Store
keyProvider comcfg.KeyProvider
// defined as a var for testing.
defaultCACertPath = "/etc/core/ca/ca.crt"
cfgMgr *comcfg.CfgManager
)
// Init configurations
func Init() {
// init key provider
initKeyProvider()
cfgMgr = comcfg.NewDBCfgManager()
log.Info("init secret store")
// init secret store
initSecretStore()
}
// InitWithSettings init config with predefined configs, and optionally overwrite the keyprovider
func InitWithSettings(cfgs map[string]interface{}, kp ...comcfg.KeyProvider) {
Init()
cfgMgr = comcfg.NewInMemoryManager()
cfgMgr.UpdateConfig(cfgs)
if len(kp) > 0 {
keyProvider = kp[0]
}
}
func initKeyProvider() {
path := os.Getenv("KEY_PATH")
if len(path) == 0 {
path = defaultKeyPath
}
log.Infof("key path: %s", path)
keyProvider = comcfg.NewFileKeyProvider(path)
}
func initSecretStore() {
m := map[string]string{}
m[JobserviceSecret()] = secret.JobserviceUser
SecretStore = secret.NewStore(m)
}
// GetCfgManager return the current config manager
func GetCfgManager() *comcfg.CfgManager {
if cfgMgr == nil {
return comcfg.NewDBCfgManager()
}
return cfgMgr
}
// Load configurations
func Load() error {
return cfgMgr.Load()
}
// Upload save all system configurations
func Upload(cfg map[string]interface{}) error {
return cfgMgr.UpdateConfig(cfg)
}
// GetSystemCfg returns the system configurations
func GetSystemCfg() (map[string]interface{}, error) {
sysCfg := cfgMgr.GetAll()
if len(sysCfg) == 0 {
return nil, errors.New("can not load system config, the database might be down")
}
return sysCfg, nil
}
// AuthMode ...
func AuthMode() (string, error) {
err := cfgMgr.Load()
if err != nil {
log.Errorf("failed to load config, error %v", err)
return "db_auth", err
}
return cfgMgr.Get(common.AUTHMode).GetString(), nil
}
// TokenPrivateKeyPath returns the path to the key for signing token for registry
func TokenPrivateKeyPath() string {
path := os.Getenv("TOKEN_PRIVATE_KEY_PATH")
if len(path) == 0 {
path = defaultRegistryTokenPrivateKeyPath
}
return path
}
// LDAPConf returns the setting of ldap server
func LDAPConf() (*model.LdapConf, error) {
err := cfgMgr.Load()
if err != nil {
return nil, err
}
return &model.LdapConf{
URL: cfgMgr.Get(common.LDAPURL).GetString(),
SearchDn: cfgMgr.Get(common.LDAPSearchDN).GetString(),
SearchPassword: cfgMgr.Get(common.LDAPSearchPwd).GetString(),
BaseDn: cfgMgr.Get(common.LDAPBaseDN).GetString(),
UID: cfgMgr.Get(common.LDAPUID).GetString(),
Filter: cfgMgr.Get(common.LDAPFilter).GetString(),
Scope: cfgMgr.Get(common.LDAPScope).GetInt(),
ConnectionTimeout: cfgMgr.Get(common.LDAPTimeout).GetInt(),
VerifyCert: cfgMgr.Get(common.LDAPVerifyCert).GetBool(),
}, nil
}
// LDAPGroupConf returns the setting of ldap group search
func LDAPGroupConf() (*model.GroupConf, error) {
err := cfgMgr.Load()
if err != nil {
return nil, err
}
return &model.GroupConf{
BaseDN: cfgMgr.Get(common.LDAPGroupBaseDN).GetString(),
Filter: cfgMgr.Get(common.LDAPGroupSearchFilter).GetString(),
NameAttribute: cfgMgr.Get(common.LDAPGroupAttributeName).GetString(),
SearchScope: cfgMgr.Get(common.LDAPGroupSearchScope).GetInt(),
AdminDN: cfgMgr.Get(common.LDAPGroupAdminDn).GetString(),
MembershipAttribute: cfgMgr.Get(common.LDAPGroupMembershipAttribute).GetString(),
}, nil
}
// TokenExpiration returns the token expiration time (in minute)
func TokenExpiration() (int, error) {
return cfgMgr.Get(common.TokenExpiration).GetInt(), nil
}
// RobotTokenDuration returns the token expiration time of robot account (in minute)
func RobotTokenDuration() int {
return cfgMgr.Get(common.RobotTokenDuration).GetInt()
}
// ExtEndpoint returns the external URL of Harbor: protocol://host:port
func ExtEndpoint() (string, error) {
return cfgMgr.Get(common.ExtEndpoint).GetString(), nil
}
// ExtURL returns the external URL: host:port
func ExtURL() (string, error) {
endpoint, err := ExtEndpoint()
if err != nil {
log.Errorf("failed to load config, error %v", err)
}
l := strings.Split(endpoint, "://")
if len(l) > 1 {
return l[1], nil
}
return endpoint, nil
}
// SecretKey returns the secret key to encrypt the password of target
func SecretKey() (string, error) {
return keyProvider.Get(nil)
}
// SelfRegistration returns the enablement of self registration
func SelfRegistration() (bool, error) {
return cfgMgr.Get(common.SelfRegistration).GetBool(), nil
}
// RegistryURL ...
func RegistryURL() (string, error) {
url := os.Getenv("REGISTRY_URL")
if len(url) == 0 {
url = "http://registry:5000"
}
return url, nil
}
// InternalJobServiceURL returns jobservice URL for internal communication between Harbor containers
func InternalJobServiceURL() string {
return os.Getenv("JOBSERVICE_URL")
}
// GetCoreURL returns the url of core from env
func GetCoreURL() string {
return os.Getenv("CORE_URL")
}
// InternalCoreURL returns the local harbor core url
func InternalCoreURL() string {
return strings.TrimSuffix(cfgMgr.Get(common.CoreURL).GetString(), "/")
}
// LocalCoreURL returns the local harbor core url
func LocalCoreURL() string {
return cfgMgr.Get(common.CoreLocalURL).GetString()
}
// InternalTokenServiceEndpoint returns token service endpoint for internal communication between Harbor containers
func InternalTokenServiceEndpoint() string {
return InternalCoreURL() + "/service/token"
}
// InternalNotaryEndpoint returns notary server endpoint for internal communication between Harbor containers
// This is currently a conventional value and can be unaccessible when Harbor is not deployed with Notary.
func InternalNotaryEndpoint() string {
return cfgMgr.Get(common.NotaryURL).GetString()
}
// InitialAdminPassword returns the initial password for administrator
func InitialAdminPassword() (string, error) {
return cfgMgr.Get(common.AdminInitialPassword).GetString(), nil
}
// OnlyAdminCreateProject returns the flag to restrict that only sys admin can create project
func OnlyAdminCreateProject() (bool, error) {
return cfgMgr.Get(common.ProjectCreationRestriction).GetString() == common.ProCrtRestrAdmOnly, nil
}
// Email returns email server settings
func Email() (*models.Email, error) {
err := cfgMgr.Load()
if err != nil {
return nil, err
}
return &models.Email{
Host: cfgMgr.Get(common.EmailHost).GetString(),
Port: cfgMgr.Get(common.EmailPort).GetInt(),
Username: cfgMgr.Get(common.EmailUsername).GetString(),
Password: cfgMgr.Get(common.EmailPassword).GetString(),
SSL: cfgMgr.Get(common.EmailSSL).GetBool(),
From: cfgMgr.Get(common.EmailFrom).GetString(),
Identity: cfgMgr.Get(common.EmailIdentity).GetString(),
Insecure: cfgMgr.Get(common.EmailInsecure).GetBool(),
}, nil
}
// Database returns database settings
func Database() (*models.Database, error) {
database := &models.Database{}
database.Type = cfgMgr.Get(common.DatabaseType).GetString()
postgresql := &models.PostGreSQL{
Host: cfgMgr.Get(common.PostGreSQLHOST).GetString(),
Port: cfgMgr.Get(common.PostGreSQLPort).GetInt(),
Username: cfgMgr.Get(common.PostGreSQLUsername).GetString(),
Password: cfgMgr.Get(common.PostGreSQLPassword).GetString(),
Database: cfgMgr.Get(common.PostGreSQLDatabase).GetString(),
SSLMode: cfgMgr.Get(common.PostGreSQLSSLMode).GetString(),
MaxIdleConns: cfgMgr.Get(common.PostGreSQLMaxIdleConns).GetInt(),
MaxOpenConns: cfgMgr.Get(common.PostGreSQLMaxOpenConns).GetInt(),
}
database.PostGreSQL = postgresql
return database, nil
}
// CoreSecret returns a secret to mark harbor-core when communicate with
// other component
func CoreSecret() string {
return os.Getenv("CORE_SECRET")
}
// RegistryCredential returns the username and password the core uses to access registry
func RegistryCredential() (string, string) {
return os.Getenv("REGISTRY_CREDENTIAL_USERNAME"), os.Getenv("REGISTRY_CREDENTIAL_PASSWORD")
}
// JobserviceSecret returns a secret to mark Jobservice when communicate with
// other component
// TODO replace it with method of SecretStore
func JobserviceSecret() string {
return os.Getenv("JOBSERVICE_SECRET")
}
// WithNotary returns a bool value to indicate if Harbor's deployed with Notary
func WithNotary() bool {
return cfgMgr.Get(common.WithNotary).GetBool()
}
// WithTrivy returns a bool value to indicate if Harbor's deployed with Trivy.
func WithTrivy() bool {
return cfgMgr.Get(common.WithTrivy).GetBool()
}
// TrivyAdapterURL returns the endpoint URL of a Trivy adapter instance, by default it's the one deployed within Harbor.
func TrivyAdapterURL() string {
return cfgMgr.Get(common.TrivyAdapterURL).GetString()
}
// UAASettings returns the UAASettings to access UAA service.
func UAASettings() (*models.UAASettings, error) {
err := cfgMgr.Load()
if err != nil {
return nil, err
}
us := &models.UAASettings{
Endpoint: cfgMgr.Get(common.UAAEndpoint).GetString(),
ClientID: cfgMgr.Get(common.UAAClientID).GetString(),
ClientSecret: cfgMgr.Get(common.UAAClientSecret).GetString(),
VerifyCert: cfgMgr.Get(common.UAAVerifyCert).GetBool(),
}
return us, nil
}
// ReadOnly returns a bool to indicates if Harbor is in read only mode.
func ReadOnly() bool {
return cfgMgr.Get(common.ReadOnly).GetBool()
}
// WithChartMuseum returns a bool to indicate if chartmuseum is deployed with Harbor.
func WithChartMuseum() bool {
return cfgMgr.Get(common.WithChartMuseum).GetBool()
}
// GetChartMuseumEndpoint returns the endpoint of the chartmuseum service
// otherwise an non nil error is returned
func GetChartMuseumEndpoint() (string, error) {
chartEndpoint := strings.TrimSpace(cfgMgr.Get(common.ChartRepoURL).GetString())
if len(chartEndpoint) == 0 {
return "", errors.New("empty chartmuseum endpoint")
}
return chartEndpoint, nil
}
// GetRedisOfRegURL returns the URL of Redis used by registry
func GetRedisOfRegURL() string {
return os.Getenv("_REDIS_URL_REG")
}
// GetPortalURL returns the URL of portal
func GetPortalURL() string {
url := os.Getenv("PORTAL_URL")
if len(url) == 0 {
return common.DefaultPortalURL
}
return url
}
// GetRegistryCtlURL returns the URL of registryctl
func GetRegistryCtlURL() string {
url := os.Getenv("REGISTRY_CONTROLLER_URL")
if len(url) == 0 {
return common.DefaultRegistryCtlURL
}
return url
}
// HTTPAuthProxySetting returns the setting of HTTP Auth proxy. the settings are only meaningful when the auth_mode is
// set to http_auth
func HTTPAuthProxySetting() (*models.HTTPAuthProxy, error) {
if err := cfgMgr.Load(); err != nil {
return nil, err
}
return &models.HTTPAuthProxy{
Endpoint: cfgMgr.Get(common.HTTPAuthProxyEndpoint).GetString(),
TokenReviewEndpoint: cfgMgr.Get(common.HTTPAuthProxyTokenReviewEndpoint).GetString(),
AdminGroups: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminGroups).GetString(), ","),
AdminUsernames: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminUsernames).GetString(), ","),
VerifyCert: cfgMgr.Get(common.HTTPAuthProxyVerifyCert).GetBool(),
SkipSearch: cfgMgr.Get(common.HTTPAuthProxySkipSearch).GetBool(),
ServerCertificate: cfgMgr.Get(common.HTTPAuthProxyServerCertificate).GetString(),
}, nil
}
// OIDCSetting returns the setting of OIDC provider, currently there's only one OIDC provider allowed for Harbor and it's
// only effective when auth_mode is set to oidc_auth
func OIDCSetting() (*models.OIDCSetting, error) {
if err := cfgMgr.Load(); err != nil {
return nil, err
}
scopeStr := cfgMgr.Get(common.OIDCScope).GetString()
extEndpoint := strings.TrimSuffix(cfgMgr.Get(common.ExtEndpoint).GetString(), "/")
scope := splitAndTrim(scopeStr, ",")
return &models.OIDCSetting{
Name: cfgMgr.Get(common.OIDCName).GetString(),
Endpoint: cfgMgr.Get(common.OIDCEndpoint).GetString(),
VerifyCert: cfgMgr.Get(common.OIDCVerifyCert).GetBool(),
AutoOnboard: cfgMgr.Get(common.OIDCAutoOnboard).GetBool(),
ClientID: cfgMgr.Get(common.OIDCCLientID).GetString(),
ClientSecret: cfgMgr.Get(common.OIDCClientSecret).GetString(),
GroupsClaim: cfgMgr.Get(common.OIDCGroupsClaim).GetString(),
AdminGroup: cfgMgr.Get(common.OIDCAdminGroup).GetString(),
RedirectURL: extEndpoint + common.OIDCCallbackPath,
Scope: scope,
UserClaim: cfgMgr.Get(common.OIDCUserClaim).GetString(),
ExtraRedirectParms: cfgMgr.Get(common.OIDCExtraRedirectParms).GetStringToStringMap(),
}, nil
}
// NotificationEnable returns a bool to indicates if notification enabled in harbor
func NotificationEnable() bool {
return cfgMgr.Get(common.NotificationEnable).GetBool()
}
// QuotaPerProjectEnable returns a bool to indicates if quota per project enabled in harbor
func QuotaPerProjectEnable() bool {
return cfgMgr.Get(common.QuotaPerProjectEnable).GetBool()
}
// QuotaSetting returns the setting of quota.
func QuotaSetting() (*models.QuotaSetting, error) {
if err := cfgMgr.Load(); err != nil {
return nil, err
}
return &models.QuotaSetting{
StoragePerProject: cfgMgr.Get(common.StoragePerProject).GetInt64(),
}, nil
}
// GetPermittedRegistryTypesForProxyCache returns the permitted registry types for proxy cache
func GetPermittedRegistryTypesForProxyCache() []string {
types := os.Getenv("PERMITTED_REGISTRY_TYPES_FOR_PROXY_CACHE")
if len(types) == 0 {
return []string{}
}
return strings.Split(types, ",")
}
// GetGCTimeWindow returns the reserve time window of blob.
func GetGCTimeWindow() int64 {
// the env is for testing/debugging. For production, Do NOT set it.
if env, exist := os.LookupEnv("GC_TIME_WINDOW_HOURS"); exist {
timeWindow, err := strconv.ParseInt(env, 10, 64)
if err == nil {
return timeWindow
}
}
return common.DefaultGCTimeWindowHours
}
// RobotPrefix user defined robot name prefix.
func RobotPrefix() string {
return cfgMgr.Get(common.RobotNamePrefix).GetString()
}
// Metric returns the overall metric settings
func Metric() *models.Metric {
return &models.Metric{
Enabled: cfgMgr.Get(common.MetricEnable).GetBool(),
Port: cfgMgr.Get(common.MetricPort).GetInt(),
Path: cfgMgr.Get(common.MetricPath).GetString(),
}
}
func splitAndTrim(s, sep string) []string {
res := make([]string, 0)
for _, s := range strings.Split(s, sep) {
if e := strings.TrimSpace(s); len(e) > 0 {
res = append(res, e)
}
}
return res
}

View File

@ -2,12 +2,13 @@ package controllers
import (
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/core/api"
"github.com/goharbor/harbor/src/core/auth/authproxy"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
)
@ -25,7 +26,7 @@ type AuthProxyController struct {
// Prepare checks the auth mode and fail early
func (apc *AuthProxyController) Prepare() {
am, err := config.AuthMode()
am, err := config.AuthMode(orm.Context())
if err != nil {
apc.SendInternalServerError(err)
return

View File

@ -17,6 +17,8 @@ package controllers
import (
"bytes"
"context"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"html/template"
"net"
"net/http"
@ -35,7 +37,6 @@ import (
email_util "github.com/goharbor/harbor/src/common/utils/email"
"github.com/goharbor/harbor/src/core/api"
"github.com/goharbor/harbor/src/core/auth"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/log"
)
@ -123,7 +124,7 @@ func (cc *CommonController) LogOut() {
// UserExists checks if user exists when user input value in sign in form.
func (cc *CommonController) UserExists() {
flag, err := config.SelfRegistration()
flag, err := config.SelfRegistration(orm.Context())
if err != nil {
log.Errorf("Failed to get the status of self registration flag, error: %v, disabling user existence check", err)
}
@ -216,7 +217,7 @@ func (cc *CommonController) SendResetEmail() {
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
}
settings, err := config.Email()
settings, err := config.Email(orm.Context())
if err != nil {
log.Errorf("failed to get email configurations: %v", err)
cc.CustomAbort(http.StatusInternalServerError, "internal_error")
@ -281,7 +282,7 @@ func isUserResetable(u *models.User) bool {
if u == nil {
return false
}
mode, err := config.AuthMode()
mode, err := config.AuthMode(orm.Context())
if err != nil {
log.Errorf("Failed to get the auth mode, error: %v", err)
return false

View File

@ -14,10 +14,11 @@
package controllers
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/core/middlewares"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"net/http/httptest"
"os"
@ -30,7 +31,6 @@ import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/models"
utilstest "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/core/config"
"github.com/stretchr/testify/assert"
)
@ -86,9 +86,9 @@ func TestUserResettable(t *testing.T) {
}
func TestRedirectForOIDC(t *testing.T) {
ctx := lib.WithAuthMode(context.Background(), common.DBAuth)
ctx := lib.WithAuthMode(orm.Context(), common.DBAuth)
assert.False(t, redirectForOIDC(ctx, "nonexist"))
ctx = lib.WithAuthMode(context.Background(), common.OIDCAuth)
ctx = lib.WithAuthMode(orm.Context(), common.OIDCAuth)
assert.True(t, redirectForOIDC(ctx, "nonexist"))
assert.False(t, redirectForOIDC(ctx, "admin"))

View File

@ -17,6 +17,8 @@ package controllers
import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"net/http"
"strings"
@ -25,7 +27,6 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/core/api"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/pkg/oidc"
@ -47,7 +48,7 @@ type onboardReq struct {
// Prepare include public code path for call request handler of OIDCController
func (oc *OIDCController) Prepare() {
if mode, _ := config.AuthMode(); mode != common.OIDCAuth {
if mode, _ := config.AuthMode(orm.Context()); mode != common.OIDCAuth {
oc.SendPreconditionFailedError(fmt.Errorf("auth mode: %s is not OIDC based", mode))
return
}
@ -121,7 +122,7 @@ func (oc *OIDCController) Callback() {
}
oc.SetSession(tokenKey, tokenBytes)
oidcSettings, err := config.OIDCSetting()
oidcSettings, err := config.OIDCSetting(ctx)
if err != nil {
oc.SendInternalServerError(err)
return

View File

@ -18,6 +18,7 @@ import (
"context"
"encoding/gob"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"net/url"
"os"
"os/signal"
@ -41,7 +42,6 @@ import (
_ "github.com/goharbor/harbor/src/core/auth/ldap"
_ "github.com/goharbor/harbor/src/core/auth/oidc"
_ "github.com/goharbor/harbor/src/core/auth/uaa"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/core/middlewares"
"github.com/goharbor/harbor/src/core/service/token"
"github.com/goharbor/harbor/src/lib/cache"
@ -189,7 +189,7 @@ func main() {
if err = migration.Migrate(database); err != nil {
log.Fatalf("failed to migrate: %v", err)
}
if err := config.Load(); err != nil {
if err := config.Load(orm.Context()); err != nil {
log.Fatalf("failed to load config: %v", err)
}

View File

@ -17,6 +17,7 @@ package token
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/controller/config"
"strings"
"time"
@ -27,7 +28,6 @@ import (
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/log"
tokenpkg "github.com/goharbor/harbor/src/pkg/token"
v2 "github.com/goharbor/harbor/src/pkg/token/claims/v2"
@ -109,12 +109,12 @@ func filterAccess(ctx context.Context, access []*token.ResourceActions,
}
// MakeToken makes a valid jwt token based on parms.
func MakeToken(username, service string, access []*token.ResourceActions) (*models.Token, error) {
func MakeToken(ctx context.Context, username, service string, access []*token.ResourceActions) (*models.Token, error) {
options, err := tokenpkg.NewOptions(signingMethod, v2.Issuer, privateKey)
if err != nil {
return nil, err
}
expiration, err := config.TokenExpiration()
expiration, err := config.TokenExpiration(ctx)
if err != nil {
return nil, err
}

View File

@ -18,6 +18,7 @@ import (
"context"
"fmt"
rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
"github.com/goharbor/harbor/src/controller/config"
"net/http"
"net/url"
"strings"
@ -27,7 +28,6 @@ import (
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/controller/project"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log"
)
@ -245,7 +245,7 @@ func (g generalCreator) Create(r *http.Request) (*models.Token, error) {
if err != nil {
return nil, err
}
return MakeToken(ctx.GetUsername(), g.service, access)
return MakeToken(r.Context(), ctx.GetUsername(), g.service, access)
}
func parseScopes(u *url.URL) []string {

View File

@ -20,6 +20,9 @@ import (
"encoding/pem"
"fmt"
"github.com/goharbor/harbor/src/common/rbac/project"
"github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/controller/config"
"github.com/goharbor/harbor/src/lib/orm"
"io/ioutil"
"net/url"
"os"
@ -32,11 +35,11 @@ import (
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/common/security"
"github.com/goharbor/harbor/src/core/config"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
test.InitDatabaseFromEnv()
config.Init()
InitCreators()
result := m.Run()
@ -133,7 +136,7 @@ func TestMakeToken(t *testing.T) {
}}
svc := "harbor-registry"
u := "tester"
tokenJSON, err := MakeToken(u, svc, ra)
tokenJSON, err := MakeToken(orm.Context(), u, svc, ra)
if err != nil {
t.Errorf("Error while making token: %v", err)
}

View File

@ -17,8 +17,7 @@ package utils
import (
"github.com/goharbor/harbor/src/common/job"
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/controller/config"
"sync"
)

View File

@ -23,12 +23,12 @@ import (
"time"
o "github.com/astaxie/beego/orm"
comcfg "github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/dao"
"github.com/goharbor/harbor/src/jobservice/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/jobservice/logger"
"github.com/goharbor/harbor/src/jobservice/logger/sweeper"
libCfg "github.com/goharbor/harbor/src/lib/config"
"github.com/goharbor/harbor/src/lib/orm"
)
@ -45,7 +45,7 @@ type Context struct {
// other required information
properties map[string]interface{}
// admin server client
cfgMgr comcfg.CfgManager
cfgMgr libCfg.Manager
// job life cycle tracker
tracker job.Tracker
// job logger configs settings map lock
@ -53,10 +53,10 @@ type Context struct {
}
// NewContext ...
func NewContext(sysCtx context.Context, cfgMgr *comcfg.CfgManager) *Context {
func NewContext(sysCtx context.Context, cfgMgr libCfg.Manager) *Context {
return &Context{
sysContext: comcfg.NewContext(sysCtx, cfgMgr),
cfgMgr: *cfgMgr,
sysContext: libCfg.NewContext(sysCtx, cfgMgr),
cfgMgr: cfgMgr,
properties: make(map[string]interface{}),
}
}
@ -70,7 +70,7 @@ func (c *Context) Init() error {
for counter == 0 || err != nil {
counter++
err = c.cfgMgr.Load()
err = c.cfgMgr.Load(c.sysContext)
if err != nil {
logger.Errorf("Job context initialization error: %s\n", err.Error())
if counter < maxRetryTimes {
@ -117,12 +117,12 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
}
// Refresh config properties
err := c.cfgMgr.Load()
err := c.cfgMgr.Load(c.sysContext)
if err != nil {
return nil, err
}
props := c.cfgMgr.GetAll()
props := c.cfgMgr.GetAll(c.sysContext)
for k, v := range props {
jContext.properties[k] = v
}

View File

@ -16,17 +16,20 @@ package impl
import (
"context"
"github.com/goharbor/harbor/src/common"
common_dao "github.com/goharbor/harbor/src/common/dao"
libCfg "github.com/goharbor/harbor/src/lib/config"
"os"
"testing"
"github.com/goharbor/harbor/src/jobservice/common/list"
comcfg "github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/jobservice/common/utils"
"github.com/goharbor/harbor/src/jobservice/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/jobservice/tests"
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
_ "github.com/goharbor/harbor/src/pkg/config/rest"
"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -110,9 +113,11 @@ func (suite *ContextImplTestSuite) TearDownSuite() {
// TestContextImpl tests the context impl
func (suite *ContextImplTestSuite) TestContextImpl() {
cfgMem := comcfg.NewInMemoryManager()
cfgMem.Set("read_only", "true")
ctx := NewContext(context.Background(), cfgMem)
cfgMem, err := libCfg.GetManager(common.InMemoryCfgManager)
assert.Nil(suite.T(), err)
cont := context.Background()
cfgMem.Set(cont, "read_only", "true")
ctx := NewContext(cont, cfgMem)
jCtx, err := ctx.Build(suite.tracker)
require.NoErrorf(suite.T(), err, "build job context: nil error expected but got %s", err)

View File

@ -16,11 +16,12 @@ package gcreadonly
import (
"fmt"
"github.com/goharbor/harbor/src/lib/config"
"github.com/goharbor/harbor/src/lib/orm"
"os"
"time"
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/registryctl"
"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/project"
@ -36,19 +37,21 @@ import (
var (
regCtlInit = registryctl.Init
getReadOnly = func(cfgMgr *config.CfgManager) (bool, error) {
if err := cfgMgr.Load(); err != nil {
getReadOnly = func(cfgMgr config.Manager) (bool, error) {
cxt := orm.Context()
if err := cfgMgr.Load(cxt); err != nil {
return false, err
}
return cfgMgr.Get(common.ReadOnly).GetBool(), nil
return cfgMgr.Get(cxt, common.ReadOnly).GetBool(), nil
}
setReadOnly = func(cfgMgr *config.CfgManager, switcher bool) error {
setReadOnly = func(cfgMgr config.Manager, switcher bool) error {
cxt := orm.Context()
cfg := map[string]interface{}{
common.ReadOnly: switcher,
}
cfgMgr.UpdateConfig(cfg)
return cfgMgr.Save()
cfgMgr.UpdateConfig(cxt, cfg)
return cfgMgr.Save(cxt)
}
)
@ -69,7 +72,7 @@ type GarbageCollector struct {
projectCtl project.Controller
registryCtlClient client.Client
logger logger.Interface
cfgMgr *config.CfgManager
cfgMgr config.Manager
CoreURL string
redisURL string
deleteUntagged bool
@ -164,15 +167,11 @@ func (gc *GarbageCollector) init(ctx job.Context, params job.Parameters) error {
return err
}
errTpl := "failed to get required property: %s"
if v, ok := ctx.Get(common.CoreURL); ok && len(v.(string)) > 0 {
gc.CoreURL = v.(string)
} else {
return fmt.Errorf(errTpl, common.CoreURL)
mgr, err := config.GetManager(common.RestCfgManager)
if err != nil {
return err
}
secret := os.Getenv("JOBSERVICE_SECRET")
configURL := gc.CoreURL + common.CoreConfigPath
gc.cfgMgr = config.NewRESTCfgManager(configURL, secret)
gc.cfgMgr = mgr
gc.redisURL = params["redis_url_reg"].(string)
// default is to delete the untagged artifact

View File

@ -15,10 +15,11 @@
package gcreadonly
import (
"github.com/goharbor/harbor/src/common"
"github.com/goharbor/harbor/src/lib/config"
"os"
"testing"
"github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/common/models"
commom_regctl "github.com/goharbor/harbor/src/common/registryctl"
"github.com/goharbor/harbor/src/controller/project"
@ -26,6 +27,10 @@ import (
"github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/artifactrash/model"
pkg_blob "github.com/goharbor/harbor/src/pkg/blob/models"
_ "github.com/goharbor/harbor/src/pkg/config/db"
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
_ "github.com/goharbor/harbor/src/pkg/config/rest"
artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact"
projecttesting "github.com/goharbor/harbor/src/testing/controller/project"
mockjobservice "github.com/goharbor/harbor/src/testing/jobservice"
@ -47,8 +52,8 @@ type gcTestSuite struct {
originalProjectCtl project.Controller
regCtlInit func()
setReadOnly func(cfgMgr *config.CfgManager, switcher bool) error
getReadOnly func(cfgMgr *config.CfgManager) (bool, error)
setReadOnly func(cfgMgr config.Manager, switcher bool) error
getReadOnly func(cfgMgr config.Manager) (bool, error)
}
func (suite *gcTestSuite) SetupTest() {
@ -62,8 +67,8 @@ func (suite *gcTestSuite) SetupTest() {
project.Ctl = suite.projectCtl
regCtlInit = func() { commom_regctl.RegistryCtlClient = suite.registryCtlClient }
setReadOnly = func(cfgMgr *config.CfgManager, switcher bool) error { return nil }
getReadOnly = func(cfgMgr *config.CfgManager) (bool, error) { return true, nil }
setReadOnly = func(cfgMgr config.Manager, switcher bool) error { return nil }
getReadOnly = func(cfgMgr config.Manager) (bool, error) { return true, nil }
}
func (suite *gcTestSuite) TearDownTest() {
@ -237,11 +242,12 @@ func (suite *gcTestSuite) TestRun() {
}, nil)
mock.OnAnything(suite.blobMgr, "CleanupAssociationsForProject").Return(nil)
mgr, err := config.GetManager(common.InMemoryCfgManager)
suite.Nil(err)
gc := &GarbageCollector{
artCtl: suite.artifactCtl,
artrashMgr: suite.artrashMgr,
cfgMgr: config.NewInMemoryManager(),
cfgMgr: mgr,
blobMgr: suite.blobMgr,
registryCtlClient: suite.registryCtlClient,
}

View File

@ -19,15 +19,15 @@ import (
"errors"
"flag"
"fmt"
"github.com/goharbor/harbor/src/common"
comcfg "github.com/goharbor/harbor/src/common/config"
"github.com/goharbor/harbor/src/jobservice/common/utils"
"github.com/goharbor/harbor/src/jobservice/config"
"github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/jobservice/job/impl"
"github.com/goharbor/harbor/src/jobservice/logger"
"github.com/goharbor/harbor/src/jobservice/runtime"
cfgLib "github.com/goharbor/harbor/src/lib/config"
_ "github.com/goharbor/harbor/src/pkg/config/rest"
)
func main() {
@ -63,9 +63,10 @@ func main() {
if utils.IsEmptyStr(secret) {
return nil, errors.New("empty auth secret")
}
coreURL := config.GetCoreURL()
configURL := coreURL + common.CoreConfigPath
cfgMgr := comcfg.NewRESTCfgManager(configURL, secret)
cfgMgr, err := cfgLib.GetManager(common.RestCfgManager)
if err != nil {
return nil, err
}
jobCtx := impl.NewContext(ctx, cfgMgr)
if err := jobCtx.Init(); err != nil {

48
src/lib/config/config.go Normal file
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 config
import (
"errors"
"github.com/goharbor/harbor/src/lib/log"
"sync"
)
var (
managersMU sync.RWMutex
managers = make(map[string]Manager)
)
// Register register the config manager
func Register(name string, mgr Manager) {
managersMU.Lock()
defer managersMU.Unlock()
if mgr == nil {
log.Error("Register manager is nil")
}
if _, dup := managers[name]; dup {
log.Errorf("Register called twice for manager " + name)
}
managers[name] = mgr
}
// GetManager get the configure manager by name
func GetManager(name string) (Manager, error) {
mgr, ok := managers[name]
if !ok {
return nil, errors.New("config manager is not registered: " + name)
}
return mgr, nil
}

View File

@ -16,17 +16,32 @@ package config
import (
"context"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/lib/config/metadata"
)
type cfgMgrKey struct{}
// Manager defines the operation for config
type Manager interface {
Load(ctx context.Context) error
Set(ctx context.Context, key string, value interface{})
Save(ctx context.Context) error
Get(ctx context.Context, key string) *metadata.ConfigureValue
UpdateConfig(ctx context.Context, cfgs map[string]interface{}) error
GetUserCfgs(ctx context.Context) map[string]interface{}
ValidateCfg(ctx context.Context, cfgs map[string]interface{}) error
GetAll(ctx context.Context) map[string]interface{}
GetDatabaseCfg() *models.Database
}
// FromContext returns CfgManager from context
func FromContext(ctx context.Context) (*CfgManager, bool) {
m, ok := ctx.Value(cfgMgrKey{}).(*CfgManager)
func FromContext(ctx context.Context) (Manager, bool) {
m, ok := ctx.Value(cfgMgrKey{}).(Manager)
return m, ok
}
// NewContext returns context with CfgManager
func NewContext(ctx context.Context, m *CfgManager) context.Context {
func NewContext(ctx context.Context, m Manager) context.Context {
return context.WithValue(ctx, cfgMgrKey{}, m)
}

Some files were not shown because too many files have changed in this diff Show More