diff --git a/daikoku/app/controllers/ApiController.scala b/daikoku/app/controllers/ApiController.scala index 104b60a52..592b17155 100644 --- a/daikoku/app/controllers/ApiController.scala +++ b/daikoku/app/controllers/ApiController.scala @@ -3394,13 +3394,21 @@ class ApiController( if (ctx.user.isDaikokuAdmin) Json.obj() else Json.obj("users.userId" -> ctx.user.id.value) + val typeFilter = if (ctx.tenant.subscriptionSecurity.isDefined + && ctx.tenant.subscriptionSecurity.exists(identity)) { + Json.obj( + "type" -> Json.obj("$ne" -> TeamType.Personal.name) + ) + } else { + Json.obj() + } for { myTeams <- env.dataStore.teamRepo.myTeams(ctx.tenant, ctx.user) teams <- env.dataStore.teamRepo .forTenant(ctx.tenant.id) .findNotDeleted( - Json.obj("name" -> searchAsRegex) ++ teamUsersFilter, + Json.obj("name" -> searchAsRegex) ++ teamUsersFilter ++ typeFilter, 5, Json.obj("name" -> 1).some ) diff --git a/daikoku/app/domain/CommonServices.scala b/daikoku/app/domain/CommonServices.scala index 9c0b03ccb..f39b16368 100644 --- a/daikoku/app/domain/CommonServices.scala +++ b/daikoku/app/domain/CommonServices.scala @@ -648,19 +648,29 @@ object CommonServices { env: Env, ec: ExecutionContext ) = { + + val typeFilter = if (ctx.tenant.subscriptionSecurity.isDefined + && ctx.tenant.subscriptionSecurity.exists(identity)) { + Json.obj( + "type" -> Json.obj("$ne" ->TeamType.Personal.name) + ) + } else { + Json.obj() + } _UberPublicUserAccess( AuditTrailEvent("@{user.name} has accessed his team list") )(ctx) { (if (ctx.user.isDaikokuAdmin) env.dataStore.teamRepo .forTenant(ctx.tenant) - .findAllNotDeleted() + .findNotDeleted(typeFilter) else env.dataStore.teamRepo .forTenant(ctx.tenant) - .findNotDeleted(Json.obj("users.userId" -> ctx.user.id.value))) + .findNotDeleted(Json.obj("users.userId" -> ctx.user.id.value) ++ typeFilter)) .map(teams => - teams.sortWith((a, b) => a.name.compareToIgnoreCase(b.name) < 0) + teams + .sortWith((a, b) => a.name.compareToIgnoreCase(b.name) < 0) ) } } @@ -673,6 +683,14 @@ object CommonServices { _TenantAdminAccessTenant( AuditTrailEvent("@{user.name} has accessed to all teams list") )(ctx) { + val typeFilter = if (ctx.tenant.subscriptionSecurity.isDefined + && ctx.tenant.subscriptionSecurity.exists(identity)) { + Json.obj( + "type" -> TeamType.Organization.name + ) + } else { + Json.obj() + } for { teams <- env.dataStore.teamRepo @@ -681,7 +699,7 @@ object CommonServices { Json.obj( "_deleted" -> false, "name" -> Json.obj("$regex" -> research) - ), + ) ++ typeFilter, offset, limit, Some(Json.obj("_humanReadableId" -> 1)) diff --git a/daikoku/app/storage/api.scala b/daikoku/app/storage/api.scala index 3745048a7..d8f6b97b5 100644 --- a/daikoku/app/storage/api.scala +++ b/daikoku/app/storage/api.scala @@ -402,15 +402,27 @@ trait TeamRepo extends TenantCapableRepo[Team, TeamId] { env: Env, ec: ExecutionContext ): Future[Seq[Team]] = { + val typeFilter = if (tenant.subscriptionSecurity.isDefined + && tenant.subscriptionSecurity.exists(identity)) { + Json.obj( + "type" -> Json.obj("$ne" -> TeamType.Personal.name) + ) + } else { + Json.obj() + } if (user.isDaikokuAdmin) { env.dataStore.teamRepo .forTenant(tenant.id) - .findAllNotDeleted() + .findNotDeleted( + typeFilter + ) + + } else { env.dataStore.teamRepo .forTenant(tenant.id) .findNotDeleted( - Json.obj("users.userId" -> user.id.value) + Json.obj("users.userId" -> user.id.value) ++ typeFilter ) } } diff --git a/daikoku/javascript/tests/completeJourney.spec.ts b/daikoku/javascript/tests/completeJourney.spec.ts index e4162589d..141f18d90 100644 --- a/daikoku/javascript/tests/completeJourney.spec.ts +++ b/daikoku/javascript/tests/completeJourney.spec.ts @@ -41,7 +41,7 @@ test('test a complete user journey', async ({ page }) => { //create a new API await page.locator('div:nth-child(4) > .notification-link').first().click(); await page.locator('span').filter({ hasText: 'API' }).first().click(); - await page.locator('div').filter({ hasText: /^The A team$/ }).click(); + await page.locator('div').filter({ hasText: /^The A team$/ }).nth(1).click(); await page.getByRole('button', { name: 'Published' }).click(); await page.getByPlaceholder('New Api').fill('Test API'); diff --git a/daikoku/test/daikoku/ApiControllerSpec.scala b/daikoku/test/daikoku/ApiControllerSpec.scala index 85c7dce83..2d836e23e 100644 --- a/daikoku/test/daikoku/ApiControllerSpec.scala +++ b/daikoku/test/daikoku/ApiControllerSpec.scala @@ -545,9 +545,9 @@ class ApiControllerSpec() resp.status mustBe 403 } - "see his teams" in { + "see his teams (graphQl)" in { setupEnvBlocking( - tenants = Seq(tenant), + tenants = Seq(tenant.copy(subscriptionSecurity = Some(true))), users = Seq(userAdmin), teams = Seq(teamOwner, teamConsumer) ) @@ -580,7 +580,135 @@ class ApiControllerSpec() resp.status mustBe 200 val result = (resp.json \ "data" \ "myTeams").as[JsArray] - result.value.length mustBe 3 + result.value.length mustBe 2 + + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(false))), + users = Seq(userAdmin), + teams = Seq(teamOwner, teamConsumer) + ) + val session2 = loginWithBlocking(userAdmin, tenant) + val resp2 = httpJsonCallBlocking( + "/api/search", + "POST", + body = Some( + Json.obj( + "query" -> + """ + |query MyTeams { + | myTeams { + | name + | _humanReadableId + | _id + | type + | users { + | user { + | userId: id + | } + | teamPermission + | } + | } + | } + |""".stripMargin + ) + ) + )(tenant, session2) + resp2.status mustBe 200 + + val result2 = (resp2.json \ "data" \ "myTeams").as[JsArray] + result2.value.length mustBe 3 + } + "see his teams" in { + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(true))), + users = Seq(userAdmin), + teams = Seq(teamOwner, teamConsumer) + ) + val session = loginWithBlocking(userAdmin, tenant) + val resp = httpJsonCallBlocking("/api/me/teams")(tenant, session) + resp.status mustBe 200 + + val result = resp.json.as[JsArray] + result.value.length mustBe 2 + + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(false))), + users = Seq(userAdmin), + teams = Seq(teamOwner, teamConsumer) + ) + val session2 = loginWithBlocking(userAdmin, tenant) + val resp2 = httpJsonCallBlocking("/api/me/teams")(tenant, session2) + resp2.status mustBe 200 + + val result2 = resp2.json.as[JsArray] + result2.value.length mustBe 3 + } + + "search a team" in { + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(true))), + users = Seq(userAdmin), + teams = Seq(teamOwner, teamConsumer) + ) + val session = loginWithBlocking(userAdmin, tenant) + + val resp = + httpJsonCallBlocking( + path = s"/api/_search", + method = "POST", + body = Some(Json.obj("search" -> "")) + )(tenant, session) + + resp.status mustBe 200 + val maybeValue = resp.json.as[JsArray].value.find(entry => (entry \ "label").as[String] == "Teams") + maybeValue.isDefined mustBe true + (maybeValue.get \ "options").as[JsArray].value.length mustBe 2 + + val resp2 = + httpJsonCallBlocking( + path = s"/api/_search", + method = "POST", + body = Some(Json.obj("search" -> "Admin")) + )(tenant, session) + + resp2.status mustBe 200 + val maybeValue2 = resp2.json.as[JsArray].value.find(entry => (entry \ "label").as[String] == "Teams") + maybeValue2.isDefined mustBe true + (maybeValue2.get \ "options").as[JsArray].value.length mustBe 0 + + //disable subscription security + + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(false))), + users = Seq(userAdmin, userApiEditor), + teams = Seq(teamOwner, teamConsumer) + ) + val session2 = loginWithBlocking(userAdmin, tenant) + + val resp3 = + httpJsonCallBlocking( + path = s"/api/_search", + method = "POST", + body = Some(Json.obj("search" -> "")) + )(tenant, session2) + + resp3.status mustBe 200 + val maybeValue3 = resp3.json.as[JsArray].value.find(entry => (entry \ "label").as[String] == "Teams") + maybeValue3.isDefined mustBe true + (maybeValue3.get \ "options").as[JsArray].value.length mustBe 3 + + val resp4 = + httpJsonCallBlocking( + path = s"/api/_search", + method = "POST", + body = Some(Json.obj("search" -> "Admin")) + )(tenant, session2) + + resp4.status mustBe 200 + val maybeValue4 = resp4.json.as[JsArray].value.find(entry => (entry \ "label").as[String] == "Teams") + maybeValue4.isDefined mustBe true + (maybeValue4.get \ "options").as[JsArray].value.length mustBe 1 + } "see one of his teams" in { @@ -3262,7 +3390,7 @@ class ApiControllerSpec() resp.status mustBe 200 val result = (resp.json \ "data" \ "myTeams").as[JsArray] - result.value.length mustBe 2 + result.value.length mustBe 1 } "see one of his teams" in { @@ -3879,7 +4007,7 @@ class ApiControllerSpec() resp.status mustBe 200 val result = (resp.json \ "data" \ "myTeams").as[JsArray] - result.value.length mustBe 3 + result.value.length mustBe 2 } "see one of his teams" in { diff --git a/daikoku/test/daikoku/TeamControllerSpec.scala b/daikoku/test/daikoku/TeamControllerSpec.scala index cba7e6143..a25365de8 100644 --- a/daikoku/test/daikoku/TeamControllerSpec.scala +++ b/daikoku/test/daikoku/TeamControllerSpec.scala @@ -186,6 +186,81 @@ class TeamControllerSpec() )(tenant, session) respDelete.status mustBe 403 } + + "list all teams" in { + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(true))), + users = Seq(userAdmin, tenantAdmin), + teams = Seq(teamOwner, teamConsumer, defaultAdminTeam) + ) + val session = loginWithBlocking(tenantAdmin, tenant) + val resp = httpJsonCallBlocking( + "/api/search", + "POST", + body = Some( + Json.obj( + "query" -> + """ + |query getAllteams($research: String, $limit: Int, $offset: Int) { + | teamsPagination(research: $research, limit: $limit, offset: $offset) { + | teams { + | _id + | _humanReadableId + | name + | avatar + | type + | } + | total + | } + | } + |""".stripMargin, + "variables" -> Json.obj( + "research" -> "", + "limit" -> 10, + "offset" -> 0 + ) + ) + ) + )(tenant, session) + + resp.status mustBe 200 + val result = (resp.json \ "data" \ "teamsPagination" \ "total").as[Int] + result mustBe 2 + + setupEnvBlocking( + tenants = Seq(tenant.copy(subscriptionSecurity = Some(false))), + users = Seq(userAdmin, tenantAdmin), + teams = Seq(teamOwner, teamConsumer, defaultAdminTeam) + ) + val session2 = loginWithBlocking(tenantAdmin, tenant) + val resp2 = httpJsonCallBlocking( + "/api/search", + "POST", + body = Some( + Json.obj( + "query" -> + """ + |query getAllteams { + | teamsPagination { + | teams { + | _id + | _humanReadableId + | name + | avatar + | type + | } + | total + | } + | } + |""".stripMargin + ) + ) + )(tenant, session2) + resp2.status mustBe 200 + + val result2 = (resp2.json \ "data" \ "teamsPagination" \ "total").as[Int] + result2 mustBe 4 + } } "a team administrator" can {