From 6719e9d8411ac339ef6026a2f663c2dd9a2417d8 Mon Sep 17 00:00:00 2001 From: Sourabh Agrawal Date: Thu, 23 Dec 2021 13:38:02 +0530 Subject: [PATCH 1/2] Allow user to assign tenant to role --- .../manager/controller/LoginController.java | 12 ++++++- .../manager/controller/RolesController.java | 31 +++++++++++++++---- .../manager/controller/TenantsController.java | 4 ++- .../manager/dao/TenantsRepositoryImpl.java | 5 +++ .../manager/entity/TenantsRepository.java | 2 ++ .../pulsar/manager/mapper/TenantsMapper.java | 4 +++ .../dao/TenantsRepositoryImplTest.java | 18 ++++++++++- 7 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/pulsar/manager/controller/LoginController.java b/src/main/java/org/apache/pulsar/manager/controller/LoginController.java index 4973a324..7e199ee6 100644 --- a/src/main/java/org/apache/pulsar/manager/controller/LoginController.java +++ b/src/main/java/org/apache/pulsar/manager/controller/LoginController.java @@ -20,6 +20,8 @@ import org.apache.pulsar.manager.entity.RoleBindingRepository; import org.apache.pulsar.manager.entity.RoleInfoEntity; import org.apache.pulsar.manager.entity.RolesRepository; +import org.apache.pulsar.manager.entity.TenantEntity; +import org.apache.pulsar.manager.entity.TenantsRepository; import org.apache.pulsar.manager.entity.UserInfoEntity; import org.apache.pulsar.manager.entity.UsersRepository; import org.apache.pulsar.manager.service.JwtService; @@ -27,6 +29,7 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; +import org.apache.pulsar.manager.utils.ResourceType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; @@ -81,6 +84,9 @@ public LoginController(JwtService jwtService) { @Autowired private RoleBindingRepository roleBindingRepository; + @Autowired + private TenantsRepository tenantsRepository; + @ApiOperation(value = "Login pulsar manager") @ApiResponses({ @ApiResponse(code = 200, message = "ok"), @@ -112,7 +118,7 @@ public ResponseEntity> login( result.put("login", "success"); headers.add("token", token); headers.add("username", userAccount); - headers.add("tenant", userAccount); + jwtService.setToken(request.getSession().getId(), token); List roleBindingEntities = roleBindingRepository. findByUserId(userInfoEntity.getUserId()); @@ -123,6 +129,10 @@ public ResponseEntity> login( if (!roleIdList.isEmpty()) { List roleInfoEntities = rolesRepository.findAllRolesByMultiId(roleIdList); for (RoleInfoEntity roleInfoEntity : roleInfoEntities) { + if(roleInfoEntity.getResourceType().equals(ResourceType.TENANTS.name())){ + Optional tenantEntity = tenantsRepository.findByTenantId(roleInfoEntity.getResourceId()); + headers.add("tenant",tenantEntity.get().getTenant()); + } if (roleInfoEntity.getFlag() == 0) { // Super users can access all types return new ResponseEntity<>(result, headers, HttpStatus.OK); diff --git a/src/main/java/org/apache/pulsar/manager/controller/RolesController.java b/src/main/java/org/apache/pulsar/manager/controller/RolesController.java index 0f94ddb3..bd044936 100644 --- a/src/main/java/org/apache/pulsar/manager/controller/RolesController.java +++ b/src/main/java/org/apache/pulsar/manager/controller/RolesController.java @@ -24,6 +24,8 @@ import org.apache.pulsar.manager.entity.NamespacesRepository; import org.apache.pulsar.manager.entity.RoleInfoEntity; import org.apache.pulsar.manager.entity.RolesRepository; +import org.apache.pulsar.manager.entity.TenantEntity; +import org.apache.pulsar.manager.entity.TenantsRepository; import org.apache.pulsar.manager.service.RolesService; import org.apache.pulsar.manager.utils.ResourceType; import org.hibernate.validator.constraints.Range; @@ -59,15 +61,19 @@ public class RolesController { private final HttpServletRequest request; + private TenantsRepository tenantsRepository; + public RolesController( RolesRepository rolesRepository, RolesService rolesService, NamespacesRepository namespacesRepository, + TenantsRepository tenantsRepository, HttpServletRequest request) { this.rolesRepository = rolesRepository; this.rolesService = rolesService; this.namespacesRepository = namespacesRepository; this.request = request; + this.tenantsRepository = tenantsRepository; } @ApiOperation(value = "Get the list of existing roles, support paging, the default is 10 per page") @@ -228,6 +234,7 @@ public ResponseEntity> deleteRole(@RequestBody RoleInfoEntit public ResponseEntity> getResourceType() { Map result = Maps.newHashMap(); Set resourceTypeList = Sets.newHashSet(); + resourceTypeList.add(ResourceType.TENANTS.name()); resourceTypeList.add(ResourceType.NAMESPACES.name()); resourceTypeList.add(ResourceType.TOPICS.name()); resourceTypeList.add(ResourceType.SCHEMAS.name()); @@ -247,18 +254,30 @@ public ResponseEntity> getResource(@PathVariable String reso Map result = Maps.newHashMap(); String token = request.getHeader("token"); String tenant = request.getHeader("tenant"); + String environment = request.getHeader("environment"); Map validateResult = rolesService.validateCurrentTenant(token, tenant); if (validateResult.get("error") != null) { result.put("error", validateResult.get("error")); return ResponseEntity.ok(result); } - List namespaceEntities = namespacesRepository.findByTenant(tenant); Set> nameSet = Sets.newHashSet(); - for (NamespaceEntity namespaceEntity : namespaceEntities) { - Map namespace = Maps.newHashMap(); - namespace.put("name", namespaceEntity.getNamespace()); - namespace.put("id", namespaceEntity.getNamespaceId()); - nameSet.add(namespace); + if(ResourceType.TENANTS.name().equals(resourceType)){ + List tenantEntities = tenantsRepository.findByEnvironment(environment); + tenantEntities.forEach(tenantEntity -> { + Map tenantMap = Maps.newHashMap(); + tenantMap.put("name", tenantEntity.getTenant()); + tenantMap.put("id", tenantEntity.getTenantId()); + nameSet.add(tenantMap); + }); + + } else if(ResourceType.NAMESPACES.name().equals(resourceType)) { + List namespaceEntities = namespacesRepository.findByTenant(tenant); + for (NamespaceEntity namespaceEntity : namespaceEntities) { + Map namespace = Maps.newHashMap(); + namespace.put("name", namespaceEntity.getNamespace()); + namespace.put("id", namespaceEntity.getNamespaceId()); + nameSet.add(namespace); + } } result.put("data", nameSet); return ResponseEntity.ok(result); diff --git a/src/main/java/org/apache/pulsar/manager/controller/TenantsController.java b/src/main/java/org/apache/pulsar/manager/controller/TenantsController.java index 1319048d..01505a1d 100644 --- a/src/main/java/org/apache/pulsar/manager/controller/TenantsController.java +++ b/src/main/java/org/apache/pulsar/manager/controller/TenantsController.java @@ -124,7 +124,9 @@ public ResponseEntity> getTenants( List roleInfoEntities = rolesRepository.findAllRolesByMultiId(roleIdList); List tenantsIdList = new ArrayList<>(); for (RoleInfoEntity roleInfoEntity : roleInfoEntities) { - tenantsIdList.add(roleInfoEntity.getResourceId()); + if(roleInfoEntity.getResourceType().equals(ResourceType.TENANTS.name())) { + tenantsIdList.add(roleInfoEntity.getResourceId()); + } } if (!tenantsIdList.isEmpty()) { tenantEntities = tenantsRepository.findByMultiId(tenantsIdList); diff --git a/src/main/java/org/apache/pulsar/manager/dao/TenantsRepositoryImpl.java b/src/main/java/org/apache/pulsar/manager/dao/TenantsRepositoryImpl.java index 40a55767..c0727566 100644 --- a/src/main/java/org/apache/pulsar/manager/dao/TenantsRepositoryImpl.java +++ b/src/main/java/org/apache/pulsar/manager/dao/TenantsRepositoryImpl.java @@ -79,4 +79,9 @@ public void remove(String tenant) { tenantsMapper.delete(tenant); } + @Override + public List findByEnvironment(String environment) { + return tenantsMapper.findAll(environment); + } + } diff --git a/src/main/java/org/apache/pulsar/manager/entity/TenantsRepository.java b/src/main/java/org/apache/pulsar/manager/entity/TenantsRepository.java index ced4f241..e27af49b 100644 --- a/src/main/java/org/apache/pulsar/manager/entity/TenantsRepository.java +++ b/src/main/java/org/apache/pulsar/manager/entity/TenantsRepository.java @@ -38,5 +38,7 @@ public interface TenantsRepository { void remove(String tenant); + List findByEnvironment(String environment); + } diff --git a/src/main/java/org/apache/pulsar/manager/mapper/TenantsMapper.java b/src/main/java/org/apache/pulsar/manager/mapper/TenantsMapper.java index 82b288a3..78cffdb5 100644 --- a/src/main/java/org/apache/pulsar/manager/mapper/TenantsMapper.java +++ b/src/main/java/org/apache/pulsar/manager/mapper/TenantsMapper.java @@ -77,4 +77,8 @@ public interface TenantsMapper { @Delete("DELETE FROM tenants WHERE tenant = #{tenant}") void delete(String tenant); + @Select("SELECT tenant, tenant_id as tenantId, admin_roles as adminRoles,allowed_clusters as allowedClusters," + + "environment_name as environmentName " + + "FROM tenants WHERE environment_name = #{environment}") + List findAll(String environment); } diff --git a/src/test/java/org/apache/pulsar/manager/dao/TenantsRepositoryImplTest.java b/src/test/java/org/apache/pulsar/manager/dao/TenantsRepositoryImplTest.java index 57c559b9..a31765f6 100644 --- a/src/test/java/org/apache/pulsar/manager/dao/TenantsRepositoryImplTest.java +++ b/src/test/java/org/apache/pulsar/manager/dao/TenantsRepositoryImplTest.java @@ -129,4 +129,20 @@ public void findByTenantId() { Assert.assertEquals("test-cluster", getTenantEntity.getAllowedClusters()); Assert.assertEquals("test-environment", getTenantEntity.getEnvironmentName()); } -} \ No newline at end of file + + @Test + public void findByEnvironment() { + TenantEntity tenantEntity = new TenantEntity(); + tenantEntity.setTenant("test"); + tenantEntity.setAdminRoles("test-role"); + tenantEntity.setAllowedClusters("test-cluster"); + tenantEntity.setEnvironmentName("test-environment"); + long tenantId = tenantsRepository.save(tenantEntity); + List result = tenantsRepository.findByEnvironment("test-environment"); + TenantEntity getTenantEntity = result.get(0); + Assert.assertEquals("test", getTenantEntity.getTenant()); + Assert.assertEquals("test-role", getTenantEntity.getAdminRoles()); + Assert.assertEquals("test-cluster", getTenantEntity.getAllowedClusters()); + Assert.assertEquals("test-environment", getTenantEntity.getEnvironmentName()); + } +} From 0d7d4e3b78ecd870a63f4811ac857d7f7dd59d10 Mon Sep 17 00:00:00 2001 From: Sourabh Agrawal Date: Sun, 26 Dec 2021 15:04:51 +0530 Subject: [PATCH 2/2] Added access field with roles to show/hide ui-elements based on access --- front-end/src/store/modules/user.js | 25 +++++++++++++++++++ .../views/management/admin/tenants/tenant.vue | 8 ++++-- .../views/management/namespaces/namespace.vue | 17 ++++++++----- .../src/views/management/tenants/tenant.vue | 9 ++++--- .../management/topics/partitionedTopic.vue | 7 +++++- .../src/views/management/topics/topic.vue | 12 ++++++--- .../manager/controller/UsersController.java | 24 +++++++++++++++++- .../pulsar/manager/entity/RoleInfoEntity.java | 4 +++ .../pulsar/manager/mapper/RolesMapper.java | 18 ++++++------- .../resources/META-INF/sql/herddb-schema.sql | 1 + .../resources/META-INF/sql/mysql-schema.sql | 3 ++- .../META-INF/sql/postgresql-schema.sql | 1 + .../resources/META-INF/sql/sqlite-schema.sql | 3 ++- .../manager/dao/RolesRepositoryImplTest.java | 3 +++ .../manager/service/PulsarEventImplTest.java | 2 ++ .../service/RoleBindingServiceImplTest.java | 5 ++++ .../manager/service/RolesServiceImplTest.java | 1 + 17 files changed, 116 insertions(+), 27 deletions(-) diff --git a/front-end/src/store/modules/user.js b/front-end/src/store/modules/user.js index b2acd3c1..7ffca267 100644 --- a/front-end/src/store/modules/user.js +++ b/front-end/src/store/modules/user.js @@ -20,6 +20,22 @@ import { Message } from 'element-ui' import { setTenant, removeTenant } from '../../utils/tenant' import { getUserInfo } from '@/api/users' +export function hasPermissionToResource(resourceType, resourceName, permission) { + if (isSuperAdmin(user)) { // Super admin will have access to everything + return true + } + if (user.state.access.hasOwnProperty(resourceType)) { + if (user.state.access[resourceType].hasOwnProperty(resourceName)) { + return user.state.access[resourceType][resourceName] === permission + } + } + return false +} + +export function isSuperAdmin(user) { + return user.state.roles.indexOf('super') != -1 +} + const user = { state: { user: '', @@ -32,6 +48,9 @@ const user = { roles: [], setting: { articlePlatform: [] + }, + access: { + } }, @@ -59,7 +78,11 @@ const user = { }, SET_ROLES: (state, roles) => { state.roles = roles + }, + SET_ACCESS: (state, access) => { + state.access = access } + }, actions: { @@ -93,6 +116,7 @@ const user = { commit('SET_ROLES', response.data.roles) commit('SET_NAME', 'admin') commit('SET_INTRODUCTION', 'Pulsar Manager') + commit('SET_ACCESS', response.data.access) resolve(response) }) }) @@ -104,6 +128,7 @@ const user = { logout(state.token).then(() => { commit('SET_TOKEN', '') commit('SET_ROLES', []) + commit('SET_ACCESS', {}) removeToken() removeCsrfToken() removeName() diff --git a/front-end/src/views/management/admin/tenants/tenant.vue b/front-end/src/views/management/admin/tenants/tenant.vue index f380924d..73cb8693 100644 --- a/front-end/src/views/management/admin/tenants/tenant.vue +++ b/front-end/src/views/management/admin/tenants/tenant.vue @@ -33,7 +33,7 @@ style="width: 200px;" @keyup.enter.native="handleFilterNamespace"/> - {{ $t('namespace.newNamespace') }} + {{ $t('namespace.newNamespace') }} @@ -59,7 +59,7 @@ - +