From fe9414ac9c1f8cb3fe45b991d14b3e33fc8496b1 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 14 Jul 2023 16:29:04 -0400 Subject: [PATCH 01/18] additional login option --- html/js/app.js | 2 +- html/js/i18n.js | 1 + html/js/routes/login.js | 7 ++++++- html/login/index.html | 12 ++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/html/js/app.js b/html/js/app.js index 6defed11d..209540bbc 100644 --- a/html/js/app.js +++ b/html/js/app.js @@ -709,7 +709,7 @@ $(document).ready(function() { const redirectCookie = this.getCookie('AUTH_REDIRECT'); if ((response.headers && response.headers['content-type'] == "text/html") || (response.status == 401) || - (redirectCookie != null && redirectCookie.length > 0)) { + (response.request.responseURL.indexOf("/login/") == -1 && redirectCookie != null && redirectCookie.length > 0)) { this.deleteCookie('AUTH_REDIRECT'); this.showLogin(); return null diff --git a/html/js/i18n.js b/html/js/i18n.js index 187189d88..60e6d9fe4 100644 --- a/html/js/i18n.js +++ b/html/js/i18n.js @@ -428,6 +428,7 @@ const i18n = { loginEnabled: 'Unlocked', loginExpired: 'The login session has expired. Refresh, or wait for the page to refresh automatically, and then try again.', loginInvalid: 'The provided credentials are invalid. Please try again.', + loginOidc: 'Continue with', loginTitle: 'Login to Security Onion', logout: 'Logout', logoutFailure: 'Unable to initiate logout. Ensure server is accessible.', diff --git a/html/js/routes/login.js b/html/js/routes/login.js index 7310eda06..e4bed47dd 100644 --- a/html/js/routes/login.js +++ b/html/js/routes/login.js @@ -27,6 +27,7 @@ routes.push({ path: '*', name: 'login', component: { script: null, email: null, }, + oidc: [], totpCodeLength: 6, rules: { required: value => !!value || this.$root.i18n.required, @@ -110,6 +111,7 @@ routes.push({ path: '*', name: 'login', component: { this.form.method = flow.data.ui.nodes.find(item => item.attributes && item.attributes.name == 'method' && item.attributes.value == 'password') ? 'password' : 'totp'; this.extractWebauthnData(flow); + this.extractOidcData(flow); this.$nextTick(function () { // Wait for next Vue tick to set focus, since at the time of this function call (or even mounted() hook), this element won't be // loaded, due to v-if's that have yet to process. @@ -165,6 +167,9 @@ routes.push({ path: '*', name: 'login', component: { }, runWebauthn() { eval(this.webauthnForm.onclick); - } + }, + extractOidcData(response) { + this.oidc = response.data.ui.nodes.filter(item => item.group == "oidc" && item.type == "input" ).map(item => item.attributes.value); + }, }, }}); diff --git a/html/login/index.html b/html/login/index.html index 18643c1a9..d8b538e4f 100644 --- a/html/login/index.html +++ b/html/login/index.html @@ -56,6 +56,18 @@ +
+ + + + + fa-brands fa-microsoft + fa-brands fa-google + fa-arrow-right-to-bracket + {{ i18n.loginOidc + ' ' + provider }} + + +
From 52e87562259a1c16f908f891d3b51a3268c06c0e Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 17 Jul 2023 11:01:34 -0400 Subject: [PATCH 02/18] improve test cleanup --- licensing/license_manager.go | 20 +++++----- licensing/license_manager_test.go | 62 ++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index 215b1f397..dfc5d83c8 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -43,8 +43,6 @@ const FEAT_TIMETRACKING = "timetracking" const FEAT_FIPS = "fips" const FEAT_STIG = "stig" -const DEFAULT_PILLAR_FILENAME = "/opt/so/saltstack/local/pillar/soc/license.sls" - const PUBLIC_KEY = ` -----BEGIN PUBLIC KEY----- MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA4w/cDz7rv6QotLWR7mn9 @@ -59,6 +57,9 @@ rdA93ynlX+ihg6jL0iS4uFEV9YveqajjOyi3DYyUFCjFAgMBAAE= -----END PUBLIC KEY----- ` +var runMode = true +var pillarFilename = "/opt/so/saltstack/local/pillar/soc/license.sls" + type licenseManager struct { status string available []string @@ -68,7 +69,6 @@ type licenseManager struct { pillarTimer *time.Timer running bool licenseKey *LicenseKey - pillarFilename string } type LicenseKey struct { @@ -94,9 +94,8 @@ var manager *licenseManager func newLicenseManager() *licenseManager { return &licenseManager{ - available: make([]string, 0, 0), - limits: make(map[string]bool), - pillarFilename: DEFAULT_PILLAR_FILENAME, + available: make([]string, 0, 0), + limits: make(map[string]bool), } } @@ -194,6 +193,7 @@ func Init(key string) { } func Test(feat string, users int, nodes int, socUrl string, dataUrl string) { + runMode = false available := make([]string, 0, 0) available = append(available, feat) licenseKey := &LicenseKey{} @@ -223,13 +223,13 @@ func createManager(status string, available []string, licenseKey *LicenseKey, st manager.status = status manager.available = available manager.licenseKey = licenseKey + manager.running = runMode if (status == LICENSE_STATUS_ACTIVE || status == LICENSE_STATUS_PENDING) && startMonitors { - manager.running = true go startExpirationMonitor() go startEffectiveMonitor() - go startPillarMonitor() } + go startPillarMonitor() log.WithFields(log.Fields{ "status": manager.status, @@ -322,9 +322,9 @@ func startPillarMonitor() { } else { contents += "features: []\n" } - err := os.WriteFile(manager.pillarFilename, []byte(contents), 0644) + err := os.WriteFile(pillarFilename, []byte(contents), 0644) if err != nil { - log.WithError(err).WithField("filename", manager.pillarFilename).Error("Failed to update features") + log.WithError(err).WithField("filename", pillarFilename).Error("Failed to update features") manager.status = LICENSE_STATUS_INVALID } diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index 4c7cc3b6b..f72b0a4df 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -23,7 +23,22 @@ import ( const EXPIRED_KEY = ` H4sIAIvZnGMAA22QR4+bUBSF9/kVFlvPBExz2ZlqijHVNo6yeMaPYsOjPWMgyn8PMyNFihTpLm75zjnS/UXAOIYRzjpIbAiaohfv1Ef5FLX5rAvxRsC+yhqAsxJ9MfR/GASKDwcftnhmZhFELZy9z6yDP1MO7izw5JlmzWz3IAWirx2sSZHdJj4GD/hOUYtpzr9UHy7KtP3rYsBhusYQ4GcDW2Lz4+cb8WxhM7WLKbe8wa+uLaOgySd1inHVbkiyLQv4SmEDv2eoA/mU90bcAAb/UgCVeIK+VzmI4ES0WYI+oyYm8VBxAEZUFbKlp2ugz6t9n15kiX0uligc+es1jf3A7HldUAoctnhtxZ6f7R3+Rc5tOdqqcM5J/F0UyZJh4raOdcNTa6EEyoq+CXR0PloieilIUFlTgwa748r0chJZj2TdmAq3owZi3iFFk4vzx9mUGV41U1N3yLA/GEnCN+tdLa3uAR1zwn6MEh+EmDxefX9VulSjqi6F08hfcQLfYGNXnWxMFpwkKY3h6bJp8bs2z/zRfvCZEu7z3VDh8HRzoOMPa/51S8ho6LrBw7KZgu3qkq7KVGeHu+1Q9LZXkzJDJwaLnnwKpJAbnfkQstcyAlq0ho++q5YLDw11nES0xj+3NmfgvLhYC7rXKFGO927oPHg7oql6MNvDqxMWYxPuBkg/K+Uum/bxnGT90mwjrvZD4+yJmly1Llo+rsGZdZugo307jKf/FbdR950Vr1BHnRrlZMwsACQml/XKq8J5iV5HxgF7sub6Xueyvk7Gfo2Km1AuVZYsWNOv0FEtB4H1WJW/ayfh1S0ZZiB+f/sDb9bxLiEDAAA= ` +func teardown() { + if manager != nil { + manager.running = false + } + os.Remove(pillarFilename) +} + +func setup() func() { + pillarFilename = "/tmp/soc_test_pillar_monitor.sls" + runMode = false + return teardown +} + func TestInit_Missing(tester *testing.T) { + defer setup()() + // None Init("") assert.Equal(tester, LICENSE_STATUS_UNPROVISIONED, GetStatus()) @@ -42,6 +57,8 @@ func TestInit_Missing(tester *testing.T) { } func TestExpirationMonitor(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, LICENSE_STATUS_UNPROVISIONED, GetStatus()) manager.licenseKey.Expiration = time.Now().Add(time.Second * 1) @@ -50,6 +67,8 @@ func TestExpirationMonitor(tester *testing.T) { } func TestEffectiveMonitor_Unprovisioned(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, LICENSE_STATUS_UNPROVISIONED, GetStatus()) manager.licenseKey.Effective = time.Now().Add(time.Second * 1) @@ -58,6 +77,8 @@ func TestEffectiveMonitor_Unprovisioned(tester *testing.T) { } func TestEffectiveMonitor_Pending(tester *testing.T) { + defer setup()() + Init("") manager.status = LICENSE_STATUS_PENDING manager.licenseKey.Effective = time.Now().Add(time.Second * 1) @@ -66,6 +87,8 @@ func TestEffectiveMonitor_Pending(tester *testing.T) { } func TestEffectiveMonitor_Exceeded(tester *testing.T) { + defer setup()() + Init("") manager.status = LICENSE_STATUS_PENDING manager.limits["foo"] = true @@ -75,6 +98,8 @@ func TestEffectiveMonitor_Exceeded(tester *testing.T) { } func TestIsEnabled(tester *testing.T) { + defer setup()() + Init("") assert.False(tester, IsEnabled("something")) @@ -88,6 +113,8 @@ func TestIsEnabled(tester *testing.T) { } func TestListAvailableFeatures(tester *testing.T) { + defer setup()() + Init("") assert.Len(tester, ListAvailableFeatures(), 0) @@ -100,6 +127,8 @@ func TestListAvailableFeatures(tester *testing.T) { } func TestListEnabledFeatures(tester *testing.T) { + defer setup()() + Init("") assert.Len(tester, ListEnabledFeatures(), 3) @@ -118,6 +147,8 @@ func TestListEnabledFeatures(tester *testing.T) { } func TestGetLicenseKey(tester *testing.T) { + defer setup()() + Init(EXPIRED_KEY) key := GetLicenseKey() assert.Equal(tester, key.Users, 1) @@ -135,6 +166,8 @@ func TestGetLicenseKey(tester *testing.T) { } func TestGetStatus(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, LICENSE_STATUS_UNPROVISIONED, GetStatus()) @@ -143,6 +176,8 @@ func TestGetStatus(tester *testing.T) { } func TestGetId(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, "", GetId()) @@ -151,6 +186,8 @@ func TestGetId(tester *testing.T) { } func TestGetLicensee(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, "", GetLicensee()) @@ -159,6 +196,8 @@ func TestGetLicensee(tester *testing.T) { } func TestGetExpiration(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, time.Time{}, GetExpiration()) @@ -168,6 +207,8 @@ func TestGetExpiration(tester *testing.T) { } func TestGetName(tester *testing.T) { + defer setup()() + Init("") assert.Equal(tester, "", GetName()) @@ -176,6 +217,8 @@ func TestGetName(tester *testing.T) { } func TestValidateUserCount(tester *testing.T) { + defer setup()() + Init("") manager.licenseKey.Users = 2 assert.True(tester, ValidateUserCount(0)) @@ -185,6 +228,8 @@ func TestValidateUserCount(tester *testing.T) { } func TestValidateNodeCount(tester *testing.T) { + defer setup()() + Init("") manager.licenseKey.Nodes = 2 assert.True(tester, ValidateNodeCount(0)) @@ -194,6 +239,8 @@ func TestValidateNodeCount(tester *testing.T) { } func TestValidateSocUrl(tester *testing.T) { + defer setup()() + Init("") manager.licenseKey.SocUrl = "foo" assert.True(tester, ValidateSocUrl("Foo")) @@ -203,6 +250,8 @@ func TestValidateSocUrl(tester *testing.T) { } func TestValidateDataUrl(tester *testing.T) { + defer setup()() + Init("") manager.licenseKey.DataUrl = "foo" assert.True(tester, ValidateDataUrl("Foo")) @@ -212,14 +261,13 @@ func TestValidateDataUrl(tester *testing.T) { } func TestPillarMonitor(tester *testing.T) { - Test("stig", 0, 0, "", "") + defer setup()() - manager.pillarFilename = "/tmp/soc_test_pillar_monitor.sls" + Test("stig", 0, 0, "", "") - os.Remove(manager.pillarFilename) startPillarMonitor() assert.Equal(tester, manager.status, LICENSE_STATUS_ACTIVE) - contents, _ := os.ReadFile(manager.pillarFilename) + contents, _ := os.ReadFile(pillarFilename) expected := ` # Copyright Jason Ertel (github.com/jertel). @@ -243,9 +291,11 @@ features: } func TestPillarMonitor_Fail(tester *testing.T) { - Init("") + defer setup()() + + pillarFilename = "/tmp/does/not/exist" - manager.pillarFilename = "/tmp/does/not/exist" + Init("") assert.Equal(tester, manager.status, LICENSE_STATUS_UNPROVISIONED) startPillarMonitor() From 73b64e74c393987e094379805a3c52bea4e24f4e Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 18 Jul 2023 16:06:03 -0400 Subject: [PATCH 03/18] improve tests --- licensing/license_manager.go | 27 ++++++++++++++++++++------- licensing/license_manager_test.go | 14 ++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index dfc5d83c8..1fed59576 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -57,8 +57,10 @@ rdA93ynlX+ihg6jL0iS4uFEV9YveqajjOyi3DYyUFCjFAgMBAAE= -----END PUBLIC KEY----- ` -var runMode = true -var pillarFilename = "/opt/so/saltstack/local/pillar/soc/license.sls" +const LICENSE_PILLAR_FILENAME = "/opt/so/saltstack/local/pillar/soc/license.sls" + +var pillarFilename = LICENSE_PILLAR_FILENAME +var pillarMonitorCount = 0 type licenseManager struct { status string @@ -67,7 +69,6 @@ type licenseManager struct { expirationTimer *time.Timer effectiveTimer *time.Timer pillarTimer *time.Timer - running bool licenseKey *LicenseKey } @@ -193,7 +194,7 @@ func Init(key string) { } func Test(feat string, users int, nodes int, socUrl string, dataUrl string) { - runMode = false + pillarFilename = "/tmp/soc_test_pillar_monitor.sls" available := make([]string, 0, 0) available = append(available, feat) licenseKey := &LicenseKey{} @@ -223,12 +224,12 @@ func createManager(status string, available []string, licenseKey *LicenseKey, st manager.status = status manager.available = available manager.licenseKey = licenseKey - manager.running = runMode if (status == LICENSE_STATUS_ACTIVE || status == LICENSE_STATUS_PENDING) && startMonitors { go startExpirationMonitor() go startEffectiveMonitor() } + pillarMonitorCount = 0 go startPillarMonitor() log.WithFields(log.Fields{ @@ -327,19 +328,30 @@ func startPillarMonitor() { log.WithError(err).WithField("filename", pillarFilename).Error("Failed to update features") manager.status = LICENSE_STATUS_INVALID } + pillarMonitorCount = pillarMonitorCount + 1 - if manager.running { + if Usable() { duration := time.Duration(rand.Intn(3600000)+1) * time.Millisecond manager.pillarTimer = time.NewTimer(duration) <-manager.pillarTimer.C go startPillarMonitor() + } else { + log.WithField("pillarFilename", pillarFilename).Info("Exiting pillar monitor") + go func() { + // Leave enough time for rest of unit tests to finish + time.Sleep(5 * time.Minute) + stopMonitor() + }() } } +func Usable() bool { + return pillarFilename == LICENSE_PILLAR_FILENAME +} + func stopMonitor() { if manager != nil { - manager.running = false if manager.expirationTimer != nil { manager.expirationTimer.Stop() } @@ -347,6 +359,7 @@ func stopMonitor() { manager.effectiveTimer.Stop() } if manager.pillarTimer != nil { + pillarFilename = "" manager.pillarTimer.Stop() } diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index f72b0a4df..fd5f305b3 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -24,15 +24,11 @@ import ( const EXPIRED_KEY = ` H4sIAIvZnGMAA22QR4+bUBSF9/kVFlvPBExz2ZlqijHVNo6yeMaPYsOjPWMgyn8PMyNFihTpLm75zjnS/UXAOIYRzjpIbAiaohfv1Ef5FLX5rAvxRsC+yhqAsxJ9MfR/GASKDwcftnhmZhFELZy9z6yDP1MO7izw5JlmzWz3IAWirx2sSZHdJj4GD/hOUYtpzr9UHy7KtP3rYsBhusYQ4GcDW2Lz4+cb8WxhM7WLKbe8wa+uLaOgySd1inHVbkiyLQv4SmEDv2eoA/mU90bcAAb/UgCVeIK+VzmI4ES0WYI+oyYm8VBxAEZUFbKlp2ugz6t9n15kiX0uligc+es1jf3A7HldUAoctnhtxZ6f7R3+Rc5tOdqqcM5J/F0UyZJh4raOdcNTa6EEyoq+CXR0PloieilIUFlTgwa748r0chJZj2TdmAq3owZi3iFFk4vzx9mUGV41U1N3yLA/GEnCN+tdLa3uAR1zwn6MEh+EmDxefX9VulSjqi6F08hfcQLfYGNXnWxMFpwkKY3h6bJp8bs2z/zRfvCZEu7z3VDh8HRzoOMPa/51S8ho6LrBw7KZgu3qkq7KVGeHu+1Q9LZXkzJDJwaLnnwKpJAbnfkQstcyAlq0ho++q5YLDw11nES0xj+3NmfgvLhYC7rXKFGO927oPHg7oql6MNvDqxMWYxPuBkg/K+Uum/bxnGT90mwjrvZD4+yJmly1Llo+rsGZdZugo307jKf/FbdR950Vr1BHnRrlZMwsACQml/XKq8J5iV5HxgF7sub6Xueyvk7Gfo2Km1AuVZYsWNOv0FEtB4H1WJW/ayfh1S0ZZiB+f/sDb9bxLiEDAAA= ` func teardown() { - if manager != nil { - manager.running = false - } os.Remove(pillarFilename) } func setup() func() { pillarFilename = "/tmp/soc_test_pillar_monitor.sls" - runMode = false return teardown } @@ -260,12 +256,18 @@ func TestValidateDataUrl(tester *testing.T) { assert.False(tester, ValidateDataUrl("bar")) } +func awaitPillarMonitor() { + for pillarMonitorCount == 0 { + time.Sleep(50 * time.Millisecond) + } +} + func TestPillarMonitor(tester *testing.T) { defer setup()() Test("stig", 0, 0, "", "") - startPillarMonitor() + awaitPillarMonitor() assert.Equal(tester, manager.status, LICENSE_STATUS_ACTIVE) contents, _ := os.ReadFile(pillarFilename) @@ -298,6 +300,6 @@ func TestPillarMonitor_Fail(tester *testing.T) { Init("") assert.Equal(tester, manager.status, LICENSE_STATUS_UNPROVISIONED) - startPillarMonitor() + awaitPillarMonitor() assert.Equal(tester, manager.status, LICENSE_STATUS_INVALID) } From de171a987bbd42f77357f9e141f6d24776e8e726 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 24 Jul 2023 12:00:24 -0400 Subject: [PATCH 04/18] rename pillar for clarification --- licensing/license_manager.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index 1fed59576..861cc6420 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -301,7 +301,6 @@ func startPillarMonitor() { log.Info("Starting pillar monitor") contents := ` -# Copyright Jason Ertel (github.com/jertel). # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at # https://securityonion.net/license; you may not use this file except in compliance with @@ -316,12 +315,12 @@ func startPillarMonitor() { # This file is generated by Security Onion and contains a list of license-enabled features. ` if manager.status == LICENSE_STATUS_ACTIVE && len(manager.licenseKey.Features) > 0 { - contents += "features:\n" + contents += "licensed_features:\n" for _, feature := range manager.licenseKey.Features { contents += "- " + feature + "\n" } } else { - contents += "features: []\n" + contents += "licensed_features: []\n" } err := os.WriteFile(pillarFilename, []byte(contents), 0644) if err != nil { From 565d61247bfe5bcad7fffcbe2fdd78cbea73aacf Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 24 Jul 2023 12:16:48 -0400 Subject: [PATCH 05/18] rename pillar for clarification --- licensing/license_manager_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index fd5f305b3..9210d847b 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -272,7 +272,6 @@ func TestPillarMonitor(tester *testing.T) { contents, _ := os.ReadFile(pillarFilename) expected := ` -# Copyright Jason Ertel (github.com/jertel). # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at # https://securityonion.net/license; you may not use this file except in compliance with @@ -285,7 +284,7 @@ func TestPillarMonitor(tester *testing.T) { # software that is protected by the license key." # This file is generated by Security Onion and contains a list of license-enabled features. -features: +licensed_features: - stig ` From 869c836b6a2572cff3dc7e75be4dc792f1bffe9d Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 24 Jul 2023 16:06:52 -0400 Subject: [PATCH 06/18] use consistent accessor --- licensing/license_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index 861cc6420..efd9d9fa8 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -314,7 +314,7 @@ func startPillarMonitor() { # This file is generated by Security Onion and contains a list of license-enabled features. ` - if manager.status == LICENSE_STATUS_ACTIVE && len(manager.licenseKey.Features) > 0 { + if manager.status == LICENSE_STATUS_ACTIVE && len(ListEnabledFeatures()) > 0 { contents += "licensed_features:\n" for _, feature := range manager.licenseKey.Features { contents += "- " + feature + "\n" From c9ac27a08aa35d4b52ccb66c35ca056596e3d4a9 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 24 Jul 2023 16:47:15 -0400 Subject: [PATCH 07/18] resolve threading issues with unit test --- licensing/license_manager.go | 10 ++++++---- licensing/license_manager_test.go | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index efd9d9fa8..1dc30c542 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -199,7 +199,6 @@ func Test(feat string, users int, nodes int, socUrl string, dataUrl string) { available = append(available, feat) licenseKey := &LicenseKey{} licenseKey.Expiration = time.Now().Add(time.Minute * 1) - licenseKey.Features = available licenseKey.Users = users licenseKey.Nodes = nodes licenseKey.SocUrl = socUrl @@ -314,9 +313,10 @@ func startPillarMonitor() { # This file is generated by Security Onion and contains a list of license-enabled features. ` - if manager.status == LICENSE_STATUS_ACTIVE && len(ListEnabledFeatures()) > 0 { + features := ListEnabledFeatures() + if manager.status == LICENSE_STATUS_ACTIVE && len(features) > 0 { contents += "licensed_features:\n" - for _, feature := range manager.licenseKey.Features { + for _, feature := range features { contents += "- " + feature + "\n" } } else { @@ -336,7 +336,9 @@ func startPillarMonitor() { go startPillarMonitor() } else { - log.WithField("pillarFilename", pillarFilename).Info("Exiting pillar monitor") + log.WithFields(log.Fields{ + "pillarFilename": pillarFilename, + }).Info("Exiting pillar monitor") go func() { // Leave enough time for rest of unit tests to finish time.Sleep(5 * time.Minute) diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index 9210d847b..1e0940bab 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -24,6 +24,7 @@ import ( const EXPIRED_KEY = ` H4sIAIvZnGMAA22QR4+bUBSF9/kVFlvPBExz2ZlqijHVNo6yeMaPYsOjPWMgyn8PMyNFihTpLm75zjnS/UXAOIYRzjpIbAiaohfv1Ef5FLX5rAvxRsC+yhqAsxJ9MfR/GASKDwcftnhmZhFELZy9z6yDP1MO7izw5JlmzWz3IAWirx2sSZHdJj4GD/hOUYtpzr9UHy7KtP3rYsBhusYQ4GcDW2Lz4+cb8WxhM7WLKbe8wa+uLaOgySd1inHVbkiyLQv4SmEDv2eoA/mU90bcAAb/UgCVeIK+VzmI4ES0WYI+oyYm8VBxAEZUFbKlp2ugz6t9n15kiX0uligc+es1jf3A7HldUAoctnhtxZ6f7R3+Rc5tOdqqcM5J/F0UyZJh4raOdcNTa6EEyoq+CXR0PloieilIUFlTgwa748r0chJZj2TdmAq3owZi3iFFk4vzx9mUGV41U1N3yLA/GEnCN+tdLa3uAR1zwn6MEh+EmDxefX9VulSjqi6F08hfcQLfYGNXnWxMFpwkKY3h6bJp8bs2z/zRfvCZEu7z3VDh8HRzoOMPa/51S8ho6LrBw7KZgu3qkq7KVGeHu+1Q9LZXkzJDJwaLnnwKpJAbnfkQstcyAlq0ho++q5YLDw11nES0xj+3NmfgvLhYC7rXKFGO927oPHg7oql6MNvDqxMWYxPuBkg/K+Uum/bxnGT90mwjrvZD4+yJmly1Llo+rsGZdZugo307jKf/FbdR950Vr1BHnRrlZMwsACQml/XKq8J5iV5HxgF7sub6Xueyvk7Gfo2Km1AuVZYsWNOv0FEtB4H1WJW/ayfh1S0ZZiB+f/sDb9bxLiEDAAA= ` func teardown() { + awaitPillarMonitor() os.Remove(pillarFilename) } From 8feeb79f68098131662d46ede07c508eebab1ae4 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 24 Jul 2023 17:36:56 -0400 Subject: [PATCH 08/18] add OIDC --- licensing/license_manager.go | 4 +++- licensing/license_manager_test.go | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index 1dc30c542..f6ff08fa2 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -39,9 +39,10 @@ const LICENSE_STATUS_INVALID = "invalid" const LICENSE_STATUS_PENDING = "pending" const LICENSE_STATUS_UNPROVISIONED = "unprovisioned" -const FEAT_TIMETRACKING = "timetracking" const FEAT_FIPS = "fips" +const FEAT_OIDC = "oidc" const FEAT_STIG = "stig" +const FEAT_TIMETRACKING = "timetracking" const PUBLIC_KEY = ` -----BEGIN PUBLIC KEY----- @@ -163,6 +164,7 @@ func verify(key string) (*LicenseKey, error) { func Init(key string) { available := make([]string, 0, 0) available = append(available, FEAT_FIPS) + available = append(available, FEAT_OIDC) available = append(available, FEAT_STIG) available = append(available, FEAT_TIMETRACKING) diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index 1e0940bab..3c762cf65 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -117,23 +117,25 @@ func TestListAvailableFeatures(tester *testing.T) { Init(EXPIRED_KEY) manager.status = LICENSE_STATUS_ACTIVE - assert.Len(tester, ListAvailableFeatures(), 3) + assert.Len(tester, ListAvailableFeatures(), 4) assert.Equal(tester, ListAvailableFeatures()[0], FEAT_FIPS) - assert.Equal(tester, ListAvailableFeatures()[1], FEAT_STIG) - assert.Equal(tester, ListAvailableFeatures()[2], FEAT_TIMETRACKING) + assert.Equal(tester, ListAvailableFeatures()[1], FEAT_OIDC) + assert.Equal(tester, ListAvailableFeatures()[2], FEAT_STIG) + assert.Equal(tester, ListAvailableFeatures()[3], FEAT_TIMETRACKING) } func TestListEnabledFeatures(tester *testing.T) { defer setup()() Init("") - assert.Len(tester, ListEnabledFeatures(), 3) + assert.Len(tester, ListEnabledFeatures(), 4) Init(EXPIRED_KEY) - assert.Len(tester, ListEnabledFeatures(), 3) + assert.Len(tester, ListEnabledFeatures(), 4) assert.Equal(tester, ListEnabledFeatures()[0], FEAT_FIPS) - assert.Equal(tester, ListEnabledFeatures()[1], FEAT_STIG) - assert.Equal(tester, ListEnabledFeatures()[2], FEAT_TIMETRACKING) + assert.Equal(tester, ListEnabledFeatures()[1], FEAT_OIDC) + assert.Equal(tester, ListEnabledFeatures()[2], FEAT_STIG) + assert.Equal(tester, ListEnabledFeatures()[3], FEAT_TIMETRACKING) Init(EXPIRED_KEY) manager.licenseKey.Features = append(manager.licenseKey.Features, "foo") @@ -152,14 +154,14 @@ func TestGetLicenseKey(tester *testing.T) { assert.Equal(tester, key.Nodes, 1) assert.Equal(tester, key.SocUrl, "https://somewhere.invalid") assert.Equal(tester, key.DataUrl, "https://another.place") - assert.Len(tester, key.Features, 3) + assert.Len(tester, key.Features, 4) // Modify the returned object and make sure it doesn't affect the orig object key.Users = 100 key.Features = append(key.Features, "foo") assert.Equal(tester, GetLicenseKey().Users, 1) - assert.Len(tester, key.Features, 4) - assert.Len(tester, GetLicenseKey().Features, 3) + assert.Len(tester, key.Features, 5) + assert.Len(tester, GetLicenseKey().Features, 4) } func TestGetStatus(tester *testing.T) { From 43e102110f9b74d165140474cf12963115883e39 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 26 Jul 2023 12:48:33 -0400 Subject: [PATCH 09/18] non-admins shouldn't see other user's status icons --- html/index.html | 10 ++++++---- html/js/app.js | 3 +++ html/js/app.test.js | 10 ++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/html/index.html b/html/index.html index dbaf8e9d4..96afa467a 100644 --- a/html/index.html +++ b/html/index.html @@ -1186,10 +1186,12 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{ - fa-user-shield - fa-user - fa-user-slash - fa-exclamation-triangle +
+ fa-user-shield + fa-user + fa-user-slash + fa-exclamation-triangle +
diff --git a/html/js/app.js b/html/js/app.js index 9ce46557c..2a4ded818 100644 --- a/html/js/app.js +++ b/html/js/app.js @@ -874,6 +874,9 @@ $(document).ready(function() { isUserAdmin(user = null) { return this.userHasRole("superuser", user); }, + isMyUser(user) { + return user != null && this.user != null && user.id == this.user.id; + }, userHasRole(role, user = null) { if (!user) { user = this.user; diff --git a/html/js/app.test.js b/html/js/app.test.js index ec29ccab0..dcfd54a60 100644 --- a/html/js/app.test.js +++ b/html/js/app.test.js @@ -113,6 +113,16 @@ test('isUserAdmin', async () => { expect(app.isUserAdmin()).toBe(true); }); +test('isMyUser', () => { + app.user = null; + expect(app.isMyUser()).toBe(false); + var user = {id:'123',email:'hi@there.net',roles:['nope', 'peon']}; + expect(app.isMyUser(user)).toBe(false); + app.user = user; + expect(app.isMyUser(user)).toBe(true); + expect(app.isMyUser()).toBe(false); +}); + test('loadServerSettings', async () => { const fakeInfo = { srvToken: 'xyz', From 6de92c927414119bdbde333edab007ad9d4f5477 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 26 Jul 2023 18:58:29 -0400 Subject: [PATCH 10/18] oidc --- html/index.html | 35 +++++++++++++++----- html/js/i18n.js | 8 +++++ html/js/routes/login.js | 19 ++++++++--- html/js/routes/login.test.js | 42 +++++++++++++++++++++++- html/js/routes/settings.js | 16 +++++++++ html/js/routes/settings.test.js | 32 +++++++++++++++++- html/login/index.html | 30 ++++++++++++----- model/user.go | 2 ++ server/modules/kratos/kratosuser.go | 10 ++++++ server/modules/kratos/kratosuser_test.go | 4 +++ 10 files changed, 175 insertions(+), 23 deletions(-) diff --git a/html/index.html b/html/index.html index 96afa467a..19a4162b8 100644 --- a/html/index.html +++ b/html/index.html @@ -1187,10 +1187,11 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{
- fa-user-shield - fa-user + fa-link + fa-user-shield + fa-fingerprint fa-user-slash - fa-exclamation-triangle + fa-exclamation-triangle
@@ -1229,6 +1230,10 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{ @click:append="showPassword = !showPassword" :append-icon="showPassword ? 'fa-eye-slash' : 'fa-eye'" :rules="[rules.required, rules.maxpasslen, rules.minpasslen, rules.badpasschs]"> + + {{ i18n.oidcResetPasswordHelp }} + + @@ -3237,7 +3242,21 @@

{{ i18n.securityInstructions }} - + + {{ i18n.oidc }} + + + + +
+ + +
+
+
+
+ + {{ i18n.password }} @@ -3251,7 +3270,7 @@

- + {{ i18n.securityInstructionsTotp }} @@ -3273,7 +3292,7 @@

- + {{ i18n.totp }} @@ -3285,7 +3304,7 @@

- + {{ i18n.webauthn }} @@ -3297,7 +3316,7 @@

-
+
diff --git a/html/js/i18n.js b/html/js/i18n.js index 60e6d9fe4..73ee09a3f 100644 --- a/html/js/i18n.js +++ b/html/js/i18n.js @@ -466,6 +466,12 @@ const i18n = { notFound: 'The selected item no longer exists', number: 'Num', numericOps: 'Numeric Ops', + oidc: 'Open ID Connect (OIDC)', + oidcLinked: 'OIDC Linked', + oidcInstructions: 'Users can link to or unlink from the OIDC providers listed below. Be aware that unlinking from all OIDC providers without having a local password set may result in being unable to access this user account. If prompted to login again to verify your identity, choose a login method which is already verified. For example, if you are linking to a new OIDC provider, you cannot use that OIDC provider to confirm your identity.', + oidcLink: 'Link with ', + oidcResetPasswordHelp: 'Resetting this user password will also unlink them from all configured OIDC providers. Consider resetting their password in the provider administrator interface instead.', + oidcUnlink: 'Unlink from ', ok: 'OK', offline: 'Offline', online: 'Online', @@ -656,6 +662,7 @@ const i18n = { toolTheHiveHelp: 'Case Management', totp: 'Time-based One-Time Password (TOTP)', totpActivate: 'Activate TOTP', + totpActive: 'TOTP Active', totpCodeHelp: 'Enter the code from your authenticator app.', totpEnabled: 'TOTP (Time-based One-Time Password) enabled', totpQrInstructions: 'TOTP is a multi-factor authentication (MFA) using an authenticator app, such as Google Authenticator. Using the app on your mobile device, scan the QR code shown below.', @@ -701,6 +708,7 @@ const i18n = { viewCase: 'Case Details', viewResults: 'View Results', webauthn: 'Security Keys (WebAuthn / PassKey)', + webauthnActive: 'Webauthn / Security Keys Active', webauthnAddKey: 'Add New Security Key', webauthnContinueHelp: 'Prepare your security key (webauthn) device, and press Login when ready.', webauthnExistingKeys: 'Existing Keys:', diff --git a/html/js/routes/login.js b/html/js/routes/login.js index e4bed47dd..18a56860f 100644 --- a/html/js/routes/login.js +++ b/html/js/routes/login.js @@ -27,6 +27,8 @@ routes.push({ path: '*', name: 'login', component: { script: null, email: null, }, + passwordEnabled: false, + totpEnabled: false, oidc: [], totpCodeLength: 6, rules: { @@ -107,15 +109,14 @@ routes.push({ path: '*', name: 'login', component: { this.csrfToken = flow.data.ui.nodes.find(item => item.attributes && item.attributes.name == 'csrf_token').attributes.value; - // method could be password or totp depending on which phase of login we're in. May be ignored if webauthn is in progress. - this.form.method = flow.data.ui.nodes.find(item => item.attributes && item.attributes.name == 'method' && item.attributes.value == 'password') ? 'password' : 'totp'; - + this.extractPasswordData(flow); + this.extractTotpData(flow); this.extractWebauthnData(flow); this.extractOidcData(flow); this.$nextTick(function () { // Wait for next Vue tick to set focus, since at the time of this function call (or even mounted() hook), this element won't be // loaded, due to v-if's that have yet to process. - if (this.form.method == "totp") { + if (this.totpEnabled) { const ele = document.getElementById("totp--0"); if (ele) { ele.focus(); @@ -171,5 +172,15 @@ routes.push({ path: '*', name: 'login', component: { extractOidcData(response) { this.oidc = response.data.ui.nodes.filter(item => item.group == "oidc" && item.type == "input" ).map(item => item.attributes.value); }, + extractPasswordData(response) { + if (response.data.ui.nodes.find(item => item.group == "password")) { + this.passwordEnabled = true; + } + }, + extractTotpData(response) { + if (response.data.ui.nodes.find(item => item.group == "totp")) { + this.totpEnabled = true; + } + }, }, }}); diff --git a/html/js/routes/login.test.js b/html/js/routes/login.test.js index 56964382a..51a348116 100644 --- a/html/js/routes/login.test.js +++ b/html/js/routes/login.test.js @@ -192,4 +192,44 @@ test('shouldRunWebauthn', () => { comp.webauthnForm.onclick = 'this.foo = 123'; comp.runWebauthn(); expect(comp.foo).toBe(123); -}); \ No newline at end of file +}); + +test('shouldExtractPasswordData', () => { + const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}}; + const passwordMethod = {group: 'password', attributes: {name: 'method', value: 'password'}}; + const nodes = [identifier, passwordMethod]; + const response = {data: {ui: {nodes: nodes}}}; + + expect(comp.passwordEnabled).toBe(false); + + comp.extractPasswordData(response); + + expect(comp.passwordEnabled).toBe(true); +}); + +test('shouldExtractTotpData', () => { + const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}}; + const totpMethod = {group: 'totp', attributes: {name: 'method', value: 'totp'}}; + const nodes = [identifier, totpMethod]; + const response = {data: {ui: {nodes: nodes}}}; + + expect(comp.totpEnabled).toBe(false); + + comp.extractTotpData(response); + + expect(comp.totpEnabled).toBe(true); +}); + +test('shouldExtractOidcData', () => { + const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}}; + const oidcMethod = {group: 'oidc', type: 'input', attributes: {value: 'SSO'}}; + const nodes = [identifier, oidcMethod]; + const response = {data: {ui: {nodes: nodes}}}; + + expect(comp.oidc.length).toBe(0); + + comp.extractOidcData(response); + + expect(comp.oidc.length).toBe(1); + expect(comp.oidc[0]).toBe('SSO'); +}); diff --git a/html/js/routes/settings.js b/html/js/routes/settings.js index 79c797267..1a5101127 100644 --- a/html/js/routes/settings.js +++ b/html/js/routes/settings.js @@ -41,6 +41,9 @@ routes.push({ path: '/settings', name: 'settings', component: { script: null, existingKeys: [], }, + passwordEnabled: false, + oidcEnabled: false, + oidcProviders: [], rules: { required: value => !!value || this.$root.i18n.required, matches: value => (!!value && value == this.passwordForm.password) || this.$root.i18n.passwordMustMatch, @@ -85,8 +88,10 @@ routes.push({ path: '/settings', name: 'settings', component: { this.profileForm.lastName = response.data.identity.traits.lastName; this.profileForm.note = response.data.identity.traits.note; } + this.extractPasswordData(response); this.extractTotpData(response); this.extractWebauthnData(response); + this.extractOidcData(response); var errorsMessage = null; if (response.data.ui.messages && response.data.ui.messages.length > 0) { @@ -147,6 +152,17 @@ routes.push({ path: '/settings', name: 'settings', component: { }, runWebauthn() { eval(this.webauthnForm.onclick); + }, + extractPasswordData(response) { + if (response.data.ui.nodes.find(item => item.group == "password")) { + this.passwordEnabled = true; + } + }, + extractOidcData(response) { + response.data.ui.nodes.filter(item => item.group == "oidc").forEach((oidc) => { + this.oidcEnabled = true; + this.oidcProviders.push({op: oidc.attributes.name, id: oidc.attributes.value}); + }); } } }}); diff --git a/html/js/routes/settings.test.js b/html/js/routes/settings.test.js index 77d64a093..f80b40e0c 100644 --- a/html/js/routes/settings.test.js +++ b/html/js/routes/settings.test.js @@ -81,4 +81,34 @@ test('shouldRunWebauthn', () => { comp.webauthnForm.onclick = 'this.foo = 123'; comp.runWebauthn(); expect(comp.foo).toBe(123); -}); \ No newline at end of file +}); + +test('shouldExtractPasswordData', () => { + const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}}; + const passwordMethod = {group: 'password', attributes: {name: 'method', value: 'password'}}; + const nodes = [identifier, passwordMethod]; + const response = {data: {ui: {nodes: nodes}}}; + + expect(comp.passwordEnabled).toBe(false); + + comp.extractPasswordData(response); + + expect(comp.passwordEnabled).toBe(true); +}); + +test('shouldExtractOidcData', () => { + const identifier = {attributes: {name: 'identifier', value: 'some_identifier'}}; + const oidcMethod = {group: 'oidc', type: 'input', attributes: {name: 'link', value: 'SSO'}}; + const nodes = [identifier, oidcMethod]; + const response = {data: {ui: {nodes: nodes}}}; + + expect(comp.oidcProviders.length).toBe(0); + expect(comp.oidcEnabled).toBe(false); + + comp.extractOidcData(response); + + expect(comp.oidcEnabled).toBe(true); + expect(comp.oidcProviders.length).toBe(1); + expect(comp.oidcProviders[0].id).toBe('SSO'); + expect(comp.oidcProviders[0].op).toBe('link'); +}); diff --git a/html/login/index.html b/html/login/index.html index d8b538e4f..b0c2da362 100644 --- a/html/login/index.html +++ b/html/login/index.html @@ -63,23 +63,35 @@ fa-brands fa-microsoft fa-brands fa-google + fa-brands fa-github fa-arrow-right-to-bracket {{ i18n.loginOidc + ' ' + provider }}
- + + - - -
-
{{ i18n.totpCodeHelp }}
- - -
+ + + + +
+ + + + +
+ + + + +
{{ i18n.totpCodeHelp }}
+ + - +
diff --git a/model/user.go b/model/user.go index 6f5156825..9e47c66af 100644 --- a/model/user.go +++ b/model/user.go @@ -18,6 +18,8 @@ type User struct { FirstName string `json:"firstName"` LastName string `json:"lastName"` TotpStatus string `json:"totpStatus"` + OidcStatus string `json:"oidcStatus"` + WebauthnStatus string `json:"webauthnStatus"` Note string `json:"note"` Roles []string `json:"roles"` Status string `json:"status"` diff --git a/server/modules/kratos/kratosuser.go b/server/modules/kratos/kratosuser.go index 6ac0378e5..fe81fecc8 100644 --- a/server/modules/kratos/kratosuser.go +++ b/server/modules/kratos/kratosuser.go @@ -100,6 +100,16 @@ func (kratosUser *KratosUser) copyToUser(user *model.User) { } else { user.TotpStatus = "disabled" } + if kratosUser.Credentials["oidc"] != nil { + user.OidcStatus = "enabled" + } else { + user.OidcStatus = "disabled" + } + if kratosUser.Credentials["webauthn"] != nil { + user.WebauthnStatus = "enabled" + } else { + user.WebauthnStatus = "disabled" + } } } diff --git a/server/modules/kratos/kratosuser_test.go b/server/modules/kratos/kratosuser_test.go index f9f026177..3a3bd8449 100644 --- a/server/modules/kratos/kratosuser_test.go +++ b/server/modules/kratos/kratosuser_test.go @@ -43,6 +43,8 @@ func TestCopyToUser(tester *testing.T) { kratosUser := NewKratosUser("myEmail", "myFirst", "myLast", "note", "inactive") kratosUser.Credentials = make(map[string]*KratosCredential) kratosUser.Credentials["totp"] = &KratosCredential{Type: "totp"} + kratosUser.Credentials["webauthn"] = &KratosCredential{Type: "webauthn"} + kratosUser.Credentials["oidc"] = &KratosCredential{Type: "oidc"} kratosUser.Credentials["password"] = &KratosCredential{Type: "password"} user := model.NewUser() kratosUser.copyToUser(user) @@ -53,6 +55,8 @@ func TestCopyToUser(tester *testing.T) { assert.Equal(tester, kratosUser.Addresses[0].Value, user.Email) assert.Equal(tester, "locked", user.Status) assert.Equal(tester, "enabled", user.TotpStatus) + assert.Equal(tester, "enabled", user.OidcStatus) + assert.Equal(tester, "enabled", user.WebauthnStatus) assert.Equal(tester, false, user.PasswordChanged) kratosUser.Credentials = make(map[string]*KratosCredential) From 463a4396d8175bc08a59abe35a1d8eecf275569d Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 26 Jul 2023 20:29:09 -0400 Subject: [PATCH 11/18] oidc --- html/index.html | 10 +++++----- html/js/i18n.js | 2 +- server/modules/kratos/kratosuser.go | 2 +- server/modules/kratos/kratosuser_test.go | 4 +++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/html/index.html b/html/index.html index 19a4162b8..efa4a5085 100644 --- a/html/index.html +++ b/html/index.html @@ -1189,7 +1189,7 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{
fa-link fa-user-shield - fa-fingerprint + fa-fingerprint fa-user-slash fa-exclamation-triangle
@@ -1225,15 +1225,15 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{ {{ i18n.access }} + + {{ i18n.oidcResetPasswordHelp }} + + - - {{ i18n.oidcResetPasswordHelp }} - - diff --git a/html/js/i18n.js b/html/js/i18n.js index 73ee09a3f..93b928534 100644 --- a/html/js/i18n.js +++ b/html/js/i18n.js @@ -470,7 +470,7 @@ const i18n = { oidcLinked: 'OIDC Linked', oidcInstructions: 'Users can link to or unlink from the OIDC providers listed below. Be aware that unlinking from all OIDC providers without having a local password set may result in being unable to access this user account. If prompted to login again to verify your identity, choose a login method which is already verified. For example, if you are linking to a new OIDC provider, you cannot use that OIDC provider to confirm your identity.', oidcLink: 'Link with ', - oidcResetPasswordHelp: 'Resetting this user password will also unlink them from all configured OIDC providers. Consider resetting their password in the provider administrator interface instead.', + oidcResetPasswordHelp: 'Administering authentication settings for OIDC users should normally be conducted in the external provider administration interface. Proceeding may cause the user to be disconnected from the OIDC provider.', oidcUnlink: 'Unlink from ', ok: 'OK', offline: 'Offline', diff --git a/server/modules/kratos/kratosuser.go b/server/modules/kratos/kratosuser.go index fe81fecc8..2ff02905d 100644 --- a/server/modules/kratos/kratosuser.go +++ b/server/modules/kratos/kratosuser.go @@ -100,7 +100,7 @@ func (kratosUser *KratosUser) copyToUser(user *model.User) { } else { user.TotpStatus = "disabled" } - if kratosUser.Credentials["oidc"] != nil { + if kratosUser.Credentials["oidc"] != nil && len(kratosUser.Credentials["oidc"].Identifiers) > 0 { user.OidcStatus = "enabled" } else { user.OidcStatus = "disabled" diff --git a/server/modules/kratos/kratosuser_test.go b/server/modules/kratos/kratosuser_test.go index 3a3bd8449..30d60bcc0 100644 --- a/server/modules/kratos/kratosuser_test.go +++ b/server/modules/kratos/kratosuser_test.go @@ -44,7 +44,9 @@ func TestCopyToUser(tester *testing.T) { kratosUser.Credentials = make(map[string]*KratosCredential) kratosUser.Credentials["totp"] = &KratosCredential{Type: "totp"} kratosUser.Credentials["webauthn"] = &KratosCredential{Type: "webauthn"} - kratosUser.Credentials["oidc"] = &KratosCredential{Type: "oidc"} + oidcIds := make([]string, 1, 1) + oidcIds[0] = "test" + kratosUser.Credentials["oidc"] = &KratosCredential{Type: "oidc", Identifiers: oidcIds} kratosUser.Credentials["password"] = &KratosCredential{Type: "password"} user := model.NewUser() kratosUser.copyToUser(user) From 583645106e1fc213be19e3fc8216a1951474ff32 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 28 Jul 2023 07:12:55 -0400 Subject: [PATCH 12/18] adjust color of icons --- html/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/html/index.html b/html/index.html index efa4a5085..75ea957ef 100644 --- a/html/index.html +++ b/html/index.html @@ -1187,9 +1187,9 @@

{{ i18n.usersEnabled }} {{ countUsersEnabled() }} / {{
- fa-link - fa-user-shield - fa-fingerprint + fa-link + fa-user-shield + fa-fingerprint fa-user-slash fa-exclamation-triangle
From 939a3c409d95fe55845ee8f1a91ddae8c3c24719 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 11 Oct 2023 15:16:08 -0400 Subject: [PATCH 13/18] merge from 2.4/dev --- html/js/app.js | 4 ++-- html/js/app.test.js | 28 ++++++++++++++++++++++++++++ licensing/license_manager.go | 24 ++++++++++++++++++------ licensing/license_manager_test.go | 23 ++++++++++++++++++++++- 4 files changed, 70 insertions(+), 9 deletions(-) diff --git a/html/js/app.js b/html/js/app.js index 2a4ded818..7c5e781a8 100644 --- a/html/js/app.js +++ b/html/js/app.js @@ -711,8 +711,8 @@ $(document).ready(function() { if (response) { const redirectCookie = this.getCookie('AUTH_REDIRECT'); if ((response.headers && response.headers['content-type'] == "text/html") || - (response.status == 401) || - (response.request.responseURL.indexOf("/login/") == -1 && redirectCookie != null && redirectCookie.length > 0)) { + (response.status == 401 && response.request.responseURL.indexOf('/api/') == -1) || + (response.request.responseURL.indexOf("/login/banner.md") == -1 && redirectCookie != null && redirectCookie.length > 0)) { this.deleteCookie('AUTH_REDIRECT'); this.showLogin(); return null diff --git a/html/js/app.test.js b/html/js/app.test.js index dcfd54a60..c539ca979 100644 --- a/html/js/app.test.js +++ b/html/js/app.test.js @@ -417,3 +417,31 @@ test('isIPv6', () => { expect(app.isIPv6('::8')).toBe(true); expect(app.isIPv6('::')).toBe(true); }); + +function testCheckForUnauthorized(url, response, authRedirectCookie, unauthorized) { + app.showLogin = jest.fn(); + app.getCookie = jest.fn(cookie => authRedirectCookie); + app.deleteCookie = jest.fn(); + + response.request = {responseURL: url}; + var result = app.checkForUnauthorized(response); + if (unauthorized) { + expect(result).toBe(null); + expect(app.showLogin).toHaveBeenCalled(); + expect(app.deleteCookie).toHaveBeenCalledWith('AUTH_REDIRECT'); + } else { + expect(result).toBe(response); + } +} + +test('checkForUnauthorized', () => { + testCheckForUnauthorized('/foo/', {headers: {'content-type': 'text/html'}}, null, true); + testCheckForUnauthorized('/foo/', {headers: {'content-type': 'application/json'}}, null, false); + testCheckForUnauthorized('/foo/', {status: 401}, null, true); + testCheckForUnauthorized('/foo/', {status: 200}, null, false); + testCheckForUnauthorized('/api/', {status: 401}, null, false); + testCheckForUnauthorized('/foo/', {}, '/blah', true); + testCheckForUnauthorized('/foo/', {}, null, false); + testCheckForUnauthorized('/login/banner.md', {}, '/blah', false); + testCheckForUnauthorized('/auth/self-service/login/browser', {}, '/blah', true); +}); \ No newline at end of file diff --git a/licensing/license_manager.go b/licensing/license_manager.go index f6ff08fa2..01e648f40 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -161,12 +161,17 @@ func verify(key string) (*LicenseKey, error) { return license, rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hash[:], sigBytes) } -func Init(key string) { +func CreateAvailableFeatureList() []string { available := make([]string, 0, 0) available = append(available, FEAT_FIPS) available = append(available, FEAT_OIDC) available = append(available, FEAT_STIG) available = append(available, FEAT_TIMETRACKING) + return available +} + +func Init(key string) { + available := CreateAvailableFeatureList() status := LICENSE_STATUS_UNPROVISIONED licenseKey := &LicenseKey{} @@ -196,11 +201,18 @@ func Init(key string) { } func Test(feat string, users int, nodes int, socUrl string, dataUrl string) { + available := CreateAvailableFeatureList() + pillarFilename = "/tmp/soc_test_pillar_monitor.sls" - available := make([]string, 0, 0) - available = append(available, feat) licenseKey := &LicenseKey{} licenseKey.Expiration = time.Now().Add(time.Minute * 1) + + if len(feat) > 0 { + features := make([]string, 0, 0) + features = append(features, feat) + licenseKey.Features = features + } + licenseKey.Users = users licenseKey.Nodes = nodes licenseKey.SocUrl = socUrl @@ -316,13 +328,13 @@ func startPillarMonitor() { # This file is generated by Security Onion and contains a list of license-enabled features. ` features := ListEnabledFeatures() - if manager.status == LICENSE_STATUS_ACTIVE && len(features) > 0 { - contents += "licensed_features:\n" + if manager.status == LICENSE_STATUS_ACTIVE { + contents += "features:\n" for _, feature := range features { contents += "- " + feature + "\n" } } else { - contents += "licensed_features: []\n" + contents += "features: []\n" } err := os.WriteFile(pillarFilename, []byte(contents), 0644) if err != nil { diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index 3c762cf65..bd4bdb8ea 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -274,6 +274,24 @@ func TestPillarMonitor(tester *testing.T) { assert.Equal(tester, manager.status, LICENSE_STATUS_ACTIVE) contents, _ := os.ReadFile(pillarFilename) + expected := ` +features: +- stig +` + + assert.Contains(tester, string(contents), expected) +} + +func TestPillarMonitorAllFeatures(tester *testing.T) { + Test("", 0, 0, "", "") + + pillarFilename = "/tmp/soc_test_pillar_monitor.sls" + + os.Remove(pillarFilename) + startPillarMonitor() + assert.Equal(tester, manager.status, LICENSE_STATUS_ACTIVE) + contents, _ := os.ReadFile(pillarFilename) + expected := ` # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at @@ -287,8 +305,11 @@ func TestPillarMonitor(tester *testing.T) { # software that is protected by the license key." # This file is generated by Security Onion and contains a list of license-enabled features. -licensed_features: +features: +- fips +- oidc - stig +- timetracking ` assert.Equal(tester, expected, string(contents)) From fdd7cfbd6937758fd599a7254bcd15367ae62463 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 11 Oct 2023 22:05:27 -0400 Subject: [PATCH 14/18] resolve CVE-2023-3978 and CVE-2023-39325 --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 69bc72e59..95faa2dbc 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/kennygrant/sanitize v1.2.4 github.com/stretchr/testify v1.8.1 github.com/tidwall/gjson v1.14.4 - golang.org/x/crypto v0.8.0 - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 405302c5e..4023aa438 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,8 @@ github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKw golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -103,16 +103,16 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 3835734300b1a83236e80c6b330d14d4adb560cd Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 25 Oct 2023 09:05:17 -0400 Subject: [PATCH 15/18] minor updates --- html/index.html | 5 +++-- html/js/i18n.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/html/index.html b/html/index.html index 771cc0ab7..bd7bd8531 100644 --- a/html/index.html +++ b/html/index.html @@ -3261,8 +3261,9 @@

{{ i18n.oidc }} - - + + +
diff --git a/html/js/i18n.js b/html/js/i18n.js index 443c1b1a2..e5f8d0c48 100644 --- a/html/js/i18n.js +++ b/html/js/i18n.js @@ -475,9 +475,10 @@ const i18n = { number: 'Num', numericOps: 'Numeric Ops', oidc: 'Open ID Connect (OIDC)', + oidcHelp: 'Single Sign-On via an external identity provider has been enabled for SOC. Authentication settings, such as password changes, should be performed in the external identity system unless the Security Onion administrators have enabled local password logins concurrently with SSO.', oidcLinked: 'OIDC Linked', - oidcInstructions: 'Users can link to or unlink from the OIDC providers listed below. Be aware that unlinking from all OIDC providers without having a local password set may result in being unable to access this user account. If prompted to login again to verify your identity, choose a login method which is already verified. For example, if you are linking to a new OIDC provider, you cannot use that OIDC provider to confirm your identity.', oidcLink: 'Link with ', + oidcLinkHelp: 'Users can link to or unlink from the OIDC providers listed below. Be aware that unlinking from all OIDC providers without having a local password set may result in being unable to access this user account. If prompted to login again to verify your identity, choose a login method which is already verified. For example, if you are linking to a new OIDC provider, you cannot use that OIDC provider to confirm your identity.', oidcResetPasswordHelp: 'Administering authentication settings for OIDC users should normally be conducted in the external provider administration interface. Proceeding may cause the user to be disconnected from the OIDC provider.', oidcUnlink: 'Unlink from ', ok: 'OK', From 8c9f2a3874e93f5f7aca09caef4f673fa49b1fe7 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 26 Oct 2023 15:23:49 -0400 Subject: [PATCH 16/18] stabilize threaded unit tests --- licensing/license_manager.go | 4 ++-- licensing/license_manager_test.go | 30 ++++++++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/licensing/license_manager.go b/licensing/license_manager.go index 01e648f40..4bf6604cb 100644 --- a/licensing/license_manager.go +++ b/licensing/license_manager.go @@ -316,7 +316,7 @@ func startPillarMonitor() { contents := ` # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at -# https://securityonion.net/license; you may not use this file except in compliance with +# https://securityonion.net/license; you may not use this file except in compliance with # the Elastic License 2.0. # Note: Per the Elastic License 2.0, the second limitation states: @@ -325,7 +325,7 @@ func startPillarMonitor() { # in the software, and you may not remove or obscure any functionality in the # software that is protected by the license key." -# This file is generated by Security Onion and contains a list of license-enabled features. +# This file is generated by Security Onion and contains a list of license-enabled features. ` features := ListEnabledFeatures() if manager.status == LICENSE_STATUS_ACTIVE { diff --git a/licensing/license_manager_test.go b/licensing/license_manager_test.go index bd4bdb8ea..1283a2b1c 100644 --- a/licensing/license_manager_test.go +++ b/licensing/license_manager_test.go @@ -18,11 +18,17 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/tj/assert" ) const EXPIRED_KEY = ` H4sIAIvZnGMAA22QR4+bUBSF9/kVFlvPBExz2ZlqijHVNo6yeMaPYsOjPWMgyn8PMyNFihTpLm75zjnS/UXAOIYRzjpIbAiaohfv1Ef5FLX5rAvxRsC+yhqAsxJ9MfR/GASKDwcftnhmZhFELZy9z6yDP1MO7izw5JlmzWz3IAWirx2sSZHdJj4GD/hOUYtpzr9UHy7KtP3rYsBhusYQ4GcDW2Lz4+cb8WxhM7WLKbe8wa+uLaOgySd1inHVbkiyLQv4SmEDv2eoA/mU90bcAAb/UgCVeIK+VzmI4ES0WYI+oyYm8VBxAEZUFbKlp2ugz6t9n15kiX0uligc+es1jf3A7HldUAoctnhtxZ6f7R3+Rc5tOdqqcM5J/F0UyZJh4raOdcNTa6EEyoq+CXR0PloieilIUFlTgwa748r0chJZj2TdmAq3owZi3iFFk4vzx9mUGV41U1N3yLA/GEnCN+tdLa3uAR1zwn6MEh+EmDxefX9VulSjqi6F08hfcQLfYGNXnWxMFpwkKY3h6bJp8bs2z/zRfvCZEu7z3VDh8HRzoOMPa/51S8ho6LrBw7KZgu3qkq7KVGeHu+1Q9LZXkzJDJwaLnnwKpJAbnfkQstcyAlq0ho++q5YLDw11nES0xj+3NmfgvLhYC7rXKFGO927oPHg7oql6MNvDqxMWYxPuBkg/K+Uum/bxnGT90mwjrvZD4+yJmly1Llo+rsGZdZugo307jKf/FbdR950Vr1BHnRrlZMwsACQml/XKq8J5iV5HxgF7sub6Xueyvk7Gfo2Km1AuVZYsWNOv0FEtB4H1WJW/ayfh1S0ZZiB+f/sDb9bxLiEDAAA= ` +func awaitPillarMonitor() { + for pillarMonitorCount == 0 { + time.Sleep(50 * time.Millisecond) + } +} + func teardown() { awaitPillarMonitor() os.Remove(pillarFilename) @@ -259,12 +265,6 @@ func TestValidateDataUrl(tester *testing.T) { assert.False(tester, ValidateDataUrl("bar")) } -func awaitPillarMonitor() { - for pillarMonitorCount == 0 { - time.Sleep(50 * time.Millisecond) - } -} - func TestPillarMonitor(tester *testing.T) { defer setup()() @@ -283,19 +283,18 @@ features: } func TestPillarMonitorAllFeatures(tester *testing.T) { - Test("", 0, 0, "", "") + defer setup()() - pillarFilename = "/tmp/soc_test_pillar_monitor.sls" + Test("", 0, 0, "", "") - os.Remove(pillarFilename) - startPillarMonitor() - assert.Equal(tester, manager.status, LICENSE_STATUS_ACTIVE) + awaitPillarMonitor() + assert.Equal(tester, LICENSE_STATUS_ACTIVE, manager.status) contents, _ := os.ReadFile(pillarFilename) expected := ` # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at -# https://securityonion.net/license; you may not use this file except in compliance with +# https://securityonion.net/license; you may not use this file except in compliance with # the Elastic License 2.0. # Note: Per the Elastic License 2.0, the second limitation states: @@ -304,7 +303,7 @@ func TestPillarMonitorAllFeatures(tester *testing.T) { # in the software, and you may not remove or obscure any functionality in the # software that is protected by the license key." -# This file is generated by Security Onion and contains a list of license-enabled features. +# This file is generated by Security Onion and contains a list of license-enabled features. features: - fips - oidc @@ -322,7 +321,6 @@ func TestPillarMonitor_Fail(tester *testing.T) { Init("") - assert.Equal(tester, manager.status, LICENSE_STATUS_UNPROVISIONED) awaitPillarMonitor() - assert.Equal(tester, manager.status, LICENSE_STATUS_INVALID) + assert.Equal(tester, LICENSE_STATUS_INVALID, manager.status) } From 890c04128a40f5984806bc805ed5a631ad9b237a Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 1 Nov 2023 09:36:18 -0400 Subject: [PATCH 17/18] load lic man in server mode --- cmd/sensoroni.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/sensoroni.go b/cmd/sensoroni.go index 40b2d420a..664f5c6d4 100644 --- a/cmd/sensoroni.go +++ b/cmd/sensoroni.go @@ -61,11 +61,10 @@ func main() { "buildTime": cfg.BuildTime, }).Info("Version Information") - licensing.Init(cfg.LicenseKey) - moduleMgr := module.NewModuleManager() var srv *server.Server if cfg.Server != nil { + licensing.Init(cfg.LicenseKey) srv = server.NewServer(cfg.Server, cfg.Version) err = moduleMgr.LaunchModules(serverModules.BuildModuleMap(srv), cfg.Server.Modules, cfg.Server.ModuleFailuresIgnored) if err == nil { From 2edd4add4806bcbb6f2d50fa22a93221dddc2b21 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Sat, 11 Nov 2023 10:44:15 -0500 Subject: [PATCH 18/18] upgrade to axios 1.6.1 --- html/index.html | 2 +- html/js/external/axios-1.4.0.min.js | 1 - html/js/external/axios-1.6.1.min.js | 1 + html/login/index.html | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 html/js/external/axios-1.4.0.min.js create mode 100644 html/js/external/axios-1.6.1.min.js diff --git a/html/index.html b/html/index.html index bd7bd8531..0d3612c36 100644 --- a/html/index.html +++ b/html/index.html @@ -3845,7 +3845,7 @@

- + diff --git a/html/js/external/axios-1.4.0.min.js b/html/js/external/axios-1.4.0.min.js deleted file mode 100644 index 8212b2526..000000000 --- a/html/js/external/axios-1.4.0.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&void 0!==arguments[2]?arguments[2]:{},a=i.allOwnKeys,s=void 0!==a&&a;if(null!=t)if("object"!==e(t)&&(t=[t]),p(t))for(r=0,o=t.length;r0;)if(t===(n=r[o]).toLowerCase())return n;return null}var N="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,C=function(e){return!h(e)&&e!==N};var x,P=(x="undefined"!=typeof Uint8Array&&c(Uint8Array),function(e){return x&&e instanceof x}),k=l("HTMLFormElement"),U=function(e){var t=Object.prototype.hasOwnProperty;return function(e,n){return t.call(e,n)}}(),_=l("RegExp"),F=function(e,t){var n=Object.getOwnPropertyDescriptors(e),r={};T(n,(function(n,o){!1!==t(n,o,e)&&(r[o]=n)})),Object.defineProperties(e,r)},B="abcdefghijklmnopqrstuvwxyz",L="0123456789",D={DIGIT:L,ALPHA:B,ALPHA_DIGIT:B+B.toUpperCase()+L};var I=l("AsyncFunction"),q={isArray:p,isArrayBuffer:m,isBuffer:function(e){return null!==e&&!h(e)&&null!==e.constructor&&!h(e.constructor)&&v(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||v(e.append)&&("formdata"===(t=f(e))||"object"===t&&v(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&m(e.buffer)},isString:y,isNumber:b,isBoolean:function(e){return!0===e||!1===e},isObject:g,isPlainObject:w,isUndefined:h,isDate:E,isFile:O,isBlob:S,isRegExp:_,isFunction:v,isStream:function(e){return g(e)&&v(e.pipe)},isURLSearchParams:A,isTypedArray:P,isFileList:R,forEach:T,merge:function e(){for(var t=C(this)&&this||{},n=t.caseless,r={},o=function(t,o){var i=n&&j(r,o)||o;w(r[i])&&w(t)?r[i]=e(r[i],t):w(t)?r[i]=e({},t):p(t)?r[i]=t.slice():r[i]=t},i=0,a=arguments.length;i3&&void 0!==arguments[3]?arguments[3]:{},o=r.allOwnKeys;return T(t,(function(t,r){n&&v(t)?e[r]=a(t,n):e[r]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,a,s={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],r&&!r(a,e,t)||s[a]||(t[a]=e[a],s[a]=!0);e=!1!==n&&c(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:f,kindOfTest:l,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(p(e))return e;var t=e.length;if(!b(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:k,hasOwnProperty:U,hasOwnProp:U,reduceDescriptors:F,freezeMethods:function(e){F(e,(function(t,n){if(v(e)&&-1!==["arguments","caller","callee"].indexOf(n))return!1;var r=e[n];v(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+n+"'")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return p(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t},findKey:j,global:N,isContextDefined:C,ALPHABET:D,generateString:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:16,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:D.ALPHA_DIGIT,n="",r=t.length;e--;)n+=t[Math.random()*r|0];return n},isSpecCompliantForm:function(e){return!!(e&&v(e.append)&&"FormData"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:function(e){var t=new Array(10);return function e(n,r){if(g(n)){if(t.indexOf(n)>=0)return;if(!("toJSON"in n)){t[r]=n;var o=p(n)?[]:{};return T(n,(function(t,n){var i=e(t,r+1);!h(i)&&(o[n]=i)})),t[r]=void 0,o}}return n}(e,0)},isAsyncFn:I,isThenable:function(e){return e&&(g(e)||v(e))&&v(e.then)&&v(e.catch)}};function M(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}q.inherits(M,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:q.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var z=M.prototype,H={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){H[e]={value:e}})),Object.defineProperties(M,H),Object.defineProperty(z,"isAxiosError",{value:!0}),M.from=function(e,t,n,r,o,i){var a=Object.create(z);return q.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),M.call(a,e.message,t,n,r,o),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};function J(e){return q.isPlainObject(e)||q.isArray(e)}function W(e){return q.endsWith(e,"[]")?e.slice(0,-2):e}function K(e,t,n){return e?e.concat(t).map((function(e,t){return e=W(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}var V=q.toFlatObject(q,{},null,(function(e){return/^is[A-Z]/.test(e)}));function G(t,n,r){if(!q.isObject(t))throw new TypeError("target must be an object");n=n||new FormData;var o=(r=q.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!q.isUndefined(t[e])}))).metaTokens,i=r.visitor||f,a=r.dots,s=r.indexes,u=(r.Blob||"undefined"!=typeof Blob&&Blob)&&q.isSpecCompliantForm(n);if(!q.isFunction(i))throw new TypeError("visitor must be a function");function c(e){if(null===e)return"";if(q.isDate(e))return e.toISOString();if(!u&&q.isBlob(e))throw new M("Blob is not supported. Use a Buffer instead.");return q.isArrayBuffer(e)||q.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function f(t,r,i){var u=t;if(t&&!i&&"object"===e(t))if(q.endsWith(r,"{}"))r=o?r:r.slice(0,-2),t=JSON.stringify(t);else if(q.isArray(t)&&function(e){return q.isArray(e)&&!e.some(J)}(t)||(q.isFileList(t)||q.endsWith(r,"[]"))&&(u=q.toArray(t)))return r=W(r),u.forEach((function(e,t){!q.isUndefined(e)&&null!==e&&n.append(!0===s?K([r],t,a):null===s?r:r+"[]",c(e))})),!1;return!!J(t)||(n.append(K(i,r,a),c(t)),!1)}var l=[],d=Object.assign(V,{defaultVisitor:f,convertValue:c,isVisitable:J});if(!q.isObject(t))throw new TypeError("data must be an object");return function e(t,r){if(!q.isUndefined(t)){if(-1!==l.indexOf(t))throw Error("Circular reference detected in "+r.join("."));l.push(t),q.forEach(t,(function(t,o){!0===(!(q.isUndefined(t)||null===t)&&i.call(n,t,q.isString(o)?o.trim():o,r,d))&&e(t,r?r.concat(o):[o])})),l.pop()}}(t),n}function $(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function X(e,t){this._pairs=[],e&&G(e,this,t)}var Q=X.prototype;function Z(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function Y(e,t,n){if(!t)return e;var r,o=n&&n.encode||Z,i=n&&n.serialize;if(r=i?i(t,n):q.isURLSearchParams(t)?t.toString():new X(t,n).toString(o)){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+r}return e}Q.append=function(e,t){this._pairs.push([e,t])},Q.toString=function(e){var t=e?function(t){return e.call(this,t,$)}:$;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var ee,te=function(){function e(){t(this,e),this.handlers=[]}return r(e,[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){q.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),ne={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},re={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:X,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},isStandardBrowserEnv:("undefined"==typeof navigator||"ReactNative"!==(ee=navigator.product)&&"NativeScript"!==ee&&"NS"!==ee)&&"undefined"!=typeof window&&"undefined"!=typeof document,isStandardBrowserWebWorkerEnv:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,protocols:["http","https","file","blob","url","data"]};function oe(e){function t(e,n,r,o){var i=e[o++],a=Number.isFinite(+i),s=o>=e.length;return i=!i&&q.isArray(r)?r.length:i,s?(q.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!a):(r[i]&&q.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&q.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=q.isObject(e);if(i&&q.isHTMLForm(e)&&(e=new FormData(e)),q.isFormData(e))return o&&o?JSON.stringify(oe(e)):e;if(q.isArrayBuffer(e)||q.isBuffer(e)||q.isStream(e)||q.isFile(e)||q.isBlob(e))return e;if(q.isArrayBufferView(e))return e.buffer;if(q.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return G(e,new re.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return re.isNode&&q.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=q.isFileList(e))||r.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return G(n?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(q.isString(e))try{return(t||JSON.parse)(e),q.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||ae.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(e&&q.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw M.from(e,M.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:re.classes.FormData,Blob:re.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};q.forEach(["delete","get","head"],(function(e){ae.headers[e]={}})),q.forEach(["post","put","patch"],(function(e){ae.headers[e]=q.merge(ie)}));var se=ae,ue=q.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),ce=Symbol("internals");function fe(e){return e&&String(e).trim().toLowerCase()}function le(e){return!1===e||null==e?e:q.isArray(e)?e.map(le):String(e)}function de(e,t,n,r,o){return q.isFunction(r)?r.call(this,t,n):(o&&(t=n),q.isString(t)?q.isString(r)?-1!==t.indexOf(r):q.isRegExp(r)?r.test(t):void 0:void 0)}var pe=function(e,n){function i(e){t(this,i),e&&this.set(e)}return r(i,[{key:"set",value:function(e,t,n){var r=this;function o(e,t,n){var o=fe(t);if(!o)throw new Error("header name must be a non-empty string");var i=q.findKey(r,o);(!i||void 0===r[i]||!0===n||void 0===n&&!1!==r[i])&&(r[i||t]=le(e))}var i,a,s,u,c,f=function(e,t){return q.forEach(e,(function(e,n){return o(e,n,t)}))};return q.isPlainObject(e)||e instanceof this.constructor?f(e,t):q.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())?f((c={},(i=e)&&i.split("\n").forEach((function(e){u=e.indexOf(":"),a=e.substring(0,u).trim().toLowerCase(),s=e.substring(u+1).trim(),!a||c[a]&&ue[a]||("set-cookie"===a?c[a]?c[a].push(s):c[a]=[s]:c[a]=c[a]?c[a]+", "+s:s)})),c),t):null!=e&&o(t,e,n),this}},{key:"get",value:function(e,t){if(e=fe(e)){var n=q.findKey(this,e);if(n){var r=this[n];if(!t)return r;if(!0===t)return function(e){for(var t,n=Object.create(null),r=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=r.exec(e);)n[t[1]]=t[2];return n}(r);if(q.isFunction(t))return t.call(this,r,n);if(q.isRegExp(t))return t.exec(r);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=fe(e)){var n=q.findKey(this,e);return!(!n||void 0===this[n]||t&&!de(0,this[n],n,t))}return!1}},{key:"delete",value:function(e,t){var n=this,r=!1;function o(e){if(e=fe(e)){var o=q.findKey(n,e);!o||t&&!de(0,n[o],o,t)||(delete n[o],r=!0)}}return q.isArray(e)?e.forEach(o):o(e),r}},{key:"clear",value:function(e){for(var t=Object.keys(this),n=t.length,r=!1;n--;){var o=t[n];e&&!de(0,this[o],o,e,!0)||(delete this[o],r=!0)}return r}},{key:"normalize",value:function(e){var t=this,n={};return q.forEach(this,(function(r,o){var i=q.findKey(n,o);if(i)return t[i]=le(r),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=le(r),n[a]=!0})),this}},{key:"concat",value:function(){for(var e,t=arguments.length,n=new Array(t),r=0;r1?n-1:0),o=1;o0;){var a=o[i],s=n[a];if(s){var u=t[a],c=void 0===u||s(u,a,t);if(!0!==c)throw new M("option "+a+" must be "+c,M.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new M("Unknown option "+a,M.ERR_BAD_OPTION)}},validators:Ce},ke=Pe.validators,Ue=function(){function e(n){t(this,e),this.defaults=n,this.interceptors={request:new te,response:new te}}return r(e,[{key:"request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n,r=t=je(this.defaults,t),o=r.transitional,i=r.paramsSerializer,a=r.headers;void 0!==o&&Pe.assertOptions(o,{silentJSONParsing:ke.transitional(ke.boolean),forcedJSONParsing:ke.transitional(ke.boolean),clarifyTimeoutError:ke.transitional(ke.boolean)},!1),null!=i&&(q.isFunction(i)?t.paramsSerializer={serialize:i}:Pe.assertOptions(i,{encode:ke.function,serialize:ke.function},!0)),t.method=(t.method||this.defaults.method||"get").toLowerCase(),(n=a&&q.merge(a.common,a[t.method]))&&q.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete a[e]})),t.headers=he.concat(n,a);var s=[],u=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(u=u&&e.synchronous,s.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,d=0;if(!u){var p=[Ae.bind(this),void 0];for(p.unshift.apply(p,s),p.push.apply(p,f),l=p.length,c=Promise.resolve(t);d0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},n((function(e,t,n){o.reason||(o.reason=new ve(e,t,n),r(o.reason))}))}return r(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Be={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Be).forEach((function(e){var t=o(e,2),n=t[0],r=t[1];Be[r]=n}));var Le=Be;var De=function e(t){var n=new _e(t),r=a(_e.prototype.request,n);return q.extend(r,_e.prototype,n,{allOwnKeys:!0}),q.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(je(t,n))},r}(se);return De.Axios=_e,De.CanceledError=ve,De.CancelToken=Fe,De.isCancel=ye,De.VERSION=Ne,De.toFormData=G,De.AxiosError=M,De.Cancel=De.CanceledError,De.all=function(e){return Promise.all(e)},De.spread=function(e){return function(t){return e.apply(null,t)}},De.isAxiosError=function(e){return q.isObject(e)&&!0===e.isAxiosError},De.mergeConfig=je,De.AxiosHeaders=he,De.formToJSON=function(e){return oe(q.isHTMLForm(e)?new FormData(e):e)},De.HttpStatusCode=Le,De.default=De,De})); \ No newline at end of file diff --git a/html/js/external/axios-1.6.1.min.js b/html/js/external/axios-1.6.1.min.js new file mode 100644 index 000000000..67bac4945 --- /dev/null +++ b/html/js/external/axios-1.6.1.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{},s=i.allOwnKeys,a=void 0!==s&&s;if(null!=t)if("object"!==e(t)&&(t=[t]),l(t))for(r=0,o=t.length;r3&&void 0!==arguments[3]?arguments[3]:{},i=r.allOwnKeys;return S(t,(function(t,r){n&&m(t)?e[r]=o(t,n):e[r]=t}),{allOwnKeys:i}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,n,r){e.prototype=Object.create(t.prototype,r),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},toFlatObject:function(e,t,n,r){var o,i,s,u={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)s=o[i],r&&!r(s,e,t)||u[s]||(t[s]=e[s],u[s]=!0);e=!1!==n&&a(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},kindOf:u,kindOfTest:c,endsWith:function(e,t,n){e=String(e),(void 0===n||n>e.length)&&(n=e.length),n-=t.length;var r=e.indexOf(t,n);return-1!==r&&r===n},toArray:function(e){if(!e)return null;if(l(e))return e;var t=e.length;if(!v(t))return null;for(var n=new Array(t);t-- >0;)n[t]=e[t];return n},forEachEntry:function(e,t){for(var n,r=(e&&e[Symbol.iterator]).call(e);(n=r.next())&&!n.done;){var o=n.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var n,r=[];null!==(n=e.exec(t));)r.push(n);return r},isHTMLForm:T,hasOwnProperty:x,hasOwnProp:x,reduceDescriptors:N,freezeMethods:function(e){N(e,(function(t,n){var r=e[n];m(r)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not read-only method '"+n+"'")}))}))},toObjectSet:function(e,t){var n={},r=function(e){e.forEach((function(e){n[e]=!0}))};return l(e)?r(e):r(String(e).split(t)),n},toCamelCase:function(e){return e.toLowerCase().replace(/[_-\s]([a-z\d])(\w*)/g,(function(e,t,n){return t.toUpperCase()+n}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t}};function _(e,t,n,r,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),o&&(this.response=o)}P.inherits(_,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var B=_.prototype,D={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){D[e]={value:e}})),Object.defineProperties(_,D),Object.defineProperty(B,"isAxiosError",{value:!0}),_.from=function(e,t,n,r,o,i){var s=Object.create(B);return P.toFlatObject(e,s,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),_.call(s,e.message,t,n,r,o),s.cause=e,s.name=e.name,i&&Object.assign(s,i),s};var F="object"==("undefined"==typeof self?"undefined":e(self))?self.FormData:window.FormData;function U(e){return P.isPlainObject(e)||P.isArray(e)}function k(e){return P.endsWith(e,"[]")?e.slice(0,-2):e}function L(e,t,n){return e?e.concat(t).map((function(e,t){return e=k(e),!n&&t?"["+e+"]":e})).join(n?".":""):t}var q=P.toFlatObject(P,{},null,(function(e){return/^is[A-Z]/.test(e)}));function z(t,n,r){if(!P.isObject(t))throw new TypeError("target must be an object");n=n||new(F||FormData);var o,i=(r=P.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!P.isUndefined(t[e])}))).metaTokens,s=r.visitor||l,a=r.dots,u=r.indexes,c=(r.Blob||"undefined"!=typeof Blob&&Blob)&&((o=n)&&P.isFunction(o.append)&&"FormData"===o[Symbol.toStringTag]&&o[Symbol.iterator]);if(!P.isFunction(s))throw new TypeError("visitor must be a function");function f(e){if(null===e)return"";if(P.isDate(e))return e.toISOString();if(!c&&P.isBlob(e))throw new _("Blob is not supported. Use a Buffer instead.");return P.isArrayBuffer(e)||P.isTypedArray(e)?c&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function l(t,r,o){var s=t;if(t&&!o&&"object"===e(t))if(P.endsWith(r,"{}"))r=i?r:r.slice(0,-2),t=JSON.stringify(t);else if(P.isArray(t)&&function(e){return P.isArray(e)&&!e.some(U)}(t)||P.isFileList(t)||P.endsWith(r,"[]")&&(s=P.toArray(t)))return r=k(r),s.forEach((function(e,t){!P.isUndefined(e)&&n.append(!0===u?L([r],t,a):null===u?r:r+"[]",f(e))})),!1;return!!U(t)||(n.append(L(o,r,a),f(t)),!1)}var d=[],h=Object.assign(q,{defaultVisitor:l,convertValue:f,isVisitable:U});if(!P.isObject(t))throw new TypeError("data must be an object");return function e(t,r){if(!P.isUndefined(t)){if(-1!==d.indexOf(t))throw Error("Circular reference detected in "+r.join("."));d.push(t),P.forEach(t,(function(t,o){!0===(!P.isUndefined(t)&&s.call(n,t,P.isString(o)?o.trim():o,r,h))&&e(t,r?r.concat(o):[o])})),d.pop()}}(t),n}function I(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function M(e,t){this._pairs=[],e&&z(e,this,t)}var J=M.prototype;function H(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function V(e,t,n){if(!t)return e;var r=e.indexOf("#");-1!==r&&(e=e.slice(0,r));var o=n&&n.encode||H,i=P.isURLSearchParams(t)?t.toString():new M(t,n).toString(o);return i&&(e+=(-1===e.indexOf("?")?"?":"&")+i),e}J.append=function(e,t){this._pairs.push([e,t])},J.toString=function(e){var t=e?function(t){return e.call(this,t,I)}:I;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var W,K=function(){function e(){t(this,e),this.handlers=[]}return r(e,[{key:"use",value:function(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!n&&n.synchronous,runWhen:n?n.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){P.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),X={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},$="undefined"!=typeof URLSearchParams?URLSearchParams:M,Q=FormData,G=("undefined"==typeof navigator||"ReactNative"!==(W=navigator.product)&&"NativeScript"!==W&&"NS"!==W)&&"undefined"!=typeof window&&"undefined"!=typeof document,Y={isBrowser:!0,classes:{URLSearchParams:$,FormData:Q,Blob:Blob},isStandardBrowserEnv:G,protocols:["http","https","file","blob","url","data"]};function Z(e){function t(e,n,r,o){var i=e[o++],s=Number.isFinite(+i),a=o>=e.length;return i=!i&&P.isArray(r)?r.length:i,a?(P.hasOwnProp(r,i)?r[i]=[r[i],n]:r[i]=n,!s):(r[i]&&P.isObject(r[i])||(r[i]=[]),t(e,n,r[i],o)&&P.isArray(r[i])&&(r[i]=function(e){var t,n,r={},o=Object.keys(e),i=o.length;for(t=0;t0;)if(t===(n=r[o]).toLowerCase())return n;return null}function le(e,t){e&&this.set(e),this[se]=t||null}function de(e,t){var n=0,r=function(e,t){e=e||10;var n,r=new Array(e),o=new Array(e),i=0,s=0;return t=void 0!==t?t:1e3,function(a){var u=Date.now(),c=o[s];n||(n=u),r[i]=a,o[i]=u;for(var f=s,l=0;f!==i;)l+=r[f++],f%=e;if((i=(i+1)%e)===s&&(s=(s+1)%e),!(u-n-1,i=P.isObject(e);if(i&&P.isHTMLForm(e)&&(e=new FormData(e)),P.isFormData(e))return o&&o?JSON.stringify(Z(e)):e;if(P.isArrayBuffer(e)||P.isBuffer(e)||P.isStream(e)||P.isFile(e)||P.isBlob(e))return e;if(P.isArrayBufferView(e))return e.buffer;if(P.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(r.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return z(e,new Y.classes.URLSearchParams,Object.assign({visitor:function(e,t,n,r){return Y.isNode&&P.isBuffer(e)?(this.append(t,e.toString("base64")),!1):r.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((n=P.isFileList(e))||r.indexOf("multipart/form-data")>-1){var s=this.env&&this.env.FormData;return z(n?{"files[]":e}:e,s&&new s,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,n){if(P.isString(e))try{return(t||JSON.parse)(e),P.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(n||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||be.transitional,n=t&&t.forcedJSONParsing,r="json"===this.responseType;if(e&&P.isString(e)&&(n&&!this.responseType||r)){var o=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw _.from(e,_.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:Y.classes.FormData,Blob:Y.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};function ge(e,t){var n=this||be,r=t||n,o=le.from(r.headers),i=r.data;return P.forEach(e,(function(e){i=e.call(n,i,o.normalize(),t?t.status:void 0)})),o.normalize(),i}function Ee(e){return!(!e||!e.__CANCEL__)}function we(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new re}function Oe(e){return we(e),e.headers=le.from(e.headers),e.data=ge.call(e,e.transformRequest),(e.adapter||be.adapter)(e).then((function(t){return we(e),t.data=ge.call(e,e.transformResponse,t),t.headers=le.from(t.headers),t}),(function(t){return Ee(t)||(we(e),t&&t.response&&(t.response.data=ge.call(e,e.transformResponse,t.response),t.response.headers=le.from(t.response.headers))),Promise.reject(t)}))}function Re(e,t){t=t||{};var n={};function r(e,t){return P.isPlainObject(e)&&P.isPlainObject(t)?P.merge(e,t):P.isPlainObject(t)?P.merge({},t):P.isArray(t)?t.slice():t}function o(n){return P.isUndefined(t[n])?P.isUndefined(e[n])?void 0:r(void 0,e[n]):r(e[n],t[n])}function i(e){if(!P.isUndefined(t[e]))return r(void 0,t[e])}function s(n){return P.isUndefined(t[n])?P.isUndefined(e[n])?void 0:r(void 0,e[n]):r(void 0,t[n])}function a(n){return n in t?r(e[n],t[n]):n in e?r(void 0,e[n]):void 0}var u={url:i,method:i,data:i,baseURL:s,transformRequest:s,transformResponse:s,paramsSerializer:s,timeout:s,timeoutMessage:s,withCredentials:s,adapter:s,responseType:s,xsrfCookieName:s,xsrfHeaderName:s,onUploadProgress:s,onDownloadProgress:s,decompress:s,maxContentLength:s,maxBodyLength:s,beforeRedirect:s,transport:s,httpAgent:s,httpsAgent:s,cancelToken:s,socketPath:s,responseEncoding:s,validateStatus:a};return P.forEach(Object.keys(e).concat(Object.keys(t)),(function(e){var t=u[e]||o,r=t(e);P.isUndefined(r)&&t!==a||(n[e]=r)})),n}P.forEach(["delete","get","head"],(function(e){be.headers[e]={}})),P.forEach(["post","put","patch"],(function(e){be.headers[e]=P.merge(ve)}));var Se="1.1.2",Ae={};["object","boolean","number","function","string","symbol"].forEach((function(t,n){Ae[t]=function(r){return e(r)===t||"a"+(n<1?"n ":" ")+t}}));var je={};Ae.transitional=function(e,t,n){function r(e,t){return"[Axios v1.1.2] Transitional option '"+e+"'"+t+(n?". "+n:"")}return function(n,o,i){if(!1===e)throw new _(r(o," has been removed"+(t?" in "+t:"")),_.ERR_DEPRECATED);return t&&!je[o]&&(je[o]=!0,console.warn(r(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(n,o,i)}};var Te={assertOptions:function(t,n,r){if("object"!==e(t))throw new _("options must be an object",_.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(t),i=o.length;i-- >0;){var s=o[i],a=n[s];if(a){var u=t[s],c=void 0===u||a(u,s,t);if(!0!==c)throw new _("option "+s+" must be "+c,_.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new _("Unknown option "+s,_.ERR_BAD_OPTION)}},validators:Ae},xe=Te.validators,Ce=function(){function e(n){t(this,e),this.defaults=n,this.interceptors={request:new K,response:new K}}return r(e,[{key:"request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var n=(t=Re(this.defaults,t)).transitional;void 0!==n&&Te.assertOptions(n,{silentJSONParsing:xe.transitional(xe.boolean),forcedJSONParsing:xe.transitional(xe.boolean),clarifyTimeoutError:xe.transitional(xe.boolean)},!1),t.method=(t.method||this.defaults.method||"get").toLowerCase();var r=t.headers&&P.merge(t.headers.common,t.headers[t.method]);r&&P.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete t.headers[e]})),t.headers=new le(t.headers,r);var o=[],i=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(i=i&&e.synchronous,o.unshift(e.fulfilled,e.rejected))}));var s,a=[];this.interceptors.response.forEach((function(e){a.push(e.fulfilled,e.rejected)}));var u,c=0;if(!i){var f=[Oe.bind(this),void 0];for(f.unshift.apply(f,o),f.push.apply(f,a),u=f.length,s=Promise.resolve(t);c0;)o._listeners[t](e);o._listeners=null}})),this.promise.then=function(e){var t,n=new Promise((function(e){o.subscribe(e),t=e})).then(e);return n.cancel=function(){o.unsubscribe(t)},n},n((function(e,t,n){o.reason||(o.reason=new re(e,t,n),r(o.reason))}))}return r(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Pe=function e(t){var n=new Ce(t),r=o(Ce.prototype.request,n);return P.extend(r,Ce.prototype,n,{allOwnKeys:!0}),P.extend(r,n,null,{allOwnKeys:!0}),r.create=function(n){return e(Re(t,n))},r}(be);return Pe.Axios=Ce,Pe.CanceledError=re,Pe.CancelToken=Ne,Pe.isCancel=Ee,Pe.VERSION=Se,Pe.toFormData=z,Pe.AxiosError=_,Pe.Cancel=Pe.CanceledError,Pe.all=function(e){return Promise.all(e)},Pe.spread=function(e){return function(t){return e.apply(null,t)}},Pe.isAxiosError=function(e){return P.isObject(e)&&!0===e.isAxiosError},Pe.formToJSON=function(e){return Z(P.isHTMLForm(e)?new FormData(e):e)},Pe})); \ No newline at end of file diff --git a/html/login/index.html b/html/login/index.html index b0c2da362..d77ce8f8a 100644 --- a/html/login/index.html +++ b/html/login/index.html @@ -147,7 +147,7 @@ - +