Przeglądaj źródła

feature: Policy support - allow hiding daignostics and logs (#569)

James Read 1 rok temu
rodzic
commit
c19428f6b6

+ 14 - 0
integration-tests/configs/policy-all-false/config.yaml

@@ -0,0 +1,14 @@
+# Integration Test Config: Policy All False
+#
+
+logLevel: "DEBUG"
+checkForUpdates: false
+
+defaultPolicy:
+  showDiagnostics: false
+  showLogList: false
+
+actions:
+- title: sleep 2 seconds
+  shell: sleep 2
+  icon: "&#x1F971"

+ 9 - 0
integration-tests/test/general.mjs

@@ -37,6 +37,15 @@ describe('config: general', function () {
     */
   })
 
+  it('navbar contains default policy links', async function () {
+    await getRootAndWait()
+
+    const logListLink = await webdriver.findElements(By.css('[href="/logs"]'))
+    expect(logListLink).to.not.be.empty
+
+    const diagnosticsLink = await webdriver.findElements(By.css('[href="/diagnostics"]'))
+    expect(diagnosticsLink).to.not.be.empty
+  })
 
   it('Footer contains promo', async function () {
     const ftr = await webdriver.findElement(By.tagName('footer')).getText()

+ 32 - 0
integration-tests/test/policy-all-false.mjs

@@ -0,0 +1,32 @@
+import {
+  getRootAndWait,
+  takeScreenshotOnFailure,
+} from '../lib/elements.js'
+
+import { By } from 'selenium-webdriver'
+import { expect } from 'chai'
+
+describe('config: policy-all-false', function () {
+  before(async function () {
+    await runner.start('policy-all-false')
+  });
+
+  after(async () => {
+    await runner.stop()
+  });
+
+  afterEach(function () {
+    takeScreenshotOnFailure(this.currentTest, webdriver);
+  });
+
+
+  it('navbar should not contain default policy links', async function () {
+    await getRootAndWait()
+
+    const logListLink = await webdriver.findElements(By.css('[href="/logs"]'))
+    expect(logListLink).to.be.empty
+
+    const diagnosticsLink = await webdriver.findElements(By.css('[href="/diagnostics"]'))
+    expect(diagnosticsLink).to.be.empty
+  })
+})

+ 13 - 0
proto/olivetin/api/v1/olivetin.proto

@@ -49,6 +49,19 @@ message GetDashboardComponentsResponse {
 
 	string authenticated_user = 5;
     string authenticated_user_provider = 6;
+
+	EffectivePolicy effective_policy = 7;
+	Diagnostics diagnostics = 8;
+}
+
+message Diagnostics {
+	string SshFoundKey = 1;
+	string SshFoundConfig = 2;
+}
+
+message EffectivePolicy {
+	bool show_diagnostics = 1;
+	bool show_log_list = 2;
 }
 
 message GetDashboardComponentsRequest {}

+ 42 - 2
service/internal/acl/acl.go

@@ -32,6 +32,8 @@ type AuthenticatedUser struct {
 	SID      string
 
 	Acls []string
+
+	EffectivePolicy *config.ConfigurationPolicy
 }
 
 func (u *AuthenticatedUser) IsGuest() bool {
@@ -43,15 +45,22 @@ func logAclNotMatched(cfg *config.Config, aclFunction string, user *Authenticate
 		log.WithFields(log.Fields{
 			"User":   user.Username,
 			"Action": action.Title,
-		}).Debugf("%v - No ACLs Matched", aclFunction)
+			"ACL":    acl.Name,
+		}).Debugf("%v - ACL Not Matched", aclFunction)
 	}
 }
 
 func logAclMatched(cfg *config.Config, aclFunction string, user *AuthenticatedUser, action *config.Action, acl *config.AccessControlList) {
+	actionTitle := "N/A"
+
+	if action != nil {
+		actionTitle = action.Title
+	}
+
 	if cfg.LogDebugOptions.AclMatched {
 		log.WithFields(log.Fields{
 			"User":   user.Username,
-			"Action": action.Title,
+			"Action": actionTitle,
 			"ACL":    acl.Name,
 		}).Debugf("%v - Matched ACL", aclFunction)
 	}
@@ -209,6 +218,8 @@ func buildUserAcls(cfg *config.Config, user *AuthenticatedUser) {
 			continue
 		}
 	}
+
+	user.EffectivePolicy = getEffectivePolicy(cfg, user)
 }
 
 func hasGroupsMatch(matchUsergroups []string, usergroup string) bool {
@@ -249,3 +260,32 @@ func getRelevantAcls(cfg *config.Config, actionAcls []string, user *Authenticate
 
 	return ret
 }
+
+func getEffectivePolicy(cfg *config.Config, user *AuthenticatedUser) *config.ConfigurationPolicy {
+	ret := &config.ConfigurationPolicy{
+		ShowDiagnostics: cfg.DefaultPolicy.ShowDiagnostics,
+		ShowLogList:     cfg.DefaultPolicy.ShowLogList,
+	}
+
+	for _, acl := range cfg.AccessControlLists {
+		if slices.Contains(user.Acls, acl.Name) {
+			logAclMatched(cfg, "GetEffectivePolicy", user, nil, acl)
+
+			ret = buildConfigurationPolicy(ret, acl.Policy)
+		}
+	}
+
+	return ret
+}
+
+func buildConfigurationPolicy(ret *config.ConfigurationPolicy, policy config.ConfigurationPolicy) *config.ConfigurationPolicy {
+	if policy.ShowDiagnostics {
+		ret.ShowDiagnostics = policy.ShowDiagnostics
+	}
+
+	if policy.ShowLogList {
+		ret.ShowLogList = policy.ShowLogList
+	}
+
+	return ret
+}

+ 11 - 0
service/internal/config/config.go

@@ -76,6 +76,13 @@ type AccessControlList struct {
 	MatchUsergroups  []string
 	MatchUsernames   []string
 	Permissions      PermissionsList
+	Policy           ConfigurationPolicy
+}
+
+// ConfigurationPolicy defines global settings which are overridden with an ACL.
+type ConfigurationPolicy struct {
+	ShowDiagnostics bool
+	ShowLogList     bool
 }
 
 type PrometheusConfig struct {
@@ -123,6 +130,7 @@ type Config struct {
 	AuthOAuth2RedirectURL           string
 	AuthOAuth2Providers             map[string]*OAuth2Provider
 	DefaultPermissions              PermissionsList
+	DefaultPolicy                   ConfigurationPolicy
 	AccessControlLists              []*AccessControlList
 	WebUIDir                        string
 	CronSupportForSeconds           bool
@@ -243,5 +251,8 @@ func DefaultConfigWithBasePort(basePort int) *Config {
 	config.ListenAddressWebUI = fmt.Sprintf("localhost:%d", basePort+3)
 	config.ListenAddressPrometheus = fmt.Sprintf("localhost:%d", basePort+4)
 
+	config.DefaultPolicy.ShowDiagnostics = true
+	config.DefaultPolicy.ShowLogList = true
+
 	return &config
 }

+ 24 - 0
service/internal/grpcapi/grpcApiActions.go

@@ -5,6 +5,7 @@ import (
 	acl "github.com/OliveTin/OliveTin/internal/acl"
 	config "github.com/OliveTin/OliveTin/internal/config"
 	executor "github.com/OliveTin/OliveTin/internal/executor"
+	installationinfo "github.com/OliveTin/OliveTin/internal/installationinfo"
 	sv "github.com/OliveTin/OliveTin/internal/stringvariables"
 	"sort"
 )
@@ -35,9 +36,32 @@ func buildDashboardResponse(ex *executor.Executor, cfg *config.Config, user *acl
 		}
 	})
 
+	res.EffectivePolicy = buildEffectivePolicy(user.EffectivePolicy)
+	res.Diagnostics = buildDiagnostics(res.EffectivePolicy.ShowDiagnostics)
+
 	return res
 }
 
