mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
configuration api can handle scan all policy
This commit is contained in:
parent
159d549dbd
commit
af0e8c85bb
@ -20,9 +20,9 @@ const (
|
||||
LDAPAuth = "ldap_auth"
|
||||
ProCrtRestrEveryone = "everyone"
|
||||
ProCrtRestrAdmOnly = "adminonly"
|
||||
LDAPScopeBase = "1"
|
||||
LDAPScopeOnelevel = "2"
|
||||
LDAPScopeSubtree = "3"
|
||||
LDAPScopeBase = 1
|
||||
LDAPScopeOnelevel = 2
|
||||
LDAPScopeSubtree = 3
|
||||
|
||||
RoleProjectAdmin = 1
|
||||
RoleDeveloper = 2
|
||||
@ -65,4 +65,5 @@ const (
|
||||
AdmiralEndpoint = "admiral_url"
|
||||
WithNotary = "with_notary"
|
||||
WithClair = "with_clair"
|
||||
ScanAllPolicy = "scan_all_policy"
|
||||
)
|
||||
|
@ -96,3 +96,28 @@ type VulnerabilityItem struct {
|
||||
Description string `json:"description"`
|
||||
Fixed string `json:"fixedVersion,omitempty"`
|
||||
}
|
||||
|
||||
// ScanAllPolicy is represent the json request and object for scan all policy, the parm is het
|
||||
type ScanAllPolicy struct {
|
||||
Type string `json:"type"`
|
||||
Parm map[string]interface{} `json:"parameter, omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ScanAllNone "none" for not doing any scan all
|
||||
ScanAllNone = "none"
|
||||
// ScanAllDaily for doing scan all daily
|
||||
ScanAllDaily = "daily"
|
||||
// ScanAllOnRefresh for doing scan all when the Clair DB is refreshed.
|
||||
ScanAllOnRefresh = "on_refresh"
|
||||
// ScanAllDailyTime the key for parm of daily scan all policy.
|
||||
ScanAllDailyTime = "daily_time"
|
||||
)
|
||||
|
||||
//DefaultScanAllPolicy ...
|
||||
var DefaultScanAllPolicy = ScanAllPolicy{
|
||||
Type: ScanAllDaily,
|
||||
Parm: map[string]interface{}{
|
||||
ScanAllDailyTime: 0,
|
||||
},
|
||||
}
|
||||
|
@ -17,10 +17,11 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"reflect"
|
||||
|
||||
"github.com/vmware/harbor/src/common"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/config"
|
||||
)
|
||||
@ -64,6 +65,33 @@ var (
|
||||
common.AdminInitialPassword,
|
||||
}
|
||||
|
||||
stringKeys = []string{
|
||||
common.ExtEndpoint,
|
||||
common.AUTHMode,
|
||||
common.DatabaseType,
|
||||
common.MySQLHost,
|
||||
common.MySQLUsername,
|
||||
common.MySQLPassword,
|
||||
common.MySQLDatabase,
|
||||
common.SQLiteFile,
|
||||
common.LDAPURL,
|
||||
common.LDAPSearchDN,
|
||||
common.LDAPSearchPwd,
|
||||
common.LDAPBaseDN,
|
||||
common.LDAPUID,
|
||||
common.LDAPFilter,
|
||||
common.TokenServiceURL,
|
||||
common.RegistryURL,
|
||||
common.EmailHost,
|
||||
common.EmailUsername,
|
||||
common.EmailPassword,
|
||||
common.EmailFrom,
|
||||
common.EmailIdentity,
|
||||
common.ProjectCreationRestriction,
|
||||
common.JobLogDir,
|
||||
common.AdminInitialPassword,
|
||||
}
|
||||
|
||||
numKeys = []string{
|
||||
common.EmailPort,
|
||||
common.LDAPScope,
|
||||
@ -131,10 +159,10 @@ func (c *ConfigAPI) Get() {
|
||||
|
||||
// Put updates configurations
|
||||
func (c *ConfigAPI) Put() {
|
||||
m := map[string]string{}
|
||||
m := map[string]interface{}{}
|
||||
c.DecodeJSONReq(&m)
|
||||
|
||||
cfg := map[string]string{}
|
||||
cfg := map[string]interface{}{}
|
||||
for _, k := range validKeys {
|
||||
if v, ok := m[k]; ok {
|
||||
cfg[k] = v
|
||||
@ -152,35 +180,7 @@ func (c *ConfigAPI) Put() {
|
||||
c.CustomAbort(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
if value, ok := cfg[common.AUTHMode]; ok {
|
||||
mode, err := config.AuthMode()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get auth mode: %v", err)
|
||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if mode != value {
|
||||
flag, err := authModeCanBeModified()
|
||||
if err != nil {
|
||||
log.Errorf("failed to determine whether auth mode can be modified: %v", err)
|
||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if !flag {
|
||||
c.CustomAbort(http.StatusBadRequest,
|
||||
fmt.Sprintf("%s can not be modified as new users have been inserted into database",
|
||||
common.AUTHMode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, err := convertForPut(cfg)
|
||||
if err != nil {
|
||||
log.Errorf("failed to convert configurations: %v", err)
|
||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
if err := config.Upload(result); err != nil {
|
||||
if err := config.Upload(cfg); err != nil {
|
||||
log.Errorf("failed to upload configurations: %v", err)
|
||||
c.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
@ -199,18 +199,53 @@ func (c *ConfigAPI) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func validateCfg(c map[string]string) (bool, error) {
|
||||
isSysErr := false
|
||||
func validateCfg(c map[string]interface{}) (bool, error) {
|
||||
strMap := map[string]string{}
|
||||
for _, k := range stringKeys {
|
||||
if _, ok := c[k]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := c[k].(string); !ok {
|
||||
return false, fmt.Errorf("Invalid value type, expected string, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
||||
}
|
||||
strMap[k] = c[k].(string)
|
||||
}
|
||||
numMap := map[string]int{}
|
||||
for _, k := range numKeys {
|
||||
if _, ok := c[k]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := c[k].(float64); !ok {
|
||||
return false, fmt.Errorf("Invalid value type, expected float64, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
||||
}
|
||||
numMap[k] = int(c[k].(float64))
|
||||
}
|
||||
boolMap := map[string]bool{}
|
||||
for _, k := range boolKeys {
|
||||
if _, ok := c[k]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := c[k].(bool); !ok {
|
||||
return false, fmt.Errorf("Invalid value type, expected bool, key: %s, value: %v, type: %v", k, c[k], reflect.TypeOf(c[k]))
|
||||
}
|
||||
boolMap[k] = c[k].(bool)
|
||||
}
|
||||
|
||||
mode, err := config.AuthMode()
|
||||
if err != nil {
|
||||
isSysErr = true
|
||||
return isSysErr, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
if value, ok := c[common.AUTHMode]; ok {
|
||||
if value, ok := strMap[common.AUTHMode]; ok {
|
||||
if value != common.DBAuth && value != common.LDAPAuth {
|
||||
return isSysErr, fmt.Errorf("invalid %s, shoud be %s or %s", common.AUTHMode, common.DBAuth, common.LDAPAuth)
|
||||
return false, fmt.Errorf("invalid %s, shoud be %s or %s", common.AUTHMode, common.DBAuth, common.LDAPAuth)
|
||||
}
|
||||
flag, err := authModeCanBeModified()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
if mode != value && !flag {
|
||||
return false, fmt.Errorf("%s can not be modified as new users have been inserted into database", common.AUTHMode)
|
||||
}
|
||||
mode = value
|
||||
}
|
||||
@ -218,123 +253,70 @@ func validateCfg(c map[string]string) (bool, error) {
|
||||
if mode == common.LDAPAuth {
|
||||
ldap, err := config.LDAP()
|
||||
if err != nil {
|
||||
isSysErr = true
|
||||
return isSysErr, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
if len(ldap.URL) == 0 {
|
||||
if _, ok := c[common.LDAPURL]; !ok {
|
||||
return isSysErr, fmt.Errorf("%s is missing", common.LDAPURL)
|
||||
if _, ok := strMap[common.LDAPURL]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPURL)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ldap.BaseDN) == 0 {
|
||||
if _, ok := c[common.LDAPBaseDN]; !ok {
|
||||
return isSysErr, fmt.Errorf("%s is missing", common.LDAPBaseDN)
|
||||
if _, ok := strMap[common.LDAPBaseDN]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPBaseDN)
|
||||
}
|
||||
}
|
||||
if len(ldap.UID) == 0 {
|
||||
if _, ok := c[common.LDAPUID]; !ok {
|
||||
return isSysErr, fmt.Errorf("%s is missing", common.LDAPUID)
|
||||
if _, ok := strMap[common.LDAPUID]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPUID)
|
||||
}
|
||||
}
|
||||
if ldap.Scope == 0 {
|
||||
if _, ok := c[common.LDAPScope]; !ok {
|
||||
return isSysErr, fmt.Errorf("%s is missing", common.LDAPScope)
|
||||
if _, ok := numMap[common.LDAPScope]; !ok {
|
||||
return false, fmt.Errorf("%s is missing", common.LDAPScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ldapURL, ok := c[common.LDAPURL]; ok && len(ldapURL) == 0 {
|
||||
return isSysErr, fmt.Errorf("%s is empty", common.LDAPURL)
|
||||
if ldapURL, ok := strMap[common.LDAPURL]; ok && len(ldapURL) == 0 {
|
||||
return false, fmt.Errorf("%s is empty", common.LDAPURL)
|
||||
}
|
||||
if baseDN, ok := c[common.LDAPBaseDN]; ok && len(baseDN) == 0 {
|
||||
return isSysErr, fmt.Errorf("%s is empty", common.LDAPBaseDN)
|
||||
if baseDN, ok := strMap[common.LDAPBaseDN]; ok && len(baseDN) == 0 {
|
||||
return false, fmt.Errorf("%s is empty", common.LDAPBaseDN)
|
||||
}
|
||||
if uID, ok := c[common.LDAPUID]; ok && len(uID) == 0 {
|
||||
return isSysErr, fmt.Errorf("%s is empty", common.LDAPUID)
|
||||
if uID, ok := strMap[common.LDAPUID]; ok && len(uID) == 0 {
|
||||
return false, fmt.Errorf("%s is empty", common.LDAPUID)
|
||||
}
|
||||
if scope, ok := c[common.LDAPScope]; ok &&
|
||||
if scope, ok := numMap[common.LDAPScope]; ok &&
|
||||
scope != common.LDAPScopeBase &&
|
||||
scope != common.LDAPScopeOnelevel &&
|
||||
scope != common.LDAPScopeSubtree {
|
||||
return isSysErr, fmt.Errorf("invalid %s, should be %s, %s or %s",
|
||||
return false, fmt.Errorf("invalid %s, should be %s, %s or %s",
|
||||
common.LDAPScope,
|
||||
common.LDAPScopeBase,
|
||||
common.LDAPScopeOnelevel,
|
||||
common.LDAPScopeSubtree)
|
||||
}
|
||||
|
||||
for _, k := range boolKeys {
|
||||
v, ok := c[k]
|
||||
if !ok {
|
||||
continue
|
||||
for k, n := range numMap {
|
||||
if n < 0 {
|
||||
return false, fmt.Errorf("invalid %s: %d", k, n)
|
||||
}
|
||||
|
||||
if v != "0" && v != "1" {
|
||||
return isSysErr, fmt.Errorf("%s should be %s or %s",
|
||||
k, "0", "1")
|
||||
}
|
||||
}
|
||||
|
||||
for _, k := range numKeys {
|
||||
v, ok := c[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil || n < 0 {
|
||||
return isSysErr, fmt.Errorf("invalid %s: %s", k, v)
|
||||
}
|
||||
|
||||
if (k == common.EmailPort ||
|
||||
k == common.MySQLPort) && n > 65535 {
|
||||
return isSysErr, fmt.Errorf("invalid %s: %s", k, v)
|
||||
return false, fmt.Errorf("invalid %s: %d", k, n)
|
||||
}
|
||||
}
|
||||
|
||||
if crt, ok := c[common.ProjectCreationRestriction]; ok &&
|
||||
if crt, ok := strMap[common.ProjectCreationRestriction]; ok &&
|
||||
crt != common.ProCrtRestrEveryone &&
|
||||
crt != common.ProCrtRestrAdmOnly {
|
||||
return isSysErr, fmt.Errorf("invalid %s, should be %s or %s",
|
||||
return false, fmt.Errorf("invalid %s, should be %s or %s",
|
||||
common.ProjectCreationRestriction,
|
||||
common.ProCrtRestrAdmOnly,
|
||||
common.ProCrtRestrEveryone)
|
||||
}
|
||||
|
||||
return isSysErr, nil
|
||||
}
|
||||
|
||||
//convert map[string]string to map[string]interface{}
|
||||
func convertForPut(m map[string]string) (map[string]interface{}, error) {
|
||||
cfg := map[string]interface{}{}
|
||||
|
||||
for k, v := range m {
|
||||
cfg[k] = v
|
||||
}
|
||||
|
||||
for _, k := range numKeys {
|
||||
if _, ok := cfg[k]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := strconv.Atoi(cfg[k].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg[k] = v
|
||||
}
|
||||
|
||||
for _, k := range boolKeys {
|
||||
if _, ok := cfg[k]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
cfg[k] = cfg[k] == "1"
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// delete sensitive attrs and add editable field to every attr
|
||||
@ -347,6 +329,9 @@ func convertForGet(cfg map[string]interface{}) (map[string]*value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := cfg[common.ScanAllPolicy]; !ok {
|
||||
cfg[common.ScanAllPolicy] = models.DefaultScanAllPolicy
|
||||
}
|
||||
for k, v := range cfg {
|
||||
result[k] = &value{
|
||||
Value: v,
|
||||
|
@ -60,8 +60,8 @@ func TestPutConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
apiTest := newHarborAPI()
|
||||
|
||||
cfg := map[string]string{
|
||||
common.VerifyRemoteCert: "0",
|
||||
cfg := map[string]interface{}{
|
||||
common.VerifyRemoteCert: false,
|
||||
}
|
||||
|
||||
code, err := apiTest.PutConfig(*admin, cfg)
|
||||
|
@ -1005,7 +1005,7 @@ func (a testapi) GetConfig(authInfo usrInfo) (int, map[string]*value, error) {
|
||||
return code, cfg, err
|
||||
}
|
||||
|
||||
func (a testapi) PutConfig(authInfo usrInfo, cfg map[string]string) (int, error) {
|
||||
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)
|
||||
|
@ -15,6 +15,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@ -339,7 +340,7 @@ func ClairEndpoint() string {
|
||||
func AdmiralEndpoint() string {
|
||||
cfg, err := mg.Get()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get configuration, will return empty string as admiral's endpoint")
|
||||
log.Errorf("Failed to get configuration, will return empty string as admiral's endpoint, error: %v", err)
|
||||
|
||||
return ""
|
||||
}
|
||||
@ -349,6 +350,30 @@ func AdmiralEndpoint() string {
|
||||
return cfg[common.AdmiralEndpoint].(string)
|
||||
}
|
||||
|
||||
// ScanAllPolicy returns the policy which controls the scan all.
|
||||
func ScanAllPolicy() models.ScanAllPolicy {
|
||||
var res models.ScanAllPolicy
|
||||
cfg, err := mg.Get()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get configuration, will return default scan all policy, error: %v", err)
|
||||
return models.DefaultScanAllPolicy
|
||||
}
|
||||
v, ok := cfg[common.ScanAllPolicy]
|
||||
if !ok {
|
||||
return models.DefaultScanAllPolicy
|
||||
}
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to Marshal the value in configuration for Scan All policy, error: %v, returning the default policy", err)
|
||||
return models.DefaultScanAllPolicy
|
||||
}
|
||||
if err := json.Unmarshal(b, &res); err != nil {
|
||||
log.Errorf("Failed to unmarshal the value in configuration for Scan All policy, error: %v, returning the default policy", err)
|
||||
return models.DefaultScanAllPolicy
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// WithAdmiral returns a bool to indicate if Harbor's deployed with admiral.
|
||||
func WithAdmiral() bool {
|
||||
return len(AdmiralEndpoint()) > 0
|
||||
|
@ -155,4 +155,9 @@ func TestConfig(t *testing.T) {
|
||||
if mode != "db_auth" {
|
||||
t.Errorf("unexpected mode: %s != %s", mode, "db_auth")
|
||||
}
|
||||
|
||||
if s := ScanAllPolicy(); s.Type != "daily" {
|
||||
t.Errorf("unexpected scan all policy %v", s)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user