WRITING CUSTOM USER OPERATION EVENT LISTENER -WSO2 IDENTITY SERVER
In this article, I'm going to show you how to write a custom user operation event listener. We have main two ways to do that. First, let’s get an idea about why we need to user operation event listeners.
The following are the sample use cases where we can use user operation event listeners.
- printing logs for operations like password reset, delete roles, delete users, etc. we can print logs with the user who performed the operation to be traced later. The sample implementation for delete user operation is explained below in this article.
- We can override the doPostUpdateCredential method if the password is reset by the user to track the user’s password reset timestamp. If the password is changed by admin we can use doPostUpdateCredentialByAdmin method.
- Another use case is to customize the account locking policies. We can override the doPostAuthenticate method and increment the number of false login attempts as a user’s claim. Then in the doPreAuthenticate method, we can check if the maximum allowed false attempts count is reached.
- We can track the password history of the user and when the user tries to reset the password, We can implement not to allow the user to use an old password. We can do this by overriding doPreUpdateCredential method. In the doPostUpdateCredential method, we can add the new password to the history.
- Send an email to a user when the user account is created. We can override the doPostAddUser method and implement the email sending. If you worried about getting the username and email for this functionality, we can get the user’s username inside the method and the email can be read from the user’s claims.
- We can override the doPostAuthenticate method and set the timestamp as a user’s claim to track the last successful login attempt timestamp of a user.
As I mentioned earlier in this article now I will show you how to write a customized user operation event listener. I will take the example of when an authorized user trying to delete another user, we have to make sure not to delete system users. So let's implement this functionality.
- Create a new maven project.
- Next, we have to add relevant dependencies. Since we need to extend org.wso2.carbon.user.core.common.AbstractUserOperationEventListener class , we need to add org.wso2.carbon.user.core dependency. We also need to add OSGi dependency as we register this customization as a service.
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.core</artifactId>
<version>${carbon.kernel.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
- Let’s create class and extend AbstractUserOperationEventListener. And override doPreDeleteUserWithID method .
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.common.*;
/**
*
*/
public class CustomUserOperationEventListener extends AbstractUserOperationEventListener {
private String systemUserPrefix = "system_";
public CustomUserOperationEventListener() {
super();
}
@Override
public boolean doPreDeleteUserWithID
(String s, UserStoreManager userStoreManager) throws UserStoreException {
if (s.contains(systemUserPrefix)) {
return false;
} else {
return true;
}
}
@Override
public int getExecutionOrderId() {
return 9000;
}
@Override
public boolean doPreDeleteUser
(String s, UserStoreManager userStoreManager) throws UserStoreException {
if (s.contains(systemUserPrefix)) {
return false;
} else {
return true;
}
}
}
- Next, we need to register this as a service in the OSGI bundle.
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.*;
import org.wso2.carbon.user.core.listener.UserOperationEventListener;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.custom.user.operation.event.listener.CustomUserOperationEventListener;
@Component(
name = "org.wso2.custom.user.operation.event.listener",
immediate = true
)
public class CustomUserOperationEventListenerServiceComponent {
private static Log log = LogFactory.getLog(CustomUserOperationEventListenerServiceComponent.class);
private static RealmService realmService;
@Activate
protected void activate(ComponentContext context) {
BundleContext bundleContext = context.getBundleContext();
bundleContext.registerService(UserOperationEventListener.class.getName(), new CustomUserOperationEventListener(), null);
log.info("CustomUserOperationEventListener bundle activated successfully..");
}
@Deactivate
protected void deactivate(ComponentContext context) {
if (log.isDebugEnabled()) {
log.info("CustomUserOperationEventListener bundle is deactivated");
}
}
@Reference(
name = "user.realmservice.default",
service = RealmService.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetRealmService"
)
protected void setRealmService(RealmService realmService) {
log.debug("Setting the Realm Service");
CustomUserOperationEventListenerServiceComponent.realmService = realmService;
}
protected void unsetRealmService(RealmService realmService) {
log.debug("UnSetting the Realm Service");
CustomUserOperationEventListenerServiceComponent.realmService = null;
}
}
- Next, build this project by mvn clean install command. Then copy and paste this into <IS_HOME>/repository/components/dropins directory.
- Next step is to add configurations to deployment.toml file located in <IS_HOME>/repository/conf/ directory.
[[event_listener]]
id = “custom-user-operation-event-listener”
type = “org.wso2.carbon.user.core.listener.UserOperationEventListener”
name = “org.wso2.custom.user.operation.event.listener.CustomUserOperationEventListener”
order = 9000 (you can change this as your execusion order id)
enable = true
- Restart the server.
You can check whether this service is activated or not by searching “CustomUserOperationEventListener bundle activated successfully..” in either log or terminal.
As I mentioned in the beginning , there are two ways to implement custom user operation event listener. The other way is to extend the custom class by org.wso2.carbon.identity.core.AbstractIdentityUserOperationEventListener. The difference is you don’t need to add toml configurations if you extend by this class. The methods that you need to override are same.
If you trying to do the customization by extending this AbstractIdentityUserOperationEventListener you have to add the identity-framework dependency.
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.core</artifactId>
<version>${carbon.identity.framework.version}</version>
</dependency>
Hope you will also try both ways. The full implementation for both can be found below.
Hope you got an idea about implementing custom user operation event listeners. Thank you for reading this article.