Add canStream user permission to control streaming access

Adds a per-user "Can Stream" permission mirroring the existing "Can Download"
pattern. Server admins can now disable streaming for specific users, encouraging
local downloads instead. Addresses #2572.

Changes:
- User model: stream permission in mapping, defaults, and getter
- ApiKey model: stream permission in defaults
- Controller: 403 enforcement on playback session creation endpoints
- Frontend: permission toggle in admin UI, play button gated by canStream,
  download button shown when streaming disabled, message when neither allowed
- Tests: 11 Mocha tests (model + controller), 1 Cypress test (card UI)
- Localization: English strings for toggle label and fallback message

The getter uses !== false (rather than !!) so existing users without the
stream key in their permissions JSON default to allowed on upgrade.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
zriddle 2026-04-02 14:12:08 -06:00
parent 64cbf59609
commit 464b720d9e
11 changed files with 236 additions and 1 deletions

View file

@ -42,6 +42,7 @@ function createMountOptions() {
'user/getUserCanUpdate': true,
'user/getUserCanDelete': true,
'user/getUserCanDownload': true,
'user/getUserCanStream': true,
'user/getIsAdminOrUp': true,
'user/getUserMediaProgress': (id) => null,
'user/getUserSetting': (settingName) => false,
@ -163,6 +164,15 @@ describe('LazyBookCard', () => {
cy.get('&ebookFormat').should('not.exist')
})
it('hides play button on mouseover when user cannot stream', () => {
mountOptions.mocks.$store.getters['user/getUserCanStream'] = false
cy.mount(LazyBookCard, mountOptions)
cy.get('#book-card-0').trigger('mouseover')
cy.get('&overlay').should('be.visible')
cy.get('&playButton').should('be.hidden')
})
it('routes to item page when clicked', () => {
mountOptions.mocks.$router = { push: cy.stub().as('routerPush') }
cy.mount(LazyBookCard, mountOptions)