Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Idle Account Suspension API to return disabled and non-disabled users #768

Merged
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2023-2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand Down Expand Up @@ -28,6 +28,9 @@ public class IdleAccountIdentificationConstants {
public static final String DATE_INACTIVE_AFTER = "inactiveAfter";
public static final String DATE_EXCLUDE_BEFORE = "excludeBefore";
public static final String DATE_FORMAT_REGEX = "^\\d{4}-\\d{2}-\\d{2}$";
public static final String IS_DISABLED = "isDisabled";
public static final String TRUE_VALUE = "true";
public static final String FALSE_VALUE = "false";

/**
* Enums for error messages.
Expand All @@ -51,6 +54,10 @@ public enum ErrorMessage {
"Invalid date combination is provided.",
"The inactive after date must be before the exclude after date."),

ERROR_INVALID_FILTER("60005",
"Invalid filter value provided.",
"The filter value provided is invalid"),

// Server errors 650xx.
ERROR_RETRIEVING_INACTIVE_USERS("65001",
"Error while retrieving inactive users.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import io.swagger.annotations.*;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationClientException;

@Path("/inactive-users")
@Api(description = "The inactive-users API")
Expand All @@ -53,8 +54,12 @@ public InactiveUsersApi() {
@ApiResponse(code = 403, message = "Resource Forbidden", response = Void.class),
@ApiResponse(code = 500, message = "Internal Server Error", response = Error.class)
})
public Response getInactiveUsers( @Valid@ApiParam(value = "Latest active date of login.") @QueryParam("inactiveAfter") String inactiveAfter, @Valid@ApiParam(value = "Date to exclude the oldest inactive users.") @QueryParam("excludeBefore") String excludeBefore) {
public Response getInactiveUsers(
@Valid @ApiParam(value = "Latest active date of login.") @QueryParam("inactiveAfter") String inactiveAfter,
@Valid @ApiParam(value = "Date to exclude the oldest inactive users.") @QueryParam("excludeBefore") String excludeBefore,
@Valid @ApiParam(value = "Filter inactive users by account state disabled.") @QueryParam("filter") String filter)
throws IdleAccountIdentificationClientException {

return delegate.getInactiveUsers(inactiveAfter, excludeBefore );
return delegate.getInactiveUsers(inactiveAfter, excludeBefore, filter);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
* Copyright (c) 2023-2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand Down Expand Up @@ -27,9 +27,23 @@
import org.wso2.carbon.identity.api.idle.account.identification.v1.model.Error;
import org.wso2.carbon.identity.api.idle.account.identification.v1.model.InactiveUser;
import org.wso2.carbon.identity.api.idle.account.identification.v1.model.Unauthorized;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationClientException;

import javax.ws.rs.core.Response;

public interface InactiveUsersApiService {

public Response getInactiveUsers(String inactiveAfter, String excludeBefore);

/**
* Get inactive users list for a specified period.
*
* @param inactiveAfter The date after which the users are considered as inactive.
* @param excludeBefore The date before which the users are considered as inactive. (optional)
* @param filter Filter inactive users based isDisabled attribute. (optional)
* @return InactiveUser
* @throws IdleAccountIdentificationClientException If an error occurs while retrieving inactive users.
*/
Response getInactiveUsers(String inactiveAfter, String excludeBefore, String filter)
throws IdleAccountIdentificationClientException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.api.idle.account.identification.common.IdleAccountIdentificationServiceHolder;
import org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants;
import org.wso2.carbon.identity.api.idle.account.identification.v1.model.InactiveUser;
import org.wso2.carbon.identity.api.server.common.error.APIError;
import org.wso2.carbon.identity.api.server.common.error.ErrorResponse;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.model.ExpressionNode;
import org.wso2.carbon.identity.core.model.FilterTreeBuilder;
import org.wso2.carbon.identity.core.model.Node;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationClientException;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationException;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationServerException;
import org.wso2.carbon.identity.idle.account.identification.models.InactiveUserModel;
import org.wso2.carbon.identity.idle.account.identification.services.IdleAccountIdentificationService;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
Expand All @@ -44,6 +50,9 @@
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.DATE_FORMAT_REGEX;
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.DATE_INACTIVE_AFTER;
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.ErrorMessage;
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.FALSE_VALUE;
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.IS_DISABLED;
import static org.wso2.carbon.identity.api.idle.account.identification.common.util.IdleAccountIdentificationConstants.TRUE_VALUE;

/**
* Calls internal osgi services to perform idle account identification management related operations.
Expand Down Expand Up @@ -89,6 +98,41 @@ public List<InactiveUser> getInactiveUsers(String inactiveAfter, String excludeB
}
}

/**
* Get inactive users.
*
* @param inactiveAfter Latest active date of login.
* @param excludeBefore Date to exclude the oldest inactive users.
* @param tenantDomain Tenant domain.
* @return List of inactive users.
* @throws IdleAccountIdentificationClientException If an error occurs while retrieving inactive users.
*/
public List<InactiveUser> getInactiveUsers(String inactiveAfter, String excludeBefore, String tenantDomain,
String filter) throws IdleAccountIdentificationClientException {

List<ExpressionNode> expressionNodes = getExpressionNodes(filter);
if (validateExpressionNodes(expressionNodes)) {
boolean isDisabled = Boolean.parseBoolean(expressionNodes.get(0).getValue());
List<InactiveUserModel> inactiveUsers;
try {
validateDates(inactiveAfter, excludeBefore);
LocalDateTime inactiveAfterDate = convertToDateObject(inactiveAfter, DATE_INACTIVE_AFTER);
LocalDateTime excludeBeforeDate = convertToDateObject(excludeBefore, DATE_EXCLUDE_BEFORE);

validateDatesCombination(inactiveAfterDate, excludeBeforeDate);

inactiveUsers = IdleAccountIdentificationServiceHolder.getIdleAccountIdentificationService()
.filterInactiveUsersIfDisabled(inactiveAfterDate, excludeBeforeDate, tenantDomain, isDisabled);

return buildResponse(inactiveUsers);
} catch (IdleAccountIdentificationException e) {
throw handleIdleAccIdentificationException(e, ErrorMessage.ERROR_RETRIEVING_INACTIVE_USERS,
tenantDomain);
}
}
return getInactiveUsers(inactiveAfter, excludeBefore, tenantDomain);
}

/**
* Validate the dates.
*
Expand Down Expand Up @@ -263,4 +307,80 @@ private void validateDatesCombination(LocalDateTime inactiveAfterDate, LocalDate
String.format(error.getDescription()));
}
}

/**
* Get the filter node as a list.
*
* @param filter value of the filter.
* @return node tree.
* @throws IdleAccountIdentificationClientException Error when validate filters.
*/
private List<ExpressionNode> getExpressionNodes(String filter) throws IdleAccountIdentificationClientException {

// Filter example : isDisabled eq true.
List<ExpressionNode> expressionNodes = new ArrayList<>();
FilterTreeBuilder filterTreeBuilder;
try {
if (StringUtils.isNotBlank(filter)) {
KaveeshaPiumini marked this conversation as resolved.
Show resolved Hide resolved
filterTreeBuilder = new FilterTreeBuilder(filter);
Node rootNode = filterTreeBuilder.buildTree();
setExpressionNodeList(rootNode, expressionNodes);
}
} catch (IOException | IdentityException e) {
ErrorMessage error = ErrorMessage.ERROR_INVALID_FILTER;
throw new IdleAccountIdentificationClientException(error.getCode(), error.getMessage(),
String.format(error.getDescription()));
}
return expressionNodes;
}

/**
* Set the node values as list of expression.
*
* @param node filter node.
* @param expression list of expression.
* @throws IdleAccountIdentificationClientException Error when passing invalid filter.
*/
private void setExpressionNodeList(Node node, List<ExpressionNode> expression)
throws IdleAccountIdentificationClientException {

if (node instanceof ExpressionNode) {
sadilchamishka marked this conversation as resolved.
Show resolved Hide resolved
if (StringUtils.isNotBlank(((ExpressionNode) node).getAttributeValue())) {
if (((ExpressionNode) node).getAttributeValue().contains(IS_DISABLED)) {
if (TRUE_VALUE.contains(((ExpressionNode) node).getValue())) {
((ExpressionNode) node).setValue(TRUE_VALUE);
} else if (FALSE_VALUE.contains(((ExpressionNode) node).getValue())) {
((ExpressionNode) node).setValue(FALSE_VALUE);
} else {
String message = "Invalid value: " + ((ExpressionNode) node).getValue() + "is passed for '" +
IS_DISABLED + "' attribute in the filter. It should be '" + TRUE_VALUE + "' or '" +
FALSE_VALUE + "'";
ErrorMessage error = ErrorMessage.ERROR_INVALID_FILTER;
throw new IdleAccountIdentificationClientException(error.getCode(), error.getMessage(),
message);
}
}
}
expression.add((ExpressionNode) node);
}
}

/**
* Validate the expression nodes.
*
* @param expressionNodes List of expression nodes.
* @return boolean.
* @throws IdleAccountIdentificationClientException Error when validate filters.
*/
private boolean validateExpressionNodes(List<ExpressionNode> expressionNodes)
throws IdleAccountIdentificationClientException {

if (expressionNodes.get(0).getAttributeValue().equals(IS_DISABLED)) {
KaveeshaPiumini marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

ErrorMessage error = ErrorMessage.ERROR_INVALID_FILTER;
throw new IdleAccountIdentificationClientException(error.getCode(), error.getMessage(),
String.format(error.getDescription()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.wso2.carbon.identity.api.idle.account.identification.v1.InactiveUsersApiService;
import org.wso2.carbon.identity.api.idle.account.identification.v1.core.InactiveUsersManagementApiService;
import org.wso2.carbon.identity.api.idle.account.identification.v1.factories.InactiveUsersManagementApiServiceFactory;
import org.wso2.carbon.identity.idle.account.identification.exception.IdleAccountIdentificationClientException;

import javax.ws.rs.core.Response;

Expand All @@ -48,4 +49,18 @@ public Response getInactiveUsers(String inactiveAfter, String excludeBefore) {
return Response.ok().entity(inactiveUsersManagementApiService
.getInactiveUsers(inactiveAfter, excludeBefore, tenantDomain)).build();
}

@Override
public Response getInactiveUsers(String inactiveAfter, String excludeBefore, String filter)
throws IdleAccountIdentificationClientException {

String tenantDomain = ContextLoader.getTenantDomainFromContext();

if (filter != null) {
return Response.ok().entity(
sadilchamishka marked this conversation as resolved.
Show resolved Hide resolved
inactiveUsersManagementApiService.getInactiveUsers(inactiveAfter, excludeBefore, tenantDomain,
filter)).build();
}
return getInactiveUsers(inactiveAfter, excludeBefore);
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@
<version.org.wso2.orbit.javax.xml.bind>2.3.1.wso2v1</version.org.wso2.orbit.javax.xml.bind>
<maven.buildnumber.plugin.version>1.4</maven.buildnumber.plugin.version>
<org.apache.felix.annotations.version>1.2.4</org.apache.felix.annotations.version>
<identity.governance.version>1.11.21</identity.governance.version>
<identity.governance.version>1.11.27</identity.governance.version>
<carbon.identity.framework.version>7.7.95</carbon.identity.framework.version>
<maven.findbugsplugin.version>3.0.5</maven.findbugsplugin.version>
<findsecbugs-plugin.version>1.12.0</findsecbugs-plugin.version>
Expand Down
Loading