Skip to content

Commit 09fa161

Browse files
authored
chore(jellyfin): adjust default allow_ttl to 1d (#84)
1 parent a2b5d5a commit 09fa161

File tree

6 files changed

+100
-48
lines changed

6 files changed

+100
-48
lines changed

lib/doorbell/plugins/jellyfin.lua

+13-8
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ local log = require("doorbell.log").with_namespace(_M.name)
2828

2929
---@class doorbell.config.plugin.jellyfin
3030
---
31-
---@field url string # jellyfin address
32-
---@field api? string # jellyfin API address (derived from `url` if not set)
33-
---@field allow_ttl? integer
31+
---@field url string # jellyfin address
32+
---@field api? string # jellyfin API address (derived from `url` if not set)
33+
---@field allow_ttl? integer # duration (in seconds) to create `allow` rules for
3434

3535
local STATES = const.states
3636
local SHM = shm.with_namespace("jf")
@@ -41,14 +41,16 @@ local HOST
4141
---@type string
4242
local API_URL
4343

44-
local ALLOW_TTL = 60 * 60
44+
---@type string
45+
local AUTH_CHECK_URL
46+
47+
local ALLOW_TTL = 60 * 60 * 24
4548

4649
local LOGIN_PATH = "/users/authenticatebyname"
4750
local LOGOUT_PATH = "/sessions/logout"
4851
local AUTH_QUERY_PARAM = lower("api_key")
4952
local AUTH_SELF = "/Users/Me"
5053
local AUTH_SELF_HEADER = "X-Emby-Token"
51-
local AUTH_CHECK_URL
5254
local UNKNOWN = "<unknown>"
5355

5456
local TOKEN_RE = [=[Token=['"]([^'"]+)['"]]=]
@@ -79,10 +81,10 @@ local function check_jellyfin_token(token)
7981
json, err = cjson.decode(res.body)
8082
if not json then
8183
log.warn("`GET /Users/Me` returned invalid json: ", err)
82-
return true
84+
return nil, "json decode error: " .. err
8385
end
8486

85-
return json.Name or json.name or true
87+
return json.Name or json.name or UNKNOWN
8688

8789
else
8890
return false, nil, 5
@@ -92,7 +94,7 @@ end
9294
---@param token string
9395
---@return string
9496
local function auth_cache_key(token)
95-
return "doorbell.auth_token." .. sha256(token)
97+
return "jellyfin.auth_token." .. sha256(token)
9698
end
9799

98100
---@param token string
@@ -314,6 +316,9 @@ local function timer_handler()
314316
source = "plugin",
315317
plugin = "jellyfin",
316318
comment = fmt("allowed via jellyfin plugin for user %q", user),
319+
meta = {
320+
["jellyfin.user"] = user,
321+
},
317322
})
318323

319324
if rule then

spec/02-integration/99-plugins/01-jellyfin_spec.lua

+16-30
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe("jellyfin", function()
2626
jellyfin = {
2727
url = "http://jellyfin",
2828
api = fmt("http://127.0.0.1:%s", const.MOCK_UPSTREAM_PORT),
29-
allow_ttl = 3,
29+
allow_ttl = 2,
3030
}
3131
}
3232
end
@@ -123,11 +123,7 @@ describe("jellyfin", function()
123123
assert.is_nil(client.err)
124124
assert.equals(200, client.response.status)
125125

126-
local res, req = ms.mock.get_last()
127-
assert.is_table(res)
128-
assert.equals(200, res.status)
129-
assert.is_table(req)
130-
assert.is_table(req.headers)
126+
local req = ms.mock.assert_request_received()
131127
assert.equals(token, req.headers["x-emby-token"])
132128
end
133129

@@ -179,6 +175,8 @@ describe("jellyfin", function()
179175
client:send()
180176
assert.is_Nil(client.err)
181177
assert.equals(200, client.response.status)
178+
179+
ms.mock.assert_no_request_received()
182180
end)
183181
end)
184182

@@ -207,16 +205,12 @@ describe("jellyfin", function()
207205
assert.is_nil(client.err)
208206
assert.equals(200, client.response.status)
209207

210-
local res, req = ms.mock.get_last()
211-
assert.is_table(res)
212-
assert.equals(200, res.status)
213-
assert.is_table(req)
214-
assert.is_table(req.headers)
208+
local req = ms.mock.assert_request_received()
215209
assert.equals(token, req.headers["x-emby-token"])
216210

217211
local api = nginx:add_client(test.client())
218212
test.await.truthy(function()
219-
res = api:get("/rules")
213+
local res = api:get("/rules")
220214
assert.is_nil(api.err)
221215
assert.equals(200, res.status)
222216
assert.is_table(res.json)
@@ -234,7 +228,7 @@ describe("jellyfin", function()
234228
end
235229

236230
return false
237-
end, 5, 1)
231+
end, 5)
238232
end)
239233

240234
it("recreates allow rules as necessary", function()
@@ -262,19 +256,15 @@ describe("jellyfin", function()
262256
assert.is_nil(client.err)
263257
assert.equals(200, client.response.status)
264258

265-
local res, req = ms.mock.get_last()
266-
assert.is_table(res)
267-
assert.equals(200, res.status)
268-
assert.is_table(req)
269-
assert.is_table(req.headers)
259+
local req = ms.mock.assert_request_received()
270260
assert.equals(token, req.headers["x-emby-token"])
271261

272262
local api = nginx:add_client(test.client())
273263

274264
local rule_id
275265

276266
local function have_rule()
277-
res = api:get("/rules")
267+
local res = api:get("/rules")
278268
assert.is_nil(api.err)
279269
assert.equals(200, res.status)
280270
assert.is_table(res.json)
@@ -297,16 +287,16 @@ describe("jellyfin", function()
297287
return false
298288
end
299289

300-
test.await.truthy(have_rule, 5, 1)
301-
test.await.falsy(have_rule, 10, 1)
290+
test.await.truthy(have_rule, 5)
291+
test.await.falsy(have_rule, 10)
302292

303293
local old_id = rule_id
304294

305295
client:send()
306296
assert.is_nil(client.err)
307297
assert.equals(200, client.response.status)
308298

309-
test.await.truthy(have_rule, 5, 1)
299+
test.await.truthy(have_rule, 5)
310300
assert.not_equal(rule_id, old_id)
311301
end)
312302

@@ -359,17 +349,13 @@ describe("jellyfin", function()
359349
assert.is_nil(client.err)
360350
assert.equals(200, client.response.status)
361351

362-
local res, req = ms.mock.get_last()
363-
assert.is_table(res)
364-
assert.equals(200, res.status)
365-
assert.is_table(req)
366-
assert.is_table(req.headers)
352+
local req = ms.mock.assert_request_received()
367353
assert.equals(token, req.headers["x-emby-token"])
368354

369355
local api = nginx:add_client(test.client())
370356

371357
local function have_rule()
372-
res = api:get("/rules")
358+
local res = api:get("/rules")
373359
assert.is_nil(api.err)
374360
assert.equals(200, res.status)
375361
assert.is_table(res.json)
@@ -389,7 +375,7 @@ describe("jellyfin", function()
389375
return false
390376
end
391377

392-
test.await.truthy(have_rule, 5, 1)
378+
test.await.truthy(have_rule, 5)
393379

394380
client:add_x_forwarded_headers(addr, "POST", "http://jellyfin/Sessions/Logout")
395381
client.headers["X-Mediabrowser-Token"] = token
@@ -398,7 +384,7 @@ describe("jellyfin", function()
398384
assert.equals(200, client.response.status)
399385

400386
-- wait for the rule to expire
401-
test.await.falsy(have_rule, 10, 1)
387+
test.await.falsy(have_rule, 10)
402388

403389
ms.mock.prepare({
404390
path = "/Users/Me",

spec/testing.lua

+12
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,26 @@ _M.fs = require("spec.testing.fs")
4242

4343

4444
_M.await = {
45+
---@param fn function
46+
---@param timeout? number
47+
---@param step? number
48+
---@param msg any
4549
truthy = function(fn, timeout, step, msg)
4650
assert(await.truthy(timeout, step, fn), msg or "timeout reached", 2)
4751
end,
4852

53+
---@param fn function
54+
---@param timeout? number
55+
---@param step? number
56+
---@param msg any
4957
falsy = function(fn, timeout, step, msg)
5058
assert(await.falsy(timeout, step, fn), msg or "timeout reached", 2)
5159
end,
5260

61+
---@param fn function
62+
---@param timeout? number
63+
---@param step? number
64+
---@param msg any
5365
no_error = function(fn, timeout, step, msg)
5466
local wrapped = function(...)
5567
return pcall(fn, ...)

spec/testing/await.lua

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
1+
local function back_off(step)
2+
return (step * 1.25) + (0.01 * math.random())
3+
end
4+
5+
local function no_back_off(step)
6+
return step
7+
end
8+
19
---@param timeout? number
210
---@param step? number
311
---@param fn function
412
---@param ... any
513
---@return boolean ok
614
---@return number elapsed
715
local function await(timeout, step, fn, ...)
8-
step = step or 0.05
16+
local next_step
17+
if step then
18+
next_step = no_back_off
19+
20+
else
21+
step = 0.01
22+
next_step = back_off
23+
end
24+
925
timeout = timeout or 5
1026

1127
ngx.update_time()
1228
local start = ngx.now()
1329
local deadline = start + timeout
1430

15-
repeat
31+
while true do
1632
if fn(...) then
1733
ngx.update_time()
1834
return true, ngx.now() - start
1935
end
2036

21-
ngx.sleep(step)
2237
ngx.update_time()
23-
until ngx.now() >= deadline
38+
local time = ngx.now()
39+
local remain = deadline - time
40+
if remain < 0 then
41+
break
42+
end
43+
44+
step = math.min(next_step(step), remain)
45+
ngx.sleep(step)
46+
end
2447

2548
return false, ngx.now() - start
2649
end

spec/testing/mock-upstream.lua

+31-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ local cjson_safe = require "cjson.safe"
66
local http = require "doorbell.http"
77
local uuid = require("resty.jit-uuid").generate_v4
88
local resty_http = require "resty.http"
9+
local luassert = require "luassert"
910

1011
local SHM = ngx.shared.mock
1112
local EMPTY = {}
@@ -142,7 +143,7 @@ local function get_request()
142143

143144
local errors = {}
144145
local body = http.request.get_raw_body()
145-
local json = ngx.null
146+
local json
146147

147148
if body and content_type:lower():find("application/json") then
148149
local err
@@ -155,18 +156,22 @@ local function get_request()
155156

156157
body = body or ngx.null
157158

158-
---@class spec.testing.mock-upstream.request : table
159-
return {
159+
---@class spec.testing.mock-upstream.request
160+
local req = {
160161
headers = headers,
161162
method = ngx.req.get_method(),
162163
host = ngx.var.host:lower(),
163164
uri = ngx.var.request_uri,
164165
path = ngx.var.request_uri:gsub("%?.*", ""),
165166
query = ngx.req.get_uri_args(1000),
167+
---@type string|nil
166168
body = body,
169+
---@type any|nil
167170
json = json,
168171
errors = errors,
169172
}
173+
174+
return req
170175
end
171176

172177
---@param res spec.testing.mock-upstream.response
@@ -322,17 +327,38 @@ mock.mock = {
322327
assert(res.status == 200)
323328
end,
324329

330+
---@return spec.testing.mock-upstream.request|nil
325331
get_last = function()
326332
local res = mocker_send("/_/last")
327333
assert(res.status == 200 or res.status == 404)
328-
return res, cjson.decode(res.body)
334+
335+
if res.status == 404 then
336+
return
337+
end
338+
339+
return cjson.decode(res.body)
329340
end,
330341

331342
reset = function()
332343
local res = mocker_send("/_/reset", nil, { method = "POST" })
333344
assert(res.status == 200)
334345
end,
335-
336346
}
337347

348+
mock.mock.assert_no_request_received = function()
349+
local req = mock.mock.get_last()
350+
luassert.is_nil(req, "expected no recent requests to the mock upstream")
351+
end
352+
353+
354+
---@return spec.testing.mock-upstream.request
355+
mock.mock.assert_request_received = function()
356+
local req = mock.mock.get_last()
357+
luassert.not_nil(req, "expected the mock upstream to have received a request")
358+
luassert.is_table(req)
359+
luassert.is_table(req.headers)
360+
361+
return req
362+
end
363+
338364
return mock

spec/testing/nginx.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ end
3434
---@param timeout? number
3535
local function wait_pid(pid, fn, timeout)
3636
timeout = timeout or 5
37-
return await.truthy(timeout, 0.05, fn, pid)
37+
return await.truthy(timeout, nil, fn, pid)
3838
end
3939

4040

0 commit comments

Comments
 (0)