Merge pull request #7235 from ywk253100/190327_sync

Sync with master branch
This commit is contained in:
Wenkai Yin 2019-03-27 12:04:33 +08:00 committed by GitHub
commit a49c0dd18c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 353 additions and 344 deletions

View File

@ -1,4 +1,4 @@
FROM node:10.7.0 as nodeportal
FROM node:10.15.0 as nodeportal
RUN mkdir -p /portal_src
RUN mkdir -p /build_dir

View File

@ -4,7 +4,7 @@ verify_ssl = true
name = "pypi"
[packages]
pyyaml = "*"
pyyaml = "==4.2b1"
click = "*"
"jinja2" = "*"

View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "8950f4066b83c5eb792e0f828de1530b2a61d19e45531660adfc8e06a02f2e71"
"sha256": "0fd59e52429cbbc5e345259707a98d7c071a0a59ca709882d8b106321fb2191e"
},
"pipfile-spec": 6,
"requires": {
@ -34,70 +34,59 @@
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
],
"version": "==1.1.0"
"version": "==1.1.1"
},
"pyyaml": {
"hashes": [
"sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b",
"sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf",
"sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a",
"sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3",
"sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1",
"sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1",
"sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613",
"sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04",
"sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f",
"sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537",
"sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"
"sha256:ef3a0d5a5e950747f4a39ed7b204e036b37f9bddc7551c1a813b8727515a832e"
],
"index": "pypi",
"version": "==3.13"
"version": "==4.2b1"
}
},
"develop": {
"astroid": {
"hashes": [
"sha256:35b032003d6a863f5dcd7ec11abd5cd5893428beaa31ab164982403bcb311f22",
"sha256:6a5d668d7dc69110de01cdf7aeec69a679ef486862a0850cc0fd5571505b6b7e"
"sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4",
"sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4"
],
"version": "==2.1.0"
"version": "==2.2.5"
},
"isort": {
"hashes": [
"sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
"sha256:18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b",
"sha256:96151fca2c6e736503981896495d344781b60d18bfda78dc11b290c6125ebdb6"
],
"version": "==4.3.4"
"version": "==4.3.15"
},
"lazy-object-proxy": {
"hashes": [
@ -142,11 +131,11 @@
},
"pylint": {
"hashes": [
"sha256:689de29ae747642ab230c6d37be2b969bf75663176658851f456619aacf27492",
"sha256:771467c434d0d9f081741fec1d64dfb011ed26e65e12a28fe06ca2f61c4d556c"
"sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09",
"sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1"
],
"index": "pypi",
"version": "==2.2.2"
"version": "==2.3.1"
},
"six": {
"hashes": [
@ -157,36 +146,34 @@
},
"typed-ast": {
"hashes": [
"sha256:0555eca1671ebe09eb5f2176723826f6f44cca5060502fea259de9b0e893ab53",
"sha256:0ca96128ea66163aea13911c9b4b661cb345eb729a20be15c034271360fc7474",
"sha256:16ccd06d614cf81b96de42a37679af12526ea25a208bce3da2d9226f44563868",
"sha256:1e21ae7b49a3f744958ffad1737dfbdb43e1137503ccc59f4e32c4ac33b0bd1c",
"sha256:37670c6fd857b5eb68aa5d193e14098354783b5138de482afa401cc2644f5a7f",
"sha256:46d84c8e3806619ece595aaf4f37743083f9454c9ea68a517f1daa05126daf1d",
"sha256:5b972bbb3819ece283a67358103cc6671da3646397b06e7acea558444daf54b2",
"sha256:6306ffa64922a7b58ee2e8d6f207813460ca5a90213b4a400c2e730375049246",
"sha256:6cb25dc95078931ecbd6cbcc4178d1b8ae8f2b513ae9c3bd0b7f81c2191db4c6",
"sha256:7e19d439fee23620dea6468d85bfe529b873dace39b7e5b0c82c7099681f8a22",
"sha256:7f5cd83af6b3ca9757e1127d852f497d11c7b09b4716c355acfbebf783d028da",
"sha256:81e885a713e06faeef37223a5b1167615db87f947ecc73f815b9d1bbd6b585be",
"sha256:94af325c9fe354019a29f9016277c547ad5d8a2d98a02806f27a7436b2da6735",
"sha256:b1e5445c6075f509d5764b84ce641a1535748801253b97f3b7ea9d948a22853a",
"sha256:cb061a959fec9a514d243831c514b51ccb940b58a5ce572a4e209810f2507dcf",
"sha256:cc8d0b703d573cbabe0d51c9d68ab68df42a81409e4ed6af45a04a95484b96a5",
"sha256:da0afa955865920edb146926455ec49da20965389982f91e926389666f5cf86a",
"sha256:dc76738331d61818ce0b90647aedde17bbba3d3f9e969d83c1d9087b4f978862",
"sha256:e7ec9a1445d27dbd0446568035f7106fa899a36f55e52ade28020f7b3845180d",
"sha256:f741ba03feb480061ab91a465d1a3ed2d40b52822ada5b4017770dfcb88f839f",
"sha256:fe800a58547dd424cd286b7270b967b5b3316b993d86453ede184a17b5a6b17d"
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
],
"markers": "python_version < '3.7' and implementation_name == 'cpython'",
"version": "==1.1.1"
"markers": "implementation_name == 'cpython'",
"version": "==1.3.1"
},
"wrapt": {
"hashes": [
"sha256:e03f19f64d81d0a3099518ca26b04550026f131eced2e76ced7b85c6b8d32128"
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
],
"version": "==1.11.0"
"version": "==1.11.1"
}
}
}

View File

@ -45,6 +45,7 @@ const (
EmailGroup = "email"
UAAGroup = "uaa"
HTTPAuthGroup = "http_auth"
OIDCGroup = "oidc"
DatabaseGroup = "database"
// Put all config items do not belong a existing group into basic
BasicGroup = "basic"
@ -130,9 +131,16 @@ var (
{Name: "uaa_endpoint", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false},
{Name: "uaa_verify_cert", Scope: UserScope, Group: UAAGroup, EnvKey: "UAA_VERIFY_CERT", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: common.HTTPAuthProxyEndpoint, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_ENDPOINT", DefaultValue: "", ItemType: &StringType{}, Editable: false},
{Name: common.HTTPAuthProxySkipCertVerify, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_SKIP_CERT_VERIFY", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: common.HTTPAuthProxyAlwaysOnboard, Scope: UserScope, Group: HTTPAuthGroup, EnvKey: "HTTP_AUTHPROXY_ALWAYS_ONBOARD", DefaultValue: "false", ItemType: &BoolType{}, Editable: false},
{Name: common.HTTPAuthProxyEndpoint, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},
{Name: common.HTTPAuthProxySkipCertVerify, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "false", ItemType: &BoolType{}},
{Name: common.HTTPAuthProxyAlwaysOnboard, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "false", ItemType: &BoolType{}},
{Name: common.OIDCName, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
{Name: common.OIDCEndpoint, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
{Name: common.OIDCCLientID, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
{Name: common.OIDCClientSecret, Scope: UserScope, Group: OIDCGroup, ItemType: &PasswordType{}},
{Name: common.OIDCScope, Scope: UserScope, Group: OIDCGroup, ItemType: &StringType{}},
{Name: common.OIDCSkipCertVerify, Scope: UserScope, Group: OIDCGroup, DefaultValue: "false", ItemType: &BoolType{}},
{Name: "with_chartmuseum", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CHARTMUSEUM", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},
{Name: "with_clair", Scope: SystemScope, Group: BasicGroup, EnvKey: "WITH_CLAIR", DefaultValue: "false", ItemType: &BoolType{}, Editable: true},

View File

@ -20,6 +20,7 @@ const (
LDAPAuth = "ldap_auth"
UAAAuth = "uaa_auth"
HTTPAuth = "http_auth"
OIDCAuth = "oidc_auth"
ProCrtRestrEveryone = "everyone"
ProCrtRestrAdmOnly = "adminonly"
LDAPScopeBase = 0
@ -98,6 +99,12 @@ const (
HTTPAuthProxyEndpoint = "http_authproxy_endpoint"
HTTPAuthProxySkipCertVerify = "http_authproxy_skip_cert_verify"
HTTPAuthProxyAlwaysOnboard = "http_authproxy_always_onboard"
OIDCName = "oidc_name"
OIDCEndpoint = "oidc_endpoint"
OIDCCLientID = "oidc_client_id"
OIDCClientSecret = "oidc_client_secret"
OIDCSkipCertVerify = "oidc_skip_cert_verify"
OIDCScope = "oidc_scope"
DefaultClairEndpoint = "http://clair:6060"
CfgDriverDB = "db"
@ -124,4 +131,6 @@ const (
RobotPrefix = "robot$"
CoreConfigPath = "/api/internal/configurations"
RobotTokenDuration = "robot_token_duration"
OIDCCallbackPath = "/c/oidc_callback"
)

View File

@ -72,6 +72,17 @@ type HTTPAuthProxy struct {
AlwaysOnBoard bool `json:"always_onboard"`
}
// OIDCSetting wraps the settings for OIDC auth endpoint
type OIDCSetting struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
SkipCertVerify bool `json:"skip_cert_verify"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURL string `json:"redirect_url"`
Scope []string `json:"scope"`
}
// ConfigEntry ...
type ConfigEntry struct {
ID int64 `orm:"pk;auto;column(id)" json:"-"`

View File

@ -477,3 +477,27 @@ func HTTPAuthProxySetting() (*models.HTTPAuthProxy, error) {
}, 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 := []string{}
for _, s := range strings.Split(scopeStr, ",") {
scope = append(scope, strings.TrimSpace(s))
}
return &models.OIDCSetting{
Name: cfgMgr.Get(common.OIDCName).GetString(),
Endpoint: cfgMgr.Get(common.OIDCEndpoint).GetString(),
SkipCertVerify: cfgMgr.Get(common.OIDCSkipCertVerify).GetBool(),
ClientID: cfgMgr.Get(common.OIDCCLientID).GetString(),
ClientSecret: cfgMgr.Get(common.OIDCClientSecret).GetString(),
RedirectURL: extEndpoint + common.OIDCCallbackPath,
Scope: scope,
}, nil
}

View File

@ -241,3 +241,25 @@ func TestHTTPAuthProxySetting(t *testing.T) {
SkipCertVerify: true,
})
}
func TestOIDCSetting(t *testing.T) {
m := map[string]interface{}{
common.OIDCName: "test",
common.OIDCEndpoint: "https://oidc.test",
common.OIDCSkipCertVerify: "true",
common.OIDCScope: "openid, profile",
common.OIDCCLientID: "client",
common.OIDCClientSecret: "secret",
common.ExtEndpoint: "https://harbor.test",
}
InitWithSettings(m)
v, e := OIDCSetting()
assert.Nil(t, e)
assert.Equal(t, "test", v.Name)
assert.Equal(t, "https://oidc.test", v.Endpoint)
assert.True(t, v.SkipCertVerify)
assert.Equal(t, "client", v.ClientID)
assert.Equal(t, "secret", v.ClientSecret)
assert.Equal(t, "https://harbor.test/c/oidc_callback", v.RedirectURL)
assert.ElementsMatch(t, []string{"openid", "profile"}, v.Scope)
}

View File

@ -54,8 +54,8 @@ export class GcComponent implements OnInit {
}
public initSchedule(schedule: any) {
if (schedule && schedule.length > 0) {
this.schedule = schedule[0];
if (schedule && schedule.schedule !== null) {
this.schedule = schedule;
this.originCron = this.schedule.schedule;
} else {
this.originCron = {

View File

@ -10,7 +10,7 @@
<clr-tab>
<button id="config-vulnerability" clrTabLink>{{'CONFIG.VULNERABILITY' | translate}}</button>
<clr-tab-content id="vulnerability" *clrIfActive>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true"></vulnerability-config>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [showSubTitle]="true"></vulnerability-config>
</clr-tab-content>
</clr-tab>
<clr-tab>

View File

@ -0,0 +1,42 @@
import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { throwError as observableThrowError, Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { SERVICE_CONFIG, IServiceConfig } from "../../service.config";
export abstract class ScanApiRepository {
abstract postSchedule(param): Observable<any>;
abstract putSchedule(param): Observable<any>;
abstract getSchedule(): Observable<any>;
}
@Injectable()
export class ScanApiDefaultRepository extends ScanApiRepository {
constructor(
private http: Http,
@Inject(SERVICE_CONFIG) private config: IServiceConfig
) {
super();
}
public postSchedule(param): Observable<any> {
return this.http.post(`${this.config.ScanAllEndpoint}/schedule`, param)
.pipe(catchError(error => observableThrowError(error)));
}
public putSchedule(param): Observable<any> {
return this.http.put(`${this.config.ScanAllEndpoint}/schedule`, param)
.pipe(catchError(error => observableThrowError(error)));
}
public getSchedule(): Observable<any> {
return this.http.get(`${this.config.ScanAllEndpoint}/schedule`)
.pipe(catchError(error => observableThrowError(error)))
.pipe(map(response => response.json()));
}
}

View File

@ -0,0 +1,50 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs';
import { ScanApiRepository } from './scanAll.api.repository';
import { ErrorHandler } from '../../error-handler/index';
@Injectable()
export class ScanAllRepoService {
constructor(private http: Http,
private scanApiRepository: ScanApiRepository,
private errorHandler: ErrorHandler) {
}
public manualScan(): Observable <any> {
let param = {
"schedule": {
"type": "Manual"
}
};
return this.scanApiRepository.postSchedule(param);
}
public getSchedule(): Observable <any> {
return this.scanApiRepository.getSchedule();
}
public postSchedule(type, cron): Observable <any> {
let param = {
"schedule": {
"type": type,
"cron": cron,
}
};
return this.scanApiRepository.postSchedule(param);
}
public putSchedule(type, cron): Observable <any> {
let param = {
"schedule": {
"type": type,
"cron": cron,
}
};
return this.scanApiRepository.putSchedule(param);
}
}

View File

@ -23,30 +23,7 @@
</clr-dropdown>
<span *ngIf="isClairDBFullyReady && !showScanningNamespaces">{{ updatedTimestamp | date:'MM/dd/y HH:mm:ss' }} AM</span>
</div>
<div class="form-group vertical-center normal-wrapper" *ngIf="!isEditMode">
<span>{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</span>
<span>{{ (scanningType ? 'SCHEDULE.'+ scanningType.toUpperCase(): "") | translate }}</span>
<span [hidden]="scanningType===SCHEDULE_TYPE.NONE">{{'SCHEDULE.AT' | translate}}</span>
<span [hidden]="scanningType===SCHEDULE_TYPE.NONE">{{ dailyTime | translate }} AM</span>
<button class="btn btn-outline btn-sm" (click)="editSchedule()" id="editSchedule">{{'BUTTON.EDIT' | translate}}</button>
</div>
<div class="form-group vertical-center" *ngIf="isEditMode">
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
<div class="select">
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="scanningType">
<option value="none">{{ 'SCHEDULE.NONE' | translate }}</option>
<option value="daily">{{ 'SCHEDULE.DAILY' | translate }}</option>
</select>
</div>
<span [hidden]="!showTimePicker">{{'SCHEDULE.AT' | translate}}</span>
<input type="time" name="dailyTimePicker" required [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.SCANNING_POLICY' | translate}}</span>
</a>
<button id="config_vulnerbility_save" class="btn btn-primary btn-sm" (click)="save()">{{'BUTTON.SAVE' | translate}}</button>
<button class="btn btn-primary btn-sm" (click)="cancel()" >{{'BUTTON.CANCEL' | translate}}</button>
</div>
<cron-selection [labelCurrent]="getLabelCurrent" [labelEdit]='getLabelCurrent' [originCron]='originCron' (inputvalue)="scanAll($event)"></cron-selection>
<div class="btn-scan-right btn-scan">
<button class="btn btn-primary btn-sm btn-scan" (click)="scanNow()" [disabled]="!scanAvailable">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</button><br>
</div>

View File

@ -13,12 +13,12 @@ import { ErrorHandler } from '../../error-handler/index';
import { isEmptyObject, clone} from '../../utils';
import { TranslateService } from '@ngx-translate/core';
import { ClairDetail } from '../../service/interface';
import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../service/interface';
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component";
const ONE_HOUR_SECONDS: number = 3600;
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
const SCHEDULE_TYPE = {
NONE: "none",
DAILY: "daily"
};
const SCHEDULE_TYPE_NONE = "None";
@Component({
selector: 'vulnerability-config',
templateUrl: './vulnerability-config.component.html',
@ -26,39 +26,25 @@ const SCHEDULE_TYPE = {
})
export class VulnerabilityConfigComponent implements OnInit {
_localTime: Date = new Date();
isEditMode: boolean = false;
SCHEDULE_TYPE = SCHEDULE_TYPE;
configCopy: Configuration;
originCron: OriginCron;
schedule: any;
onSubmitting: boolean = false;
config: Configuration;
openState: boolean = false;
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
getLabelCurrent: string;
@ViewChild(CronScheduleComponent)
CronScheduleComponent: CronScheduleComponent;
@Input()
get vulnerabilityConfig(): Configuration {
return this.config;
}
set vulnerabilityConfig(cfg: Configuration) {
this.config = cfg;
if (this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
if (this.config.scan_all_policy.value.type === "daily") {
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
}
}
this.configChange.emit(this.config);
}
@Input() showSubTitle: boolean = false;
@Input() showScanningNamespaces: boolean = false;
systemInfo: SystemInfo;
constructor(
private scanningService: ScanningResultService,
// private scanningService: ScanningResultService,
private scanningService: ScanAllRepoService,
private errorHandler: ErrorHandler,
private translate: TranslateService,
private systemInfoService: SystemInfoService,
@ -69,6 +55,12 @@ export class VulnerabilityConfigComponent implements OnInit {
return !this.onSubmitting;
}
getScanText() {
this.translate.get('CONFIG.SCANNING.SCAN_ALL').subscribe((res: string) => {
this.getLabelCurrent = res;
});
}
get updatedTimestamp(): Date {
if (this.systemInfo &&
this.systemInfo.clair_vulnerability_status &&
@ -90,146 +82,29 @@ export class VulnerabilityConfigComponent implements OnInit {
return [];
}
// UTC time
get dailyTime(): string {
if (!(this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value &&
this.config.scan_all_policy.value.type === "daily")) {
return "00:00";
}
getSchedule() {
this.scanningService.getSchedule().subscribe(schedule => {
this.initSchedule(schedule);
});
}
let timeOffset: number = 0; // seconds
if (this.config.scan_all_policy.value.parameter) {
let daily_time = this.config.scan_all_policy.value.parameter.daily_time;
if (daily_time && typeof daily_time === "number") {
timeOffset = +daily_time;
}
}
// Convert to current time
let timezoneOffset: number = this._localTime.getTimezoneOffset();
// Local time
timeOffset = timeOffset - timezoneOffset * 60;
if (timeOffset < 0) {
timeOffset = timeOffset + ONE_DAY_SECONDS;
}
if (timeOffset >= ONE_DAY_SECONDS) {
timeOffset -= ONE_DAY_SECONDS;
}
// To time string
let hours: number = Math.floor(timeOffset / ONE_HOUR_SECONDS);
let minutes: number = Math.floor((timeOffset - hours * ONE_HOUR_SECONDS) / 60);
let timeStr: string = "" + hours;
if (hours < 10) {
timeStr = "0" + timeStr;
}
if (minutes < 10) {
timeStr += ":0";
public initSchedule(schedule: any) {
if (schedule && schedule.schedule !== null) {
this.schedule = schedule;
this.originCron = this.schedule.schedule;
} else {
timeStr += ":";
this.originCron = {
type: SCHEDULE_TYPE_NONE,
cron: ''
};
}
timeStr += minutes;
return timeStr;
}
set dailyTime(v: string) {
if (!v || v === "") {
return;
}
if (!(this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value &&
this.config.scan_all_policy.value.type === "daily")) {
return;
}
// Double confirm inner parameter existing.
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
let values: string[] = v.split(":");
if (!values || values.length !== 2) {
return;
}
let hours: number = +values[0];
let minutes: number = +values[1];
// Convert to UTC time
let timezoneOffset: number = this._localTime.getTimezoneOffset();
let utcTimes: number = hours * ONE_HOUR_SECONDS + minutes * 60;
utcTimes += timezoneOffset * 60;
if (utcTimes < 0) {
utcTimes += ONE_DAY_SECONDS;
}
if (utcTimes >= ONE_DAY_SECONDS) {
utcTimes -= ONE_DAY_SECONDS;
}
this.config.scan_all_policy.value.parameter.daily_time = utcTimes;
}
// Scanning type
get scanningType(): string {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
return this.config.scan_all_policy.value.type;
} else {
// default
return "none";
}
}
set scanningType(v: string) {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
let type: string = (v && v.trim() !== "") ? v : "none";
this.config.scan_all_policy.value.type = type;
if (type !== "daily") {
// No parameter
if (this.config.scan_all_policy.value.parameter) {
delete (this.config.scan_all_policy.value.parameter);
}
} else {
// Has parameter
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
}
}
}
}
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
get editable(): boolean {
return this.vulnerabilityConfig &&
this.vulnerabilityConfig.scan_all_policy &&
this.vulnerabilityConfig.scan_all_policy.editable;
}
get isValid(): boolean {
return this.systemSettingsForm && this.systemSettingsForm.valid;
}
get showTimePicker(): boolean {
return this.vulnerabilityConfig &&
this.vulnerabilityConfig.scan_all_policy &&
this.vulnerabilityConfig.scan_all_policy.value &&
this.vulnerabilityConfig.scan_all_policy.value.type === "daily";
}
get isClairDBFullyReady(): boolean {
return this.systemInfo &&
this.systemInfo.clair_vulnerability_status &&
@ -238,23 +113,10 @@ export class VulnerabilityConfigComponent implements OnInit {
ngOnInit(): void {
this.getSystemInfo();
this.getConfigurations();
this.getScanText();
this.getSchedule();
}
getConfigurations(): void {
this.configService.getConfigurations()
.subscribe((config: Configuration) => {
this.configCopy = clone(config);
this.config = config;
}, error => {
this.errorHandler.error(error);
});
}
editSchedule() {
this.isEditMode = true;
}
convertToLocalTime(utcTime: number): Date {
let dt: Date = new Date();
dt.setTime(utcTime * 1000);
@ -272,7 +134,7 @@ export class VulnerabilityConfigComponent implements OnInit {
}
this.onSubmitting = true;
this.scanningService.startScanningAll()
this.scanningService.manualScan()
.subscribe(() => {
this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_SUCCESS").subscribe((res: string) => {
this.errorHandler.info(res);
@ -306,38 +168,43 @@ export class VulnerabilityConfigComponent implements OnInit {
}));
}
save(): void {
let getchanges = this.config.scan_all_policy.value;
let changes = {"scan_all_policy": getchanges};
reset(cron): void {
this.schedule = {
schedule: {
type: this.CronScheduleComponent.scheduleType,
cron: cron
}
};
}
if (isEmptyObject(changes)) {
return;
}
this.configService.saveConfigurations(changes)
.subscribe(() => {
this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res: string) => {
this.errorHandler.info(res);
scanAll(cron: string): void {
let schedule = this.schedule;
if (schedule && schedule.schedule && schedule.schedule.type !== SCHEDULE_TYPE_NONE) {
this.scanningService.putSchedule(this.CronScheduleComponent.scheduleType, cron)
.subscribe(response => {
this.translate
.get("CONFIG.SAVE_SUCCESS")
.subscribe((res) => {
this.errorHandler.info(res);
});
this.getConfigurations();
this.isEditMode = false;
}, error => {
this.errorHandler.error(error);
this.reset();
});
}
cancel(): void {
this.reset();
this.isEditMode = false;
}
reset(): void {
// Reset to the values of copy
let getchanges = this.config.scan_all_policy.value;
let changes = {"scan_all_policy": getchanges};
for (let prop of Object.keys(changes)) {
this.config[prop] = clone(this.configCopy[prop]);
this.reset(cron);
},
error => {
this.errorHandler.error(error);
}
);
} else {
this.scanningService.postSchedule(this.CronScheduleComponent.scheduleType, cron)
.subscribe(response => {
this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res) => {
this.errorHandler.info(res);
});
this.reset(cron);
},
error => {
this.errorHandler.error(error);
}
);
}
}
}

View File

@ -15,14 +15,14 @@
</a>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ oriCron }}</span>
<button class="btn btn-outline btn-sm" (click)="editSchedule()">
<button class="btn btn-outline btn-sm" (click)="editSchedule()" id="editSchedule">
{{ "BUTTON.EDIT" | translate }}
</button>
</div>
<div class="setting-wrapper flex-layout" *ngIf="isEditMode">
<span class="font-style">{{ labelEdit | translate }}</span>
<div class="select select-schedule">
<select name="gcPolicy" [(ngModel)]="scheduleType">
<select name="selectPolicy" id="selectPolicy" [(ngModel)]="scheduleType">
<option [value]="SCHEDULE_TYPE.NONE">{{'SCHEDULE.NONE' | translate}}</option>
<option [value]="SCHEDULE_TYPE.HOURLY">{{'SCHEDULE.HOURLY' | translate}}</option>
<option [value]="SCHEDULE_TYPE.DAILY">{{'SCHEDULE.DAILY' | translate}}</option>
@ -31,9 +31,9 @@
</select>
</div>
<span [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span>
<div [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM" >
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="cronStringInput.invalid && (cronStringInput.dirty || cronStringInput.touched)" class="tooltip tooltip-validation tooltip-md tooltip-top-right">
<input type="text" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="form-control cron-input"
<div class="form-group" [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="cronStringInput.invalid && (cronStringInput.dirty || cronStringInput.touched)" class="tooltip tooltip-validation tooltip-md tooltip-top-right cron-label">
<input type="text" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="form-control"
[(ngModel)]="cronString">
<span class="tooltip-content">
{{'TOOLTIP.CRON_REQUIRED' | translate }}
@ -41,10 +41,10 @@
</label>
</div>
<button class="btn btn-primary btn-sm"
(click)="save()">
(click)="save()" id="config-save">
{{ "BUTTON.SAVE" | translate }}
</button>
<button class="btn btn-primary btn-sm" (click)="isEditMode= false">
<button class="btn btn-primary btn-sm" (click)="isEditMode=false">
{{ "BUTTON.CANCEL" | translate }}
</button>
</div>

View File

@ -34,8 +34,8 @@
display: inline-block;
width: 100px;
}
.cron-input {
.cron-label {
width: 195px;
}
}

View File

@ -58,8 +58,10 @@ import {
UserPermissionDefaultService
} from './service/index';
import { GcRepoService } from './config/gc/gc.service';
import { ScanAllRepoService } from './config/vulnerability/scanAll.service';
import {GcViewModelFactory} from './config/gc/gc.viewmodel.factory';
import {GcApiRepository, GcApiDefaultRepository} from './config/gc/gc.api.repository';
import {ScanApiRepository, ScanApiDefaultRepository} from './config/vulnerability/scanAll.api.repository';
import {
ErrorHandler,
DefaultErrorHandler
@ -99,7 +101,8 @@ export const DefaultServiceConfig: IServiceConfig = {
labelEndpoint: "/api/labels",
helmChartEndpoint: "/api/chartrepo",
downloadChartEndpoint: "/chartrepo",
gcEndpoint: "/api/system/gc"
gcEndpoint: "/api/system/gc",
ScanAllEndpoint: "/api/system/scanAll"
};
/**
@ -159,6 +162,9 @@ export interface HarborModuleConfig {
// Service implementation for gc
gcApiRepository?: Provider;
// Service implementation for scanAll
ScanApiRepository?: Provider;
}
/**
@ -260,6 +266,7 @@ export class HarborLibraryModule {
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
config.gcApiRepository || {provide: GcApiRepository, useClass: GcApiDefaultRepository},
config.ScanApiRepository || {provide: ScanApiRepository, useClass: ScanApiDefaultRepository},
// Do initializing
TranslateServiceInitializer,
{
@ -271,6 +278,7 @@ export class HarborLibraryModule {
ChannelService,
OperationService,
GcRepoService,
ScanAllRepoService,
GcViewModelFactory
]
};
@ -296,9 +304,11 @@ export class HarborLibraryModule {
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
config.gcApiRepository || {provide: GcApiRepository, useClass: GcApiDefaultRepository},
config.ScanApiRepository || {provide: ScanApiRepository, useClass: ScanApiDefaultRepository},
ChannelService,
OperationService,
GcRepoService,
ScanAllRepoService,
GcViewModelFactory
]
};

View File

@ -229,4 +229,6 @@ export interface IServiceConfig {
downloadChartEndpoint?: string;
gcEndpoint?: string;
ScanAllEndpoint?: string;
}

View File

@ -40,7 +40,7 @@ registerLocaleData(localeFr, 'fr-fr');
export function initConfig(configService: AppConfigService, skinableService: SkinableConfig) {
return () => {
skinableService.getCustomFile();
configService.load();
configService.load().subscribe();
};
}

View File

@ -36,7 +36,7 @@
<button id="config-vulnerability" clrTabLink>{{'CONFIG.VULNERABILITY' | translate }}</button>
<ng-template [(clrIfActive)]="vulnerabilityActive">
<clr-tab-content id="vulnerability" *ngIf="withClair">
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
<vulnerability-config></vulnerability-config>
</clr-tab-content>
</ng-template>
</clr-tab>

View File

@ -283,7 +283,7 @@ export class ChartVersionComponent implements OnInit {
ConfirmationTargets.HELM_CHART_VERSION,
ConfirmationButtons.DELETE_CANCEL
);
this.confirmationDialog.openMessage(message);
this.confirmationDialog.open(message);
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 2000);
}

View File

@ -258,7 +258,7 @@ export class HelmChartComponent implements OnInit {
ConfirmationTargets.HELM_CHART,
ConfirmationButtons.DELETE_CANCEL
);
this.confirmationDialog.openMessage(message);
this.confirmationDialog.open(message);
}
confirmDeletion(message: ConfirmationAcknowledgement) {

View File

@ -55,18 +55,17 @@ export class ConfirmationDialogComponent implements OnDestroy {
}
}
open(): void {
this.opened = true;
}
openMessage(msg: ConfirmationMessage): void {
this.dialogTitle = msg.title;
this.message = msg;
this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res);
this.translate.get(msg.message, { 'param': msg.param }).subscribe((res: string) => {
this.dialogContent = res;
});
// Open dialog
this.buttons = msg.buttons;
open(msg?: ConfirmationMessage): void {
if (msg) {
this.dialogTitle = msg.title;
this.message = msg;
this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res);
this.translate.get(msg.message, { 'param': msg.param }).subscribe((res: string) => {
this.dialogContent = res;
});
// Open dialog
this.buttons = msg.buttons;
}
this.opened = true;
}

View File

@ -74,7 +74,8 @@ const uiLibConfig: IServiceConfig = {
labelEndpoint: "/api/labels",
helmChartEndpoint: "/api/chartrepo",
downloadChartEndpoint: "/chartrepo",
gcEndpoint: "/api/system/gc"
gcEndpoint: "/api/system/gc",
ScanAllEndpoint: "/api/system/scanAll"
};
@NgModule({

View File

@ -23,7 +23,7 @@ ${config_save_button_xpath} //config//div/button[contains(.,'SAVE')]
${config_email_save_button_xpath} //*[@id='config_email_save']
${config_auth_save_button_xpath} //*[@id='config_auth_save']
${config_system_save_button_xpath} //*[@id='config_system_save']
${vulnerbility_save_button_xpath} //*[@id='config_vulnerbility_save']
${vulnerbility_save_button_xpath} //*[@id='config-save']
${configuration_xpath} //clr-vertical-nav-group-children/a[contains(.,'Configuration')]
${system_config_xpath} //*[@id='config-system']
${garbage_collection_xpath} //*[@id='config-gc']

View File

@ -7,10 +7,10 @@ Resource ../../resources/Util.robot
*** Keywords ***
Disable Scan Schedule
Click Element //vulnerability-config//button[@id='editSchedule']
Click Element //vulnerability-config//select[@id='scanAllPolicy']
Click Element //vulnerability-config//select[@id='scanAllPolicy']//option[contains(.,'None')]
Click Element //button[@id='config_vulnerbility_save']
Click Element //vulnerability-config//cron-selection//button[contains(.,'EDIT')]
Click Element //vulnerability-config//cron-selection//select[@id='selectPolicy']
Click Element //vulnerability-config//cron-selection//select[@id='selectPolicy']//option[contains(.,'None')]
Click Element //cron-selection//button[contains(.,'SAVE')]
Go To Vulnerability Config
Click Element //config//button[contains(.,'Vulnerability')]