Explorar o código

fix: restart the event stream when the authenticated user changes

The server filters stream events by the identity the stream connected
with, so after a guest logged in, execution feedback for actions only
the new user could see never arrived until a page refresh.

Closes #1148

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jamesread hai 1 día
pai
achega
497a79587d

+ 6 - 1
frontend/resources/vue/App.vue

@@ -184,7 +184,7 @@ import Navigation from 'picocrank/vue/components/Navigation.vue'
 import Header from 'picocrank/vue/components/Header.vue'
 import QuickSearch from 'picocrank/vue/components/QuickSearch.vue'
 import ConnectionBanner from './components/ConnectionBanner.vue'
-import { connectEventStreamIfNeeded } from '../../js/websocket.js'
+import { connectEventStreamIfNeeded, stopEventStream } from '../../js/websocket.js'
 import { DashboardSquare01Icon } from '@hugeicons/core-free-icons'
 import logoUrl from '../../OliveTinLogo.png'
 import { useI18n } from 'vue-i18n'
@@ -295,6 +295,11 @@ async function updateHeaderFromInit () {
   loginRequired.value = !!window.initResponse.loginRequired
   headerSearchEnabled.value = !!window.initResponse.features?.headerSearch
 
+  // The server filters stream events by the identity the stream connected with.
+  if (username.value !== authenticatedUser) {
+    stopEventStream()
+  }
+
   username.value = authenticatedUser
   isLoggedIn.value = authenticatedUser !== '' && authenticatedUser !== 'guest'
   currentVersion.value = window.initResponse.currentVersion

+ 50 - 0
integration-tests/tests/authChangeEventStream/authChangeEventStream.mjs

@@ -0,0 +1,50 @@
+import { By, until } from 'selenium-webdriver'
+import {
+  getRootAndWait,
+  takeScreenshotOnFailure,
+} from '../../lib/elements.js'
+
+const UI_WAIT_MS = 5000
+
+function actionButtonLocator (title) {
+  return By.xpath(`//*[contains(@class, 'action-button')]//button[contains(., '${title}')]`)
+}
+
+async function clickActionAndWaitForSuccess (title) {
+  const button = await webdriver.wait(until.elementLocated(actionButtonLocator(title)), UI_WAIT_MS)
+  await button.click()
+
+  await webdriver.wait(async () => {
+    const classes = await button.getAttribute('class')
+    return classes.includes('action-success')
+  }, UI_WAIT_MS)
+}
+
+describe('config: authChangeEventStream', function () {
+  before(async function () {
+    await runner.start('authChangeEventStream')
+  })
+
+  after(async () => {
+    await runner.stop()
+  })
+
+  afterEach(function () {
+    takeScreenshotOnFailure(this.currentTest, webdriver)
+  })
+
+  it('delivers execution events for the new user after logging in without a page refresh', async () => {
+    await getRootAndWait()
+    await clickActionAndWaitForSuccess('Guest action')
+
+    const loginLink = await webdriver.wait(until.elementLocated(By.css('a.login-link')), UI_WAIT_MS)
+    await loginLink.click()
+
+    const username = await webdriver.wait(until.elementLocated(By.id('username')), UI_WAIT_MS)
+    await username.sendKeys('alice')
+    await webdriver.findElement(By.id('password')).sendKeys('password')
+    await webdriver.findElement(By.css('button.login-button')).click()
+
+    await clickActionAndWaitForSuccess('Admin action')
+  })
+})

+ 46 - 0
integration-tests/tests/authChangeEventStream/config.yaml

@@ -0,0 +1,46 @@
+#
+# Integration Test Config: Event stream identity follows login
+#
+
+listenAddressSingleHTTPFrontend: 0.0.0.0:1337
+
+logLevel: "DEBUG"
+checkForUpdates: false
+authRequireGuestsToLogin: false
+
+authLocalUsers:
+  enabled: true
+  users:
+    - username: "alice"
+      usergroup: "admin"
+      password: "$argon2id$v=19$m=65536,t=4,p=6$ORxyZZGW6E3FWZnbQmHJ9Q$BzIOWeXry/BZ6+JV1T4UASBnebVLB9QJ4f5TmUPXsg4" # notsecret: password
+
+defaultPermissions:
+  view: false
+  exec: false
+  logs: false
+
+accessControlLists:
+  - name: admin
+    addToEveryAction: true
+    matchUsergroups: ["admin"]
+    permissions:
+      view: true
+      exec: true
+      logs: true
+
+  - name: guests
+    matchUsernames: ["guest"]
+    permissions:
+      view: true
+      exec: true
+      logs: true
+
+actions:
+  - title: Guest action
+    shell: echo guest
+    acls:
+      - guests
+
+  - title: Admin action
+    shell: echo admin