+func buildEffectivePolicy(policy *config.ConfigurationPolicy) *apiv1.EffectivePolicy {
+	ret := &apiv1.EffectivePolicy{
+		ShowDiagnostics: policy.ShowDiagnostics,
+		ShowLogList:     policy.ShowLogList,
+	}
+
+	return ret
+}
+
+func buildDiagnostics(showDiagnostics bool) *apiv1.Diagnostics {
+	ret := &apiv1.Diagnostics{}
+
+	if showDiagnostics {
+		ret.SshFoundKey = installationinfo.Runtime.SshFoundKey
+		ret.SshFoundConfig = installationinfo.Runtime.SshFoundConfig
+	}
+
+	return ret
+}
+
 func buildAction(actionId string, actionBinding *executor.ActionBinding, user *acl.AuthenticatedUser) *apiv1.Action {
 	action := actionBinding.Action
 

+ 0 - 4
service/internal/httpservers/webuiServer.go

@@ -29,8 +29,6 @@ type webUISettings struct {
 	PageTitle              string
 	SectionNavigationStyle string
 	DefaultIconForBack     string
-	SshFoundKey            string
-	SshFoundConfig         string
 	EnableCustomJs         bool
 	AuthLoginUrl           string
 	AuthLocalLogin         bool
@@ -138,8 +136,6 @@ func generateWebUISettings(w http.ResponseWriter, r *http.Request) {
 		PageTitle:              cfg.PageTitle,
 		SectionNavigationStyle: cfg.SectionNavigationStyle,
 		DefaultIconForBack:     cfg.DefaultIconForBack,
-		SshFoundKey:            installationinfo.Runtime.SshFoundKey,
-		SshFoundConfig:         installationinfo.Runtime.SshFoundConfig,
 		EnableCustomJs:         cfg.EnableCustomJs,
 		AuthLoginUrl:           cfg.AuthLoginUrl,
 		AuthLocalLogin:         cfg.AuthLocalUsers.Enabled,

+ 0 - 6
webui.dev/index.html

@@ -35,12 +35,6 @@
 				</ul>
 
 				<ul id = "supplemental-links">
-					<li title = "Diagnostics">
-						<a id = "showDiagnostics">Diagnostics</a>
-					</li>
-					<li title = "Logs">
-						<a id = "showLogs">Logs</a>
-					</li>
 				</ul>
 			</nav>
 

+ 33 - 0
webui.dev/js/NavigationBar.js

@@ -0,0 +1,33 @@
+export class NavigationBar {
+	constructor() {
+		this.navbar = document.getElementsByTagName('nav')[0]
+		this.mainLinks = document.getElementById('navigation-links')
+		this.supplementalLinks = document.getElementById('supplemental-links')
+	}
+
+	createLink(title, url, isSupplemental) {
+		const linkA = document.createElement('a')
+		linkA.href = url
+		linkA.innerText = title
+
+		const navigationLi = document.createElement('li')
+		navigationLi.appendChild(linkA)
+		navigationLi.title = title
+
+		if (isSupplemental) {
+			this.supplementalLinks.appendChild(navigationLi)
+		} else {
+			this.mainLinks.appendChild(navigationLi)
+		}
+	}
+
+	refreshSectionPolicyLinks(policy) {
+		if (policy.showDiagnostics) {
+			this.createLink('Diagnostics', '/diagnostics', true)
+		}
+
+		if (policy.showLogList) {
+			this.createLink('Logs', '/logs', true)
+		}
+	}
+}

+ 15 - 14
webui.dev/js/marshaller.js

@@ -1,4 +1,5 @@
 import './ActionButton.js' // To define action-button
+import { NavigationBar } from './NavigationBar.js'
 import { ExecutionDialog } from './ExecutionDialog.js'
 import { ActionStatusDisplay } from './ActionStatusDisplay.js'
 
@@ -76,6 +77,8 @@ function createAnnotation (key, val) {
  * This is a weird function that just sets some globals.
  */
 export function initMarshaller () {
+  window.navbar = new NavigationBar()
+
   window.showSection = showSection
   window.showSectionView = showSectionView
 
@@ -124,6 +127,10 @@ export function marshalDashboardComponentsJsonToHtml (json) {
 
     marshalActionsJsonToHtml(json)
     marshalDashboardStructureToHtml(json)
+
+	window.navbar.refreshSectionPolicyLinks(json.effectivePolicy)
+
+	refreshDiagnostics(json)
   }
 
   document.body.setAttribute('initial-marshal-complete', 'true')
@@ -342,8 +349,8 @@ export function setupSectionNavigation (style) {
   }
 
   registerSection('/', 'Actions', null, document.getElementById('showActions'))
-  registerSection('/diagnostics', 'Diagnostics', null, document.getElementById('showDiagnostics'))
-  registerSection('/logs', 'Logs', null, document.getElementById('showLogs'))
+  registerSection('/diagnostics', 'Diagnostics', null, null)
+  registerSection('/logs', 'Logs', null, null)
   registerSection('/login', 'Login', null, null)
 }
 
@@ -368,9 +375,9 @@ function addLinkToSection (pathName, element) {
   }
 }
 
-export function refreshDiagnostics () {
-  document.getElementById('diagnostics-sshfoundkey').innerHTML = window.settings.SshFoundKey
-  document.getElementById('diagnostics-sshfoundconfig').innerHTML = window.settings.SshFoundConfig
+function refreshDiagnostics (json) {
+  document.getElementById('diagnostics-sshfoundkey').innerHTML = json.diagnostics.SshFoundKey
+  document.getElementById('diagnostics-sshfoundconfig').innerHTML = json.diagnostics.SshFoundConfig
 }
 
 function getSystemTitle (title) {
@@ -400,17 +407,11 @@ function marshalSingleDashboard (dashboard, nav) {
     oldLi.remove()
   }
 
-  const navigationA = document.createElement('a')
-  navigationA.title = dashboard.title
-  navigationA.innerText = dashboard.title
-
-  registerSection('/' + getSystemTitle(section.title), section.title, null, navigationA)
+  const systemTitleUrl = '/' + getSystemTitle(dashboard.title)
 
-  const navigationLi = document.createElement('li')
-  navigationLi.appendChild(navigationA)
-  navigationLi.title = dashboard.title
+  window.navbar.createLink(dashboard.title, systemTitleUrl, false)
 
-  document.getElementById('navigation-links').appendChild(navigationLi)
+  registerSection(systemTitleUrl, section.title, null, null)
 }
 
 function marshalDashboardStructureToHtml (json) {

+ 0 - 3
webui.dev/main.js

@@ -6,7 +6,6 @@ import {
   marshalDashboardComponentsJsonToHtml,
   marshalLogsJsonToHtml,
   refreshServerConnectionLabel,
-  refreshDiagnostics
 } from './js/marshaller.js'
 import { checkWebsocketConnection } from './js/websocket.js'
 
@@ -139,8 +138,6 @@ function processWebuiSettingsJson (settings) {
   document.getElementsByTagName('main')[0].appendChild(loginForm)
 
   window.settings = settings
-
-  refreshDiagnostics()
 }
 
 function processAdditionalLinks (links) {