diff --git a/config-service/src/main/scala/org/apache/texera/service/resource/ConfigResource.scala b/config-service/src/main/scala/org/apache/texera/service/resource/ConfigResource.scala index 2cb951d01e4..4f0c468abd1 100644 --- a/config-service/src/main/scala/org/apache/texera/service/resource/ConfigResource.scala +++ b/config-service/src/main/scala/org/apache/texera/service/resource/ConfigResource.scala @@ -76,7 +76,7 @@ class ConfigResource { ) @GET - @RolesAllowed(Array("REGULAR", "ADMIN")) + @PermitAll @Path("/user-system") def getUserSystemConfig: Map[String, Any] = Map( diff --git a/config-service/src/test/scala/org/apache/texera/service/resource/ConfigResourceAuthSpec.scala b/config-service/src/test/scala/org/apache/texera/service/resource/ConfigResourceAuthSpec.scala index da912843346..2fd36c272d4 100644 --- a/config-service/src/test/scala/org/apache/texera/service/resource/ConfigResourceAuthSpec.scala +++ b/config-service/src/test/scala/org/apache/texera/service/resource/ConfigResourceAuthSpec.scala @@ -36,13 +36,15 @@ import org.scalatest.matchers.should.Matchers // Wires ConfigResource through the same Jersey auth pipeline production uses // (JwtAuthFilter + RolesAllowedDynamicFeature) and fires HTTP requests with and -// without an Authorization header. /config/pre-login is the only @PermitAll -// endpoint and must answer unauthenticated callers (bootstrap regression guard, +// without an Authorization header. /config/pre-login and /config/user-system are +// @PermitAll and must answer unauthenticated callers (bootstrap regression guard, // same shape as the break that caused PR #5049 to be reverted in #5173). -// /config/gui and /config/user-system are @RolesAllowed; they must reject -// anonymous traffic with a 401 (now from JwtAuthFilter's eager check, not -// from a downstream RolesAllowedRequestFilter 403) and accept callers with a -// valid Bearer token. +// /config/user-system stays anonymous because a freshly-registered user is INACTIVE +// (so cannot reach @RolesAllowed endpoints) yet the frontend needs its inviteOnly +// flag to decide whether to show the registration-request form. +// /config/gui is @RolesAllowed; it must reject anonymous traffic with a 401 (now +// from JwtAuthFilter's eager check, not from a downstream RolesAllowedRequestFilter +// 403) and accept callers with a valid Bearer token. class ConfigResourceAuthSpec extends AnyFlatSpec with Matchers with BeforeAndAfterAll { // Mirror production's mapper: ConfigService bootstraps Dropwizard's default mapper @@ -136,14 +138,24 @@ class ConfigResourceAuthSpec extends AnyFlatSpec with Matchers with BeforeAndAft ) } - "GET /config/user-system" should "return 401 with a Bearer challenge without an Authorization header" in { + "GET /config/user-system" should "return 200 without an Authorization header" in { + // @PermitAll: a freshly-registered user is INACTIVE and cannot reach the + // @RolesAllowed endpoints, yet the frontend needs inviteOnly at exactly that + // point to decide whether to show the registration-request form. val response = resources.target("/config/user-system").request(MediaType.APPLICATION_JSON).get() - response.getStatus shouldBe 401 - response.getHeaderString("WWW-Authenticate") shouldBe JwtAuthFilter.BearerChallenge + response.getStatus shouldBe 200 } - it should "return 200 with a valid Bearer token whose role matches @RolesAllowed" in { + it should "expose exactly the inviteOnly flag and nothing else" in { + val payload = resources + .target("/config/user-system") + .request(MediaType.APPLICATION_JSON) + .get(classOf[Map[String, Any]]) + payload.keySet shouldBe Set("inviteOnly") + } + + it should "also return 200 with a valid Bearer token" in { val response = resources .target("/config/user-system") .request(MediaType.APPLICATION_JSON)