From 21e720c420e0ec174f3eb44a5eece1cc0cefdb22 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Sat, 27 Jun 2026 14:28:57 -0400 Subject: [PATCH] Also check Seerr parent permissions (#1631) ## Description When saving Seerr permissions on the WebUI, if a "parent" permission is checked, only its bit is flipped and its children's bits are not. So clients must check if a user has the permission _or_ its parent. For example, if just "Request 4K" is checked, the flags for "Request 4K Movies" and "Request 4K TV" will not set explicitly. ### Related issues Fixes #1630 ### Testing Emulator & unit tests, also manually reviewed API requests/responses on Seerr's web UI ## Screenshots N/A ## AI or LLM usage None --- .../wholphin/data/model/SeerrPermission.kt | 32 ++++---- .../wholphin/test/TestSeerrPermission.kt | 74 +++++++++++++++++++ 2 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 app/src/test/java/com/github/damontecres/wholphin/test/TestSeerrPermission.kt diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/SeerrPermission.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/SeerrPermission.kt index a6886cf2..8e5a5b06 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/SeerrPermission.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/SeerrPermission.kt @@ -7,6 +7,7 @@ import com.github.damontecres.wholphin.services.SeerrUserConfig */ enum class SeerrPermission( private val flag: Int, + vararg val parents: SeerrPermission, ) { // Source: https://github.com/seerr-team/seerr/blob/develop/server/lib/permissions.ts NONE(0), @@ -17,28 +18,28 @@ enum class SeerrPermission( REQUEST(32), VOTE(64), AUTO_APPROVE(128), - AUTO_APPROVE_MOVIE(256), - AUTO_APPROVE_TV(512), + AUTO_APPROVE_MOVIE(256, AUTO_APPROVE), + AUTO_APPROVE_TV(512, AUTO_APPROVE), REQUEST_4K(1024), - REQUEST_4K_MOVIE(2048), - REQUEST_4K_TV(4096), + REQUEST_4K_MOVIE(2048, REQUEST_4K), + REQUEST_4K_TV(4096, REQUEST_4K), REQUEST_ADVANCED(8192), REQUEST_VIEW(16384), AUTO_APPROVE_4K(32768), - AUTO_APPROVE_4K_MOVIE(65536), - AUTO_APPROVE_4K_TV(131072), - REQUEST_MOVIE(262144), - REQUEST_TV(524288), + AUTO_APPROVE_4K_MOVIE(65536, AUTO_APPROVE_4K), + AUTO_APPROVE_4K_TV(131072, AUTO_APPROVE_4K), + REQUEST_MOVIE(262144, REQUEST), + REQUEST_TV(524288, REQUEST), MANAGE_ISSUES(1048576), - VIEW_ISSUES(2097152), - CREATE_ISSUES(4194304), + VIEW_ISSUES(2097152, MANAGE_ISSUES), + CREATE_ISSUES(4194304, MANAGE_ISSUES), AUTO_REQUEST(8388608), - AUTO_REQUEST_MOVIE(16777216), - AUTO_REQUEST_TV(33554432), + AUTO_REQUEST_MOVIE(16777216, AUTO_REQUEST), + AUTO_REQUEST_TV(33554432, AUTO_REQUEST), RECENT_VIEW(67108864), WATCHLIST_VIEW(134217728), MANAGE_BLACKLIST(268435456), - VIEW_BLACKLIST(1073741824), + VIEW_BLACKLIST(1073741824, MANAGE_BLACKLIST), ; internal fun hasPermission(permissions: Int) = flag.and(permissions) == flag @@ -48,5 +49,8 @@ enum class SeerrPermission( * Check whether the user has the given permissions (or is an admin) */ fun SeerrUserConfig?.hasPermission(permission: SeerrPermission): Boolean { - return permission.hasPermission(this?.permissions ?: return false) || SeerrPermission.ADMIN.hasPermission(permissions) + if (this?.permissions == null) return false + return permission.hasPermission(permissions) || + permission.parents.any { hasPermission(it) } || + SeerrPermission.ADMIN.hasPermission(permissions) } diff --git a/app/src/test/java/com/github/damontecres/wholphin/test/TestSeerrPermission.kt b/app/src/test/java/com/github/damontecres/wholphin/test/TestSeerrPermission.kt new file mode 100644 index 00000000..b5dfa5c3 --- /dev/null +++ b/app/src/test/java/com/github/damontecres/wholphin/test/TestSeerrPermission.kt @@ -0,0 +1,74 @@ +package com.github.damontecres.wholphin.test + +import com.github.damontecres.wholphin.data.model.SeerrPermission +import com.github.damontecres.wholphin.data.model.hasPermission +import com.github.damontecres.wholphin.services.SeerrUserConfig +import org.junit.Assert +import org.junit.Test + +class TestSeerrPermission { + @Test + fun `Is Admin`() { + val user = + SeerrUserConfig( + id = 0, + permissions = 2, + ) + Assert.assertTrue(user.hasPermission(SeerrPermission.ADMIN)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_TV)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K)) + } + + @Test + fun `Request any 4k has request movie 4k`() { + val user = + SeerrUserConfig( + id = 0, + permissions = 1024, + ) + Assert.assertFalse(user.hasPermission(SeerrPermission.ADMIN)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_TV)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K)) + } + + @Test + fun `Request movie 4k and does not have request tv 4k`() { + val user = + SeerrUserConfig( + id = 0, + permissions = 2048, + ) + Assert.assertFalse(user.hasPermission(SeerrPermission.ADMIN)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)) + Assert.assertFalse(user.hasPermission(SeerrPermission.REQUEST_4K_TV)) + Assert.assertFalse(user.hasPermission(SeerrPermission.REQUEST_4K)) + } + + @Test + fun `Has explicit request 4k and children`() { + val user = + SeerrUserConfig( + id = 0, + permissions = 7168, + ) + Assert.assertFalse(user.hasPermission(SeerrPermission.ADMIN)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_TV)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K)) + } + + @Test + fun `Has explicit request 4k children`() { + val user = + SeerrUserConfig( + id = 0, + permissions = 6144, + ) + Assert.assertFalse(user.hasPermission(SeerrPermission.ADMIN)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_MOVIE)) + Assert.assertTrue(user.hasPermission(SeerrPermission.REQUEST_4K_TV)) + Assert.assertFalse(user.hasPermission(SeerrPermission.REQUEST_4K)) + } +}