mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-11-13 12:23:34 +01:00
101 lines
3.7 KiB
JavaScript
101 lines
3.7 KiB
JavaScript
const { expect } = require('chai')
|
|
const sinon = require('sinon')
|
|
|
|
// Load Database first so Auth resolves OidcAuthStrategy before the circular require completes.
|
|
require('../../../server/Database')
|
|
const OidcAuthStrategy = require('../../../server/auth/OidcAuthStrategy')
|
|
const Logger = require('../../../server/Logger')
|
|
|
|
describe('OidcAuthStrategy - isValidWebCallbackUrl', () => {
|
|
/** @type {OidcAuthStrategy} */
|
|
let strategy
|
|
|
|
beforeEach(() => {
|
|
global.RouterBasePath = ''
|
|
strategy = new OidcAuthStrategy()
|
|
sinon.stub(Logger, 'warn')
|
|
sinon.stub(Logger, 'error')
|
|
})
|
|
|
|
afterEach(() => {
|
|
sinon.restore()
|
|
})
|
|
|
|
function mockReq({ secure = false, host = 'books.example.com', xForwardedProto = null } = {}) {
|
|
return {
|
|
secure,
|
|
get(header) {
|
|
if (header === 'host') return host
|
|
if (header === 'x-forwarded-proto') return xForwardedProto
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
it('accepts a same-origin relative path when router base path is empty', () => {
|
|
expect(strategy.isValidWebCallbackUrl('/library', mockReq())).to.equal(true)
|
|
})
|
|
|
|
it('accepts a same-origin absolute https URL', () => {
|
|
const req = mockReq({ secure: true })
|
|
expect(strategy.isValidWebCallbackUrl('https://books.example.com/library', req)).to.equal(true)
|
|
})
|
|
|
|
it('rejects protocol-relative URLs', () => {
|
|
expect(strategy.isValidWebCallbackUrl('//evil.example/capture', mockReq())).to.equal(false)
|
|
})
|
|
|
|
it('rejects backslash-prefixed URLs', () => {
|
|
expect(strategy.isValidWebCallbackUrl('/\\evil.example/capture', mockReq())).to.equal(false)
|
|
})
|
|
|
|
it('rejects absolute external URLs', () => {
|
|
expect(strategy.isValidWebCallbackUrl('http://evil.example/capture', mockReq())).to.equal(false)
|
|
})
|
|
|
|
it('rejects encoded protocol-relative path segments', () => {
|
|
expect(strategy.isValidWebCallbackUrl('/%2F%2Fevil.example/capture', mockReq())).to.equal(false)
|
|
})
|
|
|
|
it('rejects same-origin URLs outside router base path', () => {
|
|
global.RouterBasePath = '/audiobookshelf'
|
|
expect(strategy.isValidWebCallbackUrl('/login', mockReq())).to.equal(false)
|
|
expect(strategy.isValidWebCallbackUrl('/audiobookshelf/login', mockReq())).to.equal(true)
|
|
})
|
|
|
|
it('rejects empty and malformed callback URLs', () => {
|
|
expect(strategy.isValidWebCallbackUrl('', mockReq())).to.equal(false)
|
|
expect(strategy.isValidWebCallbackUrl(null, mockReq())).to.equal(false)
|
|
expect(strategy.isValidWebCallbackUrl('not a url', mockReq())).to.equal(false)
|
|
})
|
|
|
|
it('uses x-forwarded-proto when determining same-origin https URLs', () => {
|
|
const req = mockReq({ xForwardedProto: 'https' })
|
|
expect(strategy.isValidWebCallbackUrl('https://books.example.com/login', req)).to.equal(true)
|
|
expect(strategy.isValidWebCallbackUrl('http://books.example.com/login', req)).to.equal(false)
|
|
})
|
|
})
|
|
|
|
describe('OidcAuthStrategy - openIdAuthSession', () => {
|
|
/** @type {OidcAuthStrategy} */
|
|
let strategy
|
|
|
|
beforeEach(() => {
|
|
global.RouterBasePath = ''
|
|
strategy = new OidcAuthStrategy()
|
|
})
|
|
|
|
it('configures a 10 minute TTL with autopurge so abandoned mobile logins are cleaned up', () => {
|
|
expect(strategy.openIdAuthSession.ttl).to.equal(10 * 60 * 1000)
|
|
expect(strategy.openIdAuthSession.ttlAutopurge).to.equal(true)
|
|
})
|
|
|
|
it('still supports the same set/has/get/delete usage as a plain Map', () => {
|
|
strategy.openIdAuthSession.set('some-state', { mobile_redirect_uri: 'myapp://callback' })
|
|
expect(strategy.openIdAuthSession.has('some-state')).to.equal(true)
|
|
expect(strategy.openIdAuthSession.get('some-state')).to.deep.equal({ mobile_redirect_uri: 'myapp://callback' })
|
|
strategy.openIdAuthSession.delete('some-state')
|
|
expect(strategy.openIdAuthSession.has('some-state')).to.equal(false)
|
|
})
|
|
})
|