Skip to content

Commit

Permalink
When deleting Aiven acls, the associated service user will now be del…
Browse files Browse the repository at this point in the history
…eted (#2374)

* Deleting service account user

Signed-off-by: Muralidhar Basani <[email protected]>

* Deleting service account user

Signed-off-by: Muralidhar Basani <[email protected]>

* 🤖 Auto-update API types for TypeScript usage

* Deleting service account user

Signed-off-by: Muralidhar Basani <[email protected]>

* Deleting service account user

Signed-off-by: Muralidhar Basani <[email protected]>

* Deleting service account user

Signed-off-by: Muralidhar Basani <[email protected]>

---------

Signed-off-by: Muralidhar Basani <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Aindriú Lavelle <[email protected]>
  • Loading branch information
3 people authored Apr 2, 2024
1 parent 9577db4 commit 96b2c96
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public class AivenApiService {
public static final String PROJECT_NAME = "projectName";
public static final String SERVICE_NAME = "serviceName";
public static final String USERNAME = "username";
public static final String PRINCIPLE = "principle";
public static final String RESOURCE_TYPE = "resourceType";
public static final String USER_NAME = "userName";

private RestTemplate restTemplate;

Expand All @@ -58,7 +61,7 @@ public class AivenApiService {
private String addServiceAccountApiEndpoint;

@Value("${klaw.clusters.aiven.getserviceaccount.api:api}")
private String getServiceAccountApiEndpoint;
private String serviceAccountApiEndpoint;

@Value("${klaw.clusters.aiven.servicedetails.api:api}")
private String serviceDetailsApiEndpoint;
Expand Down Expand Up @@ -161,6 +164,29 @@ private void createServiceAccount(
}
}

private String deleteServiceAccountUser(
String projectName, String serviceName, String serviceAccountUser) {
log.debug(
"Deleting service account user : projectName serviceName serviceAccountUser :{} {} {}",
projectName,
serviceName,
serviceAccountUser);
String uri =
serviceAccountApiEndpoint
.replace(PROJECT_NAME, projectName)
.replace(SERVICE_NAME, serviceName)
.replace(USER_NAME, serviceAccountUser);
try {
HttpHeaders headers = getHttpHeaders();
HttpEntity<?> request = new HttpEntity<>(headers);
restTemplate.exchange(uri, HttpMethod.DELETE, request, Object.class);
} catch (Exception e) {
log.error("Exception:", e);
return ApiResultStatus.FAILURE.value;
}
return ApiResultStatus.SUCCESS.value;
}

// Get Aiven service account details
public ServiceAccountDetails getServiceAccountDetails(
String projectName, String serviceName, String userName) {
Expand All @@ -171,10 +197,10 @@ public ServiceAccountDetails getServiceAccountDetails(
userName);
HttpHeaders headers = getHttpHeaders();
String uri =
getServiceAccountApiEndpoint
serviceAccountApiEndpoint
.replace(PROJECT_NAME, projectName)
.replace(SERVICE_NAME, serviceName)
.replace("userName", userName);
.replace(USER_NAME, userName);
HttpEntity<Map<String, String>> request = new HttpEntity<>(headers);
ServiceAccountDetails serviceAccountDetails = new ServiceAccountDetails();
serviceAccountDetails.setAccountFound(false);
Expand Down Expand Up @@ -253,6 +279,9 @@ public String deleteAcls(ClusterAclRequest clusterAclRequest) throws Exception {
HttpHeaders headers = getHttpHeaders();
HttpEntity<?> request = new HttpEntity<>(headers);
restTemplate.exchange(uri, HttpMethod.DELETE, request, Object.class);

// Check if service user can be deleted
deleteServiceAccountUser(clusterAclRequest);
} catch (Exception e) {
log.error("Exception:", e);
if (e instanceof HttpClientErrorException) {
Expand All @@ -266,6 +295,27 @@ public String deleteAcls(ClusterAclRequest clusterAclRequest) throws Exception {
return ApiResultStatus.SUCCESS.value;
}

private void deleteServiceAccountUser(ClusterAclRequest clusterAclRequest) throws Exception {
Set<Map<String, String>> aclsList =
listAcls(clusterAclRequest.getProjectName(), clusterAclRequest.getServiceName());
long aclsListFiltered =
aclsList.stream()
.filter(
aclMap ->
aclMap.containsKey(PRINCIPLE)
&& aclMap.containsKey(RESOURCE_TYPE)
&& aclMap.get(PRINCIPLE).equals(clusterAclRequest.getUsername())
&& aclMap.get(RESOURCE_TYPE).equals("TOPIC"))
.count();
// Check if there are any other topics using the same principle
if (aclsListFiltered == 0) {
deleteServiceAccountUser(
clusterAclRequest.getProjectName(),
clusterAclRequest.getServiceName(),
clusterAclRequest.getUsername());
}
}

public Set<Map<String, String>> listAcls(String projectName, String serviceName)
throws Exception {
RestTemplate restTemplate = getRestTemplate();
Expand All @@ -292,17 +342,17 @@ public Set<Map<String, String>> listAcls(String projectName, String serviceName)
case "id" -> aclsMapUpdated.put("aivenaclid", aclsMap.get(keyAcls));
case "permission" -> {
aclsMapUpdated.put("operation", aclsMap.get(keyAcls).toUpperCase());
aclsMapUpdated.put("resourceType", "TOPIC");
aclsMapUpdated.put(RESOURCE_TYPE, "TOPIC");
}
case "topic" -> aclsMapUpdated.put("resourceName", aclsMap.get(keyAcls));
case USERNAME -> aclsMapUpdated.put("principle", aclsMap.get(keyAcls));
case USERNAME -> aclsMapUpdated.put(PRINCIPLE, aclsMap.get(keyAcls));
}
}
aclsMapUpdated.put("host", "*");
aclsMapUpdated.put("permissionType", "ALLOW");
if ("READ".equals(aclsMapUpdated.get("operation"))) {
Map<String, String> newRGroupMap = new HashMap<>(aclsMapUpdated);
newRGroupMap.put("resourceType", "GROUP");
newRGroupMap.put(RESOURCE_TYPE, "GROUP");
newRGroupMap.put("resourceName", "-na-");
aclsListUpdated.add(newRGroupMap);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.aiven.klaw.clusterapi.services;

import static io.aiven.klaw.clusterapi.services.AivenApiService.OBJECT_MAPPER;
import static io.aiven.klaw.clusterapi.services.AivenApiService.PROJECT_NAME;
import static io.aiven.klaw.clusterapi.services.AivenApiService.SERVICE_NAME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import io.aiven.klaw.clusterapi.UtilMethods;
Expand Down Expand Up @@ -35,6 +39,9 @@
@ExtendWith(SpringExtension.class)
public class AivenApiServiceTest {

public static final String TESTUSER = "testuser";
public static final String SERVICE_ACLS_ENDPOINT =
"https://api.aiven.io/v1/project/projectName/service/serviceName/user/userName";
AivenApiService aivenApiService;
@Mock RestTemplate restTemplate;

Expand All @@ -59,9 +66,7 @@ public void setUp() {
"deleteAclsApiEndpoint",
"https://api.aiven.io/v1/project/projectName/service/serviceName/acl/aclId");
ReflectionTestUtils.setField(
aivenApiService,
"getServiceAccountApiEndpoint",
"https://api.aiven.io/v1/project/projectName/service/serviceName/user/userName");
aivenApiService, "serviceAccountApiEndpoint", SERVICE_ACLS_ENDPOINT);
ReflectionTestUtils.setField(
aivenApiService,
"addServiceAccountApiEndpoint",
Expand Down Expand Up @@ -158,7 +163,7 @@ public void createAclsServiceAccountExists() throws Exception {
+ clusterAclRequest.getUsername();
Map<String, Map<String, String>> serviceAccountResponse = new HashMap<>();
Map<String, String> userNameMap = new HashMap<>();
userNameMap.put("username", "testuser");
userNameMap.put("username", TESTUSER);
serviceAccountResponse.put("user", userNameMap);
ResponseEntity<Map<String, Map<String, String>>> responseEntityServiceAccount =
new ResponseEntity<>(serviceAccountResponse, HttpStatus.OK);
Expand Down Expand Up @@ -252,19 +257,7 @@ public void getServiceAccountsDontExist() {
public void listAcls() throws Exception {
String getAclsUrl = ACLS_BASE_URL + "testproject" + "/service/" + "testservice" + "/acl";

Map<String, String> aclMap1 = Map.of("permission", "read");
Map<String, String> aclMap2 = Map.of("permission", "write");
Map<String, String> aclMap3 = Map.of("permission", "ADMIN");
Map<String, String> aclMap4 = Map.of("permission", "READWRITE");
Map<String, String> aclMap5 = Map.of("id", "ID");
Map<String, String> aclMap6 = Map.of("topic", "TOPIC");
Map<String, String> aclMap7 = Map.of("username", "USERNAME");

List<Map<String, String>> aclList =
List.of(aclMap1, aclMap2, aclMap3, aclMap4, aclMap5, aclMap6, aclMap7);

Map<String, List<Map<String, String>>> aclsResp = Map.of("acl", aclList);

Map<String, List<Map<String, String>>> aclsResp = getAclListMap("testuser");
ResponseEntity<Map<String, List<Map<String, String>>>> responseEntityServiceAccount =
new ResponseEntity<>(aclsResp, HttpStatus.OK);

Expand All @@ -279,6 +272,23 @@ public void listAcls() throws Exception {
assertThat(acls).hasSize(6);
}

private static Map<String, List<Map<String, String>>> getAclListMap(String userName) {
Map<String, String> aclMap1 = Map.of("permission", "read");
Map<String, String> aclMap2 = Map.of("permission", "write");
Map<String, String> aclMap3 = Map.of("permission", "ADMIN");
Map<String, String> aclMap4 = Map.of("permission", "READWRITE");
Map<String, String> aclMap5 = Map.of("id", "ID");
Map<String, String> aclMap6 = Map.of("topic", "TOPIC");
Map<String, String> aclMap7 =
Map.of("username", userName, "topic", "testtopic", "permission", "write");

List<Map<String, String>> aclList =
List.of(aclMap1, aclMap2, aclMap3, aclMap4, aclMap5, aclMap6, aclMap7);

Map<String, List<Map<String, String>>> aclsResp = Map.of("acl", aclList);
return aclsResp;
}

@Test
public void listAclsFailure() throws Exception {
String getAclsUrl = ACLS_BASE_URL + "testproject" + "/service/" + "testservice" + "/acl";
Expand All @@ -297,18 +307,75 @@ public void listAclsFailure() throws Exception {
}

@Test
public void deleteAclsTest() throws Exception {
public void deleteAclsTestAndServiceUser() throws Exception {
String projectName = "testproject";
String serviceName = "testservice";

ClusterAclRequest clusterAclRequest =
ClusterAclRequest.builder()
.aivenAclKey("4322342")
.projectName("testproject")
.serviceName("serviceName")
.projectName(projectName)
.serviceName(serviceName)
.username(TESTUSER)
.build();

handleListAcls(projectName, serviceName, "testuser1"); // different user association

String actual = aivenApiService.deleteAcls(clusterAclRequest);
String expected = ApiResultStatus.SUCCESS.value;

assertThat(actual).isEqualTo(expected);
String uri =
SERVICE_ACLS_ENDPOINT
.replace(PROJECT_NAME, projectName)
.replace(SERVICE_NAME, serviceName)
.replace("userName", TESTUSER);

verify(restTemplate, times(1))
.exchange(eq(uri), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Object.class));
}

@Test
public void deleteAclsTestAndNotServiceUser() throws Exception {
String projectName = "testproject";
String serviceName = "testservice";

ClusterAclRequest clusterAclRequest =
ClusterAclRequest.builder()
.aivenAclKey("4322342")
.projectName(projectName)
.serviceName(serviceName)
.username(TESTUSER)
.build();

handleListAcls(projectName, serviceName, TESTUSER); // same user association

String actual = aivenApiService.deleteAcls(clusterAclRequest);
String expected = ApiResultStatus.SUCCESS.value;

assertThat(actual).isEqualTo(expected);
String uri =
SERVICE_ACLS_ENDPOINT
.replace(PROJECT_NAME, projectName)
.replace(SERVICE_NAME, serviceName)
.replace("userName", TESTUSER);

verify(restTemplate, times(0)) // service user is not deleted
.exchange(eq(uri), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Object.class));
}

private void handleListAcls(String projectName, String serviceName, String userName) {
Map<String, List<Map<String, String>>> aclsResp = getAclListMap(userName);
ResponseEntity<Map<String, List<Map<String, String>>>> responseEntityServiceAccount =
new ResponseEntity<>(aclsResp, HttpStatus.OK);

String getAclsUrl = ACLS_BASE_URL + projectName + "/service/" + serviceName + "/acl";
when(restTemplate.exchange(
eq(getAclsUrl),
eq(HttpMethod.GET),
any(),
(ParameterizedTypeReference<Map<String, List<Map<String, String>>>>) any()))
.thenReturn(responseEntityServiceAccount);
}

@Test
Expand Down

0 comments on commit 96b2c96

Please sign in to comment